hypgraph 0.7.16__tar.gz → 0.7.17__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 (117) hide show
  1. {hypgraph-0.7.16 → hypgraph-0.7.17}/CHANGELOG.md +23 -0
  2. {hypgraph-0.7.16 → hypgraph-0.7.17}/PKG-INFO +1 -1
  3. {hypgraph-0.7.16 → hypgraph-0.7.17}/docs/PLAN.md +1 -1
  4. {hypgraph-0.7.16 → hypgraph-0.7.17}/docs/RELEASE-READINESS.md +11 -0
  5. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/__init__.py +1 -1
  6. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/bundle.py +30 -11
  7. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/cli.py +2 -0
  8. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/ui/hypgraph.css +2 -0
  9. hypgraph-0.7.17/src/hypgraph/ui/hypgraph.i18n.js +543 -0
  10. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/ui/hypgraph.js +449 -213
  11. hypgraph-0.7.17/tests/test_i18n.py +183 -0
  12. hypgraph-0.7.17/tests/test_verify_lifecycle.py +195 -0
  13. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_verify_views.py +2 -1
  14. {hypgraph-0.7.16 → hypgraph-0.7.17}/.gitignore +0 -0
  15. {hypgraph-0.7.16 → hypgraph-0.7.17}/AGENTS.md +0 -0
  16. {hypgraph-0.7.16 → hypgraph-0.7.17}/CITATION.cff +0 -0
  17. {hypgraph-0.7.16 → hypgraph-0.7.17}/DISCLAIMER.md +0 -0
  18. {hypgraph-0.7.16 → hypgraph-0.7.17}/LICENSE +0 -0
  19. {hypgraph-0.7.16 → hypgraph-0.7.17}/NOTICE +0 -0
  20. {hypgraph-0.7.16 → hypgraph-0.7.17}/README.md +0 -0
  21. {hypgraph-0.7.16 → hypgraph-0.7.17}/docs/decisions/ADR-001-token-bound-authors.md +0 -0
  22. {hypgraph-0.7.16 → hypgraph-0.7.17}/docs/decisions/ADR-002-governance-profiles.md +0 -0
  23. {hypgraph-0.7.16 → hypgraph-0.7.17}/docs/profile-workflows-plan.md +0 -0
  24. {hypgraph-0.7.16 → hypgraph-0.7.17}/docs/review-2026-10-24.md +0 -0
  25. {hypgraph-0.7.16 → hypgraph-0.7.17}/docs/site-plan.md +0 -0
  26. {hypgraph-0.7.16 → hypgraph-0.7.17}/docs/spec.md +0 -0
  27. {hypgraph-0.7.16 → hypgraph-0.7.17}/docs/ui-plan.md +0 -0
  28. {hypgraph-0.7.16 → hypgraph-0.7.17}/docs/verification.md +0 -0
  29. {hypgraph-0.7.16 → hypgraph-0.7.17}/docs/workflows/README.md +0 -0
  30. {hypgraph-0.7.16 → hypgraph-0.7.17}/docs/workflows/knowledge.md +0 -0
  31. {hypgraph-0.7.16 → hypgraph-0.7.17}/docs/workflows/optimization.md +0 -0
  32. {hypgraph-0.7.16 → hypgraph-0.7.17}/docs/workflows/scientific.md +0 -0
  33. {hypgraph-0.7.16 → hypgraph-0.7.17}/docs/workflows/software.md +0 -0
  34. {hypgraph-0.7.16 → hypgraph-0.7.17}/docs/workflows/trading.md +0 -0
  35. {hypgraph-0.7.16 → hypgraph-0.7.17}/examples/frameworks/README.md +0 -0
  36. {hypgraph-0.7.16 → hypgraph-0.7.17}/pyproject.toml +0 -0
  37. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/__main__.py +0 -0
  38. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/blind.py +0 -0
  39. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/checks.py +0 -0
  40. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/coord.py +0 -0
  41. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/demos/__init__.py +0 -0
  42. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/demos/broad_street.py +0 -0
  43. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/derive.py +0 -0
  44. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/graph.py +0 -0
  45. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/language.py +0 -0
  46. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/mcp.py +0 -0
  47. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/plan.py +0 -0
  48. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/policy.py +0 -0
  49. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/profiles.py +0 -0
  50. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/references.py +0 -0
  51. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/refusals.py +0 -0
  52. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/report_check.py +0 -0
  53. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/schema.py +0 -0
  54. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/server.py +0 -0
  55. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/skill/SKILL.md +0 -0
  56. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/stats.py +0 -0
  57. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/store.py +0 -0
  58. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/ui/__init__.py +0 -0
  59. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/ui/atlas.css +0 -0
  60. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/ui/atlas.js +0 -0
  61. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/ui/hypgraph.svg +0 -0
  62. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/ui/index.html +0 -0
  63. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/ui/vendor/LICENSES.md +0 -0
  64. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/ui/vendor/cose-base.js +0 -0
  65. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/ui/vendor/cytoscape-fcose.js +0 -0
  66. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/ui/vendor/cytoscape.min.js +0 -0
  67. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/ui/vendor/layout-base.js +0 -0
  68. {hypgraph-0.7.16 → hypgraph-0.7.17}/src/hypgraph/workflow.py +0 -0
  69. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_accept_work_kind.py +0 -0
  70. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_anchoring.py +0 -0
  71. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_blind.py +0 -0
  72. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_blind_oracles.py +0 -0
  73. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_bound_support.py +0 -0
  74. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_bundle_telemetry.py +0 -0
  75. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_cancel_releases_lease.py +0 -0
  76. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_cited_and_failed_support.py +0 -0
  77. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_coord.py +0 -0
  78. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_d10.py +0 -0
  79. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_engine.py +0 -0
  80. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_engine_fixes.py +0 -0
  81. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_framework_examples.py +0 -0
  82. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_incremental.py +0 -0
  83. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_link_translate.py +0 -0
  84. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_log_and_check.py +0 -0
  85. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_measure_questions.py +0 -0
  86. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_opposed_answers.py +0 -0
  87. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_option_lead.py +0 -0
  88. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_order_detail.py +0 -0
  89. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_profiles.py +0 -0
  90. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_profiles_041.py +0 -0
  91. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_questions_not_assertions.py +0 -0
  92. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_references.py +0 -0
  93. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_refusal_friction.py +0 -0
  94. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_refusal_results.py +0 -0
  95. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_refusals.py +0 -0
  96. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_relation_words.py +0 -0
  97. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_report_answers.py +0 -0
  98. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_rerunnable.py +0 -0
  99. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_restates_scale.py +0 -0
  100. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_server_auth.py +0 -0
  101. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_server_hosts.py +0 -0
  102. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_shape_refusals.py +0 -0
  103. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_stats_not_backtests.py +0 -0
  104. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_stats_values.py +0 -0
  105. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_surfaces.py +0 -0
  106. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_telemetry_stream.py +0 -0
  107. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_trust_boundary.py +0 -0
  108. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_ui_app.py +0 -0
  109. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_ui_engine.py +0 -0
  110. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_uncorrected_effect.py +0 -0
  111. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_unnamed_inputs.py +0 -0
  112. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_verify_complete.py +0 -0
  113. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_verify_scope.py +0 -0
  114. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_verify_trust.py +0 -0
  115. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_versions_not_figures.py +0 -0
  116. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_vocabulary_friction.py +0 -0
  117. {hypgraph-0.7.16 → hypgraph-0.7.17}/tests/test_workflows.py +0 -0
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.17 — 2026-10-31: a superseded node can't verify as active
4
+
5
+ - **A superseded node can't verify as active.** A pre-launch audit of 0.7.16 found that the verifier accepted a node's
6
+ lifecycle under either the current rule or 0.3's, which counted only an active replacement: in a chain RS3
7
+ supersedes RS2 supersedes RS1, 0.3 calls RS1 active, so a bundle edited to show RS1 active verified with
8
+ `views: "checked"`, its chain, rows and head untouched; setting the bundle's `generator` (which the chain doesn't
9
+ cover) to 0.3 also turned `--standing` from a problem into "not checked". Now, in the Python and browser verifiers
10
+ alike, a lifecycle is checked against the current rule only. One that matches only 0.3's rule still verifies (a 0.3
11
+ export shows it so) but the result says `views: "historical"`, not `"checked"`, and the warning names each such node
12
+ and what the current rule makes it; `hypgraph verify` prints `views: NOT CHECKED`. The generator is never consulted.
13
+ Covered for `supersedes` and `corrects`, chains of three, withdrawn replacements, scoped bundles and a forged or
14
+ deleted generator, with the browser verifier held to the same verdict, warning and coverage
15
+ (`tests/test_verify_lifecycle.py`).
16
+ - **The Explorer and the website speak Spanish, French, German and Simplified Chinese.** The UI's strings are in a
17
+ table (English, the source, in `hypgraph.js`; translations in `hypgraph.i18n.js`, loaded on demand or registered
18
+ with `HypGraph.addStrings`). The language comes from `?lang=`, the host page's `lang`, a saved choice, then the
19
+ browser's, else English; a switcher in the header changes it (`langSwitch: false` hides it in an embed). The main
20
+ site pages are built in each language under `site/<lang>/` by `dev/build_site.py` from `i18n/site/<lang>.json`,
21
+ with `hreflang` alternates, a language menu and a note that the English is authoritative. `i18n/glossary.json`
22
+ fixes how claim, standing, supersedes, withdrawn, head, bundle, scope … are rendered, and the tests hold every
23
+ table to it (`tests/test_i18n.py`). What the verifier, the engine and the CLI say stays English and byte-identical
24
+ in Python and JS: agents and audits parse it.
25
+
3
26
  ## 0.7.16 — 2026-10-31: a bundle's scope must be a real one
4
27
 
5
28
  - **A bundle's scope must be a real one.** A re-audit of 0.7.15 found that `scope.inv`, which the chain doesn't
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: hypgraph
3
- Version: 0.7.16
3
+ Version: 0.7.17
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.7.16 (2026-10-31)
112
+ ## Status at 0.7.17 (2026-10-31)
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
@@ -47,6 +47,17 @@ was taken from a changelog without being checked against the code.*
47
47
  > **Where that stands.** #72 is merged with green CI (17 of 17 checks); the suite is 412 passed, and hypswarm and
48
48
  > hypmesh pass on it. Released as 0.7.16.
49
49
 
50
+ > **Pre-launch audit of 0.7.16 (2026-10-31).** Confirmed and fixed for 0.7.17: the verifier accepted a lifecycle under
51
+ > either the current rule or 0.3's, so the oldest node of a chain of two or more replacements could be shown active
52
+ > and the bundle verified with `views: "checked"`, chain, rows and head untouched; a `generator` set to 0.3 (not
53
+ > covered by the chain) also turned `--standing` into "not checked". A lifecycle is now checked against the current
54
+ > rule only; one that matches only 0.3's verifies with `views: "historical"` and a warning naming each node, in
55
+ > Python and the browser alike, whatever the generator says. Tests: `tests/test_verify_lifecycle.py` (five fail on
56
+ > the 0.7.16 code). The required checks are now all 17 CI jobs, and the repository has its homepage and topics.
57
+ >
58
+ > **Where that stands.** #75 is merged with green CI (17 of 17 checks); from the 0.7.17 wheel in a fresh venv the
59
+ > suite is 424 passed, and hypswarm (118 passed) and hypmesh (37 passed) pass on it. Released as 0.7.17.
60
+
50
61
  ## Recommendation: CONDITIONAL GO
51
62
 
52
63
  HypGraph is ready for a public developer preview once #59, #60 and #61 are merged with green CI and released as one
@@ -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.16"
19
+ __version__ = "0.7.17"
20
20
 
21
21
  from .bundle import anchored, verify_bundle # noqa: E402
22
22
  from .graph import Author, Graph, GraphError, file_sha256 # noqa: E402
@@ -241,13 +241,14 @@ def _verify_bundle(b: dict, policy: dict | None = None) -> dict:
241
241
  problem = _scope_problem(b, tables, red)
242
242
  if problem:
243
243
  return {"ok": False, "problem": problem}
244
- problem = _derived_problem(b, tables)
244
+ historical: list = []
245
+ problem = _derived_problem(b, tables, historical)
245
246
  if problem:
246
247
  return {"ok": False, "problem": f"the derived views contradict the verified record: {problem}"}
247
248
  out = {"ok": True, "checked": checked, "head": prev, "complete": red == 0, "redacted": red,
248
249
  "nodes": len(tables["nodes"]), "edges": len(tables["edges"]), "unchecked": rows["unchecked"],
249
250
  "coverage": "complete" if not rows["unchecked"] and not red else "partial",
250
- "views": "none" if b.get("derived") is None else "checked",
251
+ "views": "none" if b.get("derived") is None else "historical" if historical else "checked",
251
252
  "policy": {"trusted": sorted(policy or {}), "importer": importer},
252
253
  "standing": {"checked": False, "note": STANDING_NOTE}}
253
254
  notes = []
@@ -259,11 +260,21 @@ def _verify_bundle(b: dict, policy: dict | None = None) -> dict:
259
260
  if stated != importer:
260
261
  notes.append(f"The bundle names {stated} as its importer; the chain doesn't cover that, so {importer} was "
261
262
  "used.")
263
+ if historical:
264
+ notes.append(_historical_note(historical))
262
265
  if notes:
263
266
  out["warning"] = " ".join(notes)
264
267
  return out
265
268
 
266
269
 
270
+ def _historical_note(historical: list) -> str:
271
+ shown = "; ".join(f"{name} shown {s}, the current rule makes it {w}" for name, s, w in historical[:8])
272
+ more = f"; and {len(historical) - 8} more" if len(historical) > 8 else ""
273
+ return (f"The views are not checked: {len(historical)} lifecycle(s) follow hypgraph 0.3's rule, not the current "
274
+ f"one ({shown}{more}). A bundle exported by 0.3 shows them so, but the chain doesn't record which version "
275
+ "exported a bundle, so this can't be told from an edited view. Go by the current rule.")
276
+
277
+
267
278
  def _j(v: Any) -> str:
268
279
  """A value in a problem, written as the browser's verifier writes it (JSON.stringify)."""
269
280
  return json.dumps(v, ensure_ascii=False, separators=(",", ":"))
@@ -316,12 +327,17 @@ def _eid(v: Any) -> int | None:
316
327
  return int(v)
317
328
 
318
329
 
319
- def _derived_problem(b: dict, tables: dict) -> str:
330
+ def _derived_problem(b: dict, tables: dict, historical: list | None = None) -> str:
320
331
  """The derived views are presentation, but a viewer shows them beside a verified chain: every fact in them that
321
332
  the rows fix — which investigations there are, which nodes and live links each has, each node's type, code,
322
333
  author, time, version, title and lifecycle, each link's ends and fields — must be present and agree with the
323
334
  verified rows. A view that leaves a fact out fails: a viewer would fill the gap with a default (a withdrawn claim
324
- shown as active). The rest of standing can't be checked this way."""
335
+ shown as active). The rest of standing can't be checked this way.
336
+
337
+ A lifecycle is checked against the current rule only. One that matches only 0.3's rule (the end of a chain of
338
+ replacements shown active) is no problem — a 0.3 export shows it so — but it isn't checked either: the chain
339
+ doesn't record which version exported a bundle, so it can't be told from an edited view. Each such node goes on
340
+ ``historical`` as (name, shown, current), and the bundle's views are reported as historical, not checked."""
325
341
  d = b.get("derived")
326
342
  if d is None:
327
343
  return ""
@@ -373,13 +389,16 @@ def _derived_problem(b: dict, tables: dict) -> str:
373
389
  st = x.get("state")
374
390
  if not isinstance(st, dict) or "lifecycle" not in st:
375
391
  return f"{where} shows {name} without its lifecycle"
376
- w = lifecycle(n["id"], True)
377
- allowed = {w, lifecycle(n["id"], False)}
378
- if scope and n["id"] not in withdrawn:
379
- allowed.add("superseded") # a scoped bundle leaves out links to other investigations: one may replace it
380
- if st["lifecycle"] not in allowed:
381
- return f"{where} shows {name} as {_j(st['lifecycle'])}; the record makes it {_j(w)}"
382
- return ""
392
+ w, shown = lifecycle(n["id"], True), st["lifecycle"]
393
+ if shown == w:
394
+ return ""
395
+ if scope and n["id"] not in withdrawn and shown == "superseded":
396
+ return "" # a scoped bundle leaves out links to other investigations: one may replace it
397
+ if shown == lifecycle(n["id"], False):
398
+ if historical is not None and (name, shown, w) not in historical:
399
+ historical.append((name, shown, w))
400
+ return ""
401
+ return f"{where} shows {name} as {_j(shown)}; the record makes it {_j(w)}"
383
402
  by_inv = d.get("by_inv")
384
403
  if not isinstance(by_inv, dict) or set(by_inv) != invs:
385
404
  return "the views don't cover the record's investigations"
@@ -297,6 +297,8 @@ def cmd_verify(args) -> int:
297
297
  text += "\n coverage: PARTIAL — not every row field is vouched for by its event (see below)"
298
298
  if ok and res.get("views") == "none":
299
299
  text += "\n no derived views: exported without them, so a viewer has nothing to show beyond the rows"
300
+ if ok and res.get("views") == "historical":
301
+ text += "\n views: NOT CHECKED — some lifecycles follow hypgraph 0.3's rule, not the current one (see the note)"
300
302
  if ok and res.get("unchecked"):
301
303
  text += ("\n not covered (written before events hashed these fields): "
302
304
  + ", ".join(f"{f} ×{n}" for f, n in sorted(res["unchecked"].items())))
@@ -48,6 +48,7 @@
48
48
  .hg .hg-search { display: flex; align-items: center; gap: 8px; width: 260px; padding: 6px 10px; border: 1px solid var(--line);
49
49
  border-radius: 8px; background: var(--surface2); color: var(--ink3); cursor: pointer; }
50
50
  .hg .hg-search kbd { margin-left: auto; font: 11px var(--mono); padding: 1px 6px; border: 1px solid var(--line2); border-radius: 4px; }
51
+ .hg .hg-lang { padding: 5px 6px; font-size: 12.5px; max-width: 112px; cursor: pointer; } /* the language menu */
51
52
  .hg .btn { border: 1px solid var(--line); background: var(--surface); border-radius: 8px; padding: 6px 10px; cursor: pointer;
52
53
  display: inline-flex; gap: 6px; align-items: center; white-space: nowrap; }
53
54
  .hg .btn:hover { background: var(--sunken); } .hg .btn:disabled { opacity: .5; cursor: default; background: var(--surface); } .hg .btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
@@ -117,6 +118,7 @@
117
118
  .hg .note { display: flex; gap: 10px; align-items: flex-start; padding: 9px 14px; border-radius: 9px; margin-bottom: 14px;
118
119
  background: var(--accent-soft); color: var(--accent-ink); font-size: 12.5px; }
119
120
  .hg .note.warn { background: var(--warn-soft); color: var(--warn); }
121
+ .hg .note[data-exported] { display: block; } /* one sentence with inline code: keep it one paragraph */
120
122
 
121
123
  /* stages and gates */
122
124
  .hg .bar { display: flex; gap: 3px; }