hedgemony 1.0.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.
- hedgemony-1.0.0/AGENT.md +279 -0
- hedgemony-1.0.0/LICENSE +557 -0
- hedgemony-1.0.0/MANIFEST.in +16 -0
- hedgemony-1.0.0/PKG-INFO +679 -0
- hedgemony-1.0.0/README.md +659 -0
- hedgemony-1.0.0/SKILL.md +132 -0
- hedgemony-1.0.0/examples/broken.py +35 -0
- hedgemony-1.0.0/examples/clean.py +11 -0
- hedgemony-1.0.0/examples/generated_contracts_hold.py +42 -0
- hedgemony-1.0.0/examples/generated_sample.py +28 -0
- hedgemony-1.0.0/examples/generated_with_contracts.py +78 -0
- hedgemony-1.0.0/hedgemony/__init__.py +15 -0
- hedgemony-1.0.0/hedgemony/__main__.py +7 -0
- hedgemony-1.0.0/hedgemony/_runner.py +276 -0
- hedgemony-1.0.0/hedgemony/board.py +113 -0
- hedgemony-1.0.0/hedgemony/cli.py +292 -0
- hedgemony-1.0.0/hedgemony/contracts.py +179 -0
- hedgemony-1.0.0/hedgemony/environment.py +221 -0
- hedgemony-1.0.0/hedgemony/report.py +371 -0
- hedgemony-1.0.0/hedgemony/resolve.py +227 -0
- hedgemony-1.0.0/hedgemony/sandbox.py +400 -0
- hedgemony-1.0.0/hedgemony/scan.py +321 -0
- hedgemony-1.0.0/hedgemony.egg-info/PKG-INFO +679 -0
- hedgemony-1.0.0/hedgemony.egg-info/SOURCES.txt +38 -0
- hedgemony-1.0.0/hedgemony.egg-info/dependency_links.txt +1 -0
- hedgemony-1.0.0/hedgemony.egg-info/entry_points.txt +2 -0
- hedgemony-1.0.0/hedgemony.egg-info/top_level.txt +1 -0
- hedgemony-1.0.0/pyproject.toml +46 -0
- hedgemony-1.0.0/setup.cfg +4 -0
- hedgemony-1.0.0/tests/run_all.py +44 -0
- hedgemony-1.0.0/tests/test_board.py +163 -0
- hedgemony-1.0.0/tests/test_cli.py +285 -0
- hedgemony-1.0.0/tests/test_contracts.py +175 -0
- hedgemony-1.0.0/tests/test_end_to_end.py +156 -0
- hedgemony-1.0.0/tests/test_environment.py +192 -0
- hedgemony-1.0.0/tests/test_generated.py +146 -0
- hedgemony-1.0.0/tests/test_proof.py +204 -0
- hedgemony-1.0.0/tests/test_regressions.py +242 -0
- hedgemony-1.0.0/tests/test_sandbox.py +285 -0
- hedgemony-1.0.0/tests/test_scan.py +128 -0
hedgemony-1.0.0/AGENT.md
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# hedgemony — guide for an agent
|
|
2
|
+
|
|
3
|
+
You are an agent that writes or reviews code. `hedgemony` tells you which names in that code do not
|
|
4
|
+
exist, and which code contradicts its own stated examples. Every verdict comes from the Python
|
|
5
|
+
interpreter or a package registry. **No language model is consulted at any point**, which is
|
|
6
|
+
what makes a finding a fact rather than a second opinion.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1. The one rule that prevents harm
|
|
11
|
+
|
|
12
|
+
**A clean result is not a proof of correctness.** It means no fabricated *name* was found.
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
0 fabrications in 40 lines <- every name exists
|
|
16
|
+
<- the code may still be completely wrong
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Never report "hedgemony passed, the code is correct." Report "no fabricated names; behaviour
|
|
20
|
+
unchecked" unless contracts also ran and held. Treating silence as approval is the only way
|
|
21
|
+
this tool makes things worse.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 1b. Use these words, not "hallucination" or "lying"
|
|
26
|
+
|
|
27
|
+
When you report a finding to a person, name it precisely. Both popular words are imprecise and
|
|
28
|
+
one is simply wrong.
|
|
29
|
+
|
|
30
|
+
| word | means | decidable |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| **fabrication** | umbrella: a claim about the world that is false | yes |
|
|
33
|
+
| **invention** | the name exists nowhere (`import ghostlib`) | yes |
|
|
34
|
+
| **misattribution** | a real name on the wrong owner (`json.serialise`) | yes |
|
|
35
|
+
| **malformation** | real target, impossible call (`sqrt(2, 3)`) | yes |
|
|
36
|
+
| **contradiction** | code disagrees with its own stated example | yes |
|
|
37
|
+
| **confabulation** | plausible wrong logic, nothing stated to check it | **no — not detected** |
|
|
38
|
+
|
|
39
|
+
**Never say the model "lied."** Lying requires intent; a model has none. Report truth value:
|
|
40
|
+
*this name does not exist*, *this example did not hold*. Do not attribute motive.
|
|
41
|
+
|
|
42
|
+
**Never say "no hallucinations found."** Say *no fabricated names found*. Confabulation — the
|
|
43
|
+
last row — is invisible to every check here, so a clean scan is silent about it rather than
|
|
44
|
+
clearing it.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 2. When to run it
|
|
49
|
+
|
|
50
|
+
| situation | run |
|
|
51
|
+
|---|---|
|
|
52
|
+
| you just wrote code using a library you are not certain about | always |
|
|
53
|
+
| you are about to hand code to a user | always |
|
|
54
|
+
| a dependency failed to import and you must decide install-vs-rewrite | always |
|
|
55
|
+
| you are reviewing code someone else generated | always |
|
|
56
|
+
| you changed only comments, strings, or formatting | skip |
|
|
57
|
+
|
|
58
|
+
Run it **before** you present code, not after the user reports a failure.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 3. Reading the output
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
hedgemony app.py
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
app.py
|
|
70
|
+
2 fabrication(s) in 21 lines = 9.5 per 100 lines
|
|
71
|
+
no stated examples in this file, so its behaviour was not checked at all
|
|
72
|
+
|
|
73
|
+
line 12 ATTR `console` has no attribute `table`
|
|
74
|
+
line 19 ATTR `console` has no attribute `progress`
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### The six name classes split into exactly two actions
|
|
78
|
+
|
|
79
|
+
| class | means | your action |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| `PACKAGE` | no such package was ever published | **rewrite** — do not try to install it |
|
|
82
|
+
| `MODPATH` | the package is real, that submodule is not | **rewrite** the import path |
|
|
83
|
+
| `IMPORT` | the module is real, it does not export that name | **rewrite** — find the real name |
|
|
84
|
+
| `ATTR` | the object has no such attribute | **rewrite** — find the real method |
|
|
85
|
+
| `KWARG` | the function is real, that keyword is not | **fix the call** — cheap |
|
|
86
|
+
| `ARITY` | the function is real, that argument count is not | **fix the call** — cheap |
|
|
87
|
+
|
|
88
|
+
The split is the useful part. `PACKAGE`/`MODPATH`/`IMPORT`/`ATTR` mean *the thing does not
|
|
89
|
+
exist* — the design is wrong. `KWARG`/`ARITY` mean *the call is wrong* — a one-line fix.
|
|
90
|
+
|
|
91
|
+
### The distinction nothing else gives you
|
|
92
|
+
|
|
93
|
+
A type checker reports the same error for both of these:
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
from humanize import naturalsize # real package, not installed here
|
|
97
|
+
import ghostlib # never existed
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`hedgemony --online` separates them. `humanize` → **install it**. `ghostlib` → **rewrite, it is
|
|
101
|
+
imaginary.** Getting this backwards wastes a turn either way.
|
|
102
|
+
|
|
103
|
+
### The seventh class: `CONTRACT`
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
line 11 CONTRACT `pages_needed(10, 3)` was stated to give `4` but gave `3`
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Every name exists. The code contradicts an example written in its own docstring. The tool does
|
|
110
|
+
**not** know which side is wrong — the docstring may be the mistake. Read both, decide, fix
|
|
111
|
+
one. Never silently change the docstring to match the code; that erases the evidence.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 3b. `NOT CHECKED` means blind, not clean
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
0 fabrication(s) in 21 lines = 0.0 per 100 lines
|
|
119
|
+
NOT CHECKED: rich — not installed in the interpreter used
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Zero findings **because nothing could be looked at.** A package that is not installed cannot be
|
|
123
|
+
asked about, so every name reached through it went unexamined.
|
|
124
|
+
|
|
125
|
+
hedgemony handles this itself: it runs the scan inside the interpreter that owns the code,
|
|
126
|
+
finding a `.venv` beside it automatically, or wherever `--python` points. It does not need to
|
|
127
|
+
be installed in that environment.
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
hedgemony src/ --python /path/to/project/.venv/bin/python
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**If you see `unchecked_imports` in the JSON and it is not empty, do not report the file as
|
|
134
|
+
clean.** Re-run against the right interpreter, or say plainly which packages went unexamined.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 4. Contract states — two of them are not verdicts
|
|
139
|
+
|
|
140
|
+
| status | meaning | what you do |
|
|
141
|
+
|---|---|---|
|
|
142
|
+
| `OK` | every stated example held | behaviour is checked and holds |
|
|
143
|
+
| `VIOLATED` | an example did not hold | fix the code or the example |
|
|
144
|
+
| `NO_CONTRACT` | the file states nothing checkable | **behaviour is unknown** — add a `>>>` example |
|
|
145
|
+
| `UNCHECKED` | the run hit a limit or the file would not import | **behaviour is unknown** — investigate |
|
|
146
|
+
|
|
147
|
+
`NO_CONTRACT` and `UNCHECKED` are *unknown*, never *fine*. If you need behavioural assurance
|
|
148
|
+
and get either, you do not have it.
|
|
149
|
+
|
|
150
|
+
**Making a file checkable costs one line.** Add an example to the docstring:
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
def pages_needed(items, per_page):
|
|
154
|
+
"""How many pages are needed to show every item.
|
|
155
|
+
|
|
156
|
+
>>> pages_needed(10, 3)
|
|
157
|
+
4
|
|
158
|
+
"""
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
That single line turns `NO_CONTRACT` into a real check. When you generate a function, generate
|
|
162
|
+
the example with it.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## 5. The rate is an escalation signal
|
|
167
|
+
|
|
168
|
+
`fabrications per 100 lines` is a scalar, so you can threshold on it:
|
|
169
|
+
|
|
170
|
+
| rate | reading | action |
|
|
171
|
+
|---|---|---|
|
|
172
|
+
| `0.0` | no invented names | proceed — behaviour still unverified |
|
|
173
|
+
| `0 < r ≤ 3` | isolated slips | fix them in place |
|
|
174
|
+
| `r > 3` | the model is guessing at this library | **stop patching.** Read the real API, or escalate to a stronger model |
|
|
175
|
+
|
|
176
|
+
Above roughly 3 per 100, fixing findings one at a time is usually the wrong move — the output
|
|
177
|
+
is unreliable about that library as a whole, and the next generation will invent something new.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## 6. Commands
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
hedgemony app.py # one file
|
|
185
|
+
hedgemony src/ # a directory, recursively
|
|
186
|
+
hedgemony src/ --json # machine-readable — parse this, not the text
|
|
187
|
+
hedgemony src/ --quiet # only files with findings
|
|
188
|
+
hedgemony app.py --report md # write a full annotated copy beside the file
|
|
189
|
+
hedgemony app.py --report html # same, as a self-contained page
|
|
190
|
+
hedgemony app.py --no-run # never run the checked file; names only
|
|
191
|
+
hedgemony app.py --online # also ask registries about uninstalled packages
|
|
192
|
+
hedgemony app.py --report both # one of each
|
|
193
|
+
hedgemony app.py --python PATH # use the interpreter that owns the code
|
|
194
|
+
hedgemony --board a/ b/ c/ # rank directories by defects per 100 lines
|
|
195
|
+
hedgemony --board a/ b/ --out board.html # the extension picks the format
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Exit codes: **0** nothing found · **1** findings · **2** the tool could not run.
|
|
199
|
+
|
|
200
|
+
### `--board`
|
|
201
|
+
|
|
202
|
+
Ranks directories against each other by **defects per 100 lines** — invented names *plus*
|
|
203
|
+
stated examples that failed. Use it to compare two models, two prompting strategies, or the
|
|
204
|
+
same source over time. Nothing is generated and no model is contacted; it reads code already on
|
|
205
|
+
disk, so it scores whatever you save into folders.
|
|
206
|
+
|
|
207
|
+
Read the line counts with the rate. A source that attempted less scores better for it, and the
|
|
208
|
+
rate measures defects, not capability.
|
|
209
|
+
|
|
210
|
+
### JSON shape
|
|
211
|
+
|
|
212
|
+
```json
|
|
213
|
+
{
|
|
214
|
+
"app.py": {
|
|
215
|
+
"language": "python",
|
|
216
|
+
"lines": 21,
|
|
217
|
+
"rate": 9.52,
|
|
218
|
+
"findings": [
|
|
219
|
+
{"line": 12, "kind": "ATTR", "token": "console.table",
|
|
220
|
+
"detail": "`console` has no attribute `table`"}
|
|
221
|
+
],
|
|
222
|
+
"unchecked_imports": ["rich"],
|
|
223
|
+
"contracts": {"status": "NO_CONTRACT", "examples": 0, "findings": [], "error": null}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
`unchecked_imports` lists packages the interpreter did not have. **Anything named there went
|
|
229
|
+
unexamined** — do not report the file as clean while that list is non-empty.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## 7. Safety — what running this does to the machine
|
|
234
|
+
|
|
235
|
+
Checking contracts means **executing the file**. Everything else is decided by parsing it.
|
|
236
|
+
|
|
237
|
+
One thing does run either way, and it is stated plainly because the distinction matters:
|
|
238
|
+
deciding whether `json` exports `serialise` means importing `json`, and importing any module
|
|
239
|
+
runs that module's top-level code. So **the checked file's dependencies are imported**, and a
|
|
240
|
+
dependency with import-time side effects will perform them. The checked file itself is not
|
|
241
|
+
run unless its contracts are being checked.
|
|
242
|
+
|
|
243
|
+
Every execution happens in a separate interpreter that is bounded on CPU, memory, process
|
|
244
|
+
count, file size and wall time, is refused network access, gets a stripped environment (your
|
|
245
|
+
tokens and keys are not visible to it), and works in a directory deleted afterwards.
|
|
246
|
+
|
|
247
|
+
- A file with **no stated examples is never executed at all** — that is decided by parsing.
|
|
248
|
+
- `--no-run` never runs the checked file. Its dependencies are still imported, as above.
|
|
249
|
+
- The default is **offline**. `--online` sends package *names* — which came from generated
|
|
250
|
+
code — to a public registry. Consider that before enabling it in a loop.
|
|
251
|
+
|
|
252
|
+
This bounds accidents. It is **not** a security boundary against code deliberately trying to
|
|
253
|
+
escape. If the code may be hostile rather than merely wrong, run the whole tool in a container.
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## 7b. Liability
|
|
258
|
+
|
|
259
|
+
This software is provided **as is, without warranty of any kind**. The sandbox limits the
|
|
260
|
+
blast radius of an accident; it is **not** a security boundary against code deliberately
|
|
261
|
+
trying to escape, and no liability is accepted for any breach or containment failure. If
|
|
262
|
+
the code may be hostile rather than merely wrong, use `--no-run` (which runs no checked file)
|
|
263
|
+
or run inside a container. See the LICENSE, sections 15–17.
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## 8. Rules
|
|
268
|
+
|
|
269
|
+
- **R1** Run hedgemony before presenting generated code that touches a library.
|
|
270
|
+
- **R2** Never say "correct" on the basis of a clean scan. Say "no fabricated names."
|
|
271
|
+
- **R3** `PACKAGE` means rewrite, never install. `IMPORT`/`ATTR` mean the name is imaginary.
|
|
272
|
+
- **R4** `KWARG`/`ARITY` are cheap fixes — the function is real.
|
|
273
|
+
- **R5** On `CONTRACT`, read both sides. Never edit the docstring to match the code.
|
|
274
|
+
- **R6** Treat `NO_CONTRACT` and `UNCHECKED` as unknown, never as pass.
|
|
275
|
+
- **R7** Above ~3 per 100 lines, stop patching and go read the real API.
|
|
276
|
+
- **R8** Generate a `>>>` example with every function you write. It costs one line and it is
|
|
277
|
+
the only thing that makes behaviour checkable.
|
|
278
|
+
- **R9** Parse `--json`. Do not scrape the human output.
|
|
279
|
+
- **R10** Leave `--online` off inside automated loops unless you need the package distinction.
|