memtrust-cli 0.3.3__tar.gz → 0.3.4__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 (110) hide show
  1. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/PKG-INFO +8 -5
  2. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/README.md +6 -4
  3. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/npm/memtrust-cli/package.json +4 -0
  4. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/pyproject.toml +2 -1
  5. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/cli.py +37 -5
  6. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/test_cli.py +53 -1
  7. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/test_init.py +20 -0
  8. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/.github/CODEOWNERS +0 -0
  9. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/.github/workflows/ci.yml +0 -0
  10. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/.github/workflows/publish-pypi.yml +0 -0
  11. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/.gitignore +0 -0
  12. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/.pre-commit-config.yaml +0 -0
  13. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/CHANGELOG.md +0 -0
  14. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/CONTRIBUTING.md +0 -0
  15. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/LICENSE +0 -0
  16. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/SECURITY.md +0 -0
  17. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/docs/assets/dev-to-demos/demo-1-zero-credentials.gif +0 -0
  18. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/docs/assets/dev-to-demos/demo-2-fictional-api-discovery.gif +0 -0
  19. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/docs/assets/dev-to-demos/demo-3-cli-surface.gif +0 -0
  20. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/docs/assets/dev-to-demos/demo-4-temporal-kg-tests.gif +0 -0
  21. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/docs/demo.gif +0 -0
  22. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/docs/methodology.md +0 -0
  23. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/docs/usage.gif +0 -0
  24. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/leaderboard/data.json +0 -0
  25. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/leaderboard/index.html +0 -0
  26. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/leaderboard/mem0_direct-2026-07-20.json +0 -0
  27. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/npm/memtrust-cli/LICENSE +0 -0
  28. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/npm/memtrust-cli/README.md +0 -0
  29. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/npm/memtrust-cli/bin/memtrust.js +0 -0
  30. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/npm/platforms/@memtrust-cli-darwin-arm64/LICENSE +0 -0
  31. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/npm/platforms/@memtrust-cli-darwin-arm64/README.md +0 -0
  32. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/npm/platforms/@memtrust-cli-darwin-arm64/package.json +0 -0
  33. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/npm/platforms/@memtrust-cli-darwin-x64/LICENSE +0 -0
  34. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/npm/platforms/@memtrust-cli-darwin-x64/README.md +0 -0
  35. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/npm/platforms/@memtrust-cli-darwin-x64/package.json +0 -0
  36. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/npm/platforms/@memtrust-cli-linux-arm64/LICENSE +0 -0
  37. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/npm/platforms/@memtrust-cli-linux-arm64/README.md +0 -0
  38. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/npm/platforms/@memtrust-cli-linux-arm64/package.json +0 -0
  39. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/npm/platforms/@memtrust-cli-linux-x64/LICENSE +0 -0
  40. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/npm/platforms/@memtrust-cli-linux-x64/README.md +0 -0
  41. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/npm/platforms/@memtrust-cli-linux-x64/package.json +0 -0
  42. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/npm/platforms/@memtrust-cli-win32-arm64/LICENSE +0 -0
  43. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/npm/platforms/@memtrust-cli-win32-arm64/README.md +0 -0
  44. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/npm/platforms/@memtrust-cli-win32-arm64/package.json +0 -0
  45. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/npm/platforms/@memtrust-cli-win32-x64/LICENSE +0 -0
  46. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/npm/platforms/@memtrust-cli-win32-x64/README.md +0 -0
  47. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/npm/platforms/@memtrust-cli-win32-x64/package.json +0 -0
  48. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/npm/scripts/fetch-binary.js +0 -0
  49. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/__init__.py +0 -0
  50. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/adapters/__init__.py +0 -0
  51. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/adapters/base.py +0 -0
  52. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/adapters/mem0_adapter.py +0 -0
  53. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/adapters/mem0_direct_adapter.py +0 -0
  54. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/adapters/mempalace_adapter.py +0 -0
  55. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/adapters/openviking_adapter.py +0 -0
  56. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/adapters/zep_graphiti_adapter.py +0 -0
  57. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/adapters/zep_graphiti_selfhosted_adapter.py +0 -0
  58. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/evals/__init__.py +0 -0
  59. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/evals/compression.py +0 -0
  60. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/evals/contradiction.py +0 -0
  61. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/evals/crash_recovery.py +0 -0
  62. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/evals/embedder_cost.py +0 -0
  63. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/evals/embedding_drift.py +0 -0
  64. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/evals/episode_temporal_leak.py +0 -0
  65. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/evals/extraction_quality.py +0 -0
  66. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/evals/filter_injection.py +0 -0
  67. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/evals/language_degradation.py +0 -0
  68. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/evals/lock_contention.py +0 -0
  69. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/evals/locomo.py +0 -0
  70. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/evals/longmemeval.py +0 -0
  71. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/evals/mempalace_metadata_scale.py +0 -0
  72. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/evals/migration_rollback.py +0 -0
  73. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/evals/orphan_cleanup.py +0 -0
  74. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/evals/ranking_quality.py +0 -0
  75. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/evals/resource_sync_safety.py +0 -0
  76. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/evals/result_consistency.py +0 -0
  77. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/evals/scale_fixtures.py +0 -0
  78. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/evals/scale_stress.py +0 -0
  79. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/evals/stats_accuracy.py +0 -0
  80. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/evals/temporal_kg_boundary.py +0 -0
  81. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/receipt.py +0 -0
  82. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/scoring/__init__.py +0 -0
  83. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/scoring/cost_tracker.py +0 -0
  84. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/src/memtrust/scoring/llm_judge.py +0 -0
  85. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/fixtures/compression_cases.json +0 -0
  86. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/fixtures/contradiction_cases.json +0 -0
  87. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/fixtures/crash_recovery_cases.json +0 -0
  88. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/fixtures/embedding_drift_cases.json +0 -0
  89. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/fixtures/extraction_quality_cases.json +0 -0
  90. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/fixtures/filter_injection_cases.json +0 -0
  91. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/fixtures/language_degradation_cases.json +0 -0
  92. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/fixtures/locomo_sample.json +0 -0
  93. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/fixtures/longmemeval_sample.json +0 -0
  94. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/fixtures/migration_rollback_cases.json +0 -0
  95. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/fixtures/orphan_cleanup_cases.json +0 -0
  96. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/fixtures/ranking_quality_cases.json +0 -0
  97. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/fixtures/resource_sync_cases.json +0 -0
  98. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/fixtures/result_consistency_cases.json +0 -0
  99. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/test_adapters.py +0 -0
  100. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/test_compression.py +0 -0
  101. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/test_episode_temporal_leak.py +0 -0
  102. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/test_evals.py +0 -0
  103. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/test_lock_contention.py +0 -0
  104. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/test_mem0_direct_adapter.py +0 -0
  105. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/test_mempalace_metadata_scale.py +0 -0
  106. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/test_receipt.py +0 -0
  107. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/test_scale_stress.py +0 -0
  108. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/test_scoring.py +0 -0
  109. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/test_stats_accuracy.py +0 -0
  110. {memtrust_cli-0.3.3 → memtrust_cli-0.3.4}/tests/test_temporal_kg_boundary.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memtrust-cli
3
- Version: 0.3.3
3
+ Version: 0.3.4
4
4
  Summary: Independent, reproducible benchmark harness for agent-memory backends (MemPalace, Mem0, Zep/Graphiti, OpenViking): runs LongMemEval, LoCoMo, contradiction-detection, and a dozen other evals against all four and publishes the raw logs, not vendor-curated numbers.
5
5
  Project-URL: Homepage, https://github.com/RudrenduPaul/memtrust
6
6
  Project-URL: Issues, https://github.com/RudrenduPaul/memtrust/issues
@@ -11,6 +11,7 @@ License: Apache-2.0
11
11
  License-File: LICENSE
12
12
  Keywords: agent-memory,ai-agents,benchmark,contradiction-detection,graphiti,llm-eval,llm-memory,locomo,longmemeval,mem0,memory-backend,mempalace,reproducible-benchmark,zep
13
13
  Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
14
15
  Classifier: Intended Audience :: Developers
15
16
  Classifier: License :: OSI Approved :: Apache Software License
16
17
  Classifier: Programming Language :: Python :: 3
@@ -607,10 +608,12 @@ distribution name is installed. `memtrust --version` now matches `pip show` unde
607
608
  name.
608
609
 
609
610
  **Can I `pip install memtrust` instead of `memtrust-cli`?** Yes -- `memtrust` is a real, separate
610
- PyPI package, kept at the same version as `memtrust-cli` on every release, publishing the identical
611
- source. It exists because the npm wrapper's `bin/memtrust.js` pins `uv tool run --from
612
- memtrust==<version>` (not `memtrust-cli`), so a working `npx memtrust-cli` depends on the
613
- `memtrust` name staying live and in sync. `memtrust-cli` is the name to lead with in new
611
+ PyPI package, publishing the identical source and currently in sync with `memtrust-cli`'s version.
612
+ That sync is a manual release step, not an automated guarantee (see CONTRIBUTING.md's Release
613
+ process for exactly how, and why it has drifted stale before) -- so "currently in sync" is the
614
+ honest framing, not "always in sync." It exists because the npm wrapper's `bin/memtrust.js` pins
615
+ `uv tool run --from memtrust==<version>` (not `memtrust-cli`), so a working `npx memtrust-cli`
616
+ depends on the `memtrust` name staying live and in sync. `memtrust-cli` is the name to lead with in new
614
617
  documentation, since the `-cli` suffix makes it unambiguous as a CLI tool at a glance; `memtrust`
615
618
  is a legitimate, supported install path, not a typo or a squatted name.
616
619
 
@@ -562,10 +562,12 @@ distribution name is installed. `memtrust --version` now matches `pip show` unde
562
562
  name.
563
563
 
564
564
  **Can I `pip install memtrust` instead of `memtrust-cli`?** Yes -- `memtrust` is a real, separate
565
- PyPI package, kept at the same version as `memtrust-cli` on every release, publishing the identical
566
- source. It exists because the npm wrapper's `bin/memtrust.js` pins `uv tool run --from
567
- memtrust==<version>` (not `memtrust-cli`), so a working `npx memtrust-cli` depends on the
568
- `memtrust` name staying live and in sync. `memtrust-cli` is the name to lead with in new
565
+ PyPI package, publishing the identical source and currently in sync with `memtrust-cli`'s version.
566
+ That sync is a manual release step, not an automated guarantee (see CONTRIBUTING.md's Release
567
+ process for exactly how, and why it has drifted stale before) -- so "currently in sync" is the
568
+ honest framing, not "always in sync." It exists because the npm wrapper's `bin/memtrust.js` pins
569
+ `uv tool run --from memtrust==<version>` (not `memtrust-cli`), so a working `npx memtrust-cli`
570
+ depends on the `memtrust` name staying live and in sync. `memtrust-cli` is the name to lead with in new
569
571
  documentation, since the `-cli` suffix makes it unambiguous as a CLI tool at a glance; `memtrust`
570
572
  is a legitimate, supported install path, not a typo or a squatted name.
571
573
 
@@ -8,6 +8,10 @@
8
8
  "os": ["darwin", "linux", "win32"],
9
9
  "cpu": ["x64", "arm64"],
10
10
  "license": "Apache-2.0",
11
+ "author": "Rudrendu Paul",
12
+ "contributors": [
13
+ "Sourav Nandy"
14
+ ],
11
15
  "repository": {
12
16
  "type": "git",
13
17
  "url": "git+https://github.com/RudrenduPaul/memtrust.git"
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "memtrust-cli"
7
- version = "0.3.3"
7
+ version = "0.3.4"
8
8
  description = "Independent, reproducible benchmark harness for agent-memory backends (MemPalace, Mem0, Zep/Graphiti, OpenViking): runs LongMemEval, LoCoMo, contradiction-detection, and a dozen other evals against all four and publishes the raw logs, not vendor-curated numbers."
9
9
  readme = "README.md"
10
10
  license = { text = "Apache-2.0" }
@@ -20,6 +20,7 @@ keywords = [
20
20
  ]
21
21
  classifiers = [
22
22
  "Development Status :: 4 - Beta",
23
+ "Environment :: Console",
23
24
  "Intended Audience :: Developers",
24
25
  "License :: OSI Approved :: Apache Software License",
25
26
  "Programming Language :: Python :: 3",
@@ -503,7 +503,13 @@ def main() -> None:
503
503
  "eval_arg",
504
504
  default="all",
505
505
  show_default=True,
506
- help=f"Comma-separated eval list ({','.join(ALL_EVALS)}), or 'all'.",
506
+ # ", ".join, not ",".join -- click's --help text wrapper only breaks
507
+ # lines on whitespace, so a bare comma-joined list with no spaces
508
+ # wraps mid-word (e.g. "longmemeval,locom" / "o,contradiction,...").
509
+ # The parser itself (_resolve_eval_names) already .strip()s each
510
+ # token, so accepting "a, b" on the command line still works too --
511
+ # this only changes what gets displayed, not what's parsed.
512
+ help=f"Comma-separated eval list ({', '.join(ALL_EVALS)}), or 'all'.",
507
513
  )
508
514
  @click.option(
509
515
  "--output",
@@ -953,14 +959,22 @@ def run(
953
959
 
954
960
  @main.command()
955
961
  @click.argument("report_path", type=click.Path(exists=True, dir_okay=False, path_type=Path))
956
- def report(report_path: Path) -> None:
962
+ @click.option("--json", "json_mode", is_flag=True, default=False, help="Print the parsed report as JSON instead of a formatted summary.")
963
+ def report(report_path: Path, json_mode: bool) -> None:
957
964
  """Read a prior `memtrust run` JSON report and print a formatted summary."""
958
965
  try:
959
966
  data = json.loads(report_path.read_text())
960
967
  except json.JSONDecodeError as exc:
961
- console.print(f"[red]Could not parse {report_path} as JSON: {exc}[/red]")
968
+ if json_mode:
969
+ print(json.dumps({"ok": False, "error": f"Could not parse {report_path} as JSON: {exc}"}))
970
+ else:
971
+ console.print(f"[red]Could not parse {report_path} as JSON: {exc}[/red]")
962
972
  sys.exit(1)
963
973
 
974
+ if json_mode:
975
+ print(json.dumps(data, default=str))
976
+ return
977
+
964
978
  console.print(f"[bold]memtrust report[/bold] -- run_id={data.get('run_id', 'unknown')}")
965
979
  console.print(f"Generated: {data.get('timestamp', 'unknown')}\n")
966
980
 
@@ -1218,7 +1232,8 @@ def keygen(private_key_path: Path, public_key_path: Path, force: bool) -> None:
1218
1232
  "Never taken from the receipt file itself: a receipt cannot vouch for its own key."
1219
1233
  ),
1220
1234
  )
1221
- def verify(receipt_path: Path, public_key_path: Path | None) -> None:
1235
+ @click.option("--json", "json_mode", is_flag=True, default=False, help="Print the verification result as JSON instead of formatted text.")
1236
+ def verify(receipt_path: Path, public_key_path: Path | None, json_mode: bool) -> None:
1222
1237
  """Verify a signed receipt produced by `memtrust run --sign`.
1223
1238
 
1224
1239
  Proves exactly two things when valid: the receipt's payload has not
@@ -1233,9 +1248,26 @@ def verify(receipt_path: Path, public_key_path: Path | None) -> None:
1233
1248
  try:
1234
1249
  result = verify_receipt_file(receipt_path, public_key_path=public_key_path)
1235
1250
  except ReceiptError as exc:
1236
- console.print(f"[red]Could not verify: {exc}[/red]")
1251
+ if json_mode:
1252
+ print(json.dumps({"ok": False, "error": str(exc)}))
1253
+ else:
1254
+ console.print(f"[red]Could not verify: {exc}[/red]")
1237
1255
  sys.exit(1)
1238
1256
 
1257
+ if json_mode:
1258
+ print(
1259
+ json.dumps(
1260
+ {
1261
+ "valid": result.valid,
1262
+ "reason": result.reason,
1263
+ "embedded_key_matches_trusted_key": result.embedded_key_matches_trusted_key,
1264
+ }
1265
+ )
1266
+ )
1267
+ if not result.valid:
1268
+ sys.exit(1)
1269
+ return
1270
+
1239
1271
  color = "green" if result.valid else "red"
1240
1272
  console.print(f"[{color}]valid: {result.valid}[/{color}]")
1241
1273
  console.print(result.reason)
@@ -11,9 +11,11 @@ from pathlib import Path
11
11
  import pytest
12
12
  from click.testing import CliRunner
13
13
  from pytest_httpx import HTTPXMock
14
+ from test_temporal_kg_boundary import _RealSemanticsFakeKGTools
14
15
 
15
16
  from memtrust.adapters.base import TemporalBoundarySignal
16
- from memtrust.cli import ALL_EVALS, _serialize_eval_result, main
17
+ from memtrust.adapters.mempalace_adapter import MemPalaceAdapter
18
+ from memtrust.cli import ADAPTER_REGISTRY, ALL_EVALS, _serialize_eval_result, main
17
19
  from memtrust.evals.temporal_kg_boundary import (
18
20
  TemporalKGBoundaryCase,
19
21
  TemporalKGBoundaryCaseResult,
@@ -390,6 +392,56 @@ def test_serialize_eval_result_handles_temporal_kg_boundary_result() -> None:
390
392
  assert serialized["cases"][0]["self_report_agrees"] is False
391
393
 
392
394
 
395
+ def test_run_temporal_kg_boundary_end_to_end_against_configured_mempalace(
396
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch
397
+ ) -> None:
398
+ """The `not_applicable`/no-credentials tests above only exercise the
399
+ SKIP paths -- neither ever reaches the `isinstance(adapter,
400
+ MemPalaceAdapter)` branch that actually calls
401
+ `run_temporal_kg_boundary_eval()` for real. This drives that branch
402
+ for real: injects `MemPalaceAdapter(mcp_tools=...)` with the same
403
+ closed-interval fake `test_temporal_kg_boundary.py` uses to reproduce
404
+ the real MemPalace/mempalace#1913 bug shape, so the eval's actual
405
+ classification logic runs end to end through the CLI, not a
406
+ hand-built result."""
407
+ case = TemporalKGBoundaryCase(
408
+ case_id="cli-e2e-1",
409
+ subject="cli-e2e-subject",
410
+ predicate="uses_model",
411
+ old_object="claude-opus-4-7",
412
+ new_object="claude-opus-4-8",
413
+ seed_valid_from="2026-05-01T00:00:00Z",
414
+ boundary="2026-06-02T12:00:00Z",
415
+ )
416
+ adapter = MemPalaceAdapter(mcp_tools=_RealSemanticsFakeKGTools(boundary_mode="closed"))
417
+ monkeypatch.setitem(ADAPTER_REGISTRY, "mempalace", lambda: adapter)
418
+ monkeypatch.setattr(
419
+ "memtrust.evals.temporal_kg_boundary.default_cases",
420
+ lambda *args, **kwargs: [case],
421
+ )
422
+
423
+ runner = CliRunner()
424
+ out_path = tmp_path / "report.json"
425
+ result = runner.invoke(
426
+ main,
427
+ [
428
+ "run",
429
+ "--backends",
430
+ "mempalace",
431
+ "--eval",
432
+ "temporal_kg_boundary",
433
+ "--output",
434
+ str(out_path),
435
+ ],
436
+ )
437
+
438
+ assert result.exit_code == 0, result.output
439
+ data = json.loads(out_path.read_text())
440
+ tkgb = data["results"]["mempalace"]["evals"]["temporal_kg_boundary"]
441
+ assert tkgb["double_count_rate"] == 1.0
442
+ assert tkgb["cases"][0]["signal"] == "double_count"
443
+
444
+
393
445
  def test_run_against_configured_backend_full_flow(
394
446
  tmp_path: Path, monkeypatch: pytest.MonkeyPatch, httpx_mock: HTTPXMock
395
447
  ) -> None:
@@ -18,6 +18,26 @@ from typing import NoReturn
18
18
  import pytest
19
19
 
20
20
 
21
+ @pytest.fixture(autouse=True)
22
+ def _restore_real_version_after_reload() -> object:
23
+ """`importlib.reload()` mutates the real, live `memtrust` module object
24
+ in place -- `monkeypatch`'s own teardown only undoes the
25
+ `importlib.metadata.version` patch, it does not know the reload already
26
+ baked a fake `__version__` into the shared module singleton, so without
27
+ this fixture a test in this file leaves `memtrust.__version__` wrong for
28
+ every test that runs afterward in the same pytest process (confirmed:
29
+ `test_cli.py::test_version_flag` fails when run after this file, passes
30
+ in isolation). Captures the real value before the test, restores it
31
+ unconditionally after -- independent of monkeypatch's own teardown
32
+ order, since it does not rely on triggering another reload to fix it.
33
+ """
34
+ import memtrust
35
+
36
+ real_version = memtrust.__version__
37
+ yield
38
+ memtrust.__version__ = real_version
39
+
40
+
21
41
  def _reload_memtrust_with_patched_version(
22
42
  monkeypatch: pytest.MonkeyPatch, fake_version: object
23
43
  ) -> str:
File without changes
File without changes
File without changes
File without changes
File without changes