proofside 0.1.0__py3-none-any.whl
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.
- proofside/__init__.py +3 -0
- proofside/__main__.py +6 -0
- proofside/acceptance.py +77 -0
- proofside/artifacts.py +9 -0
- proofside/batch.py +309 -0
- proofside/cli.py +423 -0
- proofside/contracts.py +372 -0
- proofside/proposal.py +290 -0
- proofside/specification.py +129 -0
- proofside-0.1.0.dist-info/METADATA +373 -0
- proofside-0.1.0.dist-info/RECORD +15 -0
- proofside-0.1.0.dist-info/WHEEL +5 -0
- proofside-0.1.0.dist-info/entry_points.txt +2 -0
- proofside-0.1.0.dist-info/licenses/LICENSE +202 -0
- proofside-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from io import StringIO
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import tokenize
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
EQUATION_PREFIX = "# proofside equation:"
|
|
11
|
+
INTENT_PREFIX = "# proofside intent:"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SpecificationAnnotationError(ValueError):
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class SpecificationAnnotations:
|
|
20
|
+
equations: tuple[str, ...]
|
|
21
|
+
intents: tuple[str, ...]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class AnnotatedFunction:
|
|
26
|
+
name: str
|
|
27
|
+
function: ast.FunctionDef
|
|
28
|
+
annotations: SpecificationAnnotations
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _comment_tokens(source: str) -> dict[int, tokenize.TokenInfo]:
|
|
32
|
+
lines = source.splitlines()
|
|
33
|
+
comment_tokens: dict[int, tokenize.TokenInfo] = {}
|
|
34
|
+
for token in tokenize.generate_tokens(StringIO(source).readline):
|
|
35
|
+
if token.type != tokenize.COMMENT:
|
|
36
|
+
continue
|
|
37
|
+
line_number, column = token.start
|
|
38
|
+
if lines[line_number - 1][:column].strip():
|
|
39
|
+
continue
|
|
40
|
+
comment_tokens[line_number] = token
|
|
41
|
+
return comment_tokens
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _annotations_for_function(
|
|
45
|
+
comment_tokens: dict[int, tokenize.TokenInfo],
|
|
46
|
+
function: ast.FunctionDef,
|
|
47
|
+
) -> SpecificationAnnotations:
|
|
48
|
+
leading_comments = _leading_comments(comment_tokens, function)
|
|
49
|
+
|
|
50
|
+
equations = []
|
|
51
|
+
intents = []
|
|
52
|
+
for token in leading_comments:
|
|
53
|
+
for prefix, destination, label in (
|
|
54
|
+
(EQUATION_PREFIX, equations, "equation"),
|
|
55
|
+
(INTENT_PREFIX, intents, "intent"),
|
|
56
|
+
):
|
|
57
|
+
if not token.string.startswith(prefix):
|
|
58
|
+
continue
|
|
59
|
+
text = token.string.removeprefix(prefix).strip()
|
|
60
|
+
if not text:
|
|
61
|
+
raise SpecificationAnnotationError(
|
|
62
|
+
f"empty proofside {label} annotation"
|
|
63
|
+
)
|
|
64
|
+
destination.append(text)
|
|
65
|
+
break
|
|
66
|
+
|
|
67
|
+
return SpecificationAnnotations(tuple(equations), tuple(intents))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _leading_comments(
|
|
71
|
+
comment_tokens: dict[int, tokenize.TokenInfo],
|
|
72
|
+
function: ast.FunctionDef,
|
|
73
|
+
) -> tuple[tokenize.TokenInfo, ...]:
|
|
74
|
+
leading_comments = []
|
|
75
|
+
line_number = function.lineno - 1
|
|
76
|
+
while line_number in comment_tokens:
|
|
77
|
+
token = comment_tokens[line_number]
|
|
78
|
+
if token.start[1] != function.col_offset:
|
|
79
|
+
break
|
|
80
|
+
leading_comments.append(token)
|
|
81
|
+
line_number -= 1
|
|
82
|
+
leading_comments.reverse()
|
|
83
|
+
return tuple(leading_comments)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def specification_annotations_for_function(
|
|
87
|
+
source: str,
|
|
88
|
+
function: ast.FunctionDef,
|
|
89
|
+
) -> SpecificationAnnotations:
|
|
90
|
+
return _annotations_for_function(_comment_tokens(source), function)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def marked_functions_in_source(source: str) -> tuple[ast.FunctionDef, ...]:
|
|
94
|
+
tree = ast.parse(source)
|
|
95
|
+
comment_tokens = _comment_tokens(source)
|
|
96
|
+
prefixes = (EQUATION_PREFIX, INTENT_PREFIX)
|
|
97
|
+
return tuple(
|
|
98
|
+
function
|
|
99
|
+
for function in tree.body
|
|
100
|
+
if isinstance(function, ast.FunctionDef)
|
|
101
|
+
and any(
|
|
102
|
+
token.string.startswith(prefixes)
|
|
103
|
+
for token in _leading_comments(comment_tokens, function)
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def extract_specification_annotations(source: str) -> tuple[AnnotatedFunction, ...]:
|
|
109
|
+
tree = ast.parse(source)
|
|
110
|
+
comment_tokens = _comment_tokens(source)
|
|
111
|
+
|
|
112
|
+
discovered = []
|
|
113
|
+
for function in (
|
|
114
|
+
node for node in tree.body if isinstance(node, ast.FunctionDef)
|
|
115
|
+
):
|
|
116
|
+
annotations = _annotations_for_function(comment_tokens, function)
|
|
117
|
+
if annotations.equations or annotations.intents:
|
|
118
|
+
discovered.append(
|
|
119
|
+
AnnotatedFunction(
|
|
120
|
+
function.name,
|
|
121
|
+
function,
|
|
122
|
+
annotations,
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
return tuple(discovered)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def find_annotated_functions(file_path: Path) -> tuple[AnnotatedFunction, ...]:
|
|
129
|
+
return extract_specification_annotations(file_path.read_text(encoding="utf-8"))
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: proofside
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A small formal-verification sidecar for typed mathematical Python
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
Project-URL: Repository, https://github.com/kli0n2323/proofside
|
|
7
|
+
Requires-Python: <3.15,>=3.12
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: nagini==1.3.1
|
|
11
|
+
Dynamic: license-file
|
|
12
|
+
|
|
13
|
+
# Proofside
|
|
14
|
+
|
|
15
|
+
**State the math. Review the contract. Verify the code.**
|
|
16
|
+
|
|
17
|
+
Proofside is a small formal-verification sidecar for typed mathematical Python.
|
|
18
|
+
Declare the math a function is supposed to implement, review an explicit
|
|
19
|
+
contract, then ask Nagini/Viper whether the implementation satisfies it.
|
|
20
|
+
|
|
21
|
+
Proofside annotations keep equations and plain-language intent beside the code
|
|
22
|
+
but separate from the implementation body. An optional model can translate that
|
|
23
|
+
declared specification into an untrusted candidate contract without seeing the
|
|
24
|
+
body by default. No model is required: contracts may also be authored manually,
|
|
25
|
+
and a model never determines whether a proof succeeds.
|
|
26
|
+
|
|
27
|
+
## Quick start
|
|
28
|
+
|
|
29
|
+
Proofside supports 64-bit Python 3.12–3.14 and requires a 64-bit Java 11+
|
|
30
|
+
runtime. From a checkout:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
python -m venv .venv
|
|
34
|
+
# Linux/macOS: source .venv/bin/activate
|
|
35
|
+
# Windows PowerShell: .venv\Scripts\Activate.ps1
|
|
36
|
+
python -m pip install .
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Start with a reproducible model-free verification:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
proofside check examples/sidecar/shot_budget_plain.py::allocate_remaining \
|
|
43
|
+
--contract examples/sidecar/shot_budget_contract.json
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The contract assumes nonnegative counts with `first_bucket <= total_shots` and
|
|
47
|
+
guarantees a nonnegative result that conserves the declared budget. The command
|
|
48
|
+
prints the contract, `VERIFIED`, and an explicit proof boundary.
|
|
49
|
+
|
|
50
|
+
Now check the nearly identical broken implementation:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
proofside check examples/sidecar/shot_budget_plain_bad.py::allocate_remaining \
|
|
54
|
+
--contract examples/sidecar/shot_budget_contract.json
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Nagini reports `FAILED` because the extra `+ 1` prevents budget conservation.
|
|
58
|
+
Prefixing commands with `python -m proofside` is also supported.
|
|
59
|
+
|
|
60
|
+
## Examples
|
|
61
|
+
|
|
62
|
+
| Path | Purpose |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| `examples/sidecar/` | Model-free Proofside JSON contract workflow: good and bad implementations share one explicit contract. |
|
|
65
|
+
| `examples/nagini/` | Direct handwritten Nagini contracts. Proofside supports this path but does not parse Nagini annotations back into its IR. |
|
|
66
|
+
| `examples/annotated/shot_budget_annotated.py` | Smallest annotation-first specification and proposal example. |
|
|
67
|
+
| `examples/annotated/model_workflow_stress.py` | Eight-function model-assisted stress fixture with both matching and intentionally mismatched implementations. Its annotations are normative; bodies are withheld by default. |
|
|
68
|
+
| `examples/research/` | Research-derived bookkeeping example with careful, limited provenance. |
|
|
69
|
+
| `examples/unsupported/` | A source boundary that Proofside explicitly reports as unsupported. |
|
|
70
|
+
|
|
71
|
+
## Declare the intended math
|
|
72
|
+
|
|
73
|
+
Proofside recognizes two case-sensitive comments in the contiguous comment
|
|
74
|
+
block immediately above a supported function:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
# proofside equation: result = total_shots - first_bucket
|
|
78
|
+
# proofside intent: Return the unallocated portion of the declared budget.
|
|
79
|
+
def allocate_remaining(total_shots: int, first_bucket: int) -> int:
|
|
80
|
+
return total_shots - first_bucket
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Equation and intent text is opaque, user-authored specification material.
|
|
84
|
+
Proofside preserves it rather than interpreting, normalizing, or checking the
|
|
85
|
+
mathematics at annotation-extraction time. Multiple equation or intent lines
|
|
86
|
+
are allowed.
|
|
87
|
+
|
|
88
|
+
The specification says what the function is supposed to mean; the implementation
|
|
89
|
+
is the object later checked against the accepted contract. They have different
|
|
90
|
+
roles.
|
|
91
|
+
|
|
92
|
+
## From specification to proof
|
|
93
|
+
|
|
94
|
+
```text
|
|
95
|
+
user-declared specification
|
|
96
|
+
equation / intent / selected sources
|
|
97
|
+
↓
|
|
98
|
+
optional model translation
|
|
99
|
+
↓
|
|
100
|
+
candidate contract
|
|
101
|
+
NOT VERIFIED
|
|
102
|
+
↓
|
|
103
|
+
human review/edit
|
|
104
|
+
↓
|
|
105
|
+
accepted contract ─────────┐
|
|
106
|
+
NOT VERIFIED │
|
|
107
|
+
├→ selected explicit contract
|
|
108
|
+
manually authored JSON contract ─────────┘
|
|
109
|
+
NOT VERIFIED
|
|
110
|
+
+
|
|
111
|
+
implementation under test
|
|
112
|
+
↓
|
|
113
|
+
deterministic Proofside lowering
|
|
114
|
+
↓
|
|
115
|
+
Nagini / Viper / Z3
|
|
116
|
+
↓
|
|
117
|
+
VERIFIED / FAILED / UNSUPPORTED / ERROR
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Handwritten Nagini contracts provide a lower-level route directly to Nagini.
|
|
121
|
+
In every route, Nagini/Viper—not a model—decides whether proof obligations are
|
|
122
|
+
discharged.
|
|
123
|
+
|
|
124
|
+
To ask an explicitly selected model for a candidate:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
proofside propose examples/annotated/shot_budget_annotated.py::allocate_remaining \
|
|
128
|
+
--model-source api \
|
|
129
|
+
--model MODEL_NAME
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Without `--out`, this writes:
|
|
133
|
+
|
|
134
|
+
```text
|
|
135
|
+
examples/annotated/.proofside/shot_budget_annotated.allocate_remaining.candidate.json
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The response must be exact JSON. Proofside strictly parses and structurally
|
|
139
|
+
validates it, but labels it `PROPOSED — NOT VERIFIED`. Review or edit that file,
|
|
140
|
+
then record the explicit choice to submit it for verification:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
proofside accept examples/annotated/shot_budget_annotated.py::allocate_remaining
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
This retains the candidate and creates:
|
|
147
|
+
|
|
148
|
+
```text
|
|
149
|
+
examples/annotated/.proofside/shot_budget_annotated.allocate_remaining.contract.json
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Acceptance means only “accepted for verification.” It neither asserts that the
|
|
153
|
+
contract is correct nor runs the verifier. Verification remains a separate
|
|
154
|
+
action:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
proofside check examples/annotated/shot_budget_annotated.py::allocate_remaining \
|
|
158
|
+
--contract examples/annotated/.proofside/shot_budget_annotated.allocate_remaining.contract.json
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
An explicit `--out custom.json` remains available for proposal, and
|
|
162
|
+
`accept --candidate custom.json` validates a reviewed custom candidate into the
|
|
163
|
+
deterministic accepted path. `accept --replace` is required to replace an
|
|
164
|
+
existing accepted contract, and validation completes before replacement.
|
|
165
|
+
|
|
166
|
+
No model is needed for either contract route. A manually authored JSON contract
|
|
167
|
+
may be checked directly with `check --contract`; it does not have to pass through
|
|
168
|
+
`accept`. Advanced users may instead keep handwritten Nagini annotations in the
|
|
169
|
+
function:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
proofside check examples/nagini/shot_budget_good.py::allocate_remaining
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Proofside does not parse handwritten Nagini annotations into its contract IR.
|
|
176
|
+
|
|
177
|
+
## Specification sources
|
|
178
|
+
|
|
179
|
+
`propose` and `propose-all` accept repeatable `--source` choices:
|
|
180
|
+
|
|
181
|
+
- `equation` — the function's `proofside equation` lines;
|
|
182
|
+
- `intent` — the function's `proofside intent` lines;
|
|
183
|
+
- `implementation` — the selected function body, only when explicitly chosen.
|
|
184
|
+
|
|
185
|
+
With no `--source`, Proofside uses every available annotation type in the fixed
|
|
186
|
+
order `equation`, then `intent`. It never silently falls back to the
|
|
187
|
+
implementation. An unannotated function therefore requires explicit
|
|
188
|
+
`--source implementation`; sources may also be combined.
|
|
189
|
+
|
|
190
|
+
The function name, parameter names and types, and return annotation remain
|
|
191
|
+
structural context. Unless `implementation` is selected, the model does not
|
|
192
|
+
receive the body, return expression, neighboring functions, imports, tests, or
|
|
193
|
+
other repository content.
|
|
194
|
+
|
|
195
|
+
## Candidate and accepted artifacts
|
|
196
|
+
|
|
197
|
+
For `path/module.py::function`, the source-adjacent convention is:
|
|
198
|
+
|
|
199
|
+
```text
|
|
200
|
+
path/.proofside/module.function.candidate.json
|
|
201
|
+
proposed or supplied for review; unaccepted; NOT VERIFIED
|
|
202
|
+
|
|
203
|
+
path/.proofside/module.function.contract.json
|
|
204
|
+
explicitly accepted for verification; still NOT VERIFIED
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Both files contain ordinary Proofside contract JSON without approval metadata.
|
|
208
|
+
Proposal never overwrites an existing candidate, accepts it, or verifies it.
|
|
209
|
+
Acceptance validates before writing, never calls a model or verifier, and leaves
|
|
210
|
+
the candidate in place.
|
|
211
|
+
|
|
212
|
+
## Batch workflow
|
|
213
|
+
|
|
214
|
+
Batch commands are deterministic orchestration over independent single-function
|
|
215
|
+
operations. Only marked top-level functions participate; there is no batch
|
|
216
|
+
contract or multi-function proof.
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
proofside propose-all examples/annotated/ --model-source api --model MODEL_NAME
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
`propose-all` may make one sequential model request per marked function needing
|
|
223
|
+
a candidate. It uses each function's equation/intent annotations by default and
|
|
224
|
+
withholds each body unless `--source implementation` is selected. Candidates
|
|
225
|
+
are written independently; none is accepted or verified.
|
|
226
|
+
|
|
227
|
+
Review the candidate files and accept each chosen contract explicitly:
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
proofside accept research/budget.py::remaining
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Then verify all marked functions with accepted source-adjacent contracts:
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
proofside check-all research/
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
One file or function failure does not stop later independent operations. Both
|
|
240
|
+
batch commands recursively inspect only the supplied files/directories, prune
|
|
241
|
+
hidden directories, deduplicate paths, and use deterministic ordering.
|
|
242
|
+
|
|
243
|
+
The conspicuous bypass below lets `check-all` fall back to a candidate only when
|
|
244
|
+
no accepted contract exists:
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
proofside check-all research/ --allow-unreviewed
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Candidate-backed results are labeled `UNREVIEWED CONTRACT`, and the overall
|
|
251
|
+
exit remains nonzero even if every underlying proof verifies. An accepted
|
|
252
|
+
contract always wins over a candidate.
|
|
253
|
+
|
|
254
|
+
## What a result means
|
|
255
|
+
|
|
256
|
+
- `VERIFIED`: Nagini/Viper discharged the displayed proof obligations.
|
|
257
|
+
- `FAILED`: one or more obligations were not proved. This does not by itself
|
|
258
|
+
mean that a concrete counterexample was produced.
|
|
259
|
+
- `UNSUPPORTED`: Proofside recognized input outside its deliberately narrow
|
|
260
|
+
source or contract boundary.
|
|
261
|
+
- `ERROR`: malformed input, missing tooling, or another setup/runtime problem
|
|
262
|
+
prevented meaningful verification.
|
|
263
|
+
|
|
264
|
+
`UNSUPPORTED` and `ERROR` do not imply that verification occurred.
|
|
265
|
+
|
|
266
|
+
A successful run establishes that the selected implementation satisfies the
|
|
267
|
+
displayed guarantees under the displayed assumptions, within the supported
|
|
268
|
+
Nagini/Viper semantics. Preconditions remain obligations on callers.
|
|
269
|
+
|
|
270
|
+
Formal verification does not establish that the contract captures the intended
|
|
271
|
+
mathematics, that a scientific model corresponds to reality, that an algorithm
|
|
272
|
+
is useful or optimal, or that an experiment is empirically valid. The researcher
|
|
273
|
+
remains responsible for the specification and its scientific meaning.
|
|
274
|
+
|
|
275
|
+
## Supported boundary
|
|
276
|
+
|
|
277
|
+
The closed contract language supports:
|
|
278
|
+
|
|
279
|
+
- arithmetic values: parameters, integer literals, the result in
|
|
280
|
+
postconditions, addition, subtraction, negation, and multiplication by an
|
|
281
|
+
integer constant;
|
|
282
|
+
- logical formulas: `==`, `!=`, `<`, `<=`, `>`, `>=`, `and`, `or`, `not`, and
|
|
283
|
+
implication;
|
|
284
|
+
- lists of preconditions (`requires`) and postconditions (`ensures`).
|
|
285
|
+
|
|
286
|
+
This is intentionally close to quantifier-free propositional linear integer
|
|
287
|
+
arithmetic. It excludes arbitrary variable-by-variable multiplication,
|
|
288
|
+
division, powers, quantifiers, arrays, floating point, and arbitrary Python
|
|
289
|
+
expressions.
|
|
290
|
+
|
|
291
|
+
For example, piecewise intent can be preserved directly:
|
|
292
|
+
|
|
293
|
+
```text
|
|
294
|
+
value >= 0 -> result == value
|
|
295
|
+
value < 0 -> result == 0
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
That says more than weakening the function to global bounds such as
|
|
299
|
+
`result >= 0`.
|
|
300
|
+
|
|
301
|
+
It contains no raw Python or Nagini snippets.
|
|
302
|
+
|
|
303
|
+
Proofside currently selects top-level synchronous functions with complete
|
|
304
|
+
parameter and return annotations. Decorated, async, nested, ambiguous, or
|
|
305
|
+
untyped targets are rejected. Sidecar mode also rejects function docstrings and
|
|
306
|
+
one-line bodies.
|
|
307
|
+
|
|
308
|
+
The selected function must be self-contained. Sidecar source generation does
|
|
309
|
+
not preserve arbitrary imports, helpers, closures, comments, or module state.
|
|
310
|
+
Proofside does not provide verification semantics for arbitrary Python, NumPy,
|
|
311
|
+
SciPy, or research frameworks.
|
|
312
|
+
|
|
313
|
+
## Trust and model transport
|
|
314
|
+
|
|
315
|
+
Proofside strictly parses one closed contract representation, validates names
|
|
316
|
+
against the selected function, and lowers accepted JSON deterministically to a
|
|
317
|
+
temporary Nagini source. Nagini, Viper/Silicon, and Z3 perform formal
|
|
318
|
+
verification. Temporary verifier sources are removed after each run.
|
|
319
|
+
|
|
320
|
+
Model output and annotation payloads are untrusted input. A model receives only
|
|
321
|
+
the selected specification sources plus structural signature context:
|
|
322
|
+
|
|
323
|
+
- API mode sends that context remotely. The default OpenAI endpoint alone reads
|
|
324
|
+
`OPENAI_API_KEY` implicitly.
|
|
325
|
+
- A custom API endpoint must use HTTPS and explicitly name its credential with
|
|
326
|
+
`--api-key-env`; Proofside does not forward `OPENAI_API_KEY` automatically.
|
|
327
|
+
- Local mode defaults to `http://localhost:11434/v1` and is unauthenticated.
|
|
328
|
+
- Authenticated requests refuse every HTTP redirect so credentials cannot be
|
|
329
|
+
forwarded to a redirect target.
|
|
330
|
+
|
|
331
|
+
Proofside makes one non-streaming request with a finite timeout and no automatic
|
|
332
|
+
retry, repair, or verifier-feedback loop.
|
|
333
|
+
|
|
334
|
+
## Research example
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
proofside check examples/research/research_shot_budget.py::remaining_feature_shots \
|
|
338
|
+
--contract examples/research/research_shot_budget_contract.json
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
This synthetic bookkeeping kernel is inspired by two motifs in the author's
|
|
342
|
+
research workflow: finite-shot Pauli feature acquisition and train/test
|
|
343
|
+
separation. It is not copied from that implementation and does not represent a
|
|
344
|
+
canonical allocation policy. The private research repository is not needed to
|
|
345
|
+
understand or run this self-contained example.
|
|
346
|
+
|
|
347
|
+
The contract proves a nonnegative remainder and conservation of the declared
|
|
348
|
+
counts under its assumptions. It does not prove that the split is optimal, that
|
|
349
|
+
the acquisition choices are scientifically appropriate, that the method
|
|
350
|
+
improves a downstream task, or that a policy transfers to hardware.
|
|
351
|
+
|
|
352
|
+
## Development
|
|
353
|
+
|
|
354
|
+
See [`CONTRIBUTING.md`](CONTRIBUTING.md) for setup, tests, the code map, and
|
|
355
|
+
design constraints. If Nagini cannot find Java—especially on Windows—set
|
|
356
|
+
`JAVA_HOME` to the Java installation root.
|
|
357
|
+
|
|
358
|
+
See [`ROADMAP.md`](ROADMAP.md) for suggested contribution directions.
|
|
359
|
+
|
|
360
|
+
## Third-party software and license status
|
|
361
|
+
|
|
362
|
+
| Software | Version | License | Use here |
|
|
363
|
+
| --- | --- | --- | --- |
|
|
364
|
+
| [Nagini](https://github.com/marcoeilers/nagini) | 1.3.1 | MPL-2.0 | Direct pinned verification dependency |
|
|
365
|
+
| [Viper/Silicon](https://github.com/viperproject/silicon) | Bundled by Nagini 1.3.1 | MPL-2.0 | Verification backend used through Nagini |
|
|
366
|
+
| [Z3](https://github.com/Z3Prover/z3) | 4.8.7.0 on common x64 platforms | MIT | Solver installed transitively by Nagini |
|
|
367
|
+
|
|
368
|
+
Proofside depends on these projects but contains no copied or adapted
|
|
369
|
+
third-party source. Python and Java are execution prerequisites distributed
|
|
370
|
+
under their respective licenses.
|
|
371
|
+
|
|
372
|
+
Proofside is licensed under the Apache License 2.0. See [`LICENSE`](LICENSE).
|
|
373
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
proofside/__init__.py,sha256=gS0jlO3xE9XYicoQYzF_2cRYWeUT-m4UCQiPSVktbm8,69
|
|
2
|
+
proofside/__main__.py,sha256=nbsrlOGKG0yYHkJ3aEvJMIm3ouChJxGNJz_Jp3I-1gE,82
|
|
3
|
+
proofside/acceptance.py,sha256=4nTlxBCK1SBjMAajYvHAeM8EMGVrLXOrQPZnMffKalQ,2958
|
|
4
|
+
proofside/artifacts.py,sha256=mWGtZxs80ZHwT7OqvIYCEGknxrugFjaH_GoWry5EnEA,367
|
|
5
|
+
proofside/batch.py,sha256=dVjcphCUnIyQ6hUpx5lTj8PYVmOrTJdoV5hscfjjXeE,10026
|
|
6
|
+
proofside/cli.py,sha256=uAWP8EJUSDLByvo3dHN0Lpu7GTuHTtdTES7K2E4RuD0,16294
|
|
7
|
+
proofside/contracts.py,sha256=4TPe3xDZe4SVae0m_UKT4IhZA1UQcbhdp4qA5fjmxOs,13385
|
|
8
|
+
proofside/proposal.py,sha256=AHbhjaYQ-7vapLfMQxUBasZEp8fWMPeBulbFupV_I9Q,11654
|
|
9
|
+
proofside/specification.py,sha256=BchpZB6iU9Ym1GGdpMr1h8zB4nWq-gyz6Cw2plqYynE,3835
|
|
10
|
+
proofside-0.1.0.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
11
|
+
proofside-0.1.0.dist-info/METADATA,sha256=P7mARcvu_R_SxlQC7LcQNMBoMr3me5p0H9UXdLnu5zs,14879
|
|
12
|
+
proofside-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
13
|
+
proofside-0.1.0.dist-info/entry_points.txt,sha256=OeE6fCFRsqT72CS6pI0afJcI-MSEzV4A8qzd8zf7qDE,49
|
|
14
|
+
proofside-0.1.0.dist-info/top_level.txt,sha256=kO57OCmJayhZlqnrlqOhGlQDQz7YgflBYCbbZHD1mZs,10
|
|
15
|
+
proofside-0.1.0.dist-info/RECORD,,
|