stipulate 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.
- stipulate-0.1.0/MANIFEST.in +4 -0
- stipulate-0.1.0/PKG-INFO +153 -0
- stipulate-0.1.0/README.md +134 -0
- stipulate-0.1.0/docs/ARCHITECTURE.md +71 -0
- stipulate-0.1.0/docs/COMPATIBILITY.md +45 -0
- stipulate-0.1.0/docs/COMPETITION.md +50 -0
- stipulate-0.1.0/docs/CONTRACT_ENGINE.md +100 -0
- stipulate-0.1.0/docs/DEPENDENCIES.md +35 -0
- stipulate-0.1.0/docs/DESIGN_DECISIONS.md +101 -0
- stipulate-0.1.0/docs/ERROR_MODEL.md +92 -0
- stipulate-0.1.0/docs/EXPERIENCE_DESIGN.md +129 -0
- stipulate-0.1.0/docs/IMPLEMENTATION_PLAN.md +115 -0
- stipulate-0.1.0/docs/IMPLEMENTED_0_1.md +37 -0
- stipulate-0.1.0/docs/INTERFACE_MODEL.md +131 -0
- stipulate-0.1.0/docs/OPEN_TECHNICAL_PROBLEMS.md +729 -0
- stipulate-0.1.0/docs/PERFORMANCE.md +63 -0
- stipulate-0.1.0/docs/PHASE_0_1_IMPLEMENTATION_CONTRACT.md +275 -0
- stipulate-0.1.0/docs/PHASE_0_1_IMPLEMENTATION_REPORT.md +67 -0
- stipulate-0.1.0/docs/PRODUCT_VISION.md +74 -0
- stipulate-0.1.0/docs/PYDANTIC_INTEGRATION.md +54 -0
- stipulate-0.1.0/docs/QUICKSTART.md +72 -0
- stipulate-0.1.0/docs/RELEASING.md +63 -0
- stipulate-0.1.0/docs/ROADMAP.md +263 -0
- stipulate-0.1.0/docs/STATIC_TYPING.md +84 -0
- stipulate-0.1.0/docs/TESTING_STRATEGY.md +60 -0
- stipulate-0.1.0/docs/TYPE_SYSTEM.md +69 -0
- stipulate-0.1.0/docs/VALIDATION_ENGINE.md +70 -0
- stipulate-0.1.0/docs/examples/report_scenarios.json +233 -0
- stipulate-0.1.0/docs/history/OPEN_TECHNICAL_PROBLEMS_ORIGINAL.md +1083 -0
- stipulate-0.1.0/docs/releases/0.1.0/FINAL_001_ESCALATION.md +125 -0
- stipulate-0.1.0/docs/releases/0.1.0/SOL_REVIEW_01.md +254 -0
- stipulate-0.1.0/docs/releases/0.1.0/SOL_REVIEW_02.md +213 -0
- stipulate-0.1.0/docs/releases/0.1.0/SOL_REVIEW_03.md +102 -0
- stipulate-0.1.0/docs/releases/0.1.0/benchmark.json +66 -0
- stipulate-0.1.0/pyproject.toml +38 -0
- stipulate-0.1.0/setup.cfg +4 -0
- stipulate-0.1.0/src/stipulate/__init__.py +16 -0
- stipulate-0.1.0/src/stipulate/_annotations.py +154 -0
- stipulate-0.1.0/src/stipulate/_cache.py +73 -0
- stipulate-0.1.0/src/stipulate/_compatibility.py +538 -0
- stipulate-0.1.0/src/stipulate/_compile.py +311 -0
- stipulate-0.1.0/src/stipulate/_contract.py +83 -0
- stipulate-0.1.0/src/stipulate/_errors.py +76 -0
- stipulate-0.1.0/src/stipulate/_evidence.py +112 -0
- stipulate-0.1.0/src/stipulate/_relations.py +301 -0
- stipulate-0.1.0/src/stipulate/_render.py +64 -0
- stipulate-0.1.0/src/stipulate/_signatures.py +259 -0
- stipulate-0.1.0/src/stipulate/py.typed +0 -0
- stipulate-0.1.0/src/stipulate.egg-info/PKG-INFO +153 -0
- stipulate-0.1.0/src/stipulate.egg-info/SOURCES.txt +80 -0
- stipulate-0.1.0/src/stipulate.egg-info/dependency_links.txt +1 -0
- stipulate-0.1.0/src/stipulate.egg-info/requires.txt +10 -0
- stipulate-0.1.0/src/stipulate.egg-info/top_level.txt +1 -0
- stipulate-0.1.0/tests/test_annotation_layers.py +246 -0
- stipulate-0.1.0/tests/test_boundaries.py +269 -0
- stipulate-0.1.0/tests/test_cache_and_policy.py +79 -0
- stipulate-0.1.0/tests/test_call_partition.py +101 -0
- stipulate-0.1.0/tests/test_contract.py +95 -0
- stipulate-0.1.0/tests/test_lifecycle_and_evidence.py +50 -0
- stipulate-0.1.0/tests/test_member_edges.py +103 -0
- stipulate-0.1.0/tests/test_members_and_relations.py +96 -0
- stipulate-0.1.0/tests/test_packaging_api.py +41 -0
- stipulate-0.1.0/tests/test_release_evidence.py +180 -0
- stipulate-0.1.0/tests/test_reliability.py +241 -0
- stipulate-0.1.0/tests/test_reports_and_examples.py +142 -0
- stipulate-0.1.0/tests/test_sol_review_02_blockers.py +131 -0
- stipulate-0.1.0/tests/test_sol_review_04_blockers.py +40 -0
- stipulate-0.1.0/tests/test_sol_review_blockers.py +206 -0
- stipulate-0.1.0/tests/test_type_table.py +173 -0
- stipulate-0.1.0/tests/test_wrapped_owner_identity.py +148 -0
- stipulate-0.1.0/tests/tests_support.py +16 -0
- stipulate-0.1.0/tests/typing_fixtures/negative.py +15 -0
- stipulate-0.1.0/tests/typing_fixtures/public_api.py +15 -0
- stipulate-0.1.0/tools/__init__.py +1 -0
- stipulate-0.1.0/tools/assemble_evidence.py +118 -0
- stipulate-0.1.0/tools/benchmark.py +161 -0
- stipulate-0.1.0/tools/check_docs.py +28 -0
- stipulate-0.1.0/tools/feature_inventory.json +4 -0
- stipulate-0.1.0/tools/installed_check.py +182 -0
- stipulate-0.1.0/tools/quality.py +107 -0
- stipulate-0.1.0/tools/release_evidence.py +261 -0
- stipulate-0.1.0/uv.lock +937 -0
stipulate-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: stipulate
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Contracts for Python interfaces
|
|
5
|
+
Author: Stipulate contributors
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: typing_extensions>=4.15.0
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: pytest==8.4.2; extra == "dev"
|
|
13
|
+
Requires-Dist: hypothesis==6.140.3; extra == "dev"
|
|
14
|
+
Requires-Dist: ruff==0.13.2; extra == "dev"
|
|
15
|
+
Requires-Dist: pyright==1.1.411; extra == "dev"
|
|
16
|
+
Requires-Dist: mypy==1.19.1; extra == "dev"
|
|
17
|
+
Requires-Dist: build==1.3.0; extra == "dev"
|
|
18
|
+
Requires-Dist: twine==6.2.0; extra == "dev"
|
|
19
|
+
|
|
20
|
+
# Stipulate
|
|
21
|
+
|
|
22
|
+
**Contracts for Python interfaces.**
|
|
23
|
+
|
|
24
|
+
**Define. Validate. Evolve.**
|
|
25
|
+
|
|
26
|
+
Stipulate checks dynamically supplied implementations against Python structural interfaces, explains mismatches, and preserves what available metadata cannot establish.
|
|
27
|
+
|
|
28
|
+
## Status
|
|
29
|
+
|
|
30
|
+
This repository contains the Stipulate 0.1 implementation, design specification, and design probes. The core API checks declared Protocol compatibility without executing candidate operations. Earlier prototypes are historical inputs, not release evidence.
|
|
31
|
+
|
|
32
|
+
Install with `python -m pip install .` (CPython 3.11–3.14). See the [implemented feature guide](docs/IMPLEMENTED_0_1.md) for the supported boundary. Independent Sol review is still required before release approval.
|
|
33
|
+
|
|
34
|
+
## Define and validate
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from typing import Protocol
|
|
38
|
+
from stipulate import Contract
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Storage(Protocol):
|
|
42
|
+
def read(self, key: str) -> bytes | None: ...
|
|
43
|
+
|
|
44
|
+
async def write(self, key: str, value: bytes) -> None: ...
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class MemoryStorage:
|
|
48
|
+
def __init__(self) -> None:
|
|
49
|
+
self._items: dict[str, bytes] = {}
|
|
50
|
+
|
|
51
|
+
def read(self, key: str) -> bytes | None:
|
|
52
|
+
return self._items.get(key)
|
|
53
|
+
|
|
54
|
+
async def write(self, key: str, value: bytes) -> None:
|
|
55
|
+
self._items[key] = value
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
candidate = MemoryStorage()
|
|
59
|
+
storage_contract = Contract(Storage)
|
|
60
|
+
storage = storage_contract.validate(candidate)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Implementations are ordinary Python classes. They need no inheritance, registration, or decorators. Validation returns the original object, typed as `Storage`, after checking its available declarations against the contract. The constructor uses TypeForm for inference; the [typing guide](docs/STATIC_TYPING.md) records supported checker settings, including the current mypy feature flag.
|
|
64
|
+
|
|
65
|
+
Strict validation is the default: incompatible or insufficient evidence raises `ContractError`. To allow missing implementation type annotations deliberately, use `strict=False`; known mismatches and unsupported or uninspectable candidate capabilities still fail. Invalid requirements fail contract construction.
|
|
66
|
+
|
|
67
|
+
## Understand a result
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
result = storage_contract.check(candidate)
|
|
71
|
+
print(result)
|
|
72
|
+
|
|
73
|
+
if result:
|
|
74
|
+
print("Storage declarations are compatible")
|
|
75
|
+
|
|
76
|
+
result.status
|
|
77
|
+
result.complete
|
|
78
|
+
result.errors()
|
|
79
|
+
result.unknowns()
|
|
80
|
+
result.evidence
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`check()` reports candidate mismatches without raising `ContractError`. Truthiness means compatibility was established for every requirement; unknown evidence is false. Invalid contract definitions raise `ContractDefinitionError`. The result includes locations, reasons, and suggested fixes.
|
|
84
|
+
|
|
85
|
+
Stipulate compares signatures, annotation assignability, binding, and supported member capabilities. It assumes implementations honor their declarations; it does not execute methods to verify their behavior, validate future return values, or prevent later mutation. Annotation resolution may execute Python annotation expressions in the default trusted mode.
|
|
86
|
+
|
|
87
|
+
## Friendly errors
|
|
88
|
+
|
|
89
|
+
```text
|
|
90
|
+
Incompatible with Storage
|
|
91
|
+
2 incompatible findings; 0 unknown findings (0 blocked obligations).
|
|
92
|
+
|
|
93
|
+
read.key
|
|
94
|
+
The implementation input is too narrow for required caller values
|
|
95
|
+
Required: str
|
|
96
|
+
Provided: bytes
|
|
97
|
+
[parameter_type; incompatible]
|
|
98
|
+
|
|
99
|
+
write.kind
|
|
100
|
+
The coroutine execution kind differs
|
|
101
|
+
[async_mismatch; incompatible]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Interface shorthand — experimental design target
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from stipulate import Interface
|
|
108
|
+
|
|
109
|
+
class Storage(Interface):
|
|
110
|
+
def read(self, key: str) -> bytes | None: ...
|
|
111
|
+
|
|
112
|
+
storage = Storage.validate(candidate)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
This shorthand is not a 0.1 promise. It must preserve structural typing, precise class-side methods, inheritance, and clean protocol members in installed-package Pyright/mypy tests before promotion. The supported 0.1 plan uses `Protocol` plus `Contract`, with the same method-based operations and compatibility engine.
|
|
116
|
+
|
|
117
|
+
## Evolution and tooling — later releases
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
report = Contract(StorageV1).compare(Contract(StorageV2))
|
|
121
|
+
schema = storage_contract.schema()
|
|
122
|
+
fingerprint = storage_contract.fingerprint()
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Comparison will report implementer and consumer compatibility separately. Unknown results cannot certify a non-breaking change. Schemas and fingerprints will be versioned before publication. None of these three operations is in the 0.1 public surface.
|
|
126
|
+
|
|
127
|
+
## Documentation
|
|
128
|
+
|
|
129
|
+
Start with the [quickstart](docs/QUICKSTART.md). The [experience design](docs/EXPERIENCE_DESIGN.md) specifies the API journey, readable reports, and usability bar.
|
|
130
|
+
|
|
131
|
+
- [Product Vision](docs/PRODUCT_VISION.md)
|
|
132
|
+
- [Public Interface Model](docs/INTERFACE_MODEL.md)
|
|
133
|
+
- [Contract Engine](docs/CONTRACT_ENGINE.md)
|
|
134
|
+
- [Architecture](docs/ARCHITECTURE.md)
|
|
135
|
+
- [Type System](docs/TYPE_SYSTEM.md)
|
|
136
|
+
- [Validation Engine](docs/VALIDATION_ENGINE.md)
|
|
137
|
+
- [Static Typing](docs/STATIC_TYPING.md)
|
|
138
|
+
- [Errors](docs/ERROR_MODEL.md)
|
|
139
|
+
- [Performance and Caching](docs/PERFORMANCE.md)
|
|
140
|
+
- [Testing](docs/TESTING_STRATEGY.md)
|
|
141
|
+
- [Compatibility](docs/COMPATIBILITY.md)
|
|
142
|
+
- [Dependencies](docs/DEPENDENCIES.md)
|
|
143
|
+
- [Competition](docs/COMPETITION.md)
|
|
144
|
+
- [Pydantic Integration](docs/PYDANTIC_INTEGRATION.md)
|
|
145
|
+
- [Roadmap and 0.x release phases](docs/ROADMAP.md)
|
|
146
|
+
- [Release and PyPI trusted publishing](docs/RELEASING.md)
|
|
147
|
+
- [Design Decisions](docs/DESIGN_DECISIONS.md)
|
|
148
|
+
- [Implementation Plan](docs/IMPLEMENTATION_PLAN.md)
|
|
149
|
+
- [Phase 0.1 implementation contract](docs/PHASE_0_1_IMPLEMENTATION_CONTRACT.md)
|
|
150
|
+
- [Open Technical Problems](docs/OPEN_TECHNICAL_PROBLEMS.md)
|
|
151
|
+
- [Design probes](design_probes/README.md)
|
|
152
|
+
|
|
153
|
+
The roadmap owns release scope; design decisions own durable policy; focused specifications own behavior; the open-problem register owns implementation evidence and unresolved acceptance work. Contradictions must be reconciled in the same change. A decision does not count as a tested implementation.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Stipulate
|
|
2
|
+
|
|
3
|
+
**Contracts for Python interfaces.**
|
|
4
|
+
|
|
5
|
+
**Define. Validate. Evolve.**
|
|
6
|
+
|
|
7
|
+
Stipulate checks dynamically supplied implementations against Python structural interfaces, explains mismatches, and preserves what available metadata cannot establish.
|
|
8
|
+
|
|
9
|
+
## Status
|
|
10
|
+
|
|
11
|
+
This repository contains the Stipulate 0.1 implementation, design specification, and design probes. The core API checks declared Protocol compatibility without executing candidate operations. Earlier prototypes are historical inputs, not release evidence.
|
|
12
|
+
|
|
13
|
+
Install with `python -m pip install .` (CPython 3.11–3.14). See the [implemented feature guide](docs/IMPLEMENTED_0_1.md) for the supported boundary. Independent Sol review is still required before release approval.
|
|
14
|
+
|
|
15
|
+
## Define and validate
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from typing import Protocol
|
|
19
|
+
from stipulate import Contract
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Storage(Protocol):
|
|
23
|
+
def read(self, key: str) -> bytes | None: ...
|
|
24
|
+
|
|
25
|
+
async def write(self, key: str, value: bytes) -> None: ...
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class MemoryStorage:
|
|
29
|
+
def __init__(self) -> None:
|
|
30
|
+
self._items: dict[str, bytes] = {}
|
|
31
|
+
|
|
32
|
+
def read(self, key: str) -> bytes | None:
|
|
33
|
+
return self._items.get(key)
|
|
34
|
+
|
|
35
|
+
async def write(self, key: str, value: bytes) -> None:
|
|
36
|
+
self._items[key] = value
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
candidate = MemoryStorage()
|
|
40
|
+
storage_contract = Contract(Storage)
|
|
41
|
+
storage = storage_contract.validate(candidate)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Implementations are ordinary Python classes. They need no inheritance, registration, or decorators. Validation returns the original object, typed as `Storage`, after checking its available declarations against the contract. The constructor uses TypeForm for inference; the [typing guide](docs/STATIC_TYPING.md) records supported checker settings, including the current mypy feature flag.
|
|
45
|
+
|
|
46
|
+
Strict validation is the default: incompatible or insufficient evidence raises `ContractError`. To allow missing implementation type annotations deliberately, use `strict=False`; known mismatches and unsupported or uninspectable candidate capabilities still fail. Invalid requirements fail contract construction.
|
|
47
|
+
|
|
48
|
+
## Understand a result
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
result = storage_contract.check(candidate)
|
|
52
|
+
print(result)
|
|
53
|
+
|
|
54
|
+
if result:
|
|
55
|
+
print("Storage declarations are compatible")
|
|
56
|
+
|
|
57
|
+
result.status
|
|
58
|
+
result.complete
|
|
59
|
+
result.errors()
|
|
60
|
+
result.unknowns()
|
|
61
|
+
result.evidence
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`check()` reports candidate mismatches without raising `ContractError`. Truthiness means compatibility was established for every requirement; unknown evidence is false. Invalid contract definitions raise `ContractDefinitionError`. The result includes locations, reasons, and suggested fixes.
|
|
65
|
+
|
|
66
|
+
Stipulate compares signatures, annotation assignability, binding, and supported member capabilities. It assumes implementations honor their declarations; it does not execute methods to verify their behavior, validate future return values, or prevent later mutation. Annotation resolution may execute Python annotation expressions in the default trusted mode.
|
|
67
|
+
|
|
68
|
+
## Friendly errors
|
|
69
|
+
|
|
70
|
+
```text
|
|
71
|
+
Incompatible with Storage
|
|
72
|
+
2 incompatible findings; 0 unknown findings (0 blocked obligations).
|
|
73
|
+
|
|
74
|
+
read.key
|
|
75
|
+
The implementation input is too narrow for required caller values
|
|
76
|
+
Required: str
|
|
77
|
+
Provided: bytes
|
|
78
|
+
[parameter_type; incompatible]
|
|
79
|
+
|
|
80
|
+
write.kind
|
|
81
|
+
The coroutine execution kind differs
|
|
82
|
+
[async_mismatch; incompatible]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Interface shorthand — experimental design target
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from stipulate import Interface
|
|
89
|
+
|
|
90
|
+
class Storage(Interface):
|
|
91
|
+
def read(self, key: str) -> bytes | None: ...
|
|
92
|
+
|
|
93
|
+
storage = Storage.validate(candidate)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
This shorthand is not a 0.1 promise. It must preserve structural typing, precise class-side methods, inheritance, and clean protocol members in installed-package Pyright/mypy tests before promotion. The supported 0.1 plan uses `Protocol` plus `Contract`, with the same method-based operations and compatibility engine.
|
|
97
|
+
|
|
98
|
+
## Evolution and tooling — later releases
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
report = Contract(StorageV1).compare(Contract(StorageV2))
|
|
102
|
+
schema = storage_contract.schema()
|
|
103
|
+
fingerprint = storage_contract.fingerprint()
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Comparison will report implementer and consumer compatibility separately. Unknown results cannot certify a non-breaking change. Schemas and fingerprints will be versioned before publication. None of these three operations is in the 0.1 public surface.
|
|
107
|
+
|
|
108
|
+
## Documentation
|
|
109
|
+
|
|
110
|
+
Start with the [quickstart](docs/QUICKSTART.md). The [experience design](docs/EXPERIENCE_DESIGN.md) specifies the API journey, readable reports, and usability bar.
|
|
111
|
+
|
|
112
|
+
- [Product Vision](docs/PRODUCT_VISION.md)
|
|
113
|
+
- [Public Interface Model](docs/INTERFACE_MODEL.md)
|
|
114
|
+
- [Contract Engine](docs/CONTRACT_ENGINE.md)
|
|
115
|
+
- [Architecture](docs/ARCHITECTURE.md)
|
|
116
|
+
- [Type System](docs/TYPE_SYSTEM.md)
|
|
117
|
+
- [Validation Engine](docs/VALIDATION_ENGINE.md)
|
|
118
|
+
- [Static Typing](docs/STATIC_TYPING.md)
|
|
119
|
+
- [Errors](docs/ERROR_MODEL.md)
|
|
120
|
+
- [Performance and Caching](docs/PERFORMANCE.md)
|
|
121
|
+
- [Testing](docs/TESTING_STRATEGY.md)
|
|
122
|
+
- [Compatibility](docs/COMPATIBILITY.md)
|
|
123
|
+
- [Dependencies](docs/DEPENDENCIES.md)
|
|
124
|
+
- [Competition](docs/COMPETITION.md)
|
|
125
|
+
- [Pydantic Integration](docs/PYDANTIC_INTEGRATION.md)
|
|
126
|
+
- [Roadmap and 0.x release phases](docs/ROADMAP.md)
|
|
127
|
+
- [Release and PyPI trusted publishing](docs/RELEASING.md)
|
|
128
|
+
- [Design Decisions](docs/DESIGN_DECISIONS.md)
|
|
129
|
+
- [Implementation Plan](docs/IMPLEMENTATION_PLAN.md)
|
|
130
|
+
- [Phase 0.1 implementation contract](docs/PHASE_0_1_IMPLEMENTATION_CONTRACT.md)
|
|
131
|
+
- [Open Technical Problems](docs/OPEN_TECHNICAL_PROBLEMS.md)
|
|
132
|
+
- [Design probes](design_probes/README.md)
|
|
133
|
+
|
|
134
|
+
The roadmap owns release scope; design decisions own durable policy; focused specifications own behavior; the open-problem register owns implementation evidence and unresolved acceptance work. Contradictions must be reconciled in the same change. A decision does not count as a tested implementation.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
Protocol declaration
|
|
7
|
+
-> Contract[T] facade
|
|
8
|
+
-> controlled annotation resolution and member compiler
|
|
9
|
+
-> immutable ContractIR snapshot
|
|
10
|
+
-> candidate inspection (per call)
|
|
11
|
+
-> shared capability/type relation engine
|
|
12
|
+
-> Evidence + CompatibilityResult
|
|
13
|
+
-> explicit enforcement policy
|
|
14
|
+
-> original candidate or ContractError
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Compilation failures use `ContractDefinitionError`. Inspection does not execute candidate methods or getters. Default trusted annotation evaluation may execute annotation expressions; see VALIDATION_ENGINE.md.
|
|
18
|
+
|
|
19
|
+
## Public boundary
|
|
20
|
+
|
|
21
|
+
The 0.1 entry point is `Contract(ProtocolType)`. Its constructor accepts `TypeForm[T]`, preserves T through validation, and performs a runtime supported-declaration check. A normal class, union, or arbitrary type form is not automatically a valid requirement.
|
|
22
|
+
|
|
23
|
+
`Contract` exposes `validate()`, `check()`, and `.contract` identity. Later `schema()`, `fingerprint()`, and `compare()` are release-gated. Experimental Interface class-side methods delegate to Contract and never become protocol instance members.
|
|
24
|
+
|
|
25
|
+
## Internal modules
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
src/stipulate/
|
|
29
|
+
__init__.py
|
|
30
|
+
_contract.py public typed facade
|
|
31
|
+
_compile.py requirement compilation
|
|
32
|
+
_ir.py immutable normalized records
|
|
33
|
+
_members.py static member classification
|
|
34
|
+
_signatures.py binding and legal call relations
|
|
35
|
+
_assignability.py directional type relations
|
|
36
|
+
_annotations.py trusted/raw annotation policies
|
|
37
|
+
_compatibility.py result aggregation and enforcement
|
|
38
|
+
_evidence.py immutable findings
|
|
39
|
+
_errors.py public exceptions
|
|
40
|
+
_render.py pure plain-text result/exception presentation
|
|
41
|
+
_cache.py weak compilation cache
|
|
42
|
+
_typing_compat.py isolated version-specific typing behavior
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Keep the experimental `_interface.py` bridge separate from release-critical code until accepted.
|
|
46
|
+
|
|
47
|
+
## Data ownership
|
|
48
|
+
|
|
49
|
+
The Contract facade strongly owns its immutable IR. The IR may strongly retain the declaration and type objects when needed to preserve identity and diagnostics. The global compilation cache must not strongly own that IR; both keys and cached IR values are weak references. See PERFORMANCE.md for lifecycle and race rules.
|
|
50
|
+
|
|
51
|
+
Do not attach a globally owned adapter that closes the weak-reference lifecycle through an indirect path. Do not cache candidates or successful results globally.
|
|
52
|
+
|
|
53
|
+
## Requirement versus candidate
|
|
54
|
+
|
|
55
|
+
The requirement compiler has no candidate-specific state. Candidate inspection creates an ephemeral provided view with its own resolution context and evidence provenance. Expected limitations are represented as unknown facts. A current attribute value cannot substitute for a missing declared writable type.
|
|
56
|
+
|
|
57
|
+
Read-only capabilities compare covariantly. Writable capabilities add contravariant write obligations. Methods compare every legal call shape, then argument/return relations. Execution-kind requirements are an explicit Stipulate policy, not inferred from textual return annotations alone.
|
|
58
|
+
|
|
59
|
+
## Configuration boundaries
|
|
60
|
+
|
|
61
|
+
Annotation policy and namespace identity affect compilation and cache reuse. Strictness affects enforcement only. Mutating configuration after compilation is unsupported; create a new Contract snapshot instead.
|
|
62
|
+
|
|
63
|
+
Do not hold cache locks while evaluating annotations, inspecting candidate code, or comparing types. Concurrent duplicate compilation is acceptable when publication is safe and diagnostics are deterministic. Annotation evaluation must not be advertised as exactly-once under concurrency.
|
|
64
|
+
|
|
65
|
+
## Presentation boundary
|
|
66
|
+
|
|
67
|
+
The presenter accepts normalized evidence and sanitized display metadata. It produces text without invoking candidate code, checking compatibility again, or selecting a policy implicitly. Result str/repr and exception strings share this implementation. Later terminal or browser views may style the same facts; no view is permitted to promote UNKNOWN to compatible.
|
|
68
|
+
|
|
69
|
+
## Extension rule
|
|
70
|
+
|
|
71
|
+
New types and member forms add normalized representation and relation handlers with conformance tests. They must preserve unknown evidence and share rules with future evolution analysis. A public extension/plugin framework is deferred until internal semantics stabilize.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Compatibility Policy
|
|
2
|
+
|
|
3
|
+
## Runtime targets
|
|
4
|
+
|
|
5
|
+
Minimum package metadata is CPython 3.11 (`requires-python >=3.11`). The test targets are CPython 3.11, 3.12, 3.13, and 3.14. The implementation report records full runtime and installed-consumer verification; release approval additionally requires independent review.
|
|
6
|
+
|
|
7
|
+
Advertise a minor version only after compilation, annotation resolution, signature normalization, supported type relations, evidence, lifecycle, and installed-package tests pass. Import success is insufficient. PyPy and other implementations are outside the initial support claim.
|
|
8
|
+
|
|
9
|
+
Newer annotation metadata must be handled deliberately, including deferred evaluation. New interpreter features do not automatically expand the supported type-form subset. Use typing_extensions when its behavior is tested; do not invent unavailable runtime metadata.
|
|
10
|
+
|
|
11
|
+
## Checkers and packaging
|
|
12
|
+
|
|
13
|
+
Pyright strict is permanent for first-party code. Public fixtures also run on supported mypy configurations. Record exact versions and required feature flags; the current design probe's mypy 1.19.1 requires its TypeForm flag. No required checker plugin or consumer ignores.
|
|
14
|
+
|
|
15
|
+
The TypeForm constructor and inferred return type are part of the public contract. Test installed wheels and sdists outside the source tree with py.typed and any required stubs present. Do not claim that local declaration probes prove package behavior.
|
|
16
|
+
|
|
17
|
+
## Typing semantics and runtime policy
|
|
18
|
+
|
|
19
|
+
The typing specification governs supported annotation relations. Strict evidence, trusted/raw annotation evaluation, static inspection limits, and the initial coroutine-kind rule are explicit Stipulate policies. Document intentional differences from ordinary checker assignment behavior.
|
|
20
|
+
|
|
21
|
+
Supported constructs form a versioned subset. New support can turn UNKNOWN into COMPATIBLE or INCOMPATIBLE; treat resulting acceptance changes as observable behavior and explain them in release notes. Never market strict validation as method-body or future-value enforcement.
|
|
22
|
+
|
|
23
|
+
## Versioning
|
|
24
|
+
|
|
25
|
+
During 0.x, call out breaking API, policy, diagnostic, or supported-subset changes clearly. At 1.0, semantic versioning covers documented Python APIs, supported compatibility semantics, enforcement defaults, diagnostic fields/codes, and public schema versions.
|
|
26
|
+
|
|
27
|
+
Correctness fixes can legitimately reject previously accepted candidates. Classify and document their impact; do not quietly claim acceptance is unchanged because a change is a bug fix.
|
|
28
|
+
|
|
29
|
+
## Schema and diagnostics
|
|
30
|
+
|
|
31
|
+
Public schema()/fingerprint() follow their later release gate, including explicit schema versions and identity/portability policy. Internal IR records are not a public interchange format.
|
|
32
|
+
|
|
33
|
+
Diagnostic location, code, serialized fields, and semantic category become stable at 1.0. Exact human wording remains free to improve. Unknown and incompatible categories must remain distinguishable.
|
|
34
|
+
|
|
35
|
+
## Mutation and evaluation
|
|
36
|
+
|
|
37
|
+
Contracts are immutable requirement snapshots; refresh=True makes a new snapshot. Candidate checks are point-in-time and are repeated after candidate mutation. Trusted annotation evaluation can execute expressions; raw mode does not request evaluation and can produce unresolved evidence. Neither mode is a security boundary.
|
|
38
|
+
|
|
39
|
+
## Dependencies and integrations
|
|
40
|
+
|
|
41
|
+
Core results must not change merely because Pydantic or another optional integration is installed. Third-party annotation metadata is not supported implicitly. Raising Python/dependency minimums requires an explicit compatibility decision and a tested migration path.
|
|
42
|
+
|
|
43
|
+
## Deprecation
|
|
44
|
+
|
|
45
|
+
At 1.0, announce deprecations and normally retain them through at least one minor release, with removal in a major release. An exceptional correctness/security removal must explain why ordinary deprecation was insufficient.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Competition and Positioning
|
|
2
|
+
|
|
3
|
+
## Scope
|
|
4
|
+
|
|
5
|
+
This is a positioning document, not a verified exhaustive feature ranking. Stipulate's initial implementation is in progress and its release gates remain authoritative. Planned or unsupported capabilities must not be compared as shipped advantages against other projects.
|
|
6
|
+
|
|
7
|
+
Its intended focus is explicit Python structural contracts: check a dynamically supplied implementation, explain incompatible and unknown evidence, and eventually compare contract evolution with the same underlying rules.
|
|
8
|
+
|
|
9
|
+
## Language foundation
|
|
10
|
+
|
|
11
|
+
Protocol is the foundation. Python's typing tools already provide structural checking where the relationship is statically visible; runtime-checkable Protocol checks are intentionally shallower. Stipulate's proposed addition is deeper declaration inspection at dynamic boundaries, subject to available metadata. [Python typing documentation](https://docs.python.org/3/library/typing.html#typing.runtime_checkable)
|
|
12
|
+
|
|
13
|
+
Do not claim runtime inspection proves method behavior that static checking could not prove.
|
|
14
|
+
|
|
15
|
+
## Useful adjacent references
|
|
16
|
+
|
|
17
|
+
| Reference | Documented focus | Stipulate design lesson |
|
|
18
|
+
| --- | --- | --- |
|
|
19
|
+
| Pydantic | Typed value validation and associated tooling | Simple entry points, useful diagnostics, deliberate value semantics |
|
|
20
|
+
| Griffe | Package API extraction, serialization, and breaking-change analysis | Treat public schema and change reports as compatibility surfaces |
|
|
21
|
+
| Typeguard | Runtime checking of annotated values and instrumented calls | Study runtime annotation limits without conflating value checks with declaration relations |
|
|
22
|
+
|
|
23
|
+
Sources: [Pydantic](https://pydantic.dev/docs/validation/latest/concepts/types/), [Griffe](https://mkdocstrings.github.io/griffe/), [Typeguard](https://typeguard.readthedocs.io/en/latest/). This table does not assert that adjacent tools lack all forms of Protocol support or that Stipulate has uniquely solved those problems.
|
|
24
|
+
|
|
25
|
+
Other historical interface systems, behavioral contract libraries, schema-diff tools, and API-impact projects can inform later evaluation. Specific competitive rankings require dated primary sources, concrete examples, and reproducible comparison criteria before inclusion.
|
|
26
|
+
|
|
27
|
+
## Product distinction to prove
|
|
28
|
+
|
|
29
|
+
The intended combination is:
|
|
30
|
+
|
|
31
|
+
- Standard Protocol declarations with ordinary implementations.
|
|
32
|
+
- Precise, method-based Contract API and inferred validation return types.
|
|
33
|
+
- Directional call/member/type checking for an explicitly supported subset.
|
|
34
|
+
- Unknown evidence preserved separately from mismatch and policy acceptance.
|
|
35
|
+
- Eventual universal implementer/consumer evolution analysis using shared rules.
|
|
36
|
+
- Eventual versioned schemas and portable fingerprints.
|
|
37
|
+
|
|
38
|
+
The first four belong to the 0.1 plan. The last two are later release gates, not current feature claims.
|
|
39
|
+
|
|
40
|
+
## Boundaries
|
|
41
|
+
|
|
42
|
+
Do not expand into package-wide source extraction, general function instrumentation, call-site analytics, dependency injection, behavioral preconditions, or a nominal interface ecosystem merely to match a competitor's feature list.
|
|
43
|
+
|
|
44
|
+
For tooling, a deliberate user-supplied Protocol is the contract being checked. This scope does not prevent future integrations with API extraction, but those integrations must not redefine the core.
|
|
45
|
+
|
|
46
|
+
## Reassessment triggers
|
|
47
|
+
|
|
48
|
+
Before publishing competitive claims or major releases, verify current primary documentation and test representative examples. Reevaluate when typing standards improve class-side APIs, checker TypeForm support changes, or adjacent projects add relevant declaration/evolution capabilities.
|
|
49
|
+
|
|
50
|
+
Stipulate earns adoption through demonstrated correctness and error quality. Avoid claims of completeness, unique capability, or stronger guarantees than the evidence supports.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Contract Engine
|
|
2
|
+
|
|
3
|
+
## Semantic center
|
|
4
|
+
|
|
5
|
+
The engine compares provided declarations against required declarations. Compatibility means each supported required operation is available with compatible call shape, binding, and types, assuming declarations describe actual behavior.
|
|
6
|
+
|
|
7
|
+
One normalization and relation engine powers candidate checks and eventual evolution analysis. Relation context is explicit: metadata conformance and universal contract evolution have different obligations when gradual or unknown types are involved.
|
|
8
|
+
|
|
9
|
+
## Layers
|
|
10
|
+
|
|
11
|
+
1. Compile a supported Protocol into immutable `ContractIR`.
|
|
12
|
+
2. Inspect a candidate into an ephemeral provided-contract view.
|
|
13
|
+
3. Compare capabilities and types, producing evidence.
|
|
14
|
+
4. Aggregate a policy-independent `CompatibilityResult`.
|
|
15
|
+
5. Enforce strict or permissive acceptance and return the original candidate or raise `ContractError`.
|
|
16
|
+
|
|
17
|
+
Later serialization and comparison consume the same IR and relation rules. They are not public 0.1 operations.
|
|
18
|
+
|
|
19
|
+
## Contract IR
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
ContractIR
|
|
23
|
+
declaration identity and diagnostic provenance
|
|
24
|
+
members
|
|
25
|
+
MethodContract: binding, legal calls, parameter types, return type, execution kind
|
|
26
|
+
AttributeContract: read type, write type, storage capability
|
|
27
|
+
PropertyContract: getter, optional setter
|
|
28
|
+
normalized TypeExpr graph
|
|
29
|
+
compilation policy and supported feature set
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Candidate evidence and enforcement policy do not enter the requirement IR. Public `Contract[T]` is a typed facade holding one snapshot. Keep source provenance outside the semantic fields used for future serialization.
|
|
33
|
+
|
|
34
|
+
Recursive references use explicit graph identities and a cycle-aware relation context. A cache of visited pairs must distinguish pending from established relations; recursion is not unconditional compatibility. Unsupported recursive forms remain explicit until dedicated support lands.
|
|
35
|
+
|
|
36
|
+
## Evidence and aggregation
|
|
37
|
+
|
|
38
|
+
Evidence has `PROVEN`, `INCOMPATIBLE`, or `UNKNOWN` status, a stable code and location, expected/actual metadata, provenance, and an optional actionable hint. PROVEN means an obligation about declarations was established, not that a method body was executed.
|
|
39
|
+
|
|
40
|
+
Conjunctive requirements aggregate as follows:
|
|
41
|
+
|
|
42
|
+
| Findings | Result status | Truthiness | Strict acceptance | Permissive acceptance |
|
|
43
|
+
| --- | --- | --- | --- | --- |
|
|
44
|
+
| All obligations established | COMPATIBLE | True | Yes | Yes |
|
|
45
|
+
| At least one incompatible finding, with or without unknowns | INCOMPATIBLE | False | No | No |
|
|
46
|
+
| No incompatibility; unknowns only from the allowlist below | UNKNOWN | False | No | Yes |
|
|
47
|
+
| No incompatibility; any other unknown reason | UNKNOWN | False | No | No |
|
|
48
|
+
|
|
49
|
+
`complete` means every applicable obligation was decided. It is independent of success: a fully analyzed incompatible object is complete. Blocked dependent obligations must remain explicitly unassessed; do not manufacture completeness by dropping them. An empty valid contract is compatible and complete.
|
|
50
|
+
|
|
51
|
+
Disjunctive type relations use three-valued logic: one established branch suffices for an OR, all disproven branches disprove it, otherwise it is unknown. An AND is disproven by any failed obligation, established only when all succeed, otherwise unknown. Apply this inside union and other relation algorithms before aggregating independent member requirements.
|
|
52
|
+
|
|
53
|
+
There is no second public assurance enum or confidence score in 0.1. `status`, `complete`, and detailed evidence suffice.
|
|
54
|
+
|
|
55
|
+
## Enforcement policy
|
|
56
|
+
|
|
57
|
+
`validate(candidate, strict=True)` and `result.accepted(strict=True)` share the exact same acceptance function. `check()` never applies enforcement policy.
|
|
58
|
+
|
|
59
|
+
Permissive acceptance may tolerate only candidate type unknowns with codes `annotation_missing` and `gradual_type`. Known member presence, binding, signature, and execution-kind checks must still pass. Missing required members, unsupported types, unresolved expressions, unavailable signatures, dynamic members, and inspection failures cannot be waived by `strict=False`.
|
|
60
|
+
|
|
61
|
+
Unknowns retain their original status and provenance after acceptance. A permissive return type is a documented trust boundary comparable to adopting untyped code, not a complete metadata proof. Invalid requirements always raise `ContractDefinitionError` independently of enforcement policy.
|
|
62
|
+
|
|
63
|
+
## Exceptions
|
|
64
|
+
|
|
65
|
+
`ContractError` means a valid contract rejected the candidate under the requested policy. It includes the result and the findings responsible for rejection, including unknowns rejected by strict policy.
|
|
66
|
+
|
|
67
|
+
`ContractDefinitionError` means the requirement could not be compiled. `check()` does not swallow definition errors or unexpected internal defects. Expected candidate inspection limitations become unknown evidence; process-control exceptions propagate.
|
|
68
|
+
|
|
69
|
+
## Evolution semantics — later release
|
|
70
|
+
|
|
71
|
+
For old contract O and new contract N, under fully supported, fully static semantics:
|
|
72
|
+
|
|
73
|
+
- Implementers: O is assignable to N. Every implementation meeting O also meets N.
|
|
74
|
+
- Consumers: N is assignable to O. A consumer using O's operations can use N.
|
|
75
|
+
|
|
76
|
+
| Change from old to new | Existing implementers | Existing consumers |
|
|
77
|
+
| --- | --- | --- |
|
|
78
|
+
| Add a required method | Incompatible | Compatible |
|
|
79
|
+
| Remove a required method | Compatible | Incompatible |
|
|
80
|
+
| Widen accepted parameter from int to object | Incompatible | Compatible |
|
|
81
|
+
| Narrow returned value from object to int | Incompatible | Compatible |
|
|
82
|
+
| Add an optional parameter to an existing method | May be incompatible: old implementations need not accept it | Compatible if old calls and results are preserved |
|
|
83
|
+
|
|
84
|
+
These are independent perspectives, not a single “breaking” direction. Member kinds, mutability, and call shapes remain part of the analysis.
|
|
85
|
+
|
|
86
|
+
Python gradual assignability involving Any is not a universal substitutability guarantee. For example, returns `int`, `Any`, and `str` permit pairwise gradual assignments through Any without making int assignable to str. Never infer transitive evolution safety through such evidence. Use the same engine with a universal-guarantee context; return UNKNOWN wherever the guarantee depends on unsupported or gradual assumptions. Independent mismatches can still establish incompatibility.
|
|
87
|
+
|
|
88
|
+
`report.implementers` and `.consumers` are CompatibilityResults. `report.breaking` is True if either direction is INCOMPATIBLE, False only if both are COMPATIBLE, and None otherwise. `report.complete` requires both directions complete. CI fails on incompatible or unknown required directions by default. Report objects have no implicit truthiness; reject it to avoid ambiguous policy decisions. The CLI can explicitly select which direction matters.
|
|
89
|
+
|
|
90
|
+
## Canonical schema and fingerprints — later release
|
|
91
|
+
|
|
92
|
+
Versioned schema work must specify member ordering, unions, aliases, nominal type identity, generic bindings, recursive references, default presence versus default values, ignored Annotated metadata, and portability before publication.
|
|
93
|
+
|
|
94
|
+
A semantic fingerprint excludes display names, source locations, and irrelevant default values. Callable compatibility depends on default presence; changed default behavior can be reported separately but is not behavioral proof. Preserve nominal identities; never merge unrelated types merely because names match. Process-local or non-portable identities must be rejected for portable snapshots or explicitly marked non-portable, not serialized using unstable repr output.
|
|
95
|
+
|
|
96
|
+
Use an explicit format such as `stipulate-contract-v1:sha256:<digest>`. A fingerprint difference does not imply incompatibility. Loading snapshots must not automatically import or execute arbitrary type references.
|
|
97
|
+
|
|
98
|
+
## Native implementation boundary
|
|
99
|
+
|
|
100
|
+
The initial core is Python. Normalize Python inspection objects before relation analysis, but retain the runtime identity information needed for correctness. A future native core is justified only by profiling; do not constrain 0.1 around speculative Rust or force premature portable serialization.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Dependency Strategy
|
|
2
|
+
|
|
3
|
+
## Core
|
|
4
|
+
|
|
5
|
+
Start with standard-library dataclasses, inspect, typing, weakref, and explicit typed internal records. Stipulate owns its IR, type relations, evidence, and enforcement semantics.
|
|
6
|
+
|
|
7
|
+
Use typing_extensions for TypeForm and supported cross-version features. The recorded design probes use 4.15.0; treat that as the initial tested baseline, not proof of the oldest compatible release. Freeze actual package minimums only after the release matrix passes.
|
|
8
|
+
|
|
9
|
+
attrs and typing-inspection remain optional implementation evaluations, not commitments. Adopt them only if conformance experiments demonstrate a meaningful correctness or maintenance benefit. Do not add a dependency because it resembles a small standard-library abstraction.
|
|
10
|
+
|
|
11
|
+
## Development
|
|
12
|
+
|
|
13
|
+
Use pytest, Hypothesis, Ruff, Pyright strict, and mypy public fixtures. Pin tested checker versions and document required TypeForm settings. The specification is the semantic reference; checker/runtime-library disagreement needs an explanation rather than majority voting.
|
|
14
|
+
|
|
15
|
+
Keep design probes separate from implementation tests. A local fixture dependency is not automatically a production dependency.
|
|
16
|
+
|
|
17
|
+
## Later CLI
|
|
18
|
+
|
|
19
|
+
Typer and Rich are preferred candidates for an optional stipulate[cli] extra once snapshot and comparison APIs are ready. CLI code consumes the shared contract engine and emits machine-readable reports independently of terminal rendering.
|
|
20
|
+
|
|
21
|
+
No CLI dependency is needed for library validation or the 0.1 API.
|
|
22
|
+
|
|
23
|
+
## Optional integrations
|
|
24
|
+
|
|
25
|
+
Pydantic is optional and post-1.0. It must not become the assignability engine or change core outcomes based on installation state. Integration value validation needs its own no-hidden-coercion and attribute-access policy.
|
|
26
|
+
|
|
27
|
+
Griffe may be evaluated if later work needs package API extraction. Typeguard and other runtime typing libraries can inform narrow differential experiments but do not define Stipulate's structural assignability semantics.
|
|
28
|
+
|
|
29
|
+
## Optimization
|
|
30
|
+
|
|
31
|
+
Use hashlib and json for future schema tooling once canonical semantics exist. Do not introduce Rust, PyO3, or native build infrastructure before profiling identifies a material bottleneck. Keep Python introspection on the Python side of any future native boundary.
|
|
32
|
+
|
|
33
|
+
## Acceptance rule
|
|
34
|
+
|
|
35
|
+
A dependency must demonstrably remove difficult non-differentiating work, improve tested compatibility, or provide a justified optional capability. Record its purpose, supported versions, typing quality, and effect on the package matrix before adoption.
|