hypgraph 0.2.0__tar.gz → 0.4.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 (61) hide show
  1. {hypgraph-0.2.0 → hypgraph-0.4.0}/.gitignore +3 -0
  2. {hypgraph-0.2.0 → hypgraph-0.4.0}/AGENTS.md +3 -2
  3. hypgraph-0.4.0/CHANGELOG.md +151 -0
  4. {hypgraph-0.2.0 → hypgraph-0.4.0}/CITATION.cff +1 -1
  5. {hypgraph-0.2.0 → hypgraph-0.4.0}/PKG-INFO +48 -2
  6. {hypgraph-0.2.0 → hypgraph-0.4.0}/README.md +47 -1
  7. hypgraph-0.4.0/docs/decisions/ADR-001-token-bound-authors.md +46 -0
  8. hypgraph-0.4.0/docs/decisions/ADR-002-governance-profiles.md +50 -0
  9. hypgraph-0.4.0/docs/review-2026-10-24.md +206 -0
  10. {hypgraph-0.2.0 → hypgraph-0.4.0}/docs/spec.md +38 -2
  11. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/__init__.py +1 -1
  12. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/checks.py +57 -17
  13. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/cli.py +94 -2
  14. hypgraph-0.4.0/src/hypgraph/coord.py +845 -0
  15. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/derive.py +59 -5
  16. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/graph.py +252 -8
  17. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/language.py +51 -1
  18. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/mcp.py +161 -4
  19. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/policy.py +6 -2
  20. hypgraph-0.4.0/src/hypgraph/profiles.py +387 -0
  21. hypgraph-0.4.0/src/hypgraph/refusals.py +256 -0
  22. hypgraph-0.4.0/src/hypgraph/report_check.py +271 -0
  23. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/schema.py +15 -5
  24. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/server.py +115 -15
  25. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/skill/SKILL.md +13 -1
  26. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/stats.py +10 -2
  27. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/ui/atlas.js +22 -0
  28. hypgraph-0.4.0/studies/harmony/tests/test_suite.py +107 -0
  29. hypgraph-0.4.0/tests/test_cancel_releases_lease.py +39 -0
  30. hypgraph-0.4.0/tests/test_coord.py +213 -0
  31. hypgraph-0.4.0/tests/test_d10.py +146 -0
  32. hypgraph-0.4.0/tests/test_engine_fixes.py +167 -0
  33. hypgraph-0.4.0/tests/test_log_and_check.py +205 -0
  34. hypgraph-0.4.0/tests/test_order_detail.py +31 -0
  35. hypgraph-0.4.0/tests/test_profiles.py +138 -0
  36. hypgraph-0.4.0/tests/test_server_auth.py +110 -0
  37. hypgraph-0.4.0/tests/test_stats_values.py +67 -0
  38. {hypgraph-0.2.0 → hypgraph-0.4.0}/tests/test_surfaces.py +2 -1
  39. hypgraph-0.2.0/CHANGELOG.md +0 -67
  40. {hypgraph-0.2.0 → hypgraph-0.4.0}/LICENSE +0 -0
  41. {hypgraph-0.2.0 → hypgraph-0.4.0}/NOTICE +0 -0
  42. {hypgraph-0.2.0 → hypgraph-0.4.0}/docs/PLAN.md +0 -0
  43. {hypgraph-0.2.0 → hypgraph-0.4.0}/pyproject.toml +0 -0
  44. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/__main__.py +0 -0
  45. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/bundle.py +0 -0
  46. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/demos/__init__.py +0 -0
  47. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/demos/broad_street.py +0 -0
  48. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/plan.py +0 -0
  49. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/store.py +0 -0
  50. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/ui/__init__.py +0 -0
  51. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/ui/atlas.css +0 -0
  52. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/ui/hypgraph.svg +0 -0
  53. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/ui/index.html +0 -0
  54. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/ui/vendor/LICENSES.md +0 -0
  55. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/ui/vendor/cose-base.js +0 -0
  56. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/ui/vendor/cytoscape-fcose.js +0 -0
  57. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/ui/vendor/cytoscape.min.js +0 -0
  58. {hypgraph-0.2.0 → hypgraph-0.4.0}/src/hypgraph/ui/vendor/layout-base.js +0 -0
  59. {hypgraph-0.2.0 → hypgraph-0.4.0}/tests/test_engine.py +0 -0
  60. {hypgraph-0.2.0 → hypgraph-0.4.0}/tests/test_incremental.py +0 -0
  61. {hypgraph-0.2.0 → hypgraph-0.4.0}/tests/test_refusals.py +0 -0
@@ -14,3 +14,6 @@ research.hypgraph*
14
14
  .DS_Store
15
15
  Thumbs.db
16
16
  .claude/
17
+
18
+ # study runs: outputs, sealed keys and records stay local until published on purpose
19
+ studies/*/runs/
@@ -65,10 +65,11 @@ reserved.
65
65
  are not;
66
66
  - `kind: "exclusion"` needs a result with `power: "demonstrated"` (a search that found a planted answer).
67
67
  Otherwise use `"inconclusive"`.
68
- Each refusal ends in a code (`[ABSOLUTE_UNDERPOWERED]`) and already gives the honest sentence: use it.
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.
69
70
  6. **Reviews.** Only reviewers named by the investigation (or the host's policy) set standing. Nobody reviews their
70
71
  own work. A revision makes earlier reviews stale.
71
- 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
72
73
  your interpretation underneath it.
73
74
 
74
75
  ## Reading standing
@@ -0,0 +1,151 @@
1
+ # Changelog
2
+
3
+ ## 0.4.0 — 2026-10-25: the coordination layer
4
+
5
+ Held until the harmony study had exercised every path (pilots 1–4, a 142-run confirmatory, two follow-up rounds). Governance profiles, token-bound authors, work orders with predicates, order text that carries the target, instrument reproduction, replayable refusals.
6
+
7
+ ### Identity and arithmetic (2026-10-25)
8
+ - `hypgraph serve --token SECRET[=authors]` binds writes to bearer tokens (401 without one, 403 for an author the token isn't bound to; a reserved name needs a token that names it). `--token-file`, `$HYPGRAPH_TOKEN`. Without tokens: unchanged. `Policy.extra_reserved` lets a host reserve its own names (hypswarm reserves `harness`). ADR-001.
9
+ - **Governance profiles** (`hypgraph.profiles`, ADR-002): `scientific@1` (the default), `software@1`, `trading@1`, `knowledge@1`, `optimization@1` — each a versioned set of default predicates per target, domain predicates (`tests_pass`, `backtested_oos`, `sourced`, `feasible_bounded`), write-time refusals (`PASS_WITHOUT_INSTRUMENT_RUN`, `EDGE_WITHOUT_OOS_COSTS`, `BACKTEST_WITHOUT_COSTS`, `CLAIM_WITHOUT_SOURCE`, `OPTIMAL_WITHOUT_BOUND`, `OBJECTIVE_WITHOUT_FEASIBILITY`) and completion criteria. Pinned on the investigation (`fields.profile`); `Graph.completion(inv)`, `GET /api/graph/completion|profiles`, MCP `completion`; the briefing names the profile and its criteria. A cited source counts as a claim's evidence.
10
+ - The order text carries the target's own content (a claim's statement, scope, measurements and the values of the results it rests on; a result's values; a question's text; an experiment's protocol), and names a replication of the author's run when one exists — the harmony confirmatory showed 11–16 `show` calls a run reading back what the reply had said.
11
+ - `cancel_work` releases the target's lease when the order was still open, so the target can be issued to someone else at once (a resumed hypswarm run with a replaced seat found it blocked).
12
+ - `stats.two_proportions` / `detectable` no longer divide by zero on an empty arm (undefined, p = 1, the whole range). `tests/test_stats_values.py` pins Wilson, rule-of-three, Newcombe and the z-test to reference values.
13
+
14
+
15
+ - **Honest negations pass.** A word counts only when its sentence asserts it (`language.asserted`): "A is not
16
+ significantly better", "no evidence that it ever fails", "we cannot conclude that…" are accepted; the unnegated
17
+ overclaim is refused as before. New code `SIGNIFICANCE_CONTRADICTED`: "not significant" when the bound counts
18
+ are (p < 0.05). Found by the harmony pilot, where the checker refused the honest sentence its own message asked
19
+ for.
20
+ - **The record binds the numbers it already holds.** A claim's loose numbers are bound to the author's own results
21
+ in the investigation (linked results first) before any refusal; a `derived_from` link is added to the result
22
+ they came from, and the reply says what was bound. Small integers (≤ 12) bind only to counts. In the pilot,
23
+ 61 of 106 refused numbers were already in a result.
24
+ - **The done signal.** After every write by an assignee, the reply carries its open orders with each predicate
25
+ MET / NOT MET, and "return_work now" when all are (`coord.done_lines`; `orders` on create / revise / review
26
+ results; shown by the MCP tools). In the pilot 8 of 9 sub-agents worked to their turn cap past done.
27
+ - **Orders meetable by construction.** `done_when` accepts `predicate` / `name` / a bare string as spellings of
28
+ `p`, and a bad predicate's refusal shows the valid form. Each predicate in an order carries how to meet it with
29
+ the assignee's tools. `result_exists` and `run_registered` count what the assignee wrote while holding the
30
+ order, so no link vocabulary stands between an honest result and a met predicate. A third rework of an order
31
+ whose predicate hasn't moved is refused, with the choices spelled out.
32
+ - **The question board.** The briefing's unit is the question: settled (a checked claim answers it), contested,
33
+ answered (unchecked), in progress, open; the frontier is driven by it, and one review settles a question
34
+ (`question_board`). `settle` / MCP `settle_work` accepts every returned order whose predicates are all met.
35
+ Standing in the briefing is now exceptions only (contested, at risk, fallen).
36
+ - **Absolutes over an enumerated set pass.** "A trails B in every subset: math −2.6, code −2.2, reading −0.4",
37
+ "all three subsets", "over all 818 items" quantify over what the sentence lists; they are not
38
+ generalisations and are no longer refused. "below zero" is a number, not "never".
39
+ - **Replay.** Refusals are logged with the whole claim body and its support, and `hypgraph replay-refusals`
40
+ (`Graph.replay_refusals`) runs every logged refusal through the checker as it is now: same, changed, passes.
41
+ A rule change is judged on the corpus of what agents wrote before it ships.
42
+ - **A result written twice is written once.** The same author re-registering the same numbers (the multiset of
43
+ a result's values, rounded, keys ignored) is refused with `RESULT_RESTATES`; a declared rerun (a run that
44
+ `replicates`) is kept and marked; another author's restatement is kept and its standing says `RESTATES RSn`.
45
+ Restated results are collapsed in work-order text.
46
+
47
+ - **Coordinating a team through the record** (`coord.py`). All of it is deterministic and makes no model calls.
48
+ - `briefing(inv)`: the orchestrator's context compiled from the record (mission, rules, roster, standing,
49
+ frontier, orders in flight and returned with their predicates checked, budget, what changed since the last
50
+ one), with a hash.
51
+ - Work orders are `task` nodes: `issue_work` (the target is leased to the assignee; a duplicate is refused),
52
+ `order_text`, `return_work`, `check_work` (each predicate MET / NOT MET with the nodes that satisfy it),
53
+ `accept_work` (a decision based on the evidence, only when every predicate is met), `rework`, `cancel_work`
54
+ and `cancel_moot`.
55
+ - An order's status is derived, never set: open, returned, overdue, accepted, reworked, cancelled or moot.
56
+ Only evidence written after the order was issued counts toward it.
57
+ - **Schema.**
58
+ - `task` gains optional `objective`, `assignee`, `role`, `prediction`, `method`, `done_when`, `budget` and
59
+ `note`; `kanban_id` is no longer required.
60
+ - New relations `rests_on` (an order's pinned premises) and `settles` (a decision accepting an order).
61
+ - `works_on` and `rests_on` join the at-risk relations, so the blast radius of a withdrawal now lists the work
62
+ orders it makes moot.
63
+ - **Surfaces.** MCP tools `briefing`, `issue_work`, `my_work`, `check_work`, `return_work`, `accept_work`,
64
+ `rework`, `cancel_work` and `cancel_moot`; CLI `hypgraph brief` and `hypgraph orders`.
65
+
66
+ ## 0.3.0 — 2026-10-24
67
+
68
+ - **The refusal log.** Every refused claim is logged: author, statement, codes, messages. A later accepted claim by
69
+ the same author that says the same thing (same node, or ≥ 30% shared content words within 24 hours) marks the
70
+ refusal fixed. Append-only, with its own hash chain (`verify_refusals()`); the record's chain is untouched.
71
+ - API: `refusals()`, `refusal_report()`.
72
+ - CLI: `hypgraph refusals [--author …]`. MCP: `refusal_log`. HTTP: `GET /api/graph/refusals`.
73
+ - Atlas: an "Overclaims caught" card in the Work view.
74
+ - **`hypgraph check report.md`** lints a write-up against the record (`Graph.check_text`; MCP `check_report`; HTTP
75
+ `POST /api/graph/check`). Exits 1 on errors (`--strict`: warnings too).
76
+ - Errors: citations of withdrawn nodes, settled language about unchecked, contested or at-risk work, unknown
77
+ codes, and unbacked never / no effect / significantly.
78
+ - Warnings: numbers no result contains or implies, omitted disputes, superseded citations, uncited causal
79
+ language.
80
+ - Calibrated on 24 real PegasusDMZ write-ups against its live record. That fixed negations ("cannot be proven"),
81
+ quoted passages under correction, instructions ("never use the eval set"), p-values given in the sentence, and
82
+ "results in" as a noun phrase.
83
+ - An absolute whose statement gives its n ("no leaks in 50 prompts", "nothing in 50 of 50") is scoped to what was
84
+ seen and accepted; its standing still carries the bound. Without n it is refused, as before.
85
+ - Spec draft 3 (§8 refusal log, §9 checking a write-up).
86
+
87
+ ## 0.2.0 — 2026-10-24
88
+
89
+ **Refusal, remastered.** The claim checks now do the arithmetic the writer skipped, and every refusal says what the
90
+ count actually supports and offers the honest sentence.
91
+
92
+ - **The arithmetic of never.** "Never" / "always" / "eliminates" / "100%" need a count of what they're about with
93
+ k = 0 or n (`ABSOLUTE_WITHOUT_COUNT`, `ABSOLUTE_UNRELATED_COUNT`). That count needs a 95% bound under 5%
94
+ (`ABSOLUTE_UNDERPOWERED`: "0 of 3 only rules out more than 63%"), and the statement has to show its denominator
95
+ (`ABSOLUTE_HIDES_DENOMINATOR`). The bounds are exact at k = 0 (1 − 0.05^(1/n), the rule of three) and Wilson
96
+ otherwise.
97
+ - **Exposure.** A claim can say how many uses, users or units it applies to (`exposure`, `exposure_unit`). Refusals
98
+ and the claim's standing then state how many failures the bound leaves unruled out: "up to 58,155 of 1,000,000".
99
+ - **New rules:** `COMPARATIVE_WITHOUT_COMPARISON`, `SIGNIFICANCE_WITHOUT_TEST`, `SIGNIFICANCE_NOT_SHOWN` (the
100
+ record runs a two-proportion test on bound counts itself), `CAUSAL_WITHOUT_INTERVENTION`, and
101
+ `EXCLUSION_WITHOUT_POWER` for "no effect" however it is filed, with the smallest effect the sample could have
102
+ detected.
103
+ - **True numbers pass.** Differences, ratios, percentages and relative changes computed from bound measurements are
104
+ accepted ("2.8 points", "81.2% vs 78.4%", "+3.6%"). Statistical quantities must be written to one decimal place, so
105
+ "5 points" can't borrow an interval's end.
106
+ - **Refusals agents can act on:** a stable code on every line, and `GraphError.problems` with `code`, `message`,
107
+ `fix` and `words`.
108
+ - **Blast radius.** `withdraw()`, and a review that makes a node fall, return `blast_radius`: what rests on it, by
109
+ type, with claims and decisions named (and spend, if metered). The CLI and MCP print it.
110
+ - **`hypgraph bound K N --exposure X`** and the MCP tool `bound`: what a count supports, before anyone writes "never".
111
+ - A claim's standing carries `bounds`, and `test` for "significantly" claims. Spec draft 2 (§7 rewritten).
112
+ - Credits: made by Nicolas Houghtaling at VoodooMods LLC (README, NOTICE, CITATION.cff, package metadata, site).
113
+
114
+ ## 0.1.0 — 2026-10-24
115
+
116
+ First release on PyPI.
117
+
118
+ - **Writes cost what they touch.** A write no longer copies the whole in-memory snapshot. Tables are shallow
119
+ copies, and a node's list is copied only when the write appends to it. Derived standing is kept for every node the
120
+ write can't affect (`derive.affected_by`), so the first full read after a write no longer re-derives the graph.
121
+ 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
122
+ 0.17 s, and a write at 5,000 nodes 5.8 ms instead of 12 ms. A randomized suite checks the carried standing
123
+ against a full rebuild after every kind of write.
124
+ - New indexes for display codes and the rate limit (`nodes(inv, type)`, `nodes(inv, code)`, `events(actor, kind,
125
+ at)`). Created on open; existing records just gain them.
126
+ - Positioning: the evidence layer for AI research agents. README and site lead with the MCP server and the Agent
127
+ Skill; a new engineering guide (`/engineers/`) replaces the human guide; the eval-report example replaces the
128
+ classroom one.
129
+
130
+ ### Extracted from PegasusDMZ
131
+
132
+ Extracted from the PegasusDMZ research graph and verified against it: on a copy of Pegasus's live
133
+ graph (1,357 events, 790 nodes in 5 investigations), the library reproduces the same chain head, every node's
134
+ standing, the work queues and the next-experiment rankings.
135
+
136
+ - **Engine** (stdlib only): typed nodes and edges, append-only SQLite with a SHA-256 event chain and `verify()`,
137
+ standing derived on read, claim checks on write, review pinned to versions, independence and evidence profile,
138
+ leases, next experiments, work map, digest, report.
139
+ - **Policy**: reviewers, instrument, operator, importer and system are the host's to set (they were hardcoded in
140
+ Pegasus). Optional `spend` hook for cost. Injectable `clock`; only the importer may set a write's time.
141
+ - **Bundles** (`hypgraph/1-bundle`): every event and body as the exact hashed string, so they can be verified in any
142
+ language. Scoped exports redact events outside the investigation and say so. A complete bundle replays row for row.
143
+ - **CLI**: `init`, `add`, `link`, `review`, `revise`, `withdraw`, `ls`, `show --why/--impact/--history`, `next`, `work`,
144
+ `report`, `export`, `import`, `verify`, `demo`, `schema`, `serve`, `mcp`, `skill`.
145
+ - **MCP server** (FastMCP, `hypgraph[mcp]`): 17 tools, 4 resources, a `method` prompt. Writes as one fixed author.
146
+ - **Agent skill**: `SKILL.md` ships in the package; `hypgraph skill --install`.
147
+ - **Atlas**: the framework-free explorer, with an HTTP source and a bundle source (it verifies the chain in the
148
+ browser). Cytoscape is vendored.
149
+ - **Demo**: Broad Street, 1854 — a teaching reconstruction with three refusals built into the story.
150
+ - Fixed while extracting it: a report listed stale-reviewed claims under the wrong key; claim measurements now accept
151
+ result codes (RS3) and store ids.
@@ -13,7 +13,7 @@ authors:
13
13
  affiliation: VoodooMods LLC
14
14
  - name: VoodooMods LLC
15
15
  license: Apache-2.0
16
- version: 0.2.0
16
+ version: 0.3.0
17
17
  date-released: "2026-10-24"
18
18
  url: "https://hypgraph.com"
19
19
  repository-code: "https://github.com/voodoomods/hypgraph"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: hypgraph
3
- Version: 0.2.0
3
+ Version: 0.4.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/
@@ -82,6 +82,33 @@ Every refusal has a stable code, the arithmetic, and the sentence that *would* b
82
82
  claim in one step. `hypgraph bound 0 50 --exposure 1e6` (or the MCP tool `bound`) answers "what does my count
83
83
  actually support?" before anyone writes the word.
84
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
+
85
112
  And when something does fall, the record says what falls with it:
86
113
 
87
114
  ```text
@@ -105,6 +132,25 @@ Nothing in the record is ever *set* as known. How well each claim is known is **
105
132
  Every write is an event in a **SHA-256 hash chain**. A record can be handed to a reviewer, a regulator or another
106
133
  team, and they can verify it without trusting you.
107
134
 
135
+ ## One record, five disciplines
136
+
137
+ The rules above are the scientific case written as code. A **profile** says what a domain means by evidence,
138
+ acceptance and done — and the record enforces it, so it never depends on a prompt being followed. Pin one on the
139
+ investigation (`"profile": "software@1"`); without one, the record behaves exactly as before.
140
+
141
+ | profile | an order is done when… | the record refuses | the investigation is done when |
142
+ |---|---|---|---|
143
+ | `scientific@1` | a checked claim answers the question | numbers no result accounts for, "never" without the count, "significant" without the test | every question is settled |
144
+ | `software@1` | **the instrument ran the tests** (`tests_pass`) and a claim states it | *"it passes"* without an instrument-recorded run | every requirement's claim rests on a passing run |
145
+ | `trading@1` | a backtest is **out of sample and after costs** (`backtested_oos`) | a backtest without the costs it charged; *"edge"* without both | a sized paper position is a decision based on the claim |
146
+ | `knowledge@1` | the claim **cites a source node** the record holds (`sourced`) | a factual claim with no source | every claim cites a source |
147
+ | `optimization@1` | a result carries **objective, feasible, and a bound or gap** (`feasible_bounded`) | an objective without feasibility; *"optimal"* without a bound | a feasible incumbent with a bound exists |
148
+
149
+ Profiles are versioned and frozen: a record written under `trading@1` means the same thing in a year, and a changed
150
+ rule is `@2`. `completion(inv)` reports each criterion; the briefing names the profile and where it stands;
151
+ `hypgraph serve` exposes `/api/graph/completion` and `/api/graph/profiles`. Adding a discipline is one `Profile` —
152
+ vocabulary, default predicates, domain predicates, refusals, completion — and the rest of the record is unchanged.
153
+
108
154
  ## Connect your agent
109
155
 
110
156
  ```bash
@@ -116,7 +162,7 @@ hypgraph skill --install # +
116
162
  The server writes as one author, fixed when it starts. The agent can't pass another name or write as your job runner,
117
163
  and its verdicts count only if you name it as a reviewer. Tools: `overview`, `find`, `show`, `start_investigation`,
118
164
  `add`, `link`, `revise`, `withdraw`, `retract_link`, `review`, `next_experiments`, `work_queue`, `claim_work`,
119
- `register_run`, `bound`, `report`, `verify`, `schema`. The method is in [AGENTS.md](https://github.com/voodoomods/hypgraph/blob/main/AGENTS.md); the short version is
165
+ `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
120
166
  [llms.txt](https://hypgraph.com/llms.txt).
121
167
 
122
168
  ## Wire it into your system
@@ -50,6 +50,33 @@ Every refusal has a stable code, the arithmetic, and the sentence that *would* b
50
50
  claim in one step. `hypgraph bound 0 50 --exposure 1e6` (or the MCP tool `bound`) answers "what does my count
51
51
  actually support?" before anyone writes the word.
52
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
+
53
80
  And when something does fall, the record says what falls with it:
54
81
 
55
82
  ```text
@@ -73,6 +100,25 @@ Nothing in the record is ever *set* as known. How well each claim is known is **
73
100
  Every write is an event in a **SHA-256 hash chain**. A record can be handed to a reviewer, a regulator or another
74
101
  team, and they can verify it without trusting you.
75
102
 
103
+ ## One record, five disciplines
104
+
105
+ The rules above are the scientific case written as code. A **profile** says what a domain means by evidence,
106
+ acceptance and done — and the record enforces it, so it never depends on a prompt being followed. Pin one on the
107
+ investigation (`"profile": "software@1"`); without one, the record behaves exactly as before.
108
+
109
+ | profile | an order is done when… | the record refuses | the investigation is done when |
110
+ |---|---|---|---|
111
+ | `scientific@1` | a checked claim answers the question | numbers no result accounts for, "never" without the count, "significant" without the test | every question is settled |
112
+ | `software@1` | **the instrument ran the tests** (`tests_pass`) and a claim states it | *"it passes"* without an instrument-recorded run | every requirement's claim rests on a passing run |
113
+ | `trading@1` | a backtest is **out of sample and after costs** (`backtested_oos`) | a backtest without the costs it charged; *"edge"* without both | a sized paper position is a decision based on the claim |
114
+ | `knowledge@1` | the claim **cites a source node** the record holds (`sourced`) | a factual claim with no source | every claim cites a source |
115
+ | `optimization@1` | a result carries **objective, feasible, and a bound or gap** (`feasible_bounded`) | an objective without feasibility; *"optimal"* without a bound | a feasible incumbent with a bound exists |
116
+
117
+ Profiles are versioned and frozen: a record written under `trading@1` means the same thing in a year, and a changed
118
+ rule is `@2`. `completion(inv)` reports each criterion; the briefing names the profile and where it stands;
119
+ `hypgraph serve` exposes `/api/graph/completion` and `/api/graph/profiles`. Adding a discipline is one `Profile` —
120
+ vocabulary, default predicates, domain predicates, refusals, completion — and the rest of the record is unchanged.
121
+
76
122
  ## Connect your agent
77
123
 
78
124
  ```bash
@@ -84,7 +130,7 @@ hypgraph skill --install # +
84
130
  The server writes as one author, fixed when it starts. The agent can't pass another name or write as your job runner,
85
131
  and its verdicts count only if you name it as a reviewer. Tools: `overview`, `find`, `show`, `start_investigation`,
86
132
  `add`, `link`, `revise`, `withdraw`, `retract_link`, `review`, `next_experiments`, `work_queue`, `claim_work`,
87
- `register_run`, `bound`, `report`, `verify`, `schema`. The method is in [AGENTS.md](https://github.com/voodoomods/hypgraph/blob/main/AGENTS.md); the short version is
133
+ `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
88
134
  [llms.txt](https://hypgraph.com/llms.txt).
89
135
 
90
136
  ## Wire it into your system
@@ -0,0 +1,46 @@
1
+ # ADR-001 — Writes over HTTP are bound to tokens, and hosts can reserve their own names
2
+
3
+ **Date** 2026-10-25 · **Status** accepted · **Where** `server.py`, `policy.py`, `cli.py`
4
+
5
+ ## Context
6
+
7
+ `hypgraph serve --writable` took `author` from the request body and authenticated nobody. The MCP server fixes
8
+ its author at start (`--as`), so an agent on MCP can't impersonate; over HTTP anyone who could reach the port
9
+ could write as any non-reserved author. For hypswarm that included **`harness`** — the orchestrator that owns
10
+ every order, may cancel and rework them, and settles accepted work — because `Policy.reserved` knew only the
11
+ engine's four names (instrument, operator, importer, system).
12
+
13
+ The principle at stake: the record is the authority, and authority starts with knowing who is writing.
14
+
15
+ ## Decision
16
+
17
+ 1. **`Policy.extra_reserved`**: a host may add names to the reserved set (hypswarm adds `harness`). Everything
18
+ that consults `policy.reserved` — the HTTP write path, the MCP `--as` check, the rate limiter, the fixed-record
19
+ guard — sees them. Default empty: existing policies are unchanged.
20
+ 2. **Bearer tokens on the write route.** `serve --token SECRET=a,b` binds a token to authors; `SECRET=*` (or a
21
+ bare secret) means any *non-reserved* author; `--token-file` and `$HYPGRAPH_TOKEN` are the same thing in other
22
+ places. With any token configured, a write without a known token is **401** (`WWW-Authenticate: Bearer`), a
23
+ write as an author the token isn't bound to is **403**, and a reserved name is accepted only from a token that
24
+ names it explicitly. Secrets are compared with `hmac.compare_digest`; a secret under 16 characters is refused
25
+ at parse time. Reads stay open (the Atlas needs them; a server exposed beyond localhost still belongs behind a
26
+ reverse proxy for reads).
27
+ 3. **No tokens → exactly today's behaviour**: reserved names refused, everyone else taken at their word.
28
+
29
+ ## Alternatives considered
30
+
31
+ - Make `harness` one of the engine's fixed roles: it is a hypswarm concept; the engine shouldn't know it.
32
+ `extra_reserved` lets any host reserve what it needs.
33
+ - Per-request signatures (HMAC over the body): stronger against replay, but every client would need the
34
+ signing code, and the threat here is identity, not tampering on a localhost link. Tokens over TLS (or a
35
+ reverse proxy) cover the exposure the server is documented for.
36
+ - Authenticating reads too: left to the proxy layer for now; a `--private` flag is an easy follow-up.
37
+
38
+ ## Consequences
39
+
40
+ - hypswarm's `HttpRecord(token=…)` sends the bearer; its one client writes as the harness and every seat, so its
41
+ token is bound to all of them (`SECRET=harness,analyst,…`). An outside seat (a person on MCP, another agent)
42
+ gets a token bound to its own name and cannot issue, cancel or settle orders.
43
+ - `AuthError` (hypswarm) distinguishes a refused identity from a refused write.
44
+ - Tests: `tests/test_server_auth.py` (401 / 403 / reserved / case folding / no-token path unchanged / parsing);
45
+ hypswarm `tests/test_auth.py` (a swarm runs on its token; outsider can't be the harness).
46
+ - The hypgraph suite grew from 91 to 108 tests; wall time 11.4 s → 14.6 s (the new tests start real servers).
@@ -0,0 +1,50 @@
1
+ # ADR-002 — Governance profiles: what a domain means by evidence, acceptance and done, pinned and versioned
2
+
3
+ **Date** 2026-10-25 · **Status** accepted · **Where** `profiles.py`, hooks in `schema`, `coord`, `graph`, `server`, `mcp`
4
+
5
+ ## Context
6
+
7
+ The engine's rules — claims bound to measured results, k/n for absolutes, a reviewer's verdict to settle a question
8
+ — are the scientific case written as code. A software team needs "it passes" to mean *the instrument ran the
9
+ tests*; a trading desk needs "edge" to mean *out of sample, after costs*; a research assistant needs every factual
10
+ claim to cite what was read; an optimisation run needs "optimal" to come with a bound. Each is a different answer to
11
+ the same three questions: what counts as evidence for a claim, when is an order's work accepted, and when is the
12
+ investigation done. Those answers belong in the record, not in prompts (the harmony study measured that rules in
13
+ prompts are followed inconsistently; rules the record enforces are followed always).
14
+
15
+ ## Decision
16
+
17
+ A **profile** is a named, versioned, frozen set of: vocabulary (shown, not enforced); default predicates per target
18
+ type; domain predicates the record can check (`tests_pass`, `backtested_oos`, `sourced`, `feasible_bounded`);
19
+ write-time validators on claims and results (`PASS_WITHOUT_INSTRUMENT_RUN`, `EDGE_WITHOUT_OOS_COSTS`,
20
+ `BACKTEST_WITHOUT_COSTS`, `CLAIM_WITHOUT_SOURCE`, `OPTIMAL_WITHOUT_BOUND`, `OBJECTIVE_WITHOUT_FEASIBILITY`);
21
+ completion criteria derived from the record (`questions_settled`, `requirements_verified`, `position_recorded`,
22
+ `all_sourced`, `incumbent_feasible`); and suggested seat templates. Five ship: `scientific@1` (the default — an
23
+ investigation with no profile behaves exactly as before), `software@1`, `trading@1`, `knowledge@1`,
24
+ `optimization@1`.
25
+
26
+ An investigation **pins** its profile (`fields.profile = "software@1"`; an unknown id is refused at creation).
27
+ Rules never change under a version — a changed rule is `@2` — so a record written under `trading@1` means the same
28
+ thing in a year. `issue_work` takes the profile's default predicates; `check_work` evaluates the domain predicates
29
+ with the same lease-window rule as the built-ins; `create` adds the profile's refusals to the claim checks and
30
+ refuses results the profile rejects (curators excepted, as for claim checks); `completion(inv)` reports the
31
+ criteria; the briefing's MISSION names the profile and the criteria's state; the server has `/completion` and
32
+ `/profiles`; the MCP server has `completion`. A cited source now counts as a claim's evidence (knowledge profile).
33
+
34
+ ## Alternatives considered
35
+
36
+ - Per-host subclasses of `Graph`: rules would live in code outside the record and couldn't be pinned or read back.
37
+ - Prompts per domain only: the measured failure mode (see context).
38
+ - One mutable profile per name: a record written under last month's rules would silently mean something else.
39
+
40
+ ## Consequences
41
+
42
+ - Tests (`tests/test_profiles.py`): unknown profile refused; no profile = today's predicates and criteria; software —
43
+ the tester's own "positive" result does not meet `tests_pass`, the instrument's replicating lab run does, "passes"
44
+ is refused without it, completion needs instrument evidence; trading — a costless backtest result is refused, an
45
+ in-sample edge is refused, out-of-sample + fees + a reviewed claim + a sized decision completes; optimisation —
46
+ objective without feasibility refused, "optimal" without a bound refused; knowledge — unsourced claim refused,
47
+ cited claim accepted and completes; server routes. 119 tests pass.
48
+ - hypswarm binds rosters and toolkits to these ids (hypswarm ADR-008).
49
+ - Open: a `@2` of any profile needs a migration note in CHANGELOG; validators use the same clause-level negation
50
+ as the claim checks (`language.asserted`), so "no edge was found" is not refused.