hypgraph 0.1.0__tar.gz → 0.3.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 (47) hide show
  1. {hypgraph-0.1.0 → hypgraph-0.3.0}/AGENTS.md +10 -2
  2. hypgraph-0.3.0/CHANGELOG.md +88 -0
  3. {hypgraph-0.1.0 → hypgraph-0.3.0}/CITATION.cff +3 -1
  4. hypgraph-0.3.0/NOTICE +8 -0
  5. {hypgraph-0.1.0 → hypgraph-0.3.0}/PKG-INFO +83 -12
  6. {hypgraph-0.1.0 → hypgraph-0.3.0}/README.md +79 -10
  7. {hypgraph-0.1.0 → hypgraph-0.3.0}/docs/spec.md +71 -13
  8. {hypgraph-0.1.0 → hypgraph-0.3.0}/pyproject.toml +4 -3
  9. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/__init__.py +1 -1
  10. hypgraph-0.3.0/src/hypgraph/checks.py +370 -0
  11. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/cli.py +64 -2
  12. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/derive.py +56 -0
  13. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/graph.py +89 -11
  14. hypgraph-0.3.0/src/hypgraph/language.py +59 -0
  15. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/mcp.py +62 -3
  16. hypgraph-0.3.0/src/hypgraph/refusals.py +182 -0
  17. hypgraph-0.3.0/src/hypgraph/report_check.py +271 -0
  18. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/schema.py +2 -1
  19. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/server.py +7 -0
  20. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/skill/SKILL.md +39 -11
  21. hypgraph-0.3.0/src/hypgraph/stats.py +91 -0
  22. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/ui/atlas.js +22 -0
  23. hypgraph-0.3.0/tests/test_log_and_check.py +205 -0
  24. hypgraph-0.3.0/tests/test_refusals.py +174 -0
  25. {hypgraph-0.1.0 → hypgraph-0.3.0}/tests/test_surfaces.py +2 -1
  26. hypgraph-0.1.0/src/hypgraph/checks.py +0 -140
  27. {hypgraph-0.1.0 → hypgraph-0.3.0}/.gitignore +0 -0
  28. {hypgraph-0.1.0 → hypgraph-0.3.0}/LICENSE +0 -0
  29. {hypgraph-0.1.0 → hypgraph-0.3.0}/docs/PLAN.md +0 -0
  30. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/__main__.py +0 -0
  31. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/bundle.py +0 -0
  32. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/demos/__init__.py +0 -0
  33. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/demos/broad_street.py +0 -0
  34. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/plan.py +0 -0
  35. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/policy.py +0 -0
  36. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/store.py +0 -0
  37. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/ui/__init__.py +0 -0
  38. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/ui/atlas.css +0 -0
  39. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/ui/hypgraph.svg +0 -0
  40. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/ui/index.html +0 -0
  41. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/ui/vendor/LICENSES.md +0 -0
  42. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/ui/vendor/cose-base.js +0 -0
  43. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/ui/vendor/cytoscape-fcose.js +0 -0
  44. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/ui/vendor/cytoscape.min.js +0 -0
  45. {hypgraph-0.1.0 → hypgraph-0.3.0}/src/hypgraph/ui/vendor/layout-base.js +0 -0
  46. {hypgraph-0.1.0 → hypgraph-0.3.0}/tests/test_engine.py +0 -0
  47. {hypgraph-0.1.0 → hypgraph-0.3.0}/tests/test_incremental.py +0 -0
@@ -56,12 +56,20 @@ reserved.
56
56
  ```
57
57
  with `links=[{"rel": "supports", "from": "RS3", "why": "RS3 is the measurement this claim states."}]`.
58
58
  - every number in the statement must match a bound measurement, the scope, or an investigation constant;
59
- - "never", "all", "always" or "proves" need a measurement with k = 0 or k = n;
59
+ - "never", "always", "eliminates", "100%" need a count of what they're about with k = 0 or k = n, enough
60
+ trials that the 95% bound is under 5%, and n in the statement ("never, in 3,000 probes"). Check first:
61
+ `bound(k, n, exposure)` says what a count supports and how many failures it leaves unruled out at scale;
62
+ - "beats" / "outperforms" need both sides measured; "significantly" needs a test (the record runs one on two
63
+ bound counts); "causes" needs an experiment that intervened;
64
+ - numbers computed from bound counts (differences, percentages, ratios, intervals) are fine; invented ones
65
+ are not;
60
66
  - `kind: "exclusion"` needs a result with `power: "demonstrated"` (a search that found a planted answer).
61
67
  Otherwise use `"inconclusive"`.
68
+ Each refusal ends in a code (`[ABSOLUTE_UNDERPOWERED]`) and already gives the honest sentence: use it. Refusals
69
+ are logged per author (`refusal_log`); coming back with the honest version marks yours fixed.
62
70
  6. **Reviews.** Only reviewers named by the investigation (or the host's policy) set standing. Nobody reviews their
63
71
  own work. A revision makes earlier reviews stale.
64
- 7. **Start summaries from `report()`.** It's generated from the record, so it can't restate a withdrawn claim. Write
72
+ 7. **Start summaries from `report()`, and run `check_report(text)` on the write-up before handing it over.** It's generated from the record, so it can't restate a withdrawn claim. Write
65
73
  your interpretation underneath it.
66
74
 
67
75
  ## Reading standing
@@ -0,0 +1,88 @@
1
+ # Changelog
2
+
3
+ ## 0.3.0 — 2026-10-24
4
+
5
+ - **The refusal log.** Every refused claim is logged: author, statement, codes, messages. A later accepted claim by
6
+ the same author that says the same thing (same node, or ≥ 30% shared content words within 24 hours) marks the
7
+ refusal fixed. Append-only, with its own hash chain (`verify_refusals()`); the record's chain is untouched.
8
+ - API: `refusals()`, `refusal_report()`.
9
+ - CLI: `hypgraph refusals [--author …]`. MCP: `refusal_log`. HTTP: `GET /api/graph/refusals`.
10
+ - Atlas: an "Overclaims caught" card in the Work view.
11
+ - **`hypgraph check report.md`** lints a write-up against the record (`Graph.check_text`; MCP `check_report`; HTTP
12
+ `POST /api/graph/check`). Exits 1 on errors (`--strict`: warnings too).
13
+ - Errors: citations of withdrawn nodes, settled language about unchecked, contested or at-risk work, unknown
14
+ codes, and unbacked never / no effect / significantly.
15
+ - Warnings: numbers no result contains or implies, omitted disputes, superseded citations, uncited causal
16
+ language.
17
+ - Calibrated on 24 real PegasusDMZ write-ups against its live record. That fixed negations ("cannot be proven"),
18
+ quoted passages under correction, instructions ("never use the eval set"), p-values given in the sentence, and
19
+ "results in" as a noun phrase.
20
+ - An absolute whose statement gives its n ("no leaks in 50 prompts", "nothing in 50 of 50") is scoped to what was
21
+ seen and accepted; its standing still carries the bound. Without n it is refused, as before.
22
+ - Spec draft 3 (§8 refusal log, §9 checking a write-up).
23
+
24
+ ## 0.2.0 — 2026-10-24
25
+
26
+ **Refusal, remastered.** The claim checks now do the arithmetic the writer skipped, and every refusal says what the
27
+ count actually supports and offers the honest sentence.
28
+
29
+ - **The arithmetic of never.** "Never" / "always" / "eliminates" / "100%" need a count of what they're about with
30
+ k = 0 or n (`ABSOLUTE_WITHOUT_COUNT`, `ABSOLUTE_UNRELATED_COUNT`). That count needs a 95% bound under 5%
31
+ (`ABSOLUTE_UNDERPOWERED`: "0 of 3 only rules out more than 63%"), and the statement has to show its denominator
32
+ (`ABSOLUTE_HIDES_DENOMINATOR`). The bounds are exact at k = 0 (1 − 0.05^(1/n), the rule of three) and Wilson
33
+ otherwise.
34
+ - **Exposure.** A claim can say how many uses, users or units it applies to (`exposure`, `exposure_unit`). Refusals
35
+ and the claim's standing then state how many failures the bound leaves unruled out: "up to 58,155 of 1,000,000".
36
+ - **New rules:** `COMPARATIVE_WITHOUT_COMPARISON`, `SIGNIFICANCE_WITHOUT_TEST`, `SIGNIFICANCE_NOT_SHOWN` (the
37
+ record runs a two-proportion test on bound counts itself), `CAUSAL_WITHOUT_INTERVENTION`, and
38
+ `EXCLUSION_WITHOUT_POWER` for "no effect" however it is filed, with the smallest effect the sample could have
39
+ detected.
40
+ - **True numbers pass.** Differences, ratios, percentages and relative changes computed from bound measurements are
41
+ accepted ("2.8 points", "81.2% vs 78.4%", "+3.6%"). Statistical quantities must be written to one decimal place, so
42
+ "5 points" can't borrow an interval's end.
43
+ - **Refusals agents can act on:** a stable code on every line, and `GraphError.problems` with `code`, `message`,
44
+ `fix` and `words`.
45
+ - **Blast radius.** `withdraw()`, and a review that makes a node fall, return `blast_radius`: what rests on it, by
46
+ type, with claims and decisions named (and spend, if metered). The CLI and MCP print it.
47
+ - **`hypgraph bound K N --exposure X`** and the MCP tool `bound`: what a count supports, before anyone writes "never".
48
+ - A claim's standing carries `bounds`, and `test` for "significantly" claims. Spec draft 2 (§7 rewritten).
49
+ - Credits: made by Nicolas Houghtaling at VoodooMods LLC (README, NOTICE, CITATION.cff, package metadata, site).
50
+
51
+ ## 0.1.0 — 2026-10-24
52
+
53
+ First release on PyPI.
54
+
55
+ - **Writes cost what they touch.** A write no longer copies the whole in-memory snapshot. Tables are shallow
56
+ copies, and a node's list is copied only when the write appends to it. Derived standing is kept for every node the
57
+ write can't affect (`derive.affected_by`), so the first full read after a write no longer re-derives the graph.
58
+ At 10,000 nodes: building the graph took 77 s instead of 133 s, a full read after a write 0.02 s instead of
59
+ 0.17 s, and a write at 5,000 nodes 5.8 ms instead of 12 ms. A randomized suite checks the carried standing
60
+ against a full rebuild after every kind of write.
61
+ - New indexes for display codes and the rate limit (`nodes(inv, type)`, `nodes(inv, code)`, `events(actor, kind,
62
+ at)`). Created on open; existing records just gain them.
63
+ - Positioning: the evidence layer for AI research agents. README and site lead with the MCP server and the Agent
64
+ Skill; a new engineering guide (`/engineers/`) replaces the human guide; the eval-report example replaces the
65
+ classroom one.
66
+
67
+ ### Extracted from PegasusDMZ
68
+
69
+ Extracted from the PegasusDMZ research graph and verified against it: on a copy of Pegasus's live
70
+ graph (1,357 events, 790 nodes in 5 investigations), the library reproduces the same chain head, every node's
71
+ standing, the work queues and the next-experiment rankings.
72
+
73
+ - **Engine** (stdlib only): typed nodes and edges, append-only SQLite with a SHA-256 event chain and `verify()`,
74
+ standing derived on read, claim checks on write, review pinned to versions, independence and evidence profile,
75
+ leases, next experiments, work map, digest, report.
76
+ - **Policy**: reviewers, instrument, operator, importer and system are the host's to set (they were hardcoded in
77
+ Pegasus). Optional `spend` hook for cost. Injectable `clock`; only the importer may set a write's time.
78
+ - **Bundles** (`hypgraph/1-bundle`): every event and body as the exact hashed string, so they can be verified in any
79
+ language. Scoped exports redact events outside the investigation and say so. A complete bundle replays row for row.
80
+ - **CLI**: `init`, `add`, `link`, `review`, `revise`, `withdraw`, `ls`, `show --why/--impact/--history`, `next`, `work`,
81
+ `report`, `export`, `import`, `verify`, `demo`, `schema`, `serve`, `mcp`, `skill`.
82
+ - **MCP server** (FastMCP, `hypgraph[mcp]`): 17 tools, 4 resources, a `method` prompt. Writes as one fixed author.
83
+ - **Agent skill**: `SKILL.md` ships in the package; `hypgraph skill --install`.
84
+ - **Atlas**: the framework-free explorer, with an HTTP source and a bundle source (it verifies the chain in the
85
+ browser). Cytoscape is vendored.
86
+ - **Demo**: Broad Street, 1854 — a teaching reconstruction with three refusals built into the story.
87
+ - Fixed while extracting it: a report listed stale-reviewed claims under the wrong key; claim measurements now accept
88
+ result codes (RS3) and store ids.
@@ -10,8 +10,10 @@ type: software
10
10
  authors:
11
11
  - family-names: Houghtaling
12
12
  given-names: Nicolas
13
+ affiliation: VoodooMods LLC
14
+ - name: VoodooMods LLC
13
15
  license: Apache-2.0
14
- version: 0.1.0
16
+ version: 0.3.0
15
17
  date-released: "2026-10-24"
16
18
  url: "https://hypgraph.com"
17
19
  repository-code: "https://github.com/voodoomods/hypgraph"
hypgraph-0.3.0/NOTICE ADDED
@@ -0,0 +1,8 @@
1
+ HypGraph
2
+ Copyright 2026 Nicolas Houghtaling and VoodooMods LLC
3
+
4
+ This product is licensed under the Apache License, Version 2.0 (see LICENSE).
5
+ The record format specification and documentation are licensed under CC BY 4.0.
6
+
7
+ This product includes software developed by third parties, shipped in src/hypgraph/ui/vendor/ under the MIT
8
+ license: Cytoscape.js (The Cytoscape Consortium), layout-base, cose-base and cytoscape-fcose (iVis@Bilkent).
@@ -1,14 +1,16 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: hypgraph
3
- Version: 0.1.0
3
+ Version: 0.3.0
4
4
  Summary: The evidence layer for AI research agents: a hash-chained research record that checks every claim as it is written and derives how well each is known.
5
5
  Project-URL: Homepage, https://hypgraph.com
6
6
  Project-URL: Documentation, https://hypgraph.com/engineers/
7
7
  Project-URL: Changelog, https://github.com/voodoomods/hypgraph/blob/main/CHANGELOG.md
8
8
  Project-URL: Source, https://github.com/voodoomods/hypgraph
9
- Author: Nicolas Houghtaling
9
+ Author: Nicolas Houghtaling, VoodooMods LLC
10
+ Maintainer: VoodooMods LLC
10
11
  License-Expression: Apache-2.0
11
12
  License-File: LICENSE
13
+ License-File: NOTICE
12
14
  Keywords: agent-skills,ai-agents,evaluation,hypothesis,mcp,provenance,reproducibility,research
13
15
  Classifier: Development Status :: 3 - Alpha
14
16
  Classifier: Intended Audience :: Developers
@@ -38,21 +40,86 @@ The evidence layer for AI research agents.<br>
38
40
 
39
41
  ---
40
42
 
41
- Agents doing research write fluent summaries. Some of the numbers in them match no result. "Always" gets written
42
- about one run, a search too weak to find anything becomes "ruled out", and three copies of one result get counted as
43
- three lines of evidence. HypGraph is the record they write into instead. It checks every claim as it's written, so
44
- those claims can't get in:
43
+ Agents doing research write fluent summaries. Some of the numbers in them match no result. A clean red-team run of
44
+ 50 prompts becomes "the model never leaks". A search too weak to find anything becomes "ruled out", and three copies
45
+ of one result get counted as three lines of evidence. Then other claims, decisions and deployments get built on top.
46
+ HypGraph is the record agents write into instead. It checks every claim as it's written, does the arithmetic the
47
+ writer skipped, and refuses:
45
48
 
46
49
  ```text
47
- $ hypgraph add claim statement="The fine-tune is 5 points better than the baseline." scope="2,000 held-out items, greedy decoding, one seed." --link "supports:from:RS1:the eval run"
50
+ $ hypgraph add claim statement="The model never leaks training data." exposure=1000000 exposure_unit=users …
48
51
  refused: claim refused:
49
- - numbers in the statement that no bound measurement, the scope or the investigation's constants account for: 5 — bind them to result fields (measurements) or move design constants into the scope
52
+ - 'never' rests on 0 of 50: that only rules out it happening more than 5.8% of the time (95% upper bound).
53
+ Honest version: 'no occurrences in 50 trials (95% upper bound 5.8%)'. To support a rate below 1 in 1,000 you need
54
+ about 2,995 trials without one. Across the 1,000,000 users this claim applies to, that leaves up to 58,155 not
55
+ ruled out. [ABSOLUTE_UNDERPOWERED]
50
56
  ```
51
57
 
52
- (The result said 1,624 against 1,568 correct out of 2,000. That's 2.8 points.)
58
+ The honest version goes in, and it carries its consequence wherever it's read:
53
59
 
54
- Every refusal says what to fix, so an agent can act on it. Nothing in the record is ever *set* as known. How well
55
- each claim is known is **derived on every read**:
60
+ ```text
61
+ active claim · unchecked · 1 independent line of support · … · 0 of 50 = at most 5.8% at 95%, up to 58,155 of 1,000,000 not ruled out
62
+ ```
63
+
64
+ ## The arithmetic of never
65
+
66
+ "Never" is the most expensive word in research, because everything built on it inherits it. Zero failures in *n*
67
+ trials doesn't mean a rate of zero. It means a rate below 1 − 0.05^(1/*n*), about 3/*n*, at 95% confidence. HypGraph
68
+ holds every claim to that:
69
+
70
+ | The claim says | Refused as | Because |
71
+ |---|---|---|
72
+ | "never leaks", from 0 of 50 | `ABSOLUTE_UNDERPOWERED` | it could still be 5.8%, up to 58,155 of a million users |
73
+ | "never leaks", from 0 of 3,000, without saying 3,000 | `ABSOLUTE_HIDES_DENOMINATOR` | readers hear "never", but it means "at most 1 in 1,000" |
74
+ | "eliminates hallucinations entirely" | `ABSOLUTE_WITHOUT_COUNT` | nothing was counted |
75
+ | "5 points better" (the counts say 2.8) | `UNBOUND_NUMBER` | the number comes from nowhere. 2.8 points, 81.2% vs 78.4% and +3.6% all pass, because they follow from the counts. |
76
+ | "significantly better", counts 812/1,000 vs 800/1,000 | `SIGNIFICANCE_NOT_SHOWN` | the record runs the test: p = 0.5 |
77
+ | "prompt format has no effect" | `EXCLUSION_WITHOUT_POWER` | at 2,000 items a 3.6-point effect would usually go unseen |
78
+ | "longer prompts cause the gain" | `CAUSAL_WITHOUT_INTERVENTION` | no experiment intervened |
79
+ | "outperforms the baseline", no numbers | `COMPARATIVE_WITHOUT_COMPARISON` | only one side was measured |
80
+
81
+ Every refusal has a stable code, the arithmetic, and the sentence that *would* be honest, so an agent can repair a
82
+ claim in one step. `hypgraph bound 0 50 --exposure 1e6` (or the MCP tool `bound`) answers "what does my count
83
+ actually support?" before anyone writes the word.
84
+
85
+ Every refusal is also **logged**: who tried to say what, which rules caught it, and whether they came back with
86
+ the honest version.
87
+
88
+ ```text
89
+ $ hypgraph refusals
90
+ ## Overclaims caught — 4 refused, 3 came back honest
91
+ | author | refused | fixed | open | most common |
92
+ | analyst | 4 | 3 | 1 | EXCLUSION_WITHOUT_POWER ×1, ABSOLUTE_UNDERPOWERED ×1, UNBOUND_NUMBER ×1 |
93
+ ### Latest
94
+ - analyst: “Prompt format makes no difference to accuracy.” — EXCLUSION_WITHOUT_POWER → not fixed
95
+ - analyst: “The model never leaks training data.” — ABSOLUTE_UNDERPOWERED → fixed as CL2
96
+ - analyst: “The fine-tune is 5 points better than the baseline.” — UNBOUND_NUMBER → fixed as CL1
97
+ - analyst: “The fine-tune always beats the baseline.” — ABSOLUTE_WITHOUT_COUNT, COMPARATIVE_WITHOUT_COMPARISON → fixed as CL1
98
+ ```
99
+
100
+ And because most hype lives in the write-up, not the record, **`hypgraph check summary.md`** lints the final report
101
+ against the record. It catches numbers no result contains, claims called "established" that the record has as
102
+ unchecked or contested, citations to withdrawn claims, and "never" / "no effect" / "significantly" with nothing
103
+ behind them. It exits 1 on errors, so it can gate CI:
104
+
105
+ ```text
106
+ $ hypgraph check summary.md
107
+ summary.md:8: error REPORT_OVERSTATES_STANDING: 'established' — but CL2 is unchecked in the record …
108
+ summary.md:9: warning REPORT_UNSOURCED_NUMBER: 700 isn't in any result, measurement, claim, scope or constant …
109
+ summary.md:13: error REPORT_UNKNOWN_CODE: CL9 isn't in the record for this investigation — a citation to nothing.
110
+ ```
111
+
112
+ And when something does fall, the record says what falls with it:
113
+
114
+ ```text
115
+ $ hypgraph withdraw CL1 --why "the held-out set leaked into training"
116
+ CL1 — withdrawn claim · …
117
+ ⚠ 2 nodes rest on CL1: 1 decision (D1); 1 claim (CL2) — now AT RISK
118
+ ```
119
+
120
+ ## Derived, never asserted
121
+
122
+ Nothing in the record is ever *set* as known. How well each claim is known is **derived on every read**:
56
123
 
57
124
  - **reviewed, and which version?** Only named reviewers count, and never on their own work. A revised claim goes back
58
125
  to unchecked.
@@ -76,7 +143,7 @@ hypgraph skill --install # +
76
143
  The server writes as one author, fixed when it starts. The agent can't pass another name or write as your job runner,
77
144
  and its verdicts count only if you name it as a reviewer. Tools: `overview`, `find`, `show`, `start_investigation`,
78
145
  `add`, `link`, `revise`, `withdraw`, `retract_link`, `review`, `next_experiments`, `work_queue`, `claim_work`,
79
- `register_run`, `report`, `verify`, `schema`. The method is in [AGENTS.md](https://github.com/voodoomods/hypgraph/blob/main/AGENTS.md); the short version is
146
+ `register_run`, `bound`, `check_report`, `refusal_log`, `report`, `verify`, `schema`. The method is in [AGENTS.md](https://github.com/voodoomods/hypgraph/blob/main/AGENTS.md); the short version is
80
147
  [llms.txt](https://hypgraph.com/llms.txt).
81
148
 
82
149
  ## Wire it into your system
@@ -134,6 +201,10 @@ standing takes 0.09 s, reading one node 0.3 ms, and a write about 10 ms, mostly
134
201
  costs what it touches: cached standing is kept for every node the write can't affect, and a randomized suite checks
135
202
  that against a full rebuild.
136
203
 
204
+ ## Made by
205
+
206
+ HypGraph is made by **Nicolas Houghtaling** at **[VoodooMods LLC](https://github.com/voodoomods)**.
207
+
137
208
  ## License
138
209
 
139
210
  Code: [Apache-2.0](https://github.com/voodoomods/hypgraph/blob/main/LICENSE). The record format ([docs/spec.md](https://github.com/voodoomods/hypgraph/blob/main/docs/spec.md)) and the docs: CC BY 4.0. Cite it with
@@ -8,21 +8,86 @@ The evidence layer for AI research agents.<br>
8
8
 
9
9
  ---
10
10
 
11
- Agents doing research write fluent summaries. Some of the numbers in them match no result. "Always" gets written
12
- about one run, a search too weak to find anything becomes "ruled out", and three copies of one result get counted as
13
- three lines of evidence. HypGraph is the record they write into instead. It checks every claim as it's written, so
14
- those claims can't get in:
11
+ Agents doing research write fluent summaries. Some of the numbers in them match no result. A clean red-team run of
12
+ 50 prompts becomes "the model never leaks". A search too weak to find anything becomes "ruled out", and three copies
13
+ of one result get counted as three lines of evidence. Then other claims, decisions and deployments get built on top.
14
+ HypGraph is the record agents write into instead. It checks every claim as it's written, does the arithmetic the
15
+ writer skipped, and refuses:
15
16
 
16
17
  ```text
17
- $ hypgraph add claim statement="The fine-tune is 5 points better than the baseline." scope="2,000 held-out items, greedy decoding, one seed." --link "supports:from:RS1:the eval run"
18
+ $ hypgraph add claim statement="The model never leaks training data." exposure=1000000 exposure_unit=users …
18
19
  refused: claim refused:
19
- - numbers in the statement that no bound measurement, the scope or the investigation's constants account for: 5 — bind them to result fields (measurements) or move design constants into the scope
20
+ - 'never' rests on 0 of 50: that only rules out it happening more than 5.8% of the time (95% upper bound).
21
+ Honest version: 'no occurrences in 50 trials (95% upper bound 5.8%)'. To support a rate below 1 in 1,000 you need
22
+ about 2,995 trials without one. Across the 1,000,000 users this claim applies to, that leaves up to 58,155 not
23
+ ruled out. [ABSOLUTE_UNDERPOWERED]
20
24
  ```
21
25
 
22
- (The result said 1,624 against 1,568 correct out of 2,000. That's 2.8 points.)
26
+ The honest version goes in, and it carries its consequence wherever it's read:
23
27
 
24
- Every refusal says what to fix, so an agent can act on it. Nothing in the record is ever *set* as known. How well
25
- each claim is known is **derived on every read**:
28
+ ```text
29
+ active claim · unchecked · 1 independent line of support · … · 0 of 50 = at most 5.8% at 95%, up to 58,155 of 1,000,000 not ruled out
30
+ ```
31
+
32
+ ## The arithmetic of never
33
+
34
+ "Never" is the most expensive word in research, because everything built on it inherits it. Zero failures in *n*
35
+ trials doesn't mean a rate of zero. It means a rate below 1 − 0.05^(1/*n*), about 3/*n*, at 95% confidence. HypGraph
36
+ holds every claim to that:
37
+
38
+ | The claim says | Refused as | Because |
39
+ |---|---|---|
40
+ | "never leaks", from 0 of 50 | `ABSOLUTE_UNDERPOWERED` | it could still be 5.8%, up to 58,155 of a million users |
41
+ | "never leaks", from 0 of 3,000, without saying 3,000 | `ABSOLUTE_HIDES_DENOMINATOR` | readers hear "never", but it means "at most 1 in 1,000" |
42
+ | "eliminates hallucinations entirely" | `ABSOLUTE_WITHOUT_COUNT` | nothing was counted |
43
+ | "5 points better" (the counts say 2.8) | `UNBOUND_NUMBER` | the number comes from nowhere. 2.8 points, 81.2% vs 78.4% and +3.6% all pass, because they follow from the counts. |
44
+ | "significantly better", counts 812/1,000 vs 800/1,000 | `SIGNIFICANCE_NOT_SHOWN` | the record runs the test: p = 0.5 |
45
+ | "prompt format has no effect" | `EXCLUSION_WITHOUT_POWER` | at 2,000 items a 3.6-point effect would usually go unseen |
46
+ | "longer prompts cause the gain" | `CAUSAL_WITHOUT_INTERVENTION` | no experiment intervened |
47
+ | "outperforms the baseline", no numbers | `COMPARATIVE_WITHOUT_COMPARISON` | only one side was measured |
48
+
49
+ Every refusal has a stable code, the arithmetic, and the sentence that *would* be honest, so an agent can repair a
50
+ claim in one step. `hypgraph bound 0 50 --exposure 1e6` (or the MCP tool `bound`) answers "what does my count
51
+ actually support?" before anyone writes the word.
52
+
53
+ Every refusal is also **logged**: who tried to say what, which rules caught it, and whether they came back with
54
+ the honest version.
55
+
56
+ ```text
57
+ $ hypgraph refusals
58
+ ## Overclaims caught — 4 refused, 3 came back honest
59
+ | author | refused | fixed | open | most common |
60
+ | analyst | 4 | 3 | 1 | EXCLUSION_WITHOUT_POWER ×1, ABSOLUTE_UNDERPOWERED ×1, UNBOUND_NUMBER ×1 |
61
+ ### Latest
62
+ - analyst: “Prompt format makes no difference to accuracy.” — EXCLUSION_WITHOUT_POWER → not fixed
63
+ - analyst: “The model never leaks training data.” — ABSOLUTE_UNDERPOWERED → fixed as CL2
64
+ - analyst: “The fine-tune is 5 points better than the baseline.” — UNBOUND_NUMBER → fixed as CL1
65
+ - analyst: “The fine-tune always beats the baseline.” — ABSOLUTE_WITHOUT_COUNT, COMPARATIVE_WITHOUT_COMPARISON → fixed as CL1
66
+ ```
67
+
68
+ And because most hype lives in the write-up, not the record, **`hypgraph check summary.md`** lints the final report
69
+ against the record. It catches numbers no result contains, claims called "established" that the record has as
70
+ unchecked or contested, citations to withdrawn claims, and "never" / "no effect" / "significantly" with nothing
71
+ behind them. It exits 1 on errors, so it can gate CI:
72
+
73
+ ```text
74
+ $ hypgraph check summary.md
75
+ summary.md:8: error REPORT_OVERSTATES_STANDING: 'established' — but CL2 is unchecked in the record …
76
+ summary.md:9: warning REPORT_UNSOURCED_NUMBER: 700 isn't in any result, measurement, claim, scope or constant …
77
+ summary.md:13: error REPORT_UNKNOWN_CODE: CL9 isn't in the record for this investigation — a citation to nothing.
78
+ ```
79
+
80
+ And when something does fall, the record says what falls with it:
81
+
82
+ ```text
83
+ $ hypgraph withdraw CL1 --why "the held-out set leaked into training"
84
+ CL1 — withdrawn claim · …
85
+ ⚠ 2 nodes rest on CL1: 1 decision (D1); 1 claim (CL2) — now AT RISK
86
+ ```
87
+
88
+ ## Derived, never asserted
89
+
90
+ Nothing in the record is ever *set* as known. How well each claim is known is **derived on every read**:
26
91
 
27
92
  - **reviewed, and which version?** Only named reviewers count, and never on their own work. A revised claim goes back
28
93
  to unchecked.
@@ -46,7 +111,7 @@ hypgraph skill --install # +
46
111
  The server writes as one author, fixed when it starts. The agent can't pass another name or write as your job runner,
47
112
  and its verdicts count only if you name it as a reviewer. Tools: `overview`, `find`, `show`, `start_investigation`,
48
113
  `add`, `link`, `revise`, `withdraw`, `retract_link`, `review`, `next_experiments`, `work_queue`, `claim_work`,
49
- `register_run`, `report`, `verify`, `schema`. The method is in [AGENTS.md](https://github.com/voodoomods/hypgraph/blob/main/AGENTS.md); the short version is
114
+ `register_run`, `bound`, `check_report`, `refusal_log`, `report`, `verify`, `schema`. The method is in [AGENTS.md](https://github.com/voodoomods/hypgraph/blob/main/AGENTS.md); the short version is
50
115
  [llms.txt](https://hypgraph.com/llms.txt).
51
116
 
52
117
  ## Wire it into your system
@@ -104,6 +169,10 @@ standing takes 0.09 s, reading one node 0.3 ms, and a write about 10 ms, mostly
104
169
  costs what it touches: cached standing is kept for every node the write can't affect, and a randomized suite checks
105
170
  that against a full rebuild.
106
171
 
172
+ ## Made by
173
+
174
+ HypGraph is made by **Nicolas Houghtaling** at **[VoodooMods LLC](https://github.com/voodoomods)**.
175
+
107
176
  ## License
108
177
 
109
178
  Code: [Apache-2.0](https://github.com/voodoomods/hypgraph/blob/main/LICENSE). The record format ([docs/spec.md](https://github.com/voodoomods/hypgraph/blob/main/docs/spec.md)) and the docs: CC BY 4.0. Cite it with
@@ -1,6 +1,6 @@
1
1
  # The HypGraph record format — `hypgraph/1`
2
2
 
3
- Status: draft 1 (HypGraph 0.1). Licence: CC BY 4.0. This page is enough to write another implementation, a
3
+ Status: draft 3 (HypGraph 0.3). Licence: CC BY 4.0. This page is enough to write another implementation, a
4
4
  verifier or a viewer.
5
5
 
6
6
  ## 1. Model
@@ -23,7 +23,7 @@ correction is a new row, never an edited one.
23
23
  | `investigation` | INV | **title**\*, **objective**\*, success_criteria, scope, state, reviewers, constants, strict_claims, keywords, projects, doi, note, legacy_case | The study: an objective, its scope, who reviews it. Every other node belongs to one. |
24
24
  | `question` | Q | **text**\*, why | Something you want to know. |
25
25
  | `hypothesis` | H | **statement**\*, assumptions, **predictions**\*, distinguishing, open_questions | A possible answer, with the predictions that would show it wrong. |
26
- | `claim` | CL | **statement**\*, scope, limitations, kind, measurements, evidence_note, legacy | What you assert is known, with its scope and its numbers bound to results. |
26
+ | `claim` | CL | **statement**\*, scope, limitations, kind, measurements, evidence_note, exposure, exposure_unit, legacy | What you assert is known, with its scope and its numbers bound to results. |
27
27
  | `experiment` | X | **aim**\*, **protocol**\*, controls, **success_criteria**\*, method, budget, power_check, cost_minutes, seed | A planned test: aim, protocol, controls, success criteria, and what each hypothesis expects. |
28
28
  | `run` | RUN | **kind**\*, job_id, name, state, code, inputs, outputs, args, config, seed, env, started, finished, exit_code, log_sha256, note | One execution: code, inputs and outputs hashed, seed, state. `kind=lab` only from an instrument. |
29
29
  | `result` | RS | **summary**\*, kind, outcome, values, expected, limitations, power, evidence_note, legacy | What a run (or an observation, or a citation) found: values, outcome, power. |
@@ -158,14 +158,72 @@ Implementations must derive these, never store them:
158
158
 
159
159
  ## 7. Claim checks
160
160
 
161
- 1. `finding` and `exclusion` need a scope of at least 10 characters.
162
- 2. `finding`, `exclusion` and `inconclusive` need at least one supporting node.
163
- 3. Each measurement must name a result and a key present in its `values`. Its `value` / `k` / `n` must equal the
164
- result's at the precision written; a fraction ≤ 1 may be written as a percentage.
165
- 4. Every number in the statement (digits, and the words two to twenty), except digits in quotes, must match a bound
166
- measurement's numbers, a number in the scope, or one of the investigation's `constants`.
167
- 5. Absolute words (never, always, all, every, none, impossible, cannot, proves, proven, certainly, guaranteed) need a
168
- measurement with `n` and `k ∈ {0, n}`.
169
- 6. An `exclusion` needs a supporting result with `power: demonstrated`, or one produced by a run of an experiment
170
- with a `power_check`.
171
- 7. A `convention` may not hedge (could, might, suggests, consistent with, …).
161
+ A claim is checked before it enters the record. Each failed rule is reported with a stable code, a message that does
162
+ the arithmetic, a suggested fix, and the words that triggered it. Checks are skipped for the operator and importer,
163
+ and in an investigation with `strict_claims: false`. Leads and conventions skip rules 7–9.
164
+
165
+ **Arithmetic.** For a count of k in n trials, the 95% interval is the Wilson score interval. At k = 0 (or k = n) it is
166
+ the exact one-sided bound 1 − 0.05^(1/n), the "rule of three" (≈ 3/n). "Never, in n trials" means at most that rate,
167
+ and never zero. Two counts are compared with Newcombe's hybrid score interval and a pooled two-proportion z-test. The
168
+ smallest detectable difference uses 80% power at two-sided α = 0.05.
169
+
170
+ | # | Code | Refused when |
171
+ |---|---|---|
172
+ | 1 | `SCOPE_MISSING` | a finding or exclusion has a scope shorter than 10 characters |
173
+ | 2 | `EVIDENCE_MISSING` | a finding, exclusion or inconclusive claim has no supporting node |
174
+ | 3 | `MEASUREMENT_MALFORMED` / `MEASUREMENT_UNKNOWN_FIELD` / `MEASUREMENT_MISMATCH` | a measurement doesn't name a result and a key present in its `values`, or states a `value` / `k` / `n` different from the result's at the precision written (a fraction ≤ 1 may be written as a percentage) |
175
+ | 4 | `UNBOUND_NUMBER` | a number in the statement (digits, or the words two to twenty; digits in quotes are names) is not one of: a bound measurement's numbers; for a count, its rate, complement or n − k; for any two measurements, their difference, ratios, relative change or sum; the scope's numbers; the investigation's `constants`; or the conventions 95 / 99 / 90 / 80 / 0.05 / 0.01 when the statement speaks of intervals, bounds, confidence, power or significance. *Statistical* quantities (a rate's 95% bounds; for two counts the difference interval, p-value and detectable difference) count only when written to at least one decimal place. |
176
+ | 5 | `ABSOLUTE_WITHOUT_COUNT` | an absolute word (never, always, all, every, none, impossible, cannot, proves, certainly, guaranteed, eliminates, entirely, completely, universally, consistently, perfect, solves, zero, 100%, …) with no bound count where k is 0 or n |
177
+ | 5a | `ABSOLUTE_UNRELATED_COUNT` | the counts with k ∈ {0, n} share no content word with the statement in their name or key |
178
+ | 5b | `ABSOLUTE_UNDERPOWERED` | the 95% bound on the opposite rate is above 5% and the statement doesn't say n ("never leaks" rather than "no leaks in 50 prompts" — a statement that gives its n is scoped to what was seen, and accepted). The message gives the honest wording and the trials needed for a 1-in-1,000 bound. |
179
+ | 5c | `ABSOLUTE_HIDES_DENOMINATOR` | the count is enough, but n appears in neither the statement nor the scope |
180
+ | 6 | `CONVENTION_HEDGED` | a `convention` hedges (could, might, suggests, consistent with, …) |
181
+ | 7 | `COMPARATIVE_WITHOUT_COMPARISON` | a comparative (better, beats, outperforms, higher than, improves, exceeds, …) with fewer than two measured sides, no bound p-value or interval, and no supporting claim that binds two sides |
182
+ | 8 | `SIGNIFICANCE_WITHOUT_TEST` / `SIGNIFICANCE_NOT_SHOWN` | "significantly" / "statistically" with no bound p-value and fewer than two counts, or when the bound p-value, or the test the record runs on the first two counts, is not below 0.05 |
183
+ | 9 | `CAUSAL_WITHOUT_INTERVENTION` | a causal word (causes, leads to, results in, drives, due to, because of, responsible for, …) when no supporting result was produced by a run that `executes` an experiment |
184
+ | 10 | `EXCLUSION_WITHOUT_POWER` | `kind: exclusion`, or wording that says an effect is absent (no effect, no difference, makes no difference, does not affect, rules out, ineffective, …) in a finding, without a supporting result with `power: demonstrated` or one produced by a run of an experiment with a `power_check`. With two counts, the message gives the smallest detectable difference. |
185
+
186
+ **Exposure.** A claim may set `exposure` (how many uses, users or units it will be applied to) and `exposure_unit`.
187
+ Absolute refusals then state how many failures the bound leaves unruled out at that scale. An accepted claim's
188
+ standing carries `bounds` (per count: k, n, rate, lo, hi and, with exposure, `unruled_out`), its sentence reads
189
+ "'never' = at most 0.1% (0 of 3,000, 95%), up to 998 of 1,000,000 not ruled out", and a "significantly" claim carries
190
+ `test` (the difference, its interval and p).
191
+
192
+ **Blast radius.** Withdrawing a node, or a verdict that makes it fall, returns `blast_radius`: every node resting on it
193
+ by type, with claims and decisions named and, if a spend meter is connected, the cost of the work behind them.
194
+
195
+ ## 8. The refusal log
196
+
197
+ A refused claim never enters the record, so its trace lives in a separate append-only table, `refusal_log`, with
198
+ its own SHA-256 chain over `canon({at, kind, author, inv, node, op, statement, codes, detail, ref, prev})`. The
199
+ record's own chain and `verify()` are unaffected.
200
+
201
+ - `kind: refused`: the author, the investigation, the statement (≤ 1,500 characters), the sorted codes, and the
202
+ problems (code, message, fix) it was refused with. `op` is `create` or `revise`; `node` is set for a revision.
203
+ - `kind: fixed`: a later claim or revision by the same author in the same investigation, accepted within 24 hours,
204
+ that says the same thing. "The same" means the same node for a revision; otherwise a Jaccard overlap ≥ 0.3 of the
205
+ statements' content words. `ref` is the refusal it fixes.
206
+
207
+ A report aggregates per author (refused, fixed, open, codes) and per rule.
208
+
209
+ ## 9. Checking a write-up against the record
210
+
211
+ A checker reads markdown sentence by sentence and checks it against the record. Code blocks, inline code, URLs, link
212
+ targets and HTML comments are skipped. Quoted passages and negated words ("cannot be proven") are not treated as
213
+ the writer's assertion. Codes are matched within the investigation being reported on.
214
+
215
+ | level | code | when |
216
+ |---|---|---|
217
+ | error | `REPORT_CITES_WITHDRAWN` | a sentence cites a withdrawn node and doesn't say so (withdrawn, retracted, failed, superseded, earlier, …) |
218
+ | error | `REPORT_OVERSTATES_STANDING` | settled language (confirmed, established, shown, proven, holds, …) about a claim or hypothesis that is unchecked, stale, contested or at risk, or a result that is contested or at risk |
219
+ | error | `REPORT_UNKNOWN_CODE` | an `INV` / `RUN` / `CL` / `RS` / `RV` code the investigation doesn't have |
220
+ | error | `REPORT_ABSOLUTE_UNBACKED` | never / always / impossible / guaranteed / eliminates / proven / 100% asserted, not in an instruction, with no cited claim carrying a count with k ∈ {0, n} |
221
+ | error | `REPORT_ABSENCE_UNBACKED` | "no effect" / "rules out" / "doesn't matter" without citing a claim filed as `exclusion` or `inconclusive` |
222
+ | error | `REPORT_SIGNIFICANCE_UNBACKED` | "significantly" without a p-value or interval in the sentence and without citing a node that carries a test (a negated "not significant" is a warning) |
223
+ | warning | `REPORT_UNSOURCED_NUMBER` | a number not contained in or implied by the investigation's results, live claims' measurements, statements and scopes, the investigation's scope and constants, or experiment and run settings. Integers ≤ 12, years, dates, times and 7-digit+ ids are skipped. Statistical quantities need one decimal. |
224
+ | warning | `REPORT_OMITS_DISPUTE` | cites a contested or at-risk node without saying so |
225
+ | warning | `REPORT_CITES_SUPERSEDED` | cites a superseded node without saying so |
226
+ | warning | `REPORT_CAUSAL_UNCITED` | causal language with no node cited |
227
+ | note | `REPORT_BOUND` | an absolute backed by a cited count: what the count supports |
228
+
229
+ Calibrated on 24 real write-ups from PegasusDMZ against its live record.
@@ -8,9 +8,10 @@ dynamic = ["version"]
8
8
  description = "The evidence layer for AI research agents: a hash-chained research record that checks every claim as it is written and derives how well each is known."
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
11
- license-files = ["LICENSE"]
11
+ license-files = ["LICENSE", "NOTICE"]
12
12
  requires-python = ">=3.10"
13
- authors = [{ name = "Nicolas Houghtaling" }]
13
+ authors = [{ name = "Nicolas Houghtaling" }, { name = "VoodooMods LLC" }]
14
+ maintainers = [{ name = "VoodooMods LLC" }]
14
15
  keywords = ["ai-agents", "mcp", "agent-skills", "research", "provenance", "reproducibility", "evaluation", "hypothesis"]
15
16
  classifiers = [
16
17
  "Development Status :: 3 - Alpha",
@@ -45,7 +46,7 @@ path = "src/hypgraph/__init__.py"
45
46
  packages = ["src/hypgraph"]
46
47
 
47
48
  [tool.hatch.build.targets.sdist]
48
- include = ["src/hypgraph", "tests", "docs", "README.md", "LICENSE", "CITATION.cff", "AGENTS.md"]
49
+ include = ["src/hypgraph", "tests", "docs", "README.md", "LICENSE", "NOTICE", "CITATION.cff", "AGENTS.md", "CHANGELOG.md"]
49
50
 
50
51
  [tool.pytest.ini_options]
51
52
  testpaths = ["tests", "src/hypgraph/__init__.py"]
@@ -16,7 +16,7 @@ Standing is derived on every read, never stored; every write is an event in a SH
16
16
  their numbers; reviews pin versions; independence is counted, not assumed. See https://hypgraph.com.
17
17
  """
18
18
 
19
- __version__ = "0.1.0"
19
+ __version__ = "0.3.0"
20
20
 
21
21
  from .bundle import verify_bundle # noqa: E402
22
22
  from .graph import Author, Graph, GraphError, file_sha256 # noqa: E402