hypgraph 0.8.4__tar.gz → 0.8.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.8.4 → hypgraph-0.8.5}/CHANGELOG.md +17 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/PKG-INFO +1 -1
- {hypgraph-0.8.4 → hypgraph-0.8.5}/docs/PLAN.md +1 -1
- {hypgraph-0.8.4 → hypgraph-0.8.5}/docs/RELEASE-READINESS.md +6 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/__init__.py +1 -1
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/checks.py +14 -3
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/report_check.py +26 -3
- hypgraph-0.8.5/tests/test_report_significance_denied.py +62 -0
- hypgraph-0.8.5/tests/test_statement_slots.py +60 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_versions_not_figures.py +8 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/.gitignore +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/AGENTS.md +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/CITATION.cff +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/DISCLAIMER.md +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/LICENSE +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/NOTICE +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/README.md +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/docs/decisions/ADR-001-token-bound-authors.md +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/docs/decisions/ADR-002-governance-profiles.md +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/docs/profile-workflows-plan.md +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/docs/review-2026-10-24.md +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/docs/site-plan.md +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/docs/spec.md +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/docs/ui-plan.md +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/docs/verification.md +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/docs/workflows/README.md +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/docs/workflows/knowledge.md +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/docs/workflows/optimization.md +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/docs/workflows/scientific.md +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/docs/workflows/software.md +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/docs/workflows/trading.md +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/examples/frameworks/README.md +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/pyproject.toml +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/__main__.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/assertions.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/blind.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/bundle.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/cli.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/coord.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/demos/__init__.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/demos/broad_street.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/demos/dispute.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/demos/linked.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/derive.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/graph.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/language.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/mcp.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/plan.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/policy.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/profiles.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/references.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/refusals.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/schema.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/server.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/skill/SKILL.md +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/stats.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/store.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/ui/__init__.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/ui/atlas.css +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/ui/atlas.js +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/ui/hypgraph.css +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/ui/hypgraph.i18n.js +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/ui/hypgraph.js +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/ui/hypgraph.svg +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/ui/index.html +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/ui/vendor/LICENSES.md +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/ui/vendor/cose-base.js +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/ui/vendor/cytoscape-fcose.js +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/ui/vendor/cytoscape.min.js +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/ui/vendor/layout-base.js +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/src/hypgraph/workflow.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_accept_work_kind.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_anchoring.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_assertions.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_blind.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_blind_oracles.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_borrowed_words.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_bound_support.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_bundle_telemetry.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_cancel_releases_lease.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_cited_and_failed_support.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_contradictions.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_coord.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_counts_that_bear.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_d10.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_demo_dispute.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_direction_and_units.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_engine.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_engine_fixes.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_figure_said_twice.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_framework_examples.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_i18n.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_incremental.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_link_translate.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_linked_records.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_log_and_check.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_measure_questions.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_next_order.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_opposed_answers.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_option_lead.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_order_detail.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_profiles.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_profiles_041.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_quantifiers_and_identity.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_questions_not_assertions.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_quoted_counterexample.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_references.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_refusal_friction.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_refusal_results.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_refusals.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_relation_words.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_report_answers.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_rerunnable.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_restates_scale.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_server_auth.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_server_hosts.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_shape_refusals.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_stats_not_backtests.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_stats_values.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_surfaces.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_telemetry_stream.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_trust_boundary.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_typed_audits.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_ui_app.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_ui_engine.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_ui_sources_focus.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_uncorrected_effect.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_unnamed_inputs.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_verify_complete.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_verify_lifecycle.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_verify_scope.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_verify_scoped_views.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_verify_trust.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_verify_views.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_vocabulary_friction.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_what_a_count_is_of.py +0 -0
- {hypgraph-0.8.4 → hypgraph-0.8.5}/tests/test_workflows.py +0 -0
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.8.5 — 2026-11-06: fewer false refusals
|
|
6
|
+
|
|
7
|
+
- A date a source writes as an ordinal is in the source: "the 14th of June 1887" accounts for "14 June 1887". It was refused as an unbound number, and a seat ended up with a claim saying the bridge opened "on a date".
|
|
8
|
+
- A statement with an unfilled slot is refused as STATEMENT_EMPTY: a template's `{date}`, or a variable's name where its value belongs (`accuracy = A_easy_acc`). Inline code is not read for this.
|
|
9
|
+
- **A report that denies significance isn't refused for it.** `REPORT_SIGNIFICANCE_UNBACKED` is a warning, not an
|
|
10
|
+
error, when the sentence denies the test: emphasis ("*not* significantly"), "no statistically significant difference",
|
|
11
|
+
"does not support a statistically significant advantage". Before, only the bare "not significant" counted, so an
|
|
12
|
+
honest negative was refused and small models gave up on the report. A sentence that also asserts significance
|
|
13
|
+
("not only significantly", "B is not significantly cheaper, but A is significantly better") stays an error.
|
|
14
|
+
- **A named p-value carries the test.** A result whose values name one (`p`, `p_value`, `pvalue`, `p_exact`, …) backs a
|
|
15
|
+
significance word, and so does a claim whose statement gives a p-value or that rests on such a result. Before, only a
|
|
16
|
+
value with a `p` field did, so citing the claim that held the test was refused.
|
|
17
|
+
- **A word and a figure say something.** `STATEMENT_EMPTY` no longer fires on "A's accuracy is 0.814."; placeholders
|
|
18
|
+
still do, with a number beside them or not.
|
|
19
|
+
|
|
3
20
|
## 0.8.4 — 2026-11-04: sources in the sidebar; a focus on one node's part of the graph
|
|
4
21
|
|
|
5
22
|
- **A Sources view in the Explorer's sidebar.** Everything an investigation cites, in one place: outside literature
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: hypgraph
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.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/
|
|
@@ -109,7 +109,7 @@ claim's derived standing.
|
|
|
109
109
|
Multi-writer sync between graphs (bundles cover sharing), authentication (the host's job; the HTTP server binds to
|
|
110
110
|
loopback and is read-only unless started with `--writable`), and a hosted service.
|
|
111
111
|
|
|
112
|
-
## Status at 0.8.
|
|
112
|
+
## Status at 0.8.5 (2026-11-06)
|
|
113
113
|
|
|
114
114
|
All seven phases are done: the library, bundles, the server and UI, the MCP server, the website, Pegasus running on
|
|
115
115
|
the package, and releases to PyPI from tags with trusted publishing. What came after the plan, by release, is in
|
|
@@ -261,6 +261,12 @@ was taken from a changelog without being checked against the code.*
|
|
|
261
261
|
> with two or more of them when the exporting and verifying processes hashed strings differently. The list now follows
|
|
262
262
|
> the order the hypotheses were added. #109 is merged with green CI. Released as 0.8.3.
|
|
263
263
|
|
|
264
|
+
> **After 0.8.4 (2026-11-06).** Checks only, no change to the bundle format: false refusals found running HypSwarm on a
|
|
265
|
+
> small local model. A report that denies significance is warned, not refused; a named p-value carries the test; a word
|
|
266
|
+
> and a figure are a statement, and an unfilled slot isn't; an ordinal in a source accounts for the number. Tests:
|
|
267
|
+
> `tests/test_report_significance_denied.py`, `tests/test_statement_slots.py`, `tests/test_versions_not_figures.py`.
|
|
268
|
+
> #113 is merged with green CI. Released as 0.8.5.
|
|
269
|
+
|
|
264
270
|
> **After 0.8.3 (2026-11-04).** Explorer only, no change to the engine, the checks or the bundle format: a Sources view
|
|
265
271
|
> (what an investigation cites, with what the record did and did not check about each) and a focus on one node's part
|
|
266
272
|
> of the graph, carried in the link. The focus is computed in the browser from the record's own edges;
|
|
@@ -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.8.
|
|
19
|
+
__version__ = "0.8.5"
|
|
20
20
|
|
|
21
21
|
from .bundle import anchored, verify_bundle # noqa: E402
|
|
22
22
|
from .graph import Author, Graph, GraphError, file_sha256 # noqa: E402
|
|
@@ -1598,6 +1598,9 @@ def _exclusion(s: Snap, stmt: str, kind: str, support: list[str], bound: list) -
|
|
|
1598
1598
|
"effect", words=words)]
|
|
1599
1599
|
|
|
1600
1600
|
|
|
1601
|
+
_ORDINAL = re.compile(r"\b(\d+)(?:st|nd|rd|th)\b", re.I)
|
|
1602
|
+
# a value not filled in: a template's {date}, or a variable's name where its value belongs ("accuracy = A_easy_acc")
|
|
1603
|
+
_SLOT = re.compile(r"\{\s*[A-Za-z_][\w.]*\s*\}|=\s*[A-Za-z]+_[A-Za-z0-9_]*\b(?!\s*\()")
|
|
1601
1604
|
_PLACEHOLDER = re.compile(r"^\W*(?:(?:placeholder|tbd|todo|tba|n/?a|none|wip|xxx|test|claim|statement|pending|"
|
|
1602
1605
|
r"to be (?:determined|confirmed|added)|lorem ipsum)\W*)+$", re.I)
|
|
1603
1606
|
|
|
@@ -1776,13 +1779,20 @@ def claim_issues(s: Snap, body: dict, support: list[str], inv: str) -> list[Prob
|
|
|
1776
1779
|
if invb.get("strict_claims") is False:
|
|
1777
1780
|
return []
|
|
1778
1781
|
problems: list[Problem] = []
|
|
1779
|
-
|
|
1780
|
-
|
|
1782
|
+
words = len(re.findall(r"[^\W\d_]{3,}", stmt))
|
|
1783
|
+
if _PLACEHOLDER.match(_NUM.sub("", stmt)) or words < (1 if _NUM.search(stmt) else 2):
|
|
1784
|
+
# a claim written to clear a gate (studies/challenges E2: "Placeholder.", contested and left in the record);
|
|
1785
|
+
# a word and a figure do say something: "A's accuracy is 0.814."
|
|
1781
1786
|
problems.append(Problem("STATEMENT_EMPTY", "the statement says nothing: write the finding itself — what was "
|
|
1782
1787
|
"measured, on what, and what it came to"))
|
|
1783
1788
|
if kind in ("finding", "exclusion") and len(scope.strip()) < 10:
|
|
1784
1789
|
problems.append(Problem("SCOPE_MISSING", "scope: say under which conditions this holds (the data, method, "
|
|
1785
1790
|
"sample) — at least a sentence"))
|
|
1791
|
+
slot = _SLOT.search(re.sub(r"`[^`]*`", " ", stmt))
|
|
1792
|
+
if slot:
|
|
1793
|
+
# refused the date, a seat wrote "opened on {date}", and a later version "on a date" was checked as the answer
|
|
1794
|
+
problems.append(Problem("STATEMENT_EMPTY", f"the statement has an unfilled slot ({slot.group(0).strip()}): "
|
|
1795
|
+
"write the value itself", words=[slot.group(0).strip()]))
|
|
1786
1796
|
if kind == "convention" and L.HEDGE.search(stmt):
|
|
1787
1797
|
problems.append(Problem("CONVENTION_HEDGED", "a convention states agreed notation; a hedged interpretation "
|
|
1788
1798
|
"(could / suggests / consistent with) is a finding or a lead"))
|
|
@@ -1799,7 +1809,8 @@ def claim_issues(s: Snap, body: dict, support: list[str], inv: str) -> list[Prob
|
|
|
1799
1809
|
[float(c) for c in (invb.get("constants") or []) if isinstance(c, (int, float))]
|
|
1800
1810
|
for src in support: # a cited source's own text accounts for the numbers in it
|
|
1801
1811
|
sb = s.nodes[src]["body"] if s.nodes[src]["type"] == "source" else {}
|
|
1802
|
-
|
|
1812
|
+
said = " ".join(str(sb.get(k) or "") for k in ("passage", "citation", "title"))
|
|
1813
|
+
allowed += [v for v, *_ in numbers(_ORDINAL.sub(r"\1", said))] # "the 14th of June" accounts for "14 June"
|
|
1803
1814
|
if _CONVENTIONS.search(clean): # "95% interval", "α 0.05", "80% power" are conventions
|
|
1804
1815
|
allowed += [95.0, 99.0, 90.0, 80.0, 0.05, 0.01]
|
|
1805
1816
|
loose = [p for p in numbers(clean) if not _matches(p, allowed) and not _matches_stat(p, stat)]
|
|
@@ -92,6 +92,28 @@ def _asserted(pattern: re.Pattern, text: str) -> list[str]:
|
|
|
92
92
|
return [m.group(0).lower() for m in pattern.finditer(text) if not _NEGATOR.search(text[:m.start()])]
|
|
93
93
|
|
|
94
94
|
|
|
95
|
+
_P_KEY = re.compile(r"p(?:[_-]?val(?:ue)?|_[a-z0-9_]+)?", re.I)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _carries_test(s: D.Snap, nid: str) -> bool:
|
|
99
|
+
"""Does this node hold a test: a two-sample count the record worked out, a value with a p, a value named as one
|
|
100
|
+
(p_value, p_exact, pvalue), or, for a claim, its own statement's p-value or any such result it rests on?"""
|
|
101
|
+
def holds(n: str) -> bool:
|
|
102
|
+
vals = s.nodes[n]["body"].get("values") or {}
|
|
103
|
+
return bool(D.state(s, n).get("test")) or any(
|
|
104
|
+
_P_KEY.fullmatch(str(k)) or (isinstance(v, dict) and "p" in v) for k, v in vals.items())
|
|
105
|
+
if holds(nid):
|
|
106
|
+
return True
|
|
107
|
+
n = s.nodes[nid]
|
|
108
|
+
if n["type"] != "claim":
|
|
109
|
+
return False
|
|
110
|
+
if _P_VALUE.search(n["body"].get("statement") or ""):
|
|
111
|
+
return True
|
|
112
|
+
bound = [m.get("result") for m in n["body"].get("measurements") or [] if isinstance(m, dict)]
|
|
113
|
+
rests = [e["dst"] for e in s.out.get(nid, []) if e["rel"] == "derived_from"]
|
|
114
|
+
return any(holds(r) for r in [*bound, *rests] if r in s.nodes)
|
|
115
|
+
|
|
116
|
+
|
|
95
117
|
def _blank(m: re.Match) -> str:
|
|
96
118
|
return re.sub(r"[^\n]", " ", m.group(0))
|
|
97
119
|
|
|
@@ -286,9 +308,10 @@ def check(g: "Graph", text: str, inv: str = "") -> dict:
|
|
|
286
308
|
f"'{causal[0]}' is causal and no claim is cited — cite the claim backed by an experiment, or say "
|
|
287
309
|
"'is associated with'.")
|
|
288
310
|
sig = L.found(L.SIGNIFICANCE, own)
|
|
289
|
-
if sig and not _P_VALUE.search(sent) and not any(
|
|
290
|
-
|
|
291
|
-
|
|
311
|
+
if sig and not _P_VALUE.search(sent) and not any(_carries_test(s, c) for c in cited):
|
|
312
|
+
# the sentence denies the test rather than claims it: emphasis ("*not* significantly") is no assertion
|
|
313
|
+
plain = re.sub(r"\bnot\s+(?:only|just|merely)\b", "", re.sub(r"[*_]", "", own), flags=re.I)
|
|
314
|
+
negated = bool(L.denied(L.SIGNIFICANCE, plain)) and not L.asserted(L.SIGNIFICANCE, plain)
|
|
292
315
|
add(ln, "warning" if negated else "error", "REPORT_SIGNIFICANCE_UNBACKED", sent,
|
|
293
316
|
f"'{sig[0]}' — cite the claim or result that carries the test.")
|
|
294
317
|
_check_answers(s, text or "", invs, by_code, add, counted)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""0.8.5: a report that says a difference was NOT significant isn't claiming a test passed, and a claim resting on a
|
|
2
|
+
named p-value carries its test. From gpt-oss runs on a small box: leads wrote "*not* significantly" and were refused
|
|
3
|
+
until they gave up, so two of six runs ended with no interpretation."""
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from hypgraph import Graph, Policy
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _record(values):
|
|
10
|
+
g = Graph(policy=Policy(reviewers={"critic"}))
|
|
11
|
+
inv = g.create(author="h", type="investigation", fields={"title": "Does A beat B.", "objective": "Compare A and B on the same items."})["id"]
|
|
12
|
+
q = g.create(author="h", type="question", inv=inv, code="Q2",
|
|
13
|
+
fields={"text": "Is A significantly more accurate than B (two-sided, 0.05)?"})["id"]
|
|
14
|
+
r = g.create(author="stats", type="run", inv=inv, fields={"kind": "agent", "name": "mcnemar", "state": "succeeded"})
|
|
15
|
+
rs = g.create(author="stats", type="result", inv=inv, fields={"summary": "McNemar test on the same 1,000 items.", "values": values},
|
|
16
|
+
links=[{"rel": "produced", "from": r["id"]}])
|
|
17
|
+
c = g.create(author="stats", type="claim", inv=inv,
|
|
18
|
+
fields={"statement": "The McNemar test on the 1,000 shared items gave the value in the result.",
|
|
19
|
+
"scope": "The 1,000 shared items."},
|
|
20
|
+
links=[{"rel": "answers", "to": q}, {"rel": "derived_from", "to": rs["id"]}])
|
|
21
|
+
return g, inv, c["code"], rs["code"]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _sig(g, text, inv):
|
|
25
|
+
return [f["level"] for f in g.check_text(text, inv)["findings"] if f["code"] == "REPORT_SIGNIFICANCE_UNBACKED"]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@pytest.mark.parametrize("sentence", [
|
|
29
|
+
"Model A is *not* significantly more accurate than model B.",
|
|
30
|
+
"Model A is **not** significantly more accurate than model B.",
|
|
31
|
+
"There is no statistically significant difference between A and B.",
|
|
32
|
+
"The evidence does not support a statistically significant advantage for A.",
|
|
33
|
+
"The test did not reach statistical significance, so A is not significantly better.",
|
|
34
|
+
])
|
|
35
|
+
def test_a_denial_is_a_warning_not_a_refusal(sentence):
|
|
36
|
+
g, inv, _, _ = _record({"chi2": {"value": 3.0}})
|
|
37
|
+
assert _sig(g, sentence, inv) in (["warning"], [])
|
|
38
|
+
assert g.check_text(sentence, inv)["errors"] == 0
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@pytest.mark.parametrize("sentence", [
|
|
42
|
+
"A is significantly more accurate than B.",
|
|
43
|
+
"A is not only significantly more accurate than B, it is cheaper.",
|
|
44
|
+
"A is not worse, and it is significantly more accurate than B.",
|
|
45
|
+
"B is not significantly cheaper, but A is significantly more accurate.",
|
|
46
|
+
])
|
|
47
|
+
def test_an_assertion_is_still_refused(sentence):
|
|
48
|
+
g, inv, _, _ = _record({"chi2": {"value": 3.0}})
|
|
49
|
+
assert _sig(g, sentence, inv) == ["error"]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@pytest.mark.parametrize("key", ["p", "p_value", "pvalue", "p-value", "p_exact", "p_two"])
|
|
53
|
+
def test_a_named_p_value_carries_the_test(key):
|
|
54
|
+
g, inv, cl, rs = _record({key: {"value": 0.03}})
|
|
55
|
+
assert _sig(g, f"A is significantly more accurate than B ({rs}).", inv) == [] # the result itself
|
|
56
|
+
assert _sig(g, f"A is significantly more accurate than B ({cl}).", inv) == [] # the claim that rests on it
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_a_claim_without_a_test_does_not_carry_one():
|
|
60
|
+
g, inv, cl, rs = _record({"pct_a": {"value": 90.0}, "pairs": {"value": 322}, "chi2": {"value": 3.0}})
|
|
61
|
+
assert _sig(g, f"A is significantly more accurate than B ({cl}).", inv) == ["error"]
|
|
62
|
+
assert _sig(g, f"A is significantly more accurate than B ({rs}).", inv) == ["error"]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""A number a source writes as an ordinal is in the source, and a statement with a slot in it says nothing. Found on a
|
|
2
|
+
BC-250 run of the knowledge challenge: the passage said "the 14th of June 1887", "14 June 1887" was refused as unbound
|
|
3
|
+
12 times, the seat wrote "opened on {date}", then "opened on a date", and that was checked as the answer."""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from hypgraph import Graph, GraphError, Policy
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _record():
|
|
12
|
+
g = Graph(policy=Policy(reviewers={"critic"}))
|
|
13
|
+
inv = g.create(author="a", type="investigation", fields={
|
|
14
|
+
"title": "The footbridge", "objective": "When did the footbridge open?", "profile": "knowledge@2"})["id"]
|
|
15
|
+
src = g.create(author="a", type="source", inv=inv, fields={
|
|
16
|
+
"url": "file:archive/minutes.md", "kind": "primary", "retrieved": "2026-11-05",
|
|
17
|
+
"citation": "Council minutes, 2 May",
|
|
18
|
+
"passage": "Resolved that the bridge be opened to the public on Tuesday the 14th of June 1887."})["id"]
|
|
19
|
+
return g, inv, src
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _claim(g, inv, src, statement):
|
|
23
|
+
return g.create(author="a", type="claim", inv=inv, fields={
|
|
24
|
+
"statement": statement, "scope": "The council minutes of the town, read in full."},
|
|
25
|
+
links=[{"rel": "cites", "to": src}, {"rel": "supports", "from": src, "why": "the minutes name the day it opened"}])
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@pytest.mark.parametrize("statement", ["The footbridge opened to the public on 14 June 1887.",
|
|
29
|
+
"The footbridge opened to the public on the 14th of June 1887."])
|
|
30
|
+
def test_a_date_written_as_an_ordinal_is_in_the_source(statement):
|
|
31
|
+
g, inv, src = _record()
|
|
32
|
+
assert _claim(g, inv, src, statement)["id"]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_a_number_the_source_doesnt_have_stays_unbound():
|
|
36
|
+
g, inv, src = _record()
|
|
37
|
+
with pytest.raises(GraphError, match="UNBOUND_NUMBER"):
|
|
38
|
+
_claim(g, inv, src, "The footbridge opened to the public on 15 June 1887.")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@pytest.mark.parametrize("statement", [
|
|
42
|
+
"The footbridge opened to the public on {date}.",
|
|
43
|
+
"The footbridge opened to the public on { date }.",
|
|
44
|
+
"Model A easy accuracy = A_easy_acc on the items.",
|
|
45
|
+
"The strategy with fast window {fast_window} returned the most.",
|
|
46
|
+
])
|
|
47
|
+
def test_a_statement_with_a_slot_is_refused(statement):
|
|
48
|
+
g, inv, src = _record()
|
|
49
|
+
with pytest.raises(GraphError, match="STATEMENT_EMPTY"):
|
|
50
|
+
_claim(g, inv, src, statement)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@pytest.mark.parametrize("statement", [
|
|
54
|
+
"The minutes set the option `retry = max_tries` for the footbridge.",
|
|
55
|
+
"The function returned `{date}` for the footbridge.",
|
|
56
|
+
"The footbridge opened in the summer, as the minutes say.",
|
|
57
|
+
])
|
|
58
|
+
def test_code_and_plain_sentences_are_not_slots(statement):
|
|
59
|
+
g, inv, src = _record()
|
|
60
|
+
assert _claim(g, inv, src, statement)["id"]
|
|
@@ -76,3 +76,11 @@ def test_a_statement_that_says_nothing_is_refused(tmp_path):
|
|
|
76
76
|
for i, stmt in enumerate(("Placeholder.", "Placeholder claim.", "TBD — pending.", "To be determined.", "1234567890 ...")):
|
|
77
77
|
assert "STATEMENT_EMPTY" in _claim_error(tmp_path / f"{i}.hypgraph", stmt), stmt
|
|
78
78
|
assert _claim_error(tmp_path / "real.hypgraph", "parse_duration truncates rather than rounds.") == ""
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def test_a_word_and_a_figure_say_something(tmp_path):
|
|
82
|
+
"""From gpt-oss runs: "A's accuracy is 0.814." has one word of three letters, and was refused as saying nothing."""
|
|
83
|
+
for i, stmt in enumerate(("A's accuracy is 0.814.", "Accuracy: 81.4 %.")):
|
|
84
|
+
assert "STATEMENT_EMPTY" not in _claim_error(tmp_path / f"{i}.hypgraph", stmt), stmt
|
|
85
|
+
for i, stmt in enumerate(("TBD 12.", "Placeholder 3.", "Placeholder 3 — pending.", "See RS1.", "12.")):
|
|
86
|
+
assert _claim_error(tmp_path / f"e{i}.hypgraph", stmt), stmt # some are too short to reach this check
|
|
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
|
|
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
|