verb-authority 0.10.0b14__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 (71) hide show
  1. verb_authority-0.10.0b14/.github/ISSUE_TEMPLATE/bypass-or-tool-schema.yml +68 -0
  2. verb_authority-0.10.0b14/.github/ISSUE_TEMPLATE/schema-scan-feedback.yml +61 -0
  3. verb_authority-0.10.0b14/.github/workflows/ci.yml +441 -0
  4. verb_authority-0.10.0b14/.github/workflows/publish-to-pypi.yml +411 -0
  5. verb_authority-0.10.0b14/.github/workflows/release.yml +727 -0
  6. verb_authority-0.10.0b14/CHANGELOG.md +824 -0
  7. verb_authority-0.10.0b14/CODE_OF_CONDUCT.md +27 -0
  8. verb_authority-0.10.0b14/CONTRIBUTING.md +105 -0
  9. verb_authority-0.10.0b14/LANDSCAPE.md +149 -0
  10. verb_authority-0.10.0b14/LICENSE +201 -0
  11. verb_authority-0.10.0b14/MANIFEST.in +10 -0
  12. verb_authority-0.10.0b14/PKG-INFO +381 -0
  13. verb_authority-0.10.0b14/README.md +348 -0
  14. verb_authority-0.10.0b14/SECURITY.md +30 -0
  15. verb_authority-0.10.0b14/action.yml +73 -0
  16. verb_authority-0.10.0b14/adaptive.py +127 -0
  17. verb_authority-0.10.0b14/adversarial.py +121 -0
  18. verb_authority-0.10.0b14/agent_demo.py +107 -0
  19. verb_authority-0.10.0b14/atlas/README.md +40 -0
  20. verb_authority-0.10.0b14/atlas/public_mcp_report.md +143 -0
  21. verb_authority-0.10.0b14/atlas/public_mcp_schemas.json +194 -0
  22. verb_authority-0.10.0b14/benchmarks/README.md +60 -0
  23. verb_authority-0.10.0b14/benchmarks/__init__.py +1 -0
  24. verb_authority-0.10.0b14/benchmarks/agentdojo/README.md +74 -0
  25. verb_authority-0.10.0b14/benchmarks/export_agentdojo_schemas.py +169 -0
  26. verb_authority-0.10.0b14/benchmarks/run_schema_corpus.py +235 -0
  27. verb_authority-0.10.0b14/benchmarks/schema_corpus.json +372 -0
  28. verb_authority-0.10.0b14/capability_demo.py +44 -0
  29. verb_authority-0.10.0b14/chain_demo.py +84 -0
  30. verb_authority-0.10.0b14/docs/README.md +23 -0
  31. verb_authority-0.10.0b14/docs/assets/README.md +9 -0
  32. verb_authority-0.10.0b14/docs/assets/social-preview-legacy.png +0 -0
  33. verb_authority-0.10.0b14/docs/assets/social-preview.jpg +0 -0
  34. verb_authority-0.10.0b14/docs/case-studies/external-beta-risk-evidence.md +206 -0
  35. verb_authority-0.10.0b14/docs/case-studies/index.md +59 -0
  36. verb_authority-0.10.0b14/docs/javascript-typescript.md +143 -0
  37. verb_authority-0.10.0b14/docs/limits-and-boundaries.md +59 -0
  38. verb_authority-0.10.0b14/docs/runtime-gate.md +642 -0
  39. verb_authority-0.10.0b14/docs/schema-scanner.md +449 -0
  40. verb_authority-0.10.0b14/docs/security-model.md +150 -0
  41. verb_authority-0.10.0b14/fixtures/README.md +62 -0
  42. verb_authority-0.10.0b14/fixtures/avp9_nexus_expected.json +49 -0
  43. verb_authority-0.10.0b14/fixtures/avp9_nexus_financial_controls.json +66 -0
  44. verb_authority-0.10.0b14/fixtures/avp9_nexus_financial_tool.json +24 -0
  45. verb_authority-0.10.0b14/pydantic_ai_demo.py +502 -0
  46. verb_authority-0.10.0b14/pyproject.toml +90 -0
  47. verb_authority-0.10.0b14/resolve_live.py +101 -0
  48. verb_authority-0.10.0b14/scripts/installed_pydantic_smoke.py +412 -0
  49. verb_authority-0.10.0b14/scripts/installed_wheel_smoke.py +4603 -0
  50. verb_authority-0.10.0b14/scripts/verify_release_artifacts.py +3946 -0
  51. verb_authority-0.10.0b14/setup.cfg +4 -0
  52. verb_authority-0.10.0b14/test_agentdojo_export.py +39 -0
  53. verb_authority-0.10.0b14/test_authority_diff.py +4153 -0
  54. verb_authority-0.10.0b14/test_gate.py +2851 -0
  55. verb_authority-0.10.0b14/test_pydantic_ai_integration.py +4062 -0
  56. verb_authority-0.10.0b14/test_release_artifacts.py +3127 -0
  57. verb_authority-0.10.0b14/test_runtime_integration.py +4137 -0
  58. verb_authority-0.10.0b14/test_schema_corpus.py +52 -0
  59. verb_authority-0.10.0b14/test_schema_scan.py +4062 -0
  60. verb_authority-0.10.0b14/trusted_choice_demo.py +90 -0
  61. verb_authority-0.10.0b14/validate_v01.py +59 -0
  62. verb_authority-0.10.0b14/verb_authority.egg-info/PKG-INFO +381 -0
  63. verb_authority-0.10.0b14/verb_authority.egg-info/SOURCES.txt +69 -0
  64. verb_authority-0.10.0b14/verb_authority.egg-info/dependency_links.txt +1 -0
  65. verb_authority-0.10.0b14/verb_authority.egg-info/entry_points.txt +4 -0
  66. verb_authority-0.10.0b14/verb_authority.egg-info/requires.txt +8 -0
  67. verb_authority-0.10.0b14/verb_authority.egg-info/top_level.txt +4 -0
  68. verb_authority-0.10.0b14/verb_authority.py +4493 -0
  69. verb_authority-0.10.0b14/verb_authority_diff.py +3200 -0
  70. verb_authority-0.10.0b14/verb_authority_pydantic.py +5380 -0
  71. verb_authority-0.10.0b14/verb_authority_scan.py +3324 -0
@@ -0,0 +1,68 @@
1
+ name: Bypass report or tool schema
2
+ description: Share a minimal bypass, inference error, or sanitized real tool schema.
3
+ title: "[bypass/schema] "
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ Thanks for helping map the boundary. Do not include secrets, private data, or live targets. Use SECURITY.md for sensitive reports.
9
+
10
+ - type: dropdown
11
+ id: report-type
12
+ attributes:
13
+ label: Report type
14
+ options:
15
+ - Gate or provenance bypass
16
+ - Policy inference error
17
+ - Sanitized real tool schema
18
+ - Documentation or evaluation correction
19
+ validations:
20
+ required: true
21
+
22
+ - type: input
23
+ id: version
24
+ attributes:
25
+ label: Version or commit
26
+ placeholder: v0.9.0 or a commit SHA
27
+ validations:
28
+ required: true
29
+
30
+ - type: textarea
31
+ id: schema
32
+ attributes:
33
+ label: Minimal tool schema and provenance setup
34
+ description: Remove proprietary names and data. Show Param/Tool declarations and trusted_args or ledger setup.
35
+ render: python
36
+ validations:
37
+ required: true
38
+
39
+ - type: textarea
40
+ id: call
41
+ attributes:
42
+ label: Proposed tool call
43
+ description: Provide the smallest call that reproduces the result.
44
+ render: python
45
+ validations:
46
+ required: true
47
+
48
+ - type: textarea
49
+ id: result
50
+ attributes:
51
+ label: Observed and expected decisions
52
+ description: Include allow, needs_confirm, and reason when available, then explain what you expected.
53
+ validations:
54
+ required: true
55
+
56
+ - type: textarea
57
+ id: context
58
+ attributes:
59
+ label: Additional context
60
+ description: Note transformations, false positives, or why the schema is representative.
61
+
62
+ - type: checkboxes
63
+ id: safety
64
+ attributes:
65
+ label: Safety check
66
+ options:
67
+ - label: I removed credentials, private data, proprietary schema details, and live targets.
68
+ required: true
@@ -0,0 +1,61 @@
1
+ name: Schema scan feedback
2
+ description: Correct an inference, add a public schema, or share a redacted local report.
3
+ title: "[schema scan] "
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ Thanks for helping improve the authority map. Prefer a public source URL or a `--redact-names` report. Do not upload private schemas, credentials, runtime values, customer data, or production endpoints.
9
+
10
+ - type: dropdown
11
+ id: feedback-type
12
+ attributes:
13
+ label: Feedback type
14
+ options:
15
+ - Inferred policy correction
16
+ - Inferred verb-risk correction
17
+ - MCP annotation conflict
18
+ - Public Atlas schema addition
19
+ - Redacted local scan result
20
+ validations:
21
+ required: true
22
+
23
+ - type: input
24
+ id: version
25
+ attributes:
26
+ label: Verb Authority version or commit
27
+ placeholder: 0.10.0b14 or a commit SHA
28
+ validations:
29
+ required: true
30
+
31
+ - type: input
32
+ id: public-source
33
+ attributes:
34
+ label: Immutable public source URL, if applicable
35
+ description: Link to a specific commit rather than a moving branch.
36
+ placeholder: https://github.com/owner/repo/blob/<commit>/path
37
+
38
+ - type: textarea
39
+ id: finding
40
+ attributes:
41
+ label: Minimal finding or redacted report excerpt
42
+ description: Include inferred risk/policy, the expected classification, and the reason. A `--redact-names` excerpt is welcome.
43
+ validations:
44
+ required: true
45
+
46
+ - type: textarea
47
+ id: reproduction
48
+ attributes:
49
+ label: Reproduction command
50
+ description: Show the local scan command and report format; do not include private paths.
51
+ placeholder: python -m verb_authority scan tools.json --redact-names --format json
52
+
53
+ - type: checkboxes
54
+ id: safety
55
+ attributes:
56
+ label: Safety check
57
+ options:
58
+ - label: I reviewed this submission and removed private schemas, credentials, runtime values, customer data, and production endpoints.
59
+ required: true
60
+ - label: I understand that an inference mismatch is not automatically a vulnerability in the upstream project.
61
+ required: true
@@ -0,0 +1,441 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ test:
13
+ name: Python ${{ matrix.python-version }}
14
+ runs-on: ubuntu-latest
15
+ strategy:
16
+ fail-fast: false
17
+ matrix:
18
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
19
+
20
+ steps:
21
+ - name: Check out repository
22
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
23
+ with:
24
+ persist-credentials: false
25
+
26
+ - name: Set up Python
27
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
28
+ with:
29
+ python-version: ${{ matrix.python-version }}
30
+
31
+ - name: Install package and test dependency
32
+ run: python -m pip install "setuptools>=77" ".[dev]"
33
+
34
+ - name: Run complete test suite
35
+ run: python -m pytest -v
36
+
37
+ pydantic-integration:
38
+ name: Pydantic AI 2.35 adapter
39
+ runs-on: ubuntu-latest
40
+
41
+ steps:
42
+ - name: Check out repository
43
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
44
+ with:
45
+ persist-credentials: false
46
+
47
+ - name: Set up Python
48
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
49
+ with:
50
+ python-version: "3.12"
51
+
52
+ - name: Install the optional Pydantic AI integration
53
+ run: python -m pip install "setuptools>=77" ".[dev,pydantic]"
54
+
55
+ - name: Run the Pydantic AI integration contract
56
+ run: python -m pytest -v test_pydantic_ai_integration.py
57
+
58
+ package:
59
+ name: Build and smoke-test distribution
60
+ runs-on: ubuntu-latest
61
+
62
+ steps:
63
+ - name: Check out repository
64
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
65
+ with:
66
+ persist-credentials: false
67
+
68
+ - name: Set up Python
69
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
70
+ with:
71
+ python-version: "3.12"
72
+
73
+ - name: Bind verifier and source contract to the exact commit
74
+ env:
75
+ EXPECTED_SOURCE_COMMIT: ${{ github.sha }}
76
+ run: |
77
+ while IFS= read -r variable; do
78
+ case "$variable" in
79
+ GIT_CONFIG_*|GIT_TRACE*|LD_*|DYLD_*|GCONV_PATH|LOCPATH|PYTHONPATH|PYTHONHOME)
80
+ unset "$variable" ;;
81
+ esac
82
+ done < <(compgen -e)
83
+ unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE GIT_OBJECT_DIRECTORY
84
+ unset GIT_ALTERNATE_OBJECT_DIRECTORIES GIT_COMMON_DIR GIT_NAMESPACE
85
+ unset GIT_REPLACE_REF_BASE GIT_GRAFT_FILE GIT_SHALLOW_FILE GIT_EXEC_PATH
86
+ unset GIT_PREFIX GIT_QUARANTINE_PATH GIT_EXTERNAL_DIFF GIT_DIFF_OPTS GIT_PAGER
87
+ export PATH=/usr/bin:/bin
88
+ export LC_ALL=C
89
+ export LANG=C
90
+ export GIT_CONFIG_NOSYSTEM=1
91
+ export GIT_CONFIG_GLOBAL=/dev/null
92
+ export GIT_NO_REPLACE_OBJECTS=1
93
+ export GIT_NO_LAZY_FETCH=1
94
+ export GIT_LITERAL_PATHSPECS=1
95
+ export GIT_TERMINAL_PROMPT=0
96
+ test -x /usr/bin/git && test ! -L /usr/bin/git
97
+ test "$(stat -c %u /usr/bin/git)" -eq 0
98
+ git_mode=$(stat -c %a -- /usr/bin/git)
99
+ [[ "$git_mode" =~ ^[0-7]{3,4}$ ]]
100
+ test "$((8#$git_mode & 8#022))" -eq 0
101
+ python_safe=$(readlink -f /usr/bin/python3)
102
+ case "$python_safe" in /usr/bin/python3|/usr/bin/python3.*) ;; *) exit 1 ;; esac
103
+ test -x "$python_safe" && test ! -L "$python_safe"
104
+ test "$(stat -c %u "$python_safe")" -eq 0
105
+ python_mode=$(stat -c %a -- "$python_safe")
106
+ [[ "$python_mode" =~ ^[0-7]{3,4}$ ]]
107
+ test "$((8#$python_mode & 8#022))" -eq 0
108
+ git_safe=(/usr/bin/git --no-pager --no-replace-objects \
109
+ -c core.fsmonitor=false -c core.hooksPath=/dev/null -c diff.external= \
110
+ -c protocol.allow=never -c protocol.ext.allow=never \
111
+ -c protocol.file.allow=never)
112
+ source_commit=$("${git_safe[@]}" rev-parse --verify 'HEAD^{commit}')
113
+ test "$source_commit" = "$EXPECTED_SOURCE_COMMIT"
114
+ verifier="${{ runner.temp }}/verify_release_artifacts.py"
115
+ verifier_blob=$("$python_safe" -I - \
116
+ "$source_commit" "$verifier" "${{ github.workspace }}" <<'PY'
117
+ import hashlib
118
+ import re
119
+ import subprocess
120
+ import sys
121
+
122
+ source_commit, output_path, repository = sys.argv[1:]
123
+ if re.fullmatch(r"[0-9a-f]{40}|[0-9a-f]{64}", source_commit) is None:
124
+ raise SystemExit("invalid exact source commit")
125
+ hexadecimal_size = len(source_commit)
126
+ git = [
127
+ "/usr/bin/git", "--no-pager", "--no-replace-objects",
128
+ "-c", "core.fsmonitor=false", "-c", "core.hooksPath=/dev/null",
129
+ "-c", "diff.external=", "-c", "protocol.allow=never",
130
+ "-c", "protocol.ext.allow=never", "-c", "protocol.file.allow=never",
131
+ "-C", repository,
132
+ ]
133
+
134
+ def read_object(kind, object_id, limit):
135
+ process = subprocess.Popen(
136
+ [*git, "cat-file", kind, object_id],
137
+ stdin=subprocess.DEVNULL,
138
+ stdout=subprocess.PIPE,
139
+ stderr=subprocess.DEVNULL,
140
+ )
141
+ assert process.stdout is not None
142
+ payload = process.stdout.read(limit + 1)
143
+ if len(payload) > limit:
144
+ process.kill()
145
+ process.wait()
146
+ raise SystemExit(f"oversized git {kind} object")
147
+ if process.wait() != 0:
148
+ raise SystemExit(f"cannot read git {kind} object")
149
+ framed = f"{kind} {len(payload)}\0".encode("ascii") + payload
150
+ digest = (
151
+ hashlib.sha1(framed).hexdigest()
152
+ if hexadecimal_size == 40
153
+ else hashlib.sha256(framed).hexdigest()
154
+ )
155
+ if digest != object_id:
156
+ raise SystemExit(f"git {kind} object hash mismatch")
157
+ return payload
158
+
159
+ def tree_entries(tree_id):
160
+ payload = read_object("tree", tree_id, 16 * 1024 * 1024)
161
+ width = hexadecimal_size // 2
162
+ position = 0
163
+ while position < len(payload):
164
+ space = payload.find(b" ", position)
165
+ nul = payload.find(b"\0", space + 1)
166
+ end = nul + 1 + width
167
+ if space <= position or nul <= space + 1 or end > len(payload):
168
+ raise SystemExit("malformed git tree")
169
+ yield (
170
+ payload[position:space],
171
+ payload[space + 1:nul],
172
+ payload[nul + 1:end].hex(),
173
+ )
174
+ position = end
175
+
176
+ commit = read_object("commit", source_commit, 16 * 1024 * 1024)
177
+ trees = [
178
+ line[5:].decode("ascii")
179
+ for line in commit.split(b"\n\n", 1)[0].splitlines()
180
+ if line.startswith(b"tree ")
181
+ ]
182
+ if len(trees) != 1:
183
+ raise SystemExit("commit has no unique tree")
184
+ current = trees[0]
185
+ for component, expected_mode in (
186
+ (b"scripts", b"40000"),
187
+ (b"verify_release_artifacts.py", b"100644"),
188
+ ):
189
+ matches = [entry for entry in tree_entries(current) if entry[1] == component]
190
+ if len(matches) != 1 or matches[0][0].lstrip(b"0") != expected_mode:
191
+ raise SystemExit("verifier path is absent or has an unsafe mode")
192
+ current = matches[0][2]
193
+ verifier_payload = read_object("blob", current, 16 * 1024 * 1024)
194
+ with open(output_path, "xb") as output:
195
+ output.write(verifier_payload)
196
+ print(current)
197
+ PY
198
+ )
199
+ test "$("${git_safe[@]}" hash-object --no-filters "$verifier")" = "$verifier_blob"
200
+ printf 'SOURCE_COMMIT=%s\n' "$source_commit" >> "$GITHUB_ENV"
201
+ printf 'RELEASE_VERIFIER=%s\n' "$verifier" >> "$GITHUB_ENV"
202
+
203
+ - name: Install build frontend
204
+ run: python -m pip install "build>=1.2,<2" "setuptools>=77"
205
+
206
+ - name: Build source distribution
207
+ run: python -m build --sdist
208
+
209
+ - name: Verify source distribution before extraction
210
+ run: |
211
+ python -I "$RELEASE_VERIFIER" sdist \
212
+ --project pyproject.toml \
213
+ --dist dist \
214
+ --repository "${{ github.workspace }}" \
215
+ --source-commit "$SOURCE_COMMIT"
216
+
217
+ - name: Extract and inspect source distribution
218
+ run: |
219
+ python -I "$RELEASE_VERIFIER" extract-sdist \
220
+ --project pyproject.toml \
221
+ --dist dist \
222
+ --output "${{ runner.temp }}/verb-authority-sdist" \
223
+ --repository "${{ github.workspace }}" \
224
+ --source-commit "$SOURCE_COMMIT"
225
+ sdist_root=$(find "${{ runner.temp }}/verb-authority-sdist" \
226
+ -mindepth 1 -maxdepth 1 -type d -print -quit)
227
+ test -n "$sdist_root"
228
+ test -f "$sdist_root/LANDSCAPE.md"
229
+ test -f "$sdist_root/docs/assets/social-preview.jpg"
230
+ test -f "$sdist_root/pydantic_ai_demo.py"
231
+ test -f "$sdist_root/scripts/installed_pydantic_smoke.py"
232
+ test -f "$sdist_root/scripts/installed_wheel_smoke.py"
233
+ test -f "$sdist_root/scripts/verify_release_artifacts.py"
234
+ printf 'SDIST_ROOT=%s\n' "$sdist_root" >> "$GITHUB_ENV"
235
+
236
+ - name: Test extracted source distribution
237
+ run: |
238
+ python -m pip install "$SDIST_ROOT[dev]"
239
+ cd "$SDIST_ROOT"
240
+ python -m pytest -v
241
+
242
+ - name: Build wheel from extracted source distribution
243
+ run: python -m build --wheel --outdir "${{ github.workspace }}/dist" "$SDIST_ROOT"
244
+
245
+ - name: Verify distribution identities
246
+ run: |
247
+ python -I "$RELEASE_VERIFIER" artifacts \
248
+ --project pyproject.toml \
249
+ --dist "${{ github.workspace }}/dist" \
250
+ --repository "${{ github.workspace }}" \
251
+ --source-commit "$SOURCE_COMMIT"
252
+
253
+ - name: Generate and verify SHA-256 checksums
254
+ working-directory: dist
255
+ run: |
256
+ sha256sum *.whl *.tar.gz > SHA256SUMS
257
+ sha256sum --check SHA256SUMS
258
+
259
+ - name: Install the built wheel
260
+ run: |
261
+ env -u PYTHONPATH -u PYTHONHOME \
262
+ python -I -m pip install --force-reinstall --no-deps dist/*.whl
263
+
264
+ - name: Smoke-test installed commands
265
+ working-directory: ${{ runner.temp }}
266
+ run: |
267
+ printf '%s\n' \
268
+ 'from pathlib import Path' \
269
+ 'Path("installed-smoke-shadow.marker").write_text("temp module imported", encoding="utf-8")' \
270
+ 'raise SystemExit(0)' \
271
+ > verb_authority.py
272
+ env -u PYTHONHOME PYTHONPATH="${{ runner.temp }}" \
273
+ python -I -m verb_authority
274
+ env -u PYTHONPATH -u PYTHONHOME \
275
+ verb-authority scan --help
276
+ env -u PYTHONPATH -u PYTHONHOME \
277
+ verb-authority-scan "$SDIST_ROOT/atlas/public_mcp_schemas.json" \
278
+ --format json --output authority-report.json
279
+ env -u PYTHONPATH -u PYTHONHOME \
280
+ verb-authority diff "$SDIST_ROOT/atlas/public_mcp_schemas.json" \
281
+ "$SDIST_ROOT/atlas/public_mcp_schemas.json" \
282
+ --fail-on-increase
283
+ env -u PYTHONPATH -u PYTHONHOME \
284
+ verb-authority-diff authority-report.json authority-report.json \
285
+ --format json --output authority-diff.json
286
+ cp "$SDIST_ROOT/scripts/installed_wheel_smoke.py" installed_wheel_smoke.py
287
+ expected_version=$(env -u PYTHONPATH -u PYTHONHOME python -I -c 'import sys, tomllib; print(tomllib.load(open(sys.argv[1], "rb"))["project"]["version"])' "$SDIST_ROOT/pyproject.toml")
288
+ env -u PYTHONPATH -u PYTHONHOME \
289
+ python -I installed_wheel_smoke.py \
290
+ --expected-version "$expected_version" \
291
+ --forbid-root "${{ github.workspace }}" \
292
+ --forbid-root "$SDIST_ROOT"
293
+ env -u PYTHONPATH -u PYTHONHOME \
294
+ python -I -c 'import json; assert json.load(open("authority-report.json"))["summary"]["tools"] == 10'
295
+ env -u PYTHONPATH -u PYTHONHOME \
296
+ python -I -c 'import json; assert json.load(open("authority-diff.json"))["summary"]["changes"] == 0'
297
+ env -u PYTHONPATH -u PYTHONHOME \
298
+ python -I -m pip check
299
+ test ! -e installed-smoke-shadow.marker
300
+
301
+ - name: Install and smoke-test the optional Pydantic AI adapter
302
+ run: |
303
+ shopt -s nullglob
304
+ wheels=(dist/*.whl)
305
+ test "${#wheels[@]}" -eq 1
306
+ env -u PYTHONPATH -u PYTHONHOME \
307
+ python -I -m pip install --force-reinstall "${wheels[0]}[pydantic]"
308
+ cp "$SDIST_ROOT/scripts/installed_pydantic_smoke.py" \
309
+ "${{ runner.temp }}/installed_pydantic_smoke.py"
310
+ expected_version=$(env -u PYTHONPATH -u PYTHONHOME python -I -c 'import sys, tomllib; print(tomllib.load(open(sys.argv[1], "rb"))["project"]["version"])' "$SDIST_ROOT/pyproject.toml")
311
+ cd "${{ runner.temp }}"
312
+ env -u PYTHONPATH -u PYTHONHOME \
313
+ python -I installed_pydantic_smoke.py \
314
+ --expected-version "$expected_version" \
315
+ --forbid-root "${{ github.workspace }}" \
316
+ --forbid-root "$SDIST_ROOT"
317
+ env -u PYTHONPATH -u PYTHONHOME python -I -m pip check
318
+
319
+ - name: Smoke-test local composite action
320
+ uses: ./
321
+ with:
322
+ before: ${{ github.workspace }}/atlas/public_mcp_schemas.json
323
+ after: ${{ github.workspace }}/atlas/public_mcp_schemas.json
324
+ fail_on_increase: "true"
325
+ # The Atlas intentionally preserves known review debt. This step
326
+ # verifies packaging and an identity comparison; the dedicated
327
+ # action-shadowing job below verifies fail_on_review itself.
328
+ fail_on_review: "false"
329
+
330
+ - name: Preserve distributions
331
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
332
+ with:
333
+ name: python-distributions
334
+ path: dist/
335
+ if-no-files-found: error
336
+
337
+ action-shadowing:
338
+ name: Composite action ignores consumer module shadowing
339
+ runs-on: ubuntu-latest
340
+ env:
341
+ PYTHONPATH: .
342
+
343
+ steps:
344
+ - name: Check out action below the consumer workspace
345
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
346
+ with:
347
+ path: verb-authority-action
348
+ persist-credentials: false
349
+
350
+ - name: Set up Python
351
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
352
+ with:
353
+ python-version: "3.12"
354
+
355
+ - name: Create a hostile consumer module and a widening schema diff
356
+ shell: bash
357
+ run: |
358
+ printf '%s\n' \
359
+ 'from pathlib import Path' \
360
+ 'Path("shadow-imported.marker").write_text("consumer module ran", encoding="utf-8")' \
361
+ 'raise SystemExit(0)' \
362
+ > verb_authority.py
363
+ printf '%s\n' \
364
+ 'from pathlib import Path' \
365
+ 'Path("pip-shadow-imported.marker").write_text("consumer pip module ran", encoding="utf-8")' \
366
+ 'raise SystemExit(0)' \
367
+ > pip.py
368
+ printf '%s\n' \
369
+ '{"tools":[{"name":"set_limit","inputSchema":{"type":"object","properties":{"amount":{"type":"number","maximum":10}},"required":["amount"]}}]}' \
370
+ > before.json
371
+ printf '%s\n' \
372
+ '{"tools":[{"name":"set_limit","inputSchema":{"type":"object","properties":{"amount":{"type":"number","maximum":10}},"required":["amount"]}}]}' \
373
+ > ./--help
374
+ printf '%s\n' \
375
+ '{"tools":[{"name":"set_limit","inputSchema":{"type":"object","properties":{"amount":{"type":"number","maximum":100}},"required":["amount"]}}]}' \
376
+ > after.json
377
+ printf '%s\n' \
378
+ '{"tools":[{"name":"set_limit","inputSchema":{"type":"object","properties":{"amount":{"type":"number","maximum":1}},"required":["amount"]}}]}' \
379
+ > option-before.json
380
+ printf '%s\n' \
381
+ '{"tools":[{"name":"set_limit","inputSchema":{"type":"object","properties":{"amount":{"type":"number","minimum":0}},"required":["amount"]}}]}' \
382
+ > review-before.json
383
+ printf '%s\n' \
384
+ '{"tools":[{"name":"set_limit","inputSchema":{"type":"object","properties":{"amount":{"type":"number","minimum":-100}},"required":["amount"]}}]}' \
385
+ > review-after.json
386
+
387
+ - name: Run the composite action from the hostile workspace
388
+ id: authority-diff
389
+ continue-on-error: true
390
+ uses: ./verb-authority-action
391
+ with:
392
+ before: before.json
393
+ after: after.json
394
+ fail_on_increase: "true"
395
+ fail_on_review: "false"
396
+
397
+ - name: Run the composite action on review-only drift
398
+ id: authority-review
399
+ continue-on-error: true
400
+ uses: ./verb-authority-action
401
+ with:
402
+ before: review-before.json
403
+ after: review-after.json
404
+ fail_on_increase: "false"
405
+ fail_on_review: "true"
406
+
407
+ - name: Treat an option-looking baseline as a schema path
408
+ id: authority-option-before
409
+ continue-on-error: true
410
+ uses: ./verb-authority-action
411
+ with:
412
+ before: --help
413
+ after: after.json
414
+ fail_on_increase: "true"
415
+ fail_on_review: "false"
416
+
417
+ - name: Treat an option-looking candidate as a schema path
418
+ id: authority-option-after
419
+ continue-on-error: true
420
+ uses: ./verb-authority-action
421
+ with:
422
+ before: option-before.json
423
+ after: --help
424
+ fail_on_increase: "true"
425
+ fail_on_review: "false"
426
+
427
+ - name: Verify the installed CLI enforced both real thresholds
428
+ if: always()
429
+ shell: bash
430
+ env:
431
+ INCREASE_OUTCOME: ${{ steps.authority-diff.outcome }}
432
+ REVIEW_OUTCOME: ${{ steps.authority-review.outcome }}
433
+ OPTION_BEFORE_OUTCOME: ${{ steps.authority-option-before.outcome }}
434
+ OPTION_AFTER_OUTCOME: ${{ steps.authority-option-after.outcome }}
435
+ run: |
436
+ test "$INCREASE_OUTCOME" = "failure"
437
+ test "$REVIEW_OUTCOME" = "failure"
438
+ test "$OPTION_BEFORE_OUTCOME" = "failure"
439
+ test "$OPTION_AFTER_OUTCOME" = "failure"
440
+ test ! -e shadow-imported.marker
441
+ test ! -e pip-shadow-imported.marker