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.
- verb_authority-0.10.0b14/.github/ISSUE_TEMPLATE/bypass-or-tool-schema.yml +68 -0
- verb_authority-0.10.0b14/.github/ISSUE_TEMPLATE/schema-scan-feedback.yml +61 -0
- verb_authority-0.10.0b14/.github/workflows/ci.yml +441 -0
- verb_authority-0.10.0b14/.github/workflows/publish-to-pypi.yml +411 -0
- verb_authority-0.10.0b14/.github/workflows/release.yml +727 -0
- verb_authority-0.10.0b14/CHANGELOG.md +824 -0
- verb_authority-0.10.0b14/CODE_OF_CONDUCT.md +27 -0
- verb_authority-0.10.0b14/CONTRIBUTING.md +105 -0
- verb_authority-0.10.0b14/LANDSCAPE.md +149 -0
- verb_authority-0.10.0b14/LICENSE +201 -0
- verb_authority-0.10.0b14/MANIFEST.in +10 -0
- verb_authority-0.10.0b14/PKG-INFO +381 -0
- verb_authority-0.10.0b14/README.md +348 -0
- verb_authority-0.10.0b14/SECURITY.md +30 -0
- verb_authority-0.10.0b14/action.yml +73 -0
- verb_authority-0.10.0b14/adaptive.py +127 -0
- verb_authority-0.10.0b14/adversarial.py +121 -0
- verb_authority-0.10.0b14/agent_demo.py +107 -0
- verb_authority-0.10.0b14/atlas/README.md +40 -0
- verb_authority-0.10.0b14/atlas/public_mcp_report.md +143 -0
- verb_authority-0.10.0b14/atlas/public_mcp_schemas.json +194 -0
- verb_authority-0.10.0b14/benchmarks/README.md +60 -0
- verb_authority-0.10.0b14/benchmarks/__init__.py +1 -0
- verb_authority-0.10.0b14/benchmarks/agentdojo/README.md +74 -0
- verb_authority-0.10.0b14/benchmarks/export_agentdojo_schemas.py +169 -0
- verb_authority-0.10.0b14/benchmarks/run_schema_corpus.py +235 -0
- verb_authority-0.10.0b14/benchmarks/schema_corpus.json +372 -0
- verb_authority-0.10.0b14/capability_demo.py +44 -0
- verb_authority-0.10.0b14/chain_demo.py +84 -0
- verb_authority-0.10.0b14/docs/README.md +23 -0
- verb_authority-0.10.0b14/docs/assets/README.md +9 -0
- verb_authority-0.10.0b14/docs/assets/social-preview-legacy.png +0 -0
- verb_authority-0.10.0b14/docs/assets/social-preview.jpg +0 -0
- verb_authority-0.10.0b14/docs/case-studies/external-beta-risk-evidence.md +206 -0
- verb_authority-0.10.0b14/docs/case-studies/index.md +59 -0
- verb_authority-0.10.0b14/docs/javascript-typescript.md +143 -0
- verb_authority-0.10.0b14/docs/limits-and-boundaries.md +59 -0
- verb_authority-0.10.0b14/docs/runtime-gate.md +642 -0
- verb_authority-0.10.0b14/docs/schema-scanner.md +449 -0
- verb_authority-0.10.0b14/docs/security-model.md +150 -0
- verb_authority-0.10.0b14/fixtures/README.md +62 -0
- verb_authority-0.10.0b14/fixtures/avp9_nexus_expected.json +49 -0
- verb_authority-0.10.0b14/fixtures/avp9_nexus_financial_controls.json +66 -0
- verb_authority-0.10.0b14/fixtures/avp9_nexus_financial_tool.json +24 -0
- verb_authority-0.10.0b14/pydantic_ai_demo.py +502 -0
- verb_authority-0.10.0b14/pyproject.toml +90 -0
- verb_authority-0.10.0b14/resolve_live.py +101 -0
- verb_authority-0.10.0b14/scripts/installed_pydantic_smoke.py +412 -0
- verb_authority-0.10.0b14/scripts/installed_wheel_smoke.py +4603 -0
- verb_authority-0.10.0b14/scripts/verify_release_artifacts.py +3946 -0
- verb_authority-0.10.0b14/setup.cfg +4 -0
- verb_authority-0.10.0b14/test_agentdojo_export.py +39 -0
- verb_authority-0.10.0b14/test_authority_diff.py +4153 -0
- verb_authority-0.10.0b14/test_gate.py +2851 -0
- verb_authority-0.10.0b14/test_pydantic_ai_integration.py +4062 -0
- verb_authority-0.10.0b14/test_release_artifacts.py +3127 -0
- verb_authority-0.10.0b14/test_runtime_integration.py +4137 -0
- verb_authority-0.10.0b14/test_schema_corpus.py +52 -0
- verb_authority-0.10.0b14/test_schema_scan.py +4062 -0
- verb_authority-0.10.0b14/trusted_choice_demo.py +90 -0
- verb_authority-0.10.0b14/validate_v01.py +59 -0
- verb_authority-0.10.0b14/verb_authority.egg-info/PKG-INFO +381 -0
- verb_authority-0.10.0b14/verb_authority.egg-info/SOURCES.txt +69 -0
- verb_authority-0.10.0b14/verb_authority.egg-info/dependency_links.txt +1 -0
- verb_authority-0.10.0b14/verb_authority.egg-info/entry_points.txt +4 -0
- verb_authority-0.10.0b14/verb_authority.egg-info/requires.txt +8 -0
- verb_authority-0.10.0b14/verb_authority.egg-info/top_level.txt +4 -0
- verb_authority-0.10.0b14/verb_authority.py +4493 -0
- verb_authority-0.10.0b14/verb_authority_diff.py +3200 -0
- verb_authority-0.10.0b14/verb_authority_pydantic.py +5380 -0
- 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
|