sorry
sorry marks unfinished Python code as structured debt that CI can find and refuse to ship.
It is not a productivity aid. It is a nagging conscience for the build pipeline.
Standard TODO comments are a graveyard of good intentions. sorry makes them
load-bearing: structured metadata, an AST scanner, and a calendar.
Install
pip install pysorry
The distribution is pysorry. The import is sorry.
Mark the hole
Two markers. Use whichever fits the shape of the unfinished thing.
Runtime marker
from sorry import sorry
def reconcile_authority(step):
return sorry(
"corrective monotonicity not discharged",
issue="AG-27",
owner="agent-gov",
expires="2026-06-01",
)
sorry(...) always raises UnprovenObligation (an AssertionError subclass).
The return type is NoReturn, so type checkers treat anything after it as
unreachable.
Decorator marker
from sorry import unfinished
@unfinished(
"corrective monotonicity not discharged",
issue="AG-27",
expires="2026-06-01",
)
def corrective_never_mints_authority(step) -> bool:
...
The decorator runs cleanly at import time. The wrapped function raises
UnprovenObligation when called.
Scan the hole
sorry list src tests
Lists every obligation. Always exits 0.
sorry check src tests
Exits 1 if any obligation exists. No warn-only mode. No per-path policy file. The tool is binary by design: either the debt is discharged, or the build is broken.
Metadata
All fields are optional except the reason. All values must be string literals when scanned — non-literal metadata is reported as invalid.
| field | meaning |
|---|---|
reason |
what is unfinished |
issue |
tracker ID or URL |
owner |
who is responsible |
expires |
ISO date YYYY-MM-DD; valid through that date |
basis |
informal justification or reference |
Status
Each obligation lands in one of three buckets:
- OPEN — unresolved, not yet expired
- EXPIRED —
today > expires - INVALID — non-literal metadata, malformed
expires, missing reason, etc.
sorry check fails on any non-empty bucket. Expired and invalid are visually
separated in output, but they fail the same gate.
Scanner limitations
The V0 scanner is import-aware but not scope-aware. It tracks
from sorry import … and import sorry [as …] at the module level, but it
does not model lexical scope. Two consequences:
- A function-local import (
def f(): from sorry import sorry as s) leaks its alias to the rest of the module — calls tos(...)in sibling functions will be reported as obligations. - A parameter or local variable named
sorry(or matching an alias) shadows the import at runtime, but the scanner will still flag calls to it.
In practice this means: keep sorry imports at the top of the file and
don't shadow them with locals. Scope-aware resolution may land in a later
release; until then, the limitation is documented and the false-positive
shape is predictable.
Non-goals
- No
--warn-onlyflag. - No
pyproject.tomlpolicy section. - No per-path config.
- No pytest plugin.
- No "discharge" subcommand. To discharge an obligation, delete the call.
The tool is meant to stay tiny. If you need governance, build it elsewhere.
License
Licensed under Apache-2.0. See LICENSE and NOTICE.
Metadata
Release files for pysorry 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| pysorry-0.1.0.tar.gz | 16.1 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pysorry-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 28.2 kB
Release files / pysorry-0.1.0.tar.gz
| Download URL | pysorry-0.1.0.tar.gz |
|---|---|
| Size | 16.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
57fb1bd58d07f136b5034f42fc5c7612490e799e490e4662af8049b0f32a5ae8
|
|
BLAKE2b-256 checksum How to use checksums |
bfa48bd647b69231a742d5e4f1188c1885e83b003521abab5704f1ddd6eab4b1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 1, 2026.
Transparency logRelease files / pysorry-0.1.0-py3-none-any.whl
| Download URL | pysorry-0.1.0-py3-none-any.whl |
|---|---|
| Size | 12.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7314b1b65875350e87176e49d3526ff21e417d87856004662b1641700aacfb5d
|
|
BLAKE2b-256 checksum How to use checksums |
69ffd5da300524f083d419861918ec62cb80ba75cb9b223e91cdefdfc3f83ce7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 1, 2026.
Transparency logSecurity for pysorry 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for pysorry-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 1, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.