hypgraph 0.7.3__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.
- {hypgraph-0.7.3 → hypgraph-0.7.5}/CHANGELOG.md +25 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/PKG-INFO +1 -1
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/__init__.py +1 -1
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/derive.py +25 -2
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/graph.py +14 -3
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/schema.py +19 -0
- hypgraph-0.7.5/tests/test_bound_support.py +55 -0
- hypgraph-0.7.5/tests/test_cited_and_failed_support.py +60 -0
- hypgraph-0.7.5/tests/test_relation_words.py +38 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/.gitignore +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/AGENTS.md +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/CITATION.cff +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/LICENSE +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/NOTICE +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/README.md +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/docs/PLAN.md +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/docs/decisions/ADR-001-token-bound-authors.md +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/docs/decisions/ADR-002-governance-profiles.md +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/docs/profile-workflows-plan.md +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/docs/review-2026-10-24.md +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/docs/site-plan.md +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/docs/spec.md +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/docs/ui-plan.md +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/docs/workflows/README.md +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/docs/workflows/knowledge.md +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/docs/workflows/optimization.md +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/docs/workflows/scientific.md +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/docs/workflows/software.md +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/docs/workflows/trading.md +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/pyproject.toml +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/__main__.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/bundle.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/checks.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/cli.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/coord.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/demos/__init__.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/demos/broad_street.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/language.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/mcp.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/plan.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/policy.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/profiles.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/refusals.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/report_check.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/server.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/skill/SKILL.md +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/stats.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/store.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/ui/__init__.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/ui/atlas.css +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/ui/atlas.js +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/ui/hypgraph.css +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/ui/hypgraph.js +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/ui/hypgraph.svg +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/ui/index.html +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/ui/vendor/LICENSES.md +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/ui/vendor/cose-base.js +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/ui/vendor/cytoscape-fcose.js +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/ui/vendor/cytoscape.min.js +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/ui/vendor/layout-base.js +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/src/hypgraph/workflow.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_accept_work_kind.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_bundle_telemetry.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_cancel_releases_lease.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_coord.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_d10.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_engine.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_engine_fixes.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_incremental.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_link_translate.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_log_and_check.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_measure_questions.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_opposed_answers.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_option_lead.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_order_detail.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_profiles.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_profiles_041.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_questions_not_assertions.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_refusal_friction.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_refusal_results.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_refusals.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_report_answers.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_rerunnable.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_restates_scale.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_server_auth.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_shape_refusals.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_stats_not_backtests.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_stats_values.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_surfaces.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_telemetry_stream.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_ui_app.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_ui_engine.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_uncorrected_effect.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_unnamed_inputs.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_versions_not_figures.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_vocabulary_friction.py +0 -0
- {hypgraph-0.7.3 → hypgraph-0.7.5}/tests/test_workflows.py +0 -0
|
@@ -1,4 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
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
|
+
|
|
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
|
|
14
|
+
|
|
15
|
+
- **A bound result is a line of support.** A claim's standing counted only `supports` edges into it, so a claim
|
|
16
|
+
that derives from a result and binds its numbers to it by `measurements` read "no live support", evidence
|
|
17
|
+
"unverified" — 73 of 113 active claims in the challenge records, each with its result right there. The results a
|
|
18
|
+
claim derives from and the results its measurements bind now count as lines of support (each once; shared runs
|
|
19
|
+
or data still merge into one line), so the evidence profile is the result's — lab-recorded when the instrument
|
|
20
|
+
filed it — and `unsupported` means what it says.
|
|
21
|
+
- **A relation word the grammar doesn't have is read as the relation it names.** "generated", "created", "wrote"
|
|
22
|
+
→ `produced`; "ran", "executed" → `executes`; "test" → `tests`; "answer" → `answers`; "used", "reads" → `uses`;
|
|
23
|
+
"contradicts", "refutes" → `challenges`; and so on (`schema.REL_SYNONYMS`, one reading each), noted on the
|
|
24
|
+
link like a translation. A hypothesis "derived from" a claim or result is `motivated_by` it; an experiment
|
|
25
|
+
"derived from" a hypothesis `tests` it. The link refusals left in the runs after 0.7.3: an unknown relation
|
|
26
|
+
`generated`, `derived_from hypothesis → claim`.
|
|
2
27
|
|
|
3
28
|
## 0.7.3 — 2026-10-28: the refusal log sees everything — results, shape, links — and reads a link as the relation that joins
|
|
4
29
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: hypgraph
|
|
3
|
-
Version: 0.7.
|
|
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.
|
|
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
|
-
|
|
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,7 +353,26 @@ 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
|
-
|
|
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
|
|
358
|
+
result whether or not anyone also wrote `supports`; until 0.7.4 only the edge counted, and most honest claims
|
|
359
|
+
read "no live support" with their evidence "unverified"."""
|
|
360
|
+
out = [e for e in s.inn.get(nid, []) if e["rel"] == "supports"]
|
|
361
|
+
n = s.nodes.get(nid)
|
|
362
|
+
if n and n["type"] == "claim":
|
|
363
|
+
seen = {e["src"] for e in out}
|
|
364
|
+
for e in s.out.get(nid, []):
|
|
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",)):
|
|
368
|
+
seen.add(e["dst"])
|
|
369
|
+
out.append({"src": e["dst"], "rel": e["rel"], "dst": nid, "data": {}, "by": n["created_by"]})
|
|
370
|
+
for m in n["body"].get("measurements") or []:
|
|
371
|
+
rid = m.get("result") if isinstance(m, dict) else None
|
|
372
|
+
if rid and rid in s.nodes and s.nodes[rid]["type"] == "result" and rid not in seen:
|
|
373
|
+
seen.add(rid)
|
|
374
|
+
out.append({"src": rid, "rel": "measurement", "dst": nid, "data": {}, "by": n["created_by"]})
|
|
375
|
+
return out
|
|
353
376
|
|
|
354
377
|
|
|
355
378
|
def at_risk(s: Snap, nid: str) -> list[str]:
|
|
@@ -31,7 +31,7 @@ from . import derive as D
|
|
|
31
31
|
from . import refusals, store
|
|
32
32
|
from .checks import claim_issues, value_issues
|
|
33
33
|
from .policy import Policy
|
|
34
|
-
from .schema import REPLICATION, RELS, SAME_TYPE, SYNONYMS, TRANSLATE, TYPES, VERDICTS
|
|
34
|
+
from .schema import REL_SYNONYMS, REPLICATION, RELS, SAME_TYPE, SYNONYMS, TRANSLATE, TYPES, VERDICTS
|
|
35
35
|
from .store import canon, now
|
|
36
36
|
|
|
37
37
|
|
|
@@ -462,14 +462,25 @@ class Graph:
|
|
|
462
462
|
@staticmethod
|
|
463
463
|
def _translate(s: D.Snap, src: str, rel: str, dst: str) -> tuple[str, str]:
|
|
464
464
|
"""A relation that can't join these two types, read as the one that says the same thing between them
|
|
465
|
-
(schema.TRANSLATE)
|
|
465
|
+
(schema.TRANSLATE), or a word the grammar doesn't have read as the relation it names (schema.REL_SYNONYMS):
|
|
466
|
+
the relation to apply and a note, or the given relation and ''."""
|
|
467
|
+
given = rel
|
|
468
|
+
if rel not in RELS:
|
|
469
|
+
word = str(rel or "").strip().lower().replace(" ", "_").replace("-", "_")
|
|
470
|
+
rel = REL_SYNONYMS.get(word, rel)
|
|
471
|
+
if rel in RELS and src in s.nodes and dst in s.nodes:
|
|
472
|
+
st, dt = s.nodes[src]["type"], s.nodes[dst]["type"]
|
|
473
|
+
srcs, dsts = RELS[rel]
|
|
474
|
+
if (st in srcs and dt in dsts) or (dt in srcs and st in dsts):
|
|
475
|
+
return rel, (f"{s.nodes[src].get('code') or src} -{given}-> {s.nodes[dst].get('code') or dst} "
|
|
476
|
+
f"read as {rel}")
|
|
466
477
|
if rel in RELS and src in s.nodes and dst in s.nodes:
|
|
467
478
|
st, dt = s.nodes[src]["type"], s.nodes[dst]["type"]
|
|
468
479
|
srcs, dsts = RELS[rel]
|
|
469
480
|
if not (st in srcs and dt in dsts) and not (dt in srcs and st in dsts):
|
|
470
481
|
new = TRANSLATE.get((rel, st, dt))
|
|
471
482
|
if new:
|
|
472
|
-
return new, (f"{s.nodes[src].get('code') or src} -{
|
|
483
|
+
return new, (f"{s.nodes[src].get('code') or src} -{given}-> {s.nodes[dst].get('code') or dst} "
|
|
473
484
|
f"read as {new} (a {st} {new.replace('_', ' ')} a {dt}; {rel} joins "
|
|
474
485
|
f"{', '.join(sorted(srcs))} to {', '.join(sorted(dsts))})")
|
|
475
486
|
return rel, ""
|
|
@@ -125,6 +125,25 @@ TRANSLATE: dict[tuple[str, str, str], str] = {
|
|
|
125
125
|
("part_of", "claim", "artifact"): "derived_from",
|
|
126
126
|
("supports", "decision", "hypothesis"): "based_on", ("supports", "decision", "claim"): "based_on",
|
|
127
127
|
("supports", "decision", "result"): "based_on",
|
|
128
|
+
("derived_from", "hypothesis", "claim"): "motivated_by", ("derived_from", "hypothesis", "result"): "motivated_by",
|
|
129
|
+
("derived_from", "experiment", "hypothesis"): "tests", ("derived_from", "question", "claim"): "motivated_by",
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
# A relation written as a word the grammar doesn't have — the run's own verb ("generated", "ran", "test") — read as
|
|
133
|
+
# the relation it names. Only words with one reading.
|
|
134
|
+
REL_SYNONYMS: dict[str, str] = {
|
|
135
|
+
"generated": "produced", "generates": "produced", "created": "produced", "creates": "produced",
|
|
136
|
+
"wrote": "produced", "writes": "produced", "produces": "produced", "output": "produced",
|
|
137
|
+
"ran": "executes", "runs": "executes", "executed": "executes", "execute": "executes", "performs": "executes",
|
|
138
|
+
"test": "tests", "tested": "tests", "testing": "tests", "answer": "answers", "answered": "answers",
|
|
139
|
+
"support": "supports", "supported": "supports", "challenge": "challenges", "challenged": "challenges",
|
|
140
|
+
"contradicts": "challenges", "refutes": "challenges", "derived": "derived_from", "derives_from": "derived_from",
|
|
141
|
+
"computed_from": "derived_from", "based": "based_on", "use": "uses", "used": "uses", "using": "uses",
|
|
142
|
+
"reads": "uses", "read": "uses", "cite": "cites", "cited": "cites", "citing": "cites", "replicate": "replicates",
|
|
143
|
+
"replicated": "replicates", "supersede": "supersedes", "superseded": "supersedes", "replaces": "supersedes",
|
|
144
|
+
"correct": "corrects", "corrected": "corrects", "require": "requires", "required": "requires",
|
|
145
|
+
"depends_on": "requires", "part": "part_of", "belongs_to": "part_of", "motivated": "motivated_by",
|
|
146
|
+
"rests": "rests_on", "works": "works_on", "blocked": "blocked_by", "settle": "settles", "settled": "settles",
|
|
128
147
|
}
|
|
129
148
|
|
|
130
149
|
SYNONYMS: dict[tuple[str, str], dict[str, str]] = {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""A result a claim derives from, or binds a measurement to, is a line of support; two bound results from one run are
|
|
2
|
+
one line; a bound result that fell leaves the claim unsupported. 73 of 113 active claims in the challenge records
|
|
3
|
+
read "no live support" beside the result they were bound to."""
|
|
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
|
+
lead = g.author("lead")
|
|
14
|
+
inv = lead.investigation("Support", "Find out what a bound claim rests on.")["id"]
|
|
15
|
+
run = g.create(author="lab", type="run", inv=inv, fields={"kind": "lab", "name": "count", "state": "succeeded"})["id"]
|
|
16
|
+
r1 = g.create(author="lab", type="result", inv=inv, fields={"summary": "607 of 821 rows pass the check.",
|
|
17
|
+
"values": {"rows": {"k": 607, "n": 821}}}, links=[{"rel": "produced", "from": run}])["id"]
|
|
18
|
+
r2 = g.create(author="lab", type="result", inv=inv, fields={"summary": "214 of 821 rows carry a comma.",
|
|
19
|
+
"values": {"commas": {"k": 214, "n": 821}}}, links=[{"rel": "produced", "from": run}])["id"]
|
|
20
|
+
return g, inv, run, r1, r2
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _state(g, nid):
|
|
24
|
+
return D.state(g.snapshot(), nid)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_a_claim_bound_by_measurements_and_derived_from_is_supported_by_its_result(env):
|
|
28
|
+
g, inv, run, r1, r2 = env
|
|
29
|
+
c = g.author("analyst").add("claim", inv, statement="607 of 821 rows pass the check.", scope="corpus.csv's 821 rows.",
|
|
30
|
+
measurements=[{"name": "rows", "result": r1, "key": "rows", "k": 607, "n": 821}],
|
|
31
|
+
links=[{"rel": "derived_from", "to": r1}])
|
|
32
|
+
st = _state(g, c["id"])
|
|
33
|
+
assert st["independent_lines"] == 1 and st["evidence"] != "unverified" and not st["unsupported"]
|
|
34
|
+
assert "1 independent line of support" in st["sentence"]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_two_bound_results_from_one_run_are_one_line_and_a_supports_edge_adds_none(env):
|
|
38
|
+
g, inv, run, r1, r2 = env
|
|
39
|
+
c = g.author("analyst").add("claim", inv, statement="607 of 821 rows pass and 214 of 821 carry a comma.",
|
|
40
|
+
scope="corpus.csv's 821 rows.",
|
|
41
|
+
measurements=[{"name": "rows", "result": r1, "key": "rows", "k": 607, "n": 821},
|
|
42
|
+
{"name": "commas", "result": r2, "key": "commas", "k": 214, "n": 821}])
|
|
43
|
+
assert _state(g, c["id"])["independent_lines"] == 1
|
|
44
|
+
g.author("lab").link(r1, "supports", c["id"], why="the count is the claim's measurement")
|
|
45
|
+
assert _state(g, c["id"])["independent_lines"] == 1 # the same result, not a second line
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_a_claim_whose_bound_result_fell_is_unsupported(env):
|
|
49
|
+
g, inv, run, r1, r2 = env
|
|
50
|
+
c = g.author("analyst").add("claim", inv, statement="607 of 821 rows pass the check.", scope="corpus.csv's 821 rows.",
|
|
51
|
+
measurements=[{"name": "rows", "result": r1, "key": "rows", "k": 607, "n": 821}],
|
|
52
|
+
links=[{"rel": "derived_from", "to": r1}])
|
|
53
|
+
g.author("lab").withdraw(r1, "the count read the wrong column")
|
|
54
|
+
st = _state(g, c["id"])
|
|
55
|
+
assert st["unsupported"] and st["independent_lines"] == 0
|
|
@@ -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)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""A relation word the grammar doesn't have is read as the relation it names; a hypothesis derived from a claim is
|
|
2
|
+
motivated by it. The link refusals left after 0.7.3's translation table."""
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from hypgraph import Graph, GraphError, Policy
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.fixture
|
|
9
|
+
def env():
|
|
10
|
+
g = Graph(policy=Policy(reviewers={"critic"}))
|
|
11
|
+
lead = g.author("lead")
|
|
12
|
+
inv = lead.investigation("Words", "Find out which relation words the record reads.")["id"]
|
|
13
|
+
return g, inv
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_a_run_that_generated_an_artifact_produced_it(env):
|
|
17
|
+
g, inv = env
|
|
18
|
+
a = g.author("quant")
|
|
19
|
+
art = a.add("artifact", inv, sha256="b" * 64, path="grid.csv", kind="data")["id"]
|
|
20
|
+
run = a.add("run", inv, kind="agent", name="grid", state="succeeded", links=[{"rel": "generated", "to": art}])
|
|
21
|
+
assert ("produced", art) in {(x["rel"], x["dst"]) for x in g.snapshot().out[run["id"]]}
|
|
22
|
+
assert any("read as produced" in n for n in run["notes"])
|
|
23
|
+
out = a.link(run["id"], "used", art, why="the run read the grid", role="data")
|
|
24
|
+
assert out["rel"] == "uses" and out["notes"]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_a_hypothesis_derived_from_a_claim_is_motivated_by_it_and_nonsense_is_still_refused(env):
|
|
28
|
+
g, inv = env
|
|
29
|
+
a = g.author("stats")
|
|
30
|
+
c = a.add("claim", inv, statement="steps_k is lower on the supplement arm in the pilot.", scope="pilot.csv, 40 rows.",
|
|
31
|
+
kind="lead")["id"]
|
|
32
|
+
h = a.add("hypothesis", inv, statement="The supplement lowers steps_k.",
|
|
33
|
+
predictions=["steps_k is lower on the supplement arm in the trial."],
|
|
34
|
+
links=[{"rel": "derived_from", "to": c}])
|
|
35
|
+
assert ("motivated_by", c) in {(x["rel"], x["dst"]) for x in g.snapshot().out[h["id"]]}
|
|
36
|
+
with pytest.raises(GraphError) as e:
|
|
37
|
+
a.link(h["id"], "resembles", c, why="no such relation, and no word for it")
|
|
38
|
+
assert {p["code"] for p in e.value.problems} == {"LINK_UNKNOWN_RELATION"}
|
|
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
|
|
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
|
|
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
|
|
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
|