pymmary 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.
@@ -0,0 +1,35 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "github-actions"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ commit-message:
8
+ prefix: "ci"
9
+ labels:
10
+ - "dependencies"
11
+ groups:
12
+ actions:
13
+ patterns:
14
+ - "*"
15
+
16
+ - package-ecosystem: "uv"
17
+ directory: "/"
18
+ schedule:
19
+ interval: "weekly"
20
+ commit-message:
21
+ prefix: "deps"
22
+ labels:
23
+ - "dependencies"
24
+ - "python"
25
+ # pytest appears in [project.optional-dependencies], which is the contract with
26
+ # anyone installing pymmary[pytest], not a development pin. Its floor is a
27
+ # researched compatibility boundary and moving it silently drops users. There
28
+ # is no way to scope dependabot to a single table, so pytest is off limits
29
+ # entirely; CI installs the newest one regardless, since every pin is a floor.
30
+ ignore:
31
+ - dependency-name: "pytest"
32
+ groups:
33
+ python-deps:
34
+ patterns:
35
+ - "*"
@@ -0,0 +1,166 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*"
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ validate:
13
+ name: Validate Release
14
+ runs-on: ubuntu-latest
15
+
16
+ outputs:
17
+ version: ${{ steps.version.outputs.version }}
18
+
19
+ steps:
20
+ - name: Checkout
21
+ uses: actions/checkout@v7
22
+ with:
23
+ fetch-depth: 0
24
+ persist-credentials: false
25
+
26
+ - name: Validate Branch
27
+ run: |
28
+ TAG_COMMIT=$(git rev-list -n 1 ${{ github.ref_name }})
29
+
30
+ if ! git merge-base --is-ancestor $TAG_COMMIT origin/master; then
31
+ echo "❌ ERROR: Tag ${{ github.ref_name }} is not on master branch"
32
+ echo "Tags must be created from commits that are on master."
33
+ exit 1
34
+ fi
35
+
36
+ echo "✅ Tag ${{ github.ref_name }} is on master branch"
37
+
38
+ - name: Extract Version
39
+ id: version
40
+ run: |
41
+ VERSION="${{ github.ref_name }}"
42
+ VERSION="${VERSION#v}"
43
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
44
+ echo "📦 Version: $VERSION"
45
+
46
+ - name: Validate Version Format
47
+ run: |
48
+ VERSION="${{ steps.version.outputs.version }}"
49
+ if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$'; then
50
+ echo "❌ ERROR: Invalid version format: $VERSION"
51
+ echo "Expected: X.Y.Z or X.Y.Z-suffix"
52
+ exit 1
53
+ fi
54
+ echo "✅ Version format valid: $VERSION"
55
+
56
+ build:
57
+ name: Build Package
58
+ runs-on: ubuntu-latest
59
+ needs: [validate]
60
+
61
+ steps:
62
+ - name: Checkout
63
+ uses: actions/checkout@v7
64
+ with:
65
+ fetch-depth: 0
66
+ persist-credentials: false
67
+
68
+ - name: Install uv
69
+ uses: astral-sh/setup-uv@v7
70
+
71
+ - name: Set up Python
72
+ uses: actions/setup-python@v7
73
+ with:
74
+ python-version: "3.12"
75
+
76
+ - name: Build Package
77
+ run: uv build
78
+
79
+ - name: Verify Package
80
+ run: |
81
+ echo "📦 Built packages:"
82
+ ls -la dist/
83
+
84
+ - name: Upload Artifacts
85
+ uses: actions/upload-artifact@v7
86
+ with:
87
+ name: dist
88
+ path: dist/
89
+ retention-days: 5
90
+
91
+ test-install:
92
+ name: Test Install (py${{ matrix.python-version }})
93
+ runs-on: ubuntu-latest
94
+ needs: [build]
95
+
96
+ strategy:
97
+ matrix:
98
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
99
+
100
+ steps:
101
+ - name: Set up Python ${{ matrix.python-version }}
102
+ uses: actions/setup-python@v7
103
+ with:
104
+ python-version: ${{ matrix.python-version }}
105
+
106
+ - name: Download Artifacts
107
+ uses: actions/download-artifact@v8
108
+ with:
109
+ name: dist
110
+ path: dist/
111
+
112
+ - name: Install Package
113
+ run: |
114
+ pip install dist/*.whl
115
+ pip show pymmary
116
+
117
+ publish-pypi:
118
+ name: Publish to PyPI
119
+ runs-on: ubuntu-latest
120
+ needs: [validate, build, test-install]
121
+ environment: pypi
122
+
123
+ permissions:
124
+ id-token: write
125
+
126
+ steps:
127
+ - name: Download Artifacts
128
+ uses: actions/download-artifact@v8
129
+ with:
130
+ name: dist
131
+ path: dist/
132
+
133
+ - name: Publish to PyPI
134
+ uses: pypa/gh-action-pypi-publish@release/v1
135
+ with:
136
+ print-hash: true
137
+
138
+ github-release:
139
+ name: Create GitHub Release
140
+ runs-on: ubuntu-latest
141
+ needs: [validate, publish-pypi]
142
+
143
+ permissions:
144
+ contents: write
145
+
146
+ steps:
147
+ - name: Checkout
148
+ uses: actions/checkout@v7
149
+ with:
150
+ persist-credentials: false
151
+
152
+ - name: Download Artifacts
153
+ uses: actions/download-artifact@v8
154
+ with:
155
+ name: dist
156
+ path: dist/
157
+
158
+ - name: Create Release
159
+ uses: softprops/action-gh-release@v3
160
+ with:
161
+ tag_name: ${{ github.ref_name }}
162
+ name: Release ${{ needs.validate.outputs.version }}
163
+ draft: false
164
+ prerelease: ${{ contains(github.ref_name, '-') }}
165
+ generate_release_notes: true
166
+ files: dist/*
@@ -0,0 +1,242 @@
1
+ name: Test
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+ branches: [master]
8
+ types: [opened, synchronize, reopened]
9
+
10
+ concurrency:
11
+ group: ${{ github.workflow }}-${{ github.ref }}
12
+ cancel-in-progress: true
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ jobs:
18
+ lint:
19
+ name: Lint
20
+ runs-on: ubuntu-latest
21
+
22
+ steps:
23
+ - name: Checkout
24
+ uses: actions/checkout@v7
25
+ with:
26
+ fetch-depth: 0
27
+ persist-credentials: false
28
+
29
+ - name: Install uv
30
+ uses: astral-sh/setup-uv@v7
31
+
32
+ - name: Set up Python
33
+ uses: actions/setup-python@v7
34
+ with:
35
+ python-version: "3.12"
36
+
37
+ - name: Install dependencies
38
+ run: uv sync --all-extras
39
+
40
+ - name: Check formatting
41
+ run: uv run ruff format --check src/ tests/
42
+
43
+ - name: Check linting
44
+ run: uv run ruff check src/ tests/
45
+
46
+ typecheck:
47
+ name: Type Check
48
+ runs-on: ubuntu-latest
49
+
50
+ steps:
51
+ - name: Checkout
52
+ uses: actions/checkout@v7
53
+ with:
54
+ fetch-depth: 0
55
+ persist-credentials: false
56
+
57
+ - name: Install uv
58
+ uses: astral-sh/setup-uv@v7
59
+
60
+ - name: Set up Python
61
+ uses: actions/setup-python@v7
62
+ with:
63
+ python-version: "3.12"
64
+
65
+ - name: Install dependencies
66
+ run: uv sync --all-extras
67
+
68
+ - name: Run mypy
69
+ run: uv run mypy src/pymmary
70
+
71
+ test:
72
+ name: Test (py${{ matrix.python-version }})
73
+ runs-on: ubuntu-latest
74
+ needs: [lint]
75
+
76
+ strategy:
77
+ fail-fast: false
78
+ matrix:
79
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
80
+
81
+ steps:
82
+ - name: Checkout
83
+ uses: actions/checkout@v7
84
+ with:
85
+ fetch-depth: 0
86
+ persist-credentials: false
87
+
88
+ - name: Install uv
89
+ uses: astral-sh/setup-uv@v7
90
+
91
+ - name: Set up Python ${{ matrix.python-version }}
92
+ uses: actions/setup-python@v7
93
+ with:
94
+ python-version: ${{ matrix.python-version }}
95
+
96
+ - name: Install dependencies
97
+ run: uv sync --all-extras
98
+
99
+ - name: Verify core does not import any adapter
100
+ # pymmary's core promises zero runtime dependencies. pytest is always
101
+ # present here (it is our test runner), so we cannot check for its
102
+ # absence the way pyssertive checks for django/httpx. What we can
103
+ # guarantee is that `import pymmary` never drags an adapter in.
104
+ # Append new adapter modules to `forbidden` as they are added.
105
+ run: |
106
+ uv run python - <<'PY'
107
+ import sys
108
+
109
+ forbidden = ("pytest", "unittest", "mypy")
110
+ before = set(sys.modules)
111
+ import pymmary # noqa: F401
112
+
113
+ pulled = [m for m in forbidden if m not in before and m in sys.modules]
114
+ if pulled:
115
+ print(f"ERROR: importing pymmary pulled in {pulled}")
116
+ sys.exit(1)
117
+ print("OK: pymmary core imports without any adapter")
118
+ PY
119
+
120
+ - name: Run tests
121
+ run: uv run pytest --cov=pymmary --cov-report=term-missing --cov-report=xml:coverage.xml --cov-fail-under=100
122
+
123
+ - name: Upload coverage artifact
124
+ if: matrix.python-version == '3.14'
125
+ uses: actions/upload-artifact@v7
126
+ with:
127
+ name: coverage-report
128
+ path: coverage.xml
129
+
130
+ test-min-pytest:
131
+ name: Test (oldest supported pytest, py${{ matrix.python-version }})
132
+ runs-on: ubuntu-latest
133
+ needs: [lint]
134
+
135
+ strategy:
136
+ fail-fast: false
137
+ matrix:
138
+ python-version: ["3.10", "3.14"]
139
+
140
+ steps:
141
+ - name: Checkout
142
+ uses: actions/checkout@v7
143
+ with:
144
+ fetch-depth: 0
145
+ persist-credentials: false
146
+
147
+ - name: Install uv
148
+ uses: astral-sh/setup-uv@v7
149
+
150
+ - name: Set up Python ${{ matrix.python-version }}
151
+ uses: actions/setup-python@v7
152
+ with:
153
+ python-version: ${{ matrix.python-version }}
154
+
155
+ - name: Install dependencies
156
+ run: uv sync --all-extras
157
+
158
+ - name: Pin pytest to the floor declared in pyproject
159
+ # The `test` matrix always resolves to the newest pytest, so nothing there
160
+ # can catch a broken floor. The adapter unregisters the terminal reporter,
161
+ # and before 9.1 that silently destroyed every assertion explanation —
162
+ # exactly the payload this library exists to produce.
163
+ # Bump this in lockstep with the `pytest` extra, never separately.
164
+ run: uv pip install "pytest==9.1.0"
165
+
166
+ - name: Run tests
167
+ # --no-sync or uv would resolve pytest straight back to latest.
168
+ # Coverage is enforced by the `test` job; this one is about compatibility.
169
+ run: uv run --no-sync pytest --cov-fail-under=0 --cov-report=
170
+
171
+ sonarcloud:
172
+ name: SonarCloud Analysis
173
+ runs-on: ubuntu-latest
174
+ needs: [test]
175
+
176
+ steps:
177
+ - name: Checkout
178
+ uses: actions/checkout@v7
179
+ with:
180
+ fetch-depth: 0
181
+ persist-credentials: false
182
+
183
+ - name: Download coverage artifact
184
+ uses: actions/download-artifact@v8
185
+ with:
186
+ name: coverage-report
187
+
188
+ - name: SonarCloud Scan
189
+ uses: SonarSource/sonarqube-scan-action@v8
190
+ env:
191
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
192
+
193
+ ci-success:
194
+ name: CI Success
195
+ runs-on: ubuntu-latest
196
+ needs: [lint, typecheck, test, test-min-pytest, sonarcloud]
197
+ if: always()
198
+
199
+ steps:
200
+ - name: Check all jobs
201
+ run: |
202
+ echo "===== Job Results ====="
203
+ echo "Lint: ${{ needs.lint.result }}"
204
+ echo "Typecheck: ${{ needs.typecheck.result }}"
205
+ echo "Test: ${{ needs.test.result }}"
206
+ echo "Test (min pytest): ${{ needs.test-min-pytest.result }}"
207
+ echo "SonarCloud: ${{ needs.sonarcloud.result }}"
208
+ echo "======================="
209
+
210
+ fail=0
211
+
212
+ if [ "${{ needs.lint.result }}" != "success" ]; then
213
+ echo "❌ Lint failed"
214
+ fail=1
215
+ fi
216
+
217
+ if [ "${{ needs.typecheck.result }}" != "success" ]; then
218
+ echo "❌ Typecheck failed"
219
+ fail=1
220
+ fi
221
+
222
+ if [ "${{ needs.test.result }}" != "success" ]; then
223
+ echo "❌ Tests failed"
224
+ fail=1
225
+ fi
226
+
227
+ if [ "${{ needs.test-min-pytest.result }}" != "success" ]; then
228
+ echo "❌ Tests on the oldest supported pytest failed"
229
+ fail=1
230
+ fi
231
+
232
+ if [ "${{ needs.sonarcloud.result }}" != "success" ]; then
233
+ echo "❌ SonarCloud failed"
234
+ fail=1
235
+ fi
236
+
237
+ if [ "$fail" -ne 0 ]; then
238
+ echo "🛑 Release blocked — fix the failures above."
239
+ exit 1
240
+ fi
241
+
242
+ echo "✅ All required checks passed — release-ready."
@@ -0,0 +1,90 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ env/
12
+ build/
13
+ develop-eggs/
14
+ dist/
15
+ downloads/
16
+ eggs/
17
+ .eggs/
18
+ lib/
19
+ lib64/
20
+ parts/
21
+ sdist/
22
+ var/
23
+ *.egg-info/
24
+ .installed.cfg
25
+ *.egg
26
+
27
+ # PyInstaller
28
+ # Usually these files are written by a python script from a template
29
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
30
+ *.manifest
31
+ *.spec
32
+
33
+ # Installer logs
34
+ pip-log.txt
35
+ pip-delete-this-directory.txt
36
+
37
+ # Unit test / coverage reports
38
+ htmlcov/
39
+ .tox/
40
+ .coverage
41
+ .coverage.*
42
+ .cache
43
+ nosetests.xml
44
+ coverage.xml
45
+ *,cover
46
+ .hypothesis/
47
+ .pytest_cache
48
+
49
+ # Translations
50
+ *.mo
51
+ *.pot
52
+
53
+ # Django stuff:
54
+ *.log
55
+ local_settings.py
56
+
57
+ # Flask instance folder
58
+ instance/
59
+
60
+ # Sphinx documentation
61
+ docs/_build/
62
+
63
+ # MkDocs documentation
64
+ /site/
65
+
66
+ # PyBuilder
67
+ target/
68
+
69
+ # IPython Notebook
70
+ .ipynb_checkpoints
71
+
72
+ # pyenv
73
+ .python-version
74
+
75
+ # virtual env
76
+ .venv/
77
+
78
+ # Auto-generated version file (hatch-vcs)
79
+ src/pymmary/_version.py
80
+
81
+ # uv lock file
82
+ uv.lock
83
+
84
+ # claude and other AI tool files
85
+ .claude/
86
+ .serena/
87
+ .omc/
88
+
89
+ # Mac OS files
90
+ .DS_Store
pymmary-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Unay Santisteban
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
pymmary-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,136 @@
1
+ Metadata-Version: 2.4
2
+ Name: pymmary
3
+ Version: 0.1.0
4
+ Summary: Agent-optimized output compressor for Python tooling.
5
+ Project-URL: Homepage, https://github.com/othercodes/pymmary
6
+ Project-URL: Repository, https://github.com/othercodes/pymmary.git
7
+ Project-URL: Issues, https://github.com/othercodes/pymmary/issues
8
+ Author-email: Unay Santisteban <usantisteban@othercode.io>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: ai-agents,compression,output,pytest,testing
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Framework :: Pytest
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Topic :: Software Development :: Testing
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.10
26
+ Provides-Extra: pytest
27
+ Requires-Dist: pytest>=9.1; extra == 'pytest'
28
+ Description-Content-Type: text/markdown
29
+
30
+ # pymmary
31
+
32
+ [![Build Status](https://github.com/othercodes/pymmary/actions/workflows/test.yml/badge.svg)](https://github.com/othercodes/pymmary/actions/workflows/test.yml)
33
+ [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=othercodes_pymmary&metric=coverage)](https://sonarcloud.io/summary/new_code?id=othercodes_pymmary)
34
+
35
+ Agent-optimized output compressor for Python tooling.
36
+
37
+ > **Status: early development.** The pytest adapter works; nothing is published to PyPI yet.
38
+
39
+ ## Why
40
+
41
+ When an AI agent runs `pytest`, it pays for output written for humans — progress dots, a full traceback per failure, colour codes, a summary table. A green run of a thousand tests tells the agent one thing ("everything passed") and charges thousands of tokens to say it.
42
+
43
+ Pymmary detects that a coding agent is running the tool and replaces that output with compact JSON. Outside an agent, nothing changes: no agent detected, no compression, byte-identical output for humans.
44
+
45
+ It is a decision of the *project*, not of the agent's environment. Add it as a dev dependency and any agent that clones the repo and runs `pytest` benefits, with no per-machine setup.
46
+
47
+ ## Features
48
+
49
+ - Automatic agent detection via environment variables — no configuration
50
+ - Strict no-op fallback: no agent, no change to output
51
+ - Compact JSON keyed by pytest `nodeid`, so failures are pasteable straight back into the CLI
52
+ - Zero runtime dependencies in the core; each adapter ships behind its own extra
53
+ - Hooks into the host tool's native extension points — no global monkey-patching
54
+
55
+ ## Requirements
56
+
57
+ - Python 3.10+
58
+ - pytest 9.1+ (optional, for `pymmary[pytest]`)
59
+
60
+ ## Installation
61
+
62
+ ```bash
63
+ pip install pymmary
64
+ ```
65
+
66
+ With the pytest adapter:
67
+
68
+ ```bash
69
+ pip install pymmary[pytest]
70
+ ```
71
+
72
+ ## Usage
73
+
74
+ Nothing to wire up. Install it as a dev dependency and run your tools as usual — when a supported agent is detected, output is compressed.
75
+
76
+ ```bash
77
+ pytest
78
+ ```
79
+
80
+ ```json
81
+ {"tool":"pytest","result":"passed","exit_code":0,"duration":0.32,"summary":{"collected":1002,"passed":1002}}
82
+ ```
83
+
84
+ On failure, only what the agent needs to act:
85
+
86
+ ```json
87
+ {
88
+ "tool": "pytest",
89
+ "result": "failed",
90
+ "exit_code": 1,
91
+ "duration": 0.32,
92
+ "summary": { "collected": 1002, "passed": 999, "failed": 2, "error": 1 },
93
+ "failures": [
94
+ {
95
+ "nodeid": "tests/test_api.py::TestAuth::test_login[user-2]",
96
+ "phase": "call",
97
+ "file": "tests/test_api.py",
98
+ "line": 42,
99
+ "type": "AssertionError",
100
+ "message": "assert 401 == 200"
101
+ }
102
+ ]
103
+ }
104
+ ```
105
+
106
+ A run that fails to collect is never reported as a pass — the verdict follows pytest's exit code, not our own tally:
107
+
108
+ ```json
109
+ {"tool":"pytest","result":"failed","exit_code":2,"duration":0.008,"summary":{"error":1},"failures":[{"nodeid":"test_broken.py","phase":"collect","file":"test_broken.py","line":1,"type":"ModuleNotFoundError","message":"No module named 'requests'"}]}
110
+ ```
111
+
112
+ ## Configuration
113
+
114
+ Two environment variables, no config file and no CLI flags:
115
+
116
+ | Variable | Effect |
117
+ |---|---|
118
+ | `PYMMARY_FORCE=1` | Compress even when no agent is detected — useful to see what an agent sees |
119
+ | `PYMMARY_MAX_FAILURES=N` | How many failures to spell out. Default 20; `0` keeps every one of them |
120
+
121
+ The cap is about diminishing returns, not size: an agent facing 400 failures fixes a handful and runs again, so the rest cost context and buy nothing. `summary` always counts the whole run, and whatever was left out is declared in `failures_omitted`. On a 400-failure suite: 98,671 bytes of human output, 56,826 uncapped, **2,900 by default**.
122
+
123
+ ## Limitations
124
+
125
+ - **pytest-xdist**: pymmary stands down completely under `-n`, leaving normal pytest output. The controller never runs the tests itself, so a compressed summary would count none of them. Aggregating the worker streams is planned.
126
+ - **pytest 9.1 is a hard floor.** The adapter unregisters pytest's terminal reporter to own the output. Before 9.1, pytest built assertion explanations through `config.get_terminal_writer()`, which asserts that reporter is still registered — so on older versions every `assert` failure degrades to a bare `AssertionError` pointing into pytest's internals. That is the one payload this library exists to produce, so the floor is enforced rather than worked around.
127
+
128
+ ## Related
129
+
130
+ Companion to [pyssertive](https://github.com/othercodes/pyssertive) (assert phase) and [pyrrange](https://github.com/othercodes/pyrrange) (arrange phase). Pymmary covers the report phase, for AI consumers.
131
+
132
+ Inspired by [laravel/pao](https://github.com/laravel/pao) — the PHP original. Pymmary keeps its envelope recognizable but speaks pytest's own vocabulary rather than PHPUnit's.
133
+
134
+ ## License
135
+
136
+ MIT