hypgraph 0.7.4__tar.gz → 0.7.5__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 (97) hide show
  1. {hypgraph-0.7.4 → hypgraph-0.7.5}/CHANGELOG.md +10 -0
  2. {hypgraph-0.7.4 → hypgraph-0.7.5}/PKG-INFO +1 -1
  3. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/__init__.py +1 -1
  4. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/derive.py +11 -6
  5. hypgraph-0.7.5/tests/test_cited_and_failed_support.py +60 -0
  6. {hypgraph-0.7.4 → hypgraph-0.7.5}/.gitignore +0 -0
  7. {hypgraph-0.7.4 → hypgraph-0.7.5}/AGENTS.md +0 -0
  8. {hypgraph-0.7.4 → hypgraph-0.7.5}/CITATION.cff +0 -0
  9. {hypgraph-0.7.4 → hypgraph-0.7.5}/LICENSE +0 -0
  10. {hypgraph-0.7.4 → hypgraph-0.7.5}/NOTICE +0 -0
  11. {hypgraph-0.7.4 → hypgraph-0.7.5}/README.md +0 -0
  12. {hypgraph-0.7.4 → hypgraph-0.7.5}/docs/PLAN.md +0 -0
  13. {hypgraph-0.7.4 → hypgraph-0.7.5}/docs/decisions/ADR-001-token-bound-authors.md +0 -0
  14. {hypgraph-0.7.4 → hypgraph-0.7.5}/docs/decisions/ADR-002-governance-profiles.md +0 -0
  15. {hypgraph-0.7.4 → hypgraph-0.7.5}/docs/profile-workflows-plan.md +0 -0
  16. {hypgraph-0.7.4 → hypgraph-0.7.5}/docs/review-2026-10-24.md +0 -0
  17. {hypgraph-0.7.4 → hypgraph-0.7.5}/docs/site-plan.md +0 -0
  18. {hypgraph-0.7.4 → hypgraph-0.7.5}/docs/spec.md +0 -0
  19. {hypgraph-0.7.4 → hypgraph-0.7.5}/docs/ui-plan.md +0 -0
  20. {hypgraph-0.7.4 → hypgraph-0.7.5}/docs/workflows/README.md +0 -0
  21. {hypgraph-0.7.4 → hypgraph-0.7.5}/docs/workflows/knowledge.md +0 -0
  22. {hypgraph-0.7.4 → hypgraph-0.7.5}/docs/workflows/optimization.md +0 -0
  23. {hypgraph-0.7.4 → hypgraph-0.7.5}/docs/workflows/scientific.md +0 -0
  24. {hypgraph-0.7.4 → hypgraph-0.7.5}/docs/workflows/software.md +0 -0
  25. {hypgraph-0.7.4 → hypgraph-0.7.5}/docs/workflows/trading.md +0 -0
  26. {hypgraph-0.7.4 → hypgraph-0.7.5}/pyproject.toml +0 -0
  27. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/__main__.py +0 -0
  28. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/bundle.py +0 -0
  29. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/checks.py +0 -0
  30. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/cli.py +0 -0
  31. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/coord.py +0 -0
  32. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/demos/__init__.py +0 -0
  33. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/demos/broad_street.py +0 -0
  34. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/graph.py +0 -0
  35. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/language.py +0 -0
  36. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/mcp.py +0 -0
  37. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/plan.py +0 -0
  38. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/policy.py +0 -0
  39. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/profiles.py +0 -0
  40. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/refusals.py +0 -0
  41. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/report_check.py +0 -0
  42. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/schema.py +0 -0
  43. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/server.py +0 -0
  44. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/skill/SKILL.md +0 -0
  45. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/stats.py +0 -0
  46. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/store.py +0 -0
  47. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/ui/__init__.py +0 -0
  48. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/ui/atlas.css +0 -0
  49. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/ui/atlas.js +0 -0
  50. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/ui/hypgraph.css +0 -0
  51. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/ui/hypgraph.js +0 -0
  52. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/ui/hypgraph.svg +0 -0
  53. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/ui/index.html +0 -0
  54. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/ui/vendor/LICENSES.md +0 -0
  55. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/ui/vendor/cose-base.js +0 -0
  56. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/ui/vendor/cytoscape-fcose.js +0 -0
  57. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/ui/vendor/cytoscape.min.js +0 -0
  58. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/ui/vendor/layout-base.js +0 -0
  59. {hypgraph-0.7.4 → hypgraph-0.7.5}/src/hypgraph/workflow.py +0 -0
  60. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_accept_work_kind.py +0 -0
  61. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_bound_support.py +0 -0
  62. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_bundle_telemetry.py +0 -0
  63. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_cancel_releases_lease.py +0 -0
  64. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_coord.py +0 -0
  65. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_d10.py +0 -0
  66. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_engine.py +0 -0
  67. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_engine_fixes.py +0 -0
  68. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_incremental.py +0 -0
  69. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_link_translate.py +0 -0
  70. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_log_and_check.py +0 -0
  71. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_measure_questions.py +0 -0
  72. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_opposed_answers.py +0 -0
  73. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_option_lead.py +0 -0
  74. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_order_detail.py +0 -0
  75. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_profiles.py +0 -0
  76. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_profiles_041.py +0 -0
  77. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_questions_not_assertions.py +0 -0
  78. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_refusal_friction.py +0 -0
  79. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_refusal_results.py +0 -0
  80. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_refusals.py +0 -0
  81. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_relation_words.py +0 -0
  82. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_report_answers.py +0 -0
  83. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_rerunnable.py +0 -0
  84. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_restates_scale.py +0 -0
  85. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_server_auth.py +0 -0
  86. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_shape_refusals.py +0 -0
  87. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_stats_not_backtests.py +0 -0
  88. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_stats_values.py +0 -0
  89. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_surfaces.py +0 -0
  90. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_telemetry_stream.py +0 -0
  91. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_ui_app.py +0 -0
  92. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_ui_engine.py +0 -0
  93. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_uncorrected_effect.py +0 -0
  94. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_unnamed_inputs.py +0 -0
  95. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_versions_not_figures.py +0 -0
  96. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_vocabulary_friction.py +0 -0
  97. {hypgraph-0.7.4 → hypgraph-0.7.5}/tests/test_workflows.py +0 -0
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.5 — 2026-10-28: a cited source is a line of support, and a finding of failure is not a fall
4
+
5
+ - **A cited source is a line of support, and a succeeded run's "failed" outcome is a finding, not a fall.**
6
+ A knowledge claim that cites its sources (`cites` → source, or `derived_from` → source) read "no live
7
+ support · unverified" even after 0.7.4 counted bound results; the source is what such a claim rests on,
8
+ and its line is `external`. And a result with `outcome: failed` counted as a broken premise whatever its
9
+ run did: testers write "failed" for the code under test failing its spec (30 results in the challenge
10
+ records, 13 carrying checked claims that then read UNSUPPORTED — every support has fallen). A result a
11
+ succeeded run produced did happen; it falls only when there is no run or the run did not succeed.
12
+
3
13
  ## 0.7.4 — 2026-10-28: a bound result is a line of support, and a relation word is read as the relation it names
4
14
 
5
15
  - **A bound result is a line of support.** A claim's standing counted only `supports` edges into it, so a claim
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: hypgraph
3
- Version: 0.7.4
3
+ Version: 0.7.5
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/
@@ -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.7.4"
19
+ __version__ = "0.7.5"
20
20
 
21
21
  from .bundle import verify_bundle # noqa: E402
22
22
  from .graph import Author, Graph, GraphError, file_sha256 # noqa: E402
@@ -134,7 +134,11 @@ def fallen(s: Snap, nid: str) -> bool:
134
134
  if n["type"] == "run" and n["body"].get("state") in ("failed", "timeout", "cancelled", "interrupted"):
135
135
  return True
136
136
  if n["type"] == "result" and n["body"].get("outcome") == "failed":
137
- return True
137
+ # "failed" means the measurement didn't happen. A result a succeeded run produced did happen: its author
138
+ # wrote "failed" for what it measured (the code under test failing its spec — 30 results in the challenge
139
+ # records, 13 carrying checked claims that then read UNSUPPORTED). That is a finding, not a fall.
140
+ run = producing_run(s, nid)
141
+ return not (run and s.nodes[run]["body"].get("state") == "succeeded")
138
142
  return False
139
143
 
140
144
 
@@ -349,8 +353,8 @@ def dependents(s: Snap, nid: str, limit: int = 500) -> list[tuple[str, str, int]
349
353
 
350
354
 
351
355
  def supporters(s: Snap, nid: str) -> list[dict]:
352
- """What stands behind a node: every `supports` edge into it, and — for a claim — the results it is derived from
353
- and the results its measurements bind (each once). A claim that binds its numbers to a result rests on that
356
+ """What stands behind a node: every `supports` edge into it, and — for a claim — the results it is derived from,
357
+ the sources it derives from or cites, and the results its measurements bind (each once). A claim that binds its numbers to a result rests on that
354
358
  result whether or not anyone also wrote `supports`; until 0.7.4 only the edge counted, and most honest claims
355
359
  read "no live support" with their evidence "unverified"."""
356
360
  out = [e for e in s.inn.get(nid, []) if e["rel"] == "supports"]
@@ -358,10 +362,11 @@ def supporters(s: Snap, nid: str) -> list[dict]:
358
362
  if n and n["type"] == "claim":
359
363
  seen = {e["src"] for e in out}
360
364
  for e in s.out.get(nid, []):
361
- if e["rel"] == "derived_from" and e["dst"] in s.nodes and s.nodes[e["dst"]]["type"] == "result" \
362
- and e["dst"] not in seen:
365
+ # a result it derives from, or a source it derives from or cites: what the claim rests on
366
+ if e["rel"] in ("derived_from", "cites") and e["dst"] in s.nodes and e["dst"] not in seen \
367
+ and s.nodes[e["dst"]]["type"] in (("result", "source") if e["rel"] == "derived_from" else ("source",)):
363
368
  seen.add(e["dst"])
364
- out.append({"src": e["dst"], "rel": "derived_from", "dst": nid, "data": {}, "by": n["created_by"]})
369
+ out.append({"src": e["dst"], "rel": e["rel"], "dst": nid, "data": {}, "by": n["created_by"]})
365
370
  for m in n["body"].get("measurements") or []:
366
371
  rid = m.get("result") if isinstance(m, dict) else None
367
372
  if rid and rid in s.nodes and s.nodes[rid]["type"] == "result" and rid not in seen:
@@ -0,0 +1,60 @@
1
+ """The last claims reading 'no live support' after bound results counted: a knowledge claim resting on the sources it
2
+ cites, and claims on results whose author wrote outcome 'failed' for the code under test failing its spec, on runs
3
+ that succeeded (E4: 13 checked claims read UNSUPPORTED — every support has fallen)."""
4
+ import pytest
5
+
6
+ from hypgraph import Graph, Policy
7
+ from hypgraph import derive as D
8
+
9
+
10
+ @pytest.fixture
11
+ def env():
12
+ g = Graph(policy=Policy(reviewers={"critic"}, instrument="lab"))
13
+ inv = g.author("lead").investigation("Support", "Find out what a claim rests on.")["id"]
14
+ return g, inv
15
+
16
+
17
+ def _state(g, nid):
18
+ return D.state(g.snapshot(), nid)
19
+
20
+
21
+ def test_a_cited_source_is_a_line_of_support_rated_external(env):
22
+ g, inv = env
23
+ r = g.author("researcher")
24
+ s1 = r.add("source", inv, citation="config.py in the 2.4.0 release", url="file:sources/config.py",
25
+ passage='"retry_backoff": 30, # seconds; 60 before 2.3.0')["id"]
26
+ s2 = r.add("source", inv, citation="The 2.4 configuration guide", url="file:sources/config-guide.md",
27
+ passage="retry_backoff defaults to 30 seconds")["id"]
28
+ c = r.add("claim", inv, statement="The default retry_backoff in the current release is 30 seconds.",
29
+ scope="The 2.4.0 release's config module and its configuration guide.",
30
+ links=[{"rel": "cites", "to": s1}, {"rel": "cites", "to": s2}])
31
+ st = _state(g, c["id"])
32
+ assert st["independent_lines"] == 2 and st["evidence"] == "external" and not st["unsupported"]
33
+
34
+
35
+ def test_a_failed_outcome_on_a_succeeded_run_is_a_finding_not_a_fall(env):
36
+ g, inv = env
37
+ t = g.author("tester")
38
+ run = t.add("run", inv, kind="agent", name="spec cases", state="succeeded")["id"]
39
+ r = t.add("result", inv, kind="computed", outcome="failed",
40
+ summary="Of the 8 valid-input cases, parse_duration gets exactly 1 wrong: case 8.",
41
+ values={"valid_wrong": {"k": 1, "n": 8}}, links=[{"rel": "produced", "from": run}])["id"]
42
+ assert not D.fallen(g.snapshot(), r)
43
+ c = t.add("claim", inv, statement="parse_duration gets exactly 1 of the 8 valid-input cases wrong.",
44
+ scope="SPEC.md's 8 valid-input cases on the current diff.",
45
+ measurements=[{"name": "valid wrong", "result": r, "key": "valid_wrong", "k": 1, "n": 8}],
46
+ links=[{"rel": "derived_from", "to": r}])
47
+ st = _state(g, c["id"])
48
+ assert st["independent_lines"] == 1 and not st["unsupported"] and "UNSUPPORTED" not in st["sentence"]
49
+
50
+
51
+ def test_a_failed_outcome_still_falls_when_the_run_failed_or_there_is_none(env):
52
+ g, inv = env
53
+ t = g.author("tester")
54
+ bad = t.add("run", inv, kind="agent", name="spec cases", state="failed")["id"]
55
+ r1 = t.add("result", inv, kind="computed", outcome="failed", summary="The harness crashed before the cases ran.",
56
+ values={"n_cases": 12}, links=[{"rel": "produced", "from": bad}])["id"]
57
+ r2 = t.add("result", inv, kind="computed", outcome="failed", summary="pytest could not import the module.",
58
+ values={"n_collected": 0})["id"]
59
+ s = g.snapshot()
60
+ assert D.fallen(s, r1) and D.fallen(s, r2)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes