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.
Files changed (40) hide show
  1. hedgemony-1.0.0/AGENT.md +279 -0
  2. hedgemony-1.0.0/LICENSE +557 -0
  3. hedgemony-1.0.0/MANIFEST.in +16 -0
  4. hedgemony-1.0.0/PKG-INFO +679 -0
  5. hedgemony-1.0.0/README.md +659 -0
  6. hedgemony-1.0.0/SKILL.md +132 -0
  7. hedgemony-1.0.0/examples/broken.py +35 -0
  8. hedgemony-1.0.0/examples/clean.py +11 -0
  9. hedgemony-1.0.0/examples/generated_contracts_hold.py +42 -0
  10. hedgemony-1.0.0/examples/generated_sample.py +28 -0
  11. hedgemony-1.0.0/examples/generated_with_contracts.py +78 -0
  12. hedgemony-1.0.0/hedgemony/__init__.py +15 -0
  13. hedgemony-1.0.0/hedgemony/__main__.py +7 -0
  14. hedgemony-1.0.0/hedgemony/_runner.py +276 -0
  15. hedgemony-1.0.0/hedgemony/board.py +113 -0
  16. hedgemony-1.0.0/hedgemony/cli.py +292 -0
  17. hedgemony-1.0.0/hedgemony/contracts.py +179 -0
  18. hedgemony-1.0.0/hedgemony/environment.py +221 -0
  19. hedgemony-1.0.0/hedgemony/report.py +371 -0
  20. hedgemony-1.0.0/hedgemony/resolve.py +227 -0
  21. hedgemony-1.0.0/hedgemony/sandbox.py +400 -0
  22. hedgemony-1.0.0/hedgemony/scan.py +321 -0
  23. hedgemony-1.0.0/hedgemony.egg-info/PKG-INFO +679 -0
  24. hedgemony-1.0.0/hedgemony.egg-info/SOURCES.txt +38 -0
  25. hedgemony-1.0.0/hedgemony.egg-info/dependency_links.txt +1 -0
  26. hedgemony-1.0.0/hedgemony.egg-info/entry_points.txt +2 -0
  27. hedgemony-1.0.0/hedgemony.egg-info/top_level.txt +1 -0
  28. hedgemony-1.0.0/pyproject.toml +46 -0
  29. hedgemony-1.0.0/setup.cfg +4 -0
  30. hedgemony-1.0.0/tests/run_all.py +44 -0
  31. hedgemony-1.0.0/tests/test_board.py +163 -0
  32. hedgemony-1.0.0/tests/test_cli.py +285 -0
  33. hedgemony-1.0.0/tests/test_contracts.py +175 -0
  34. hedgemony-1.0.0/tests/test_end_to_end.py +156 -0
  35. hedgemony-1.0.0/tests/test_environment.py +192 -0
  36. hedgemony-1.0.0/tests/test_generated.py +146 -0
  37. hedgemony-1.0.0/tests/test_proof.py +204 -0
  38. hedgemony-1.0.0/tests/test_regressions.py +242 -0
  39. hedgemony-1.0.0/tests/test_sandbox.py +285 -0
  40. hedgemony-1.0.0/tests/test_scan.py +128 -0
@@ -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.