zettelforge 2.7.0__tar.gz → 2.8.0__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 (434) hide show
  1. {zettelforge-2.7.0 → zettelforge-2.8.0}/.github/workflows/ci.yml +54 -18
  2. {zettelforge-2.7.0 → zettelforge-2.8.0}/.github/workflows/docs.yml +2 -2
  3. {zettelforge-2.7.0 → zettelforge-2.8.0}/.github/workflows/publish.yml +6 -2
  4. {zettelforge-2.7.0 → zettelforge-2.8.0}/.github/workflows/snyk-security.yml +3 -3
  5. {zettelforge-2.7.0 → zettelforge-2.8.0}/ARCHITECTURE.md +13 -0
  6. {zettelforge-2.7.0 → zettelforge-2.8.0}/CHANGELOG.md +48 -0
  7. zettelforge-2.8.0/GOVERNANCE.md +49 -0
  8. {zettelforge-2.7.0 → zettelforge-2.8.0}/PKG-INFO +7 -3
  9. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/BENCHMARK_REPORT.md +73 -0
  10. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/cti_retrieval_benchmark.py +2 -0
  11. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/cti_retrieval_results.json +21 -21
  12. zettelforge-2.8.0/benchmarks/instrument_lookups.py +62 -0
  13. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/locomo_benchmark.py +40 -11
  14. zettelforge-2.8.0/benchmarks/locomo_results.json +962 -0
  15. zettelforge-2.8.0/benchmarks/mine_phase_timings.py +56 -0
  16. zettelforge-2.8.0/benchmarks/profile_recall.py +61 -0
  17. zettelforge-2.8.0/benchmarks/rerank_grid.py +43 -0
  18. zettelforge-2.8.0/benchmarks/results/session_2026-06-09/locomo_results_optimized.json +962 -0
  19. {zettelforge-2.7.0 → zettelforge-2.8.0}/config.default.yaml +13 -0
  20. zettelforge-2.8.0/docs/explanation/design-philosophy-dual-hemisphere.md +159 -0
  21. zettelforge-2.8.0/docs/how-to/passive-osint-enrichment.md +70 -0
  22. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/index.md +2 -0
  23. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/configuration.md +21 -0
  24. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/governance-controls.md +47 -4
  25. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-016-osint-layer.md +13 -11
  26. zettelforge-2.8.0/docs/rfcs/RFC-018-threatrecall-next-improvements.md +255 -0
  27. {zettelforge-2.7.0 → zettelforge-2.8.0}/mkdocs.yml +1 -0
  28. {zettelforge-2.7.0 → zettelforge-2.8.0}/pyproject.toml +11 -3
  29. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/__init__.py +1 -1
  30. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/config.py +108 -0
  31. zettelforge-2.8.0/src/zettelforge/enrichment_ledger.py +60 -0
  32. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/entity_indexer.py +96 -7
  33. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/fact_extractor.py +11 -3
  34. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/governance_validator.py +9 -0
  35. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/graph_retriever.py +37 -3
  36. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/intent_classifier.py +6 -1
  37. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/json_parse.py +7 -1
  38. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/llm_client.py +13 -3
  39. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/memory_defense.py +127 -3
  40. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/memory_evolver.py +9 -2
  41. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/memory_manager.py +337 -73
  42. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/memory_updater.py +24 -2
  43. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/note_constructor.py +11 -10
  44. zettelforge-2.8.0/src/zettelforge/osint/THIRD_PARTY/AGE-120-pip-audit.md +51 -0
  45. zettelforge-2.8.0/src/zettelforge/osint/THIRD_PARTY/LICENSE-Apache-2.0.txt +201 -0
  46. zettelforge-2.8.0/src/zettelforge/osint/THIRD_PARTY/NOTICE +32 -0
  47. zettelforge-2.8.0/src/zettelforge/osint/THIRD_PARTY/PROVENANCE.md +65 -0
  48. zettelforge-2.8.0/src/zettelforge/osint/THIRD_PARTY/THIRD_PARTY_NOTICES.md +37 -0
  49. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/__init__.py +39 -10
  50. zettelforge-2.8.0/src/zettelforge/osint/collectors/breach/hibp_collector.py +167 -0
  51. zettelforge-2.8.0/src/zettelforge/osint/collectors/financial/__init__.py +15 -0
  52. zettelforge-2.8.0/src/zettelforge/osint/collectors/financial/wallet_collector.py +194 -0
  53. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/infrastructure/bgp_collector.py +4 -5
  54. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/infrastructure/dns_collector.py +69 -6
  55. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/infrastructure/whois_collector.py +58 -14
  56. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/people/__init__.py +2 -0
  57. zettelforge-2.8.0/src/zettelforge/osint/collectors/people/holehe_collector.py +44 -0
  58. zettelforge-2.8.0/src/zettelforge/osint/collectors/people/maigret_collector.py +145 -0
  59. zettelforge-2.8.0/src/zettelforge/osint/entity_resolver.py +285 -0
  60. zettelforge-2.8.0/src/zettelforge/osint/executor.py +543 -0
  61. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/ontology.py +159 -0
  62. zettelforge-2.8.0/src/zettelforge/prompt_injection_guard.py +117 -0
  63. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sigma/ingest.py +11 -2
  64. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sqlite_backend.py +174 -1
  65. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/storage_backend.py +21 -0
  66. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/synthesis_generator.py +29 -10
  67. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/vector_memory.py +56 -2
  68. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/vector_retriever.py +24 -2
  69. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/yara/cccs_metadata.py +13 -10
  70. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/yara/ingest.py +20 -2
  71. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/yara/parser.py +20 -6
  72. zettelforge-2.8.0/tests/test_cccs_metadata.py +51 -0
  73. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_conversational_entities.py +36 -0
  74. zettelforge-2.8.0/tests/test_defense_reference_window.py +87 -0
  75. zettelforge-2.8.0/tests/test_embedding_cache.py +71 -0
  76. zettelforge-2.8.0/tests/test_enrichment_switch.py +76 -0
  77. zettelforge-2.8.0/tests/test_graph_scoping.py +147 -0
  78. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_json_parse.py +18 -1
  79. zettelforge-2.8.0/tests/test_max_tokens_budgets.py +202 -0
  80. zettelforge-2.8.0/tests/test_memory_defense_equivalence.py +223 -0
  81. zettelforge-2.8.0/tests/test_memory_manager_flush.py +96 -0
  82. zettelforge-2.8.0/tests/test_osint_age119_gap_types.py +94 -0
  83. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_osint_collectors.py +4 -2
  84. zettelforge-2.8.0/tests/test_osint_enrichers_age120.py +458 -0
  85. zettelforge-2.8.0/tests/test_osint_entity_resolver.py +84 -0
  86. zettelforge-2.8.0/tests/test_osint_executor.py +274 -0
  87. zettelforge-2.8.0/tests/test_prompt_injection_guard.py +104 -0
  88. zettelforge-2.8.0/tests/test_remember_chunked.py +72 -0
  89. zettelforge-2.8.0/tests/test_rerank_policy.py +103 -0
  90. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_sigma_ingest.py +49 -6
  91. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_sqlite_backend.py +54 -0
  92. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_yara_ingest.py +45 -0
  93. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_yara_parser.py +14 -0
  94. zettelforge-2.7.0/GOVERNANCE.md +0 -24
  95. zettelforge-2.7.0/benchmarks/locomo_results.json +0 -287
  96. zettelforge-2.7.0/src/zettelforge/osint/collectors/breach/hibp_collector.py +0 -46
  97. zettelforge-2.7.0/src/zettelforge/osint/collectors/people/holehe_collector.py +0 -47
  98. zettelforge-2.7.0/src/zettelforge/osint/entity_resolver.py +0 -164
  99. {zettelforge-2.7.0 → zettelforge-2.8.0}/.github/CODEOWNERS +0 -0
  100. {zettelforge-2.7.0 → zettelforge-2.8.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  101. {zettelforge-2.7.0 → zettelforge-2.8.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  102. {zettelforge-2.7.0 → zettelforge-2.8.0}/.github/SECURITY.md +0 -0
  103. {zettelforge-2.7.0 → zettelforge-2.8.0}/.github/dependabot.yml +0 -0
  104. {zettelforge-2.7.0 → zettelforge-2.8.0}/.github/pull_request_template.md +0 -0
  105. {zettelforge-2.7.0 → zettelforge-2.8.0}/.github/workflows/stale.yml +0 -0
  106. {zettelforge-2.7.0 → zettelforge-2.8.0}/.gitignore +0 -0
  107. {zettelforge-2.7.0 → zettelforge-2.8.0}/CODEOWNERS +0 -0
  108. {zettelforge-2.7.0 → zettelforge-2.8.0}/CODE_OF_CONDUCT.md +0 -0
  109. {zettelforge-2.7.0 → zettelforge-2.8.0}/CONTRIBUTING.md +0 -0
  110. {zettelforge-2.7.0 → zettelforge-2.8.0}/CONTRIBUTORS.md +0 -0
  111. {zettelforge-2.7.0 → zettelforge-2.8.0}/Dockerfile +0 -0
  112. {zettelforge-2.7.0 → zettelforge-2.8.0}/LICENSE +0 -0
  113. {zettelforge-2.7.0 → zettelforge-2.8.0}/MANIFEST.in +0 -0
  114. {zettelforge-2.7.0 → zettelforge-2.8.0}/README.md +0 -0
  115. {zettelforge-2.7.0 → zettelforge-2.8.0}/ROADMAP.md +0 -0
  116. {zettelforge-2.7.0 → zettelforge-2.8.0}/SCOPING_DOC.md +0 -0
  117. {zettelforge-2.7.0 → zettelforge-2.8.0}/SECURITY.md +0 -0
  118. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/LOCOMO_BENCHMARK_COMPARISON.md +0 -0
  119. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/auto_ralph.py +0 -0
  120. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/benchmark_harness.py +0 -0
  121. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/cti_benchmark_v2.py +0 -0
  122. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/cti_v2_results.json +0 -0
  123. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/ctibench_benchmark.py +0 -0
  124. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/ctibench_results.json +0 -0
  125. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/dataset.json +0 -0
  126. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/enterprise-attack.json +0 -0
  127. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/evolve_benchmark.py +0 -0
  128. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/evolve_results.json +0 -0
  129. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/graph_test.py +0 -0
  130. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/locomo_results_v1.3.0_baseline.json +0 -0
  131. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/memoryagentbench.py +0 -0
  132. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/memoryagentbench_results.json +0 -0
  133. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/mempalace_benchmark.py +0 -0
  134. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/mempalace_results.json +0 -0
  135. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/naive_memory.py +0 -0
  136. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/opencti_benchmark.py +0 -0
  137. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/ragas_benchmark.py +0 -0
  138. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/ragas_cti_results.json +0 -0
  139. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/ragas_results.json +0 -0
  140. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/results/benchmark_report.md +0 -0
  141. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/results/ralph_optimization_log.json +0 -0
  142. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/scale_benchmark.py +0 -0
  143. {zettelforge-2.7.0 → zettelforge-2.8.0}/benchmarks/scale_results.json +0 -0
  144. {zettelforge-2.7.0 → zettelforge-2.8.0}/config.example.yaml +0 -0
  145. {zettelforge-2.7.0 → zettelforge-2.8.0}/docker/docker-compose.yml +0 -0
  146. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/.well-known/security.txt +0 -0
  147. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/CNAME +0 -0
  148. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/THREAT_MODEL.md +0 -0
  149. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/architecture-diagram.mmd +0 -0
  150. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/archive/PACKAGE_SUMMARY.md +0 -0
  151. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/archive/README.md +0 -0
  152. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/archive/SKILL.md +0 -0
  153. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/ZettelForge_Architecture.mmd +0 -0
  154. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/architecture-overview.mmd +0 -0
  155. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/architecture-read-path.mmd +0 -0
  156. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/architecture-write-path.mmd +0 -0
  157. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/cf-analytics.js +0 -0
  158. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/demo.gif +0 -0
  159. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/favicon-16.png +0 -0
  160. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/favicon-32.png +0 -0
  161. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/favicon-512.png +0 -0
  162. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/favicon-64.png +0 -0
  163. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/favicon-apple-touch.png +0 -0
  164. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/favicon-old.svg +0 -0
  165. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/favicon.svg +0 -0
  166. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/logo.svg +0 -0
  167. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/social-preview.png +0 -0
  168. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/threatrecall-lockup-monogram.svg +0 -0
  169. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/threatrecall-lockup.svg +0 -0
  170. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/threatrecall-logo-flat.svg +0 -0
  171. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/threatrecall-logo-philosophy.md +0 -0
  172. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/threatrecall-logo.png +0 -0
  173. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/threatrecall-mark.png +0 -0
  174. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/zettelforge_architecture-light.svg +0 -0
  175. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/assets/zettelforge_architecture.svg +0 -0
  176. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/brand/brandIdentity.md +0 -0
  177. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/brand/colors_and_type.css +0 -0
  178. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/explanation/architecture.md +0 -0
  179. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/explanation/epistemic-tiers.md +0 -0
  180. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/explanation/llm-budgets-and-timeouts.md +0 -0
  181. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/explanation/stix-in-zettelforge.md +0 -0
  182. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/explanation/two-phase-pipeline.md +0 -0
  183. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/explanation/zettelkasten-philosophy.md +0 -0
  184. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/build-extensions.md +0 -0
  185. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/configure-lancedb.md +0 -0
  186. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/configure-opencti.md +0 -0
  187. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/configure-pii.md +0 -0
  188. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/configure-sigma-ingestion.md +0 -0
  189. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/configure-typedb.md +0 -0
  190. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/configure-yara-ingestion.md +0 -0
  191. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/ingest-news-report.md +0 -0
  192. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/integrate-llm-agent.md +0 -0
  193. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/integrate-with-crewai.md +0 -0
  194. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/integrate-with-langchain.md +0 -0
  195. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/maintain-lancedb.md +0 -0
  196. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/migrate-jsonl-to-sqlite.md +0 -0
  197. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/query-apt-tools.md +0 -0
  198. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/reproduce-benchmarks.md +0 -0
  199. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/resolve-aliases.md +0 -0
  200. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/run-temporal-query.md +0 -0
  201. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/set-up-mcp-server.md +0 -0
  202. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/store-threat-actor.md +0 -0
  203. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/troubleshoot.md +0 -0
  204. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/upgrade.md +0 -0
  205. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/use-detection-rules.md +0 -0
  206. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/how-to/use-web-interface.md +0 -0
  207. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/human-evaluation-rubric.md +0 -0
  208. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/llms.txt +0 -0
  209. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/marketing/awesome-list-submissions.md +0 -0
  210. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/narrative/2026-04-16-the-memory-problem.md +0 -0
  211. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/overrides/main.html +0 -0
  212. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/architecture-deep-dive.md +0 -0
  213. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/detection-rules-schema.md +0 -0
  214. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/editions.md +0 -0
  215. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/entity-indexer-concurrency.md +0 -0
  216. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/kg-edge-schema.md +0 -0
  217. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/mcp-protocol.md +0 -0
  218. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/memory-manager-api.md +0 -0
  219. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/module-inventory.md +0 -0
  220. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/retrieval-policies.md +0 -0
  221. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/sigma-schema-reference.md +0 -0
  222. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/stix-schema.md +0 -0
  223. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/web-api.md +0 -0
  224. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/reference/yara-schema-reference.md +0 -0
  225. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-001-conversational-entity-extractor.md +0 -0
  226. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-002-universal-llm-provider.md +0 -0
  227. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-002a-retrieval-plumbing.md +0 -0
  228. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-003-adversarial-review.md +0 -0
  229. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-003-read-path-depth-routing.md +0 -0
  230. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-007-operational-telemetry.md +0 -0
  231. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-009-enrichment-pipeline-v2.md +0 -0
  232. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-010-enrichment-hotfix.md +0 -0
  233. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-011-local-llm-backend-config.md +0 -0
  234. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-012-litellm-unified-provider.md +0 -0
  235. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-013-presidio-pii-detection.md +0 -0
  236. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-014-content-limits.md +0 -0
  237. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-015-zettelforge-web-gui.md +0 -0
  238. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/rfcs/RFC-017-memsad-write-time-defenses.md +0 -0
  239. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/stylesheets/brand-tokens.css +0 -0
  240. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/stylesheets/extra.css +0 -0
  241. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/stylesheets/fonts/Neuropol.otf +0 -0
  242. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/tutorials/01-quickstart.md +0 -0
  243. {zettelforge-2.7.0 → zettelforge-2.8.0}/docs/tutorials/02-first-cti-report.md +0 -0
  244. {zettelforge-2.7.0 → zettelforge-2.8.0}/examples/athf_bridge.py +0 -0
  245. {zettelforge-2.7.0 → zettelforge-2.8.0}/examples/crewai_cti_crew.py +0 -0
  246. {zettelforge-2.7.0 → zettelforge-2.8.0}/examples/cti_analysis.ipynb +0 -0
  247. {zettelforge-2.7.0 → zettelforge-2.8.0}/examples/ingest_misp.py +0 -0
  248. {zettelforge-2.7.0 → zettelforge-2.8.0}/examples/mcp_claude_code.md +0 -0
  249. {zettelforge-2.7.0 → zettelforge-2.8.0}/examples/quickstart.py +0 -0
  250. {zettelforge-2.7.0 → zettelforge-2.8.0}/examples/sample_misp_event.json +0 -0
  251. {zettelforge-2.7.0 → zettelforge-2.8.0}/governance/controls.yaml +0 -0
  252. {zettelforge-2.7.0 → zettelforge-2.8.0}/pr-body.md +0 -0
  253. {zettelforge-2.7.0 → zettelforge-2.8.0}/scripts/migrate_jsonl_to_sqlite.py +0 -0
  254. {zettelforge-2.7.0 → zettelforge-2.8.0}/scripts/rebuild_index.py +0 -0
  255. {zettelforge-2.7.0 → zettelforge-2.8.0}/scripts/record-demo.sh +0 -0
  256. {zettelforge-2.7.0 → zettelforge-2.8.0}/scripts/typedb-setup.sh +0 -0
  257. {zettelforge-2.7.0 → zettelforge-2.8.0}/scripts/zettelforge-rebuild.service +0 -0
  258. {zettelforge-2.7.0 → zettelforge-2.8.0}/scripts/zettelforge-rebuild.timer +0 -0
  259. {zettelforge-2.7.0 → zettelforge-2.8.0}/server.json +0 -0
  260. {zettelforge-2.7.0 → zettelforge-2.8.0}/skills/claude-code-skill.md +0 -0
  261. {zettelforge-2.7.0 → zettelforge-2.8.0}/skills/openclaw-skill.md +0 -0
  262. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/__main__.py +0 -0
  263. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/alias_resolver.py +0 -0
  264. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/backend_factory.py +0 -0
  265. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/blended_retriever.py +0 -0
  266. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/cache.py +0 -0
  267. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/consolidation.py +0 -0
  268. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/demo.py +0 -0
  269. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/detection/__init__.py +0 -0
  270. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/detection/base.py +0 -0
  271. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/detection/consumers.py +0 -0
  272. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/detection/explainer.py +0 -0
  273. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/edition.py +0 -0
  274. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/extensions.py +0 -0
  275. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/integrations/__init__.py +0 -0
  276. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/integrations/crewai.py +0 -0
  277. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/integrations/langchain_retriever.py +0 -0
  278. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/knowledge_graph.py +0 -0
  279. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/lance_maintenance.py +0 -0
  280. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/llm_providers/__init__.py +0 -0
  281. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/llm_providers/base.py +0 -0
  282. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/llm_providers/litellm_provider.py +0 -0
  283. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/llm_providers/local_provider.py +0 -0
  284. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/llm_providers/mock_provider.py +0 -0
  285. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/llm_providers/ollama_provider.py +0 -0
  286. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/llm_providers/registry.py +0 -0
  287. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/log.py +0 -0
  288. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/mcp/__init__.py +0 -0
  289. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/mcp/__main__.py +0 -0
  290. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/mcp/server.py +0 -0
  291. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/memory_store.py +0 -0
  292. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/note_schema.py +0 -0
  293. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/observability.py +0 -0
  294. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/ocsf.py +0 -0
  295. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/ontology.py +0 -0
  296. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/__init__.py +0 -0
  297. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/breach/__init__.py +0 -0
  298. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/breach/breach_directory.py +0 -0
  299. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/infrastructure/__init__.py +0 -0
  300. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/infrastructure/cert_collector.py +0 -0
  301. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/infrastructure/port_scanner.py +0 -0
  302. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/people/hunter_collector.py +0 -0
  303. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/people/namechk_collector.py +0 -0
  304. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/social/__init__.py +0 -0
  305. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/social/hashtag_tracker.py +0 -0
  306. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/social/twitter_collector.py +0 -0
  307. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/tech/__init__.py +0 -0
  308. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/tech/builtwith_collector.py +0 -0
  309. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/collectors/tech/wappalyzer_collector.py +0 -0
  310. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/investigation.py +0 -0
  311. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/osint/transform_registry.py +0 -0
  312. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/pii_validator.py +0 -0
  313. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/retry.py +0 -0
  314. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/scripts/compact_lance.py +0 -0
  315. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/scripts/human_eval_sampler.py +0 -0
  316. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/scripts/telemetry_aggregator.py +0 -0
  317. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/scripts/telemetry_dashboard.py +0 -0
  318. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sigma/__init__.py +0 -0
  319. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sigma/cli.py +0 -0
  320. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sigma/entities.py +0 -0
  321. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sigma/parser.py +0 -0
  322. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sigma/schemas/NOTICE.md +0 -0
  323. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sigma/schemas/__init__.py +0 -0
  324. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sigma/schemas/sigma-correlation-rules-schema.json +0 -0
  325. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sigma/schemas/sigma-detection-rule-schema.json +0 -0
  326. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sigma/schemas/sigma-filters-schema.json +0 -0
  327. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/sigma/tags.py +0 -0
  328. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/synthesis_validator.py +0 -0
  329. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/telemetry.py +0 -0
  330. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/yara/__init__.py +0 -0
  331. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/yara/cli.py +0 -0
  332. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/yara/entities.py +0 -0
  333. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/yara/schemas/CCCS_YARA.yml +0 -0
  334. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/yara/schemas/CCCS_YARA_values.yml +0 -0
  335. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/yara/schemas/NOTICE.md +0 -0
  336. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/yara/schemas/__init__.py +0 -0
  337. {zettelforge-2.7.0 → zettelforge-2.8.0}/src/zettelforge/yara/tags.py +0 -0
  338. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/__init__.py +0 -0
  339. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/benchmark_scale.py +0 -0
  340. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/conftest.py +0 -0
  341. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/fixtures/sigma/cloud_example.yml +0 -0
  342. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/fixtures/sigma/correlation_example.yml +0 -0
  343. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/fixtures/sigma/process_creation_example.yml +0 -0
  344. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/fixtures/sigma/tagged_example.yml +0 -0
  345. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/fixtures/yara/malware_hash.yar +0 -0
  346. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/fixtures/yara/technique_loader.yar +0 -0
  347. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/fixtures/yara/webshell.yar +0 -0
  348. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_basic.py +0 -0
  349. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_blended_retriever.py +0 -0
  350. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_causal_extraction.py +0 -0
  351. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_config.py +0 -0
  352. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_consolidation.py +0 -0
  353. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_core.py +0 -0
  354. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_crewai_integration.py +0 -0
  355. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_cti_integration.py +0 -0
  356. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_detection_explainer.py +0 -0
  357. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_detection_rule_entities.py +0 -0
  358. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_edition.py +0 -0
  359. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_embedding.py +0 -0
  360. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_entity_indexer_races.py +0 -0
  361. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_extensions.py +0 -0
  362. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_fact_extractor.py +0 -0
  363. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_governance.py +0 -0
  364. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_governance_spec_drift.py +0 -0
  365. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_graph_retriever.py +0 -0
  366. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_human_eval_sampler.py +0 -0
  367. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_intent_classifier.py +0 -0
  368. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_kg_edge_schema.py +0 -0
  369. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_lance_maintenance.py +0 -0
  370. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_langchain_retriever.py +0 -0
  371. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_llm_client.py +0 -0
  372. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_llm_providers.py +0 -0
  373. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_logging_compliance.py +0 -0
  374. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_mcp_server.py +0 -0
  375. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_memory_defense.py +0 -0
  376. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_memory_evolver.py +0 -0
  377. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_memory_updater.py +0 -0
  378. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_osint_entities.py +0 -0
  379. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_performance.py +0 -0
  380. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_pii_validator.py +0 -0
  381. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_recall_integration.py +0 -0
  382. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_sigma_entities.py +0 -0
  383. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_sigma_parser.py +0 -0
  384. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_sqlite_integration.py +0 -0
  385. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_storage_backend.py +0 -0
  386. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_telemetry_aggregator.py +0 -0
  387. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_telemetry_collector.py +0 -0
  388. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_telemetry_dashboard.py +0 -0
  389. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_telemetry_integration.py +0 -0
  390. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_temporal_graph.py +0 -0
  391. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_two_phase_e2e.py +0 -0
  392. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_typedb_client.py +0 -0
  393. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_web_api.py +0 -0
  394. {zettelforge-2.7.0 → zettelforge-2.8.0}/tests/test_yara_entities.py +0 -0
  395. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/app.py +0 -0
  396. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/auth.py +0 -0
  397. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/mcp_server.py +0 -0
  398. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/static/css/design_tokens.css +0 -0
  399. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/templates/base.html +0 -0
  400. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/templates/components/remember_panel.html +0 -0
  401. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/templates/components/result_card.html +0 -0
  402. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/templates/components/search_bar.html +0 -0
  403. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/templates/components/synthesis_block.html +0 -0
  404. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/templates/components/tab_bar.html +0 -0
  405. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/templates/config_editor.html +0 -0
  406. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/templates/index.html +0 -0
  407. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/assets/favicon-16.png +0 -0
  408. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/assets/favicon-32.png +0 -0
  409. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/assets/favicon-512.png +0 -0
  410. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/assets/favicon-apple-touch.png +0 -0
  411. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/assets/logo.svg +0 -0
  412. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/assets/threatrecall-lockup.svg +0 -0
  413. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/assets/zettelforge_architecture.svg +0 -0
  414. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/colors_and_type.css +0 -0
  415. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/favicon.svg +0 -0
  416. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/fonts/Neuropol.otf +0 -0
  417. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/index.html +0 -0
  418. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/app.js +0 -0
  419. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/components/header.js +0 -0
  420. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/components/result-card.js +0 -0
  421. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/components/sidebar.js +0 -0
  422. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/components/spinner.js +0 -0
  423. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/components/tabs.js +0 -0
  424. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/components/toast.js +0 -0
  425. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/lib/api.js +0 -0
  426. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/lib/state.js +0 -0
  427. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/views/configuration.js +0 -0
  428. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/views/dashboard.js +0 -0
  429. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/views/entities.js +0 -0
  430. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/views/history.js +0 -0
  431. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/views/ingest.js +0 -0
  432. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/views/knowledge-graph.js +0 -0
  433. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/views/logs.js +0 -0
  434. {zettelforge-2.7.0 → zettelforge-2.8.0}/web/ui/js/views/search.js +0 -0
@@ -13,10 +13,10 @@ jobs:
13
13
  lint:
14
14
  runs-on: ubuntu-latest
15
15
  steps:
16
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
16
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
17
17
 
18
18
  - name: Set up Python
19
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
19
+ uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
20
20
  with:
21
21
  python-version: '3.12'
22
22
 
@@ -35,13 +35,13 @@ jobs:
35
35
  pip-audit:
36
36
  runs-on: ubuntu-latest
37
37
  steps:
38
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
38
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
39
39
  - name: Set up Python
40
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
40
+ uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
41
41
  with:
42
42
  python-version: '3.12'
43
43
  - name: Install pip-audit
44
- run: pip install pip-audit
44
+ run: pip install pip-audit==2.9.0
45
45
  - name: Audit dependencies (any reported vuln blocks)
46
46
  run: |
47
47
  pip install -e ".[dev]" || pip install -e "."
@@ -49,7 +49,7 @@ jobs:
49
49
  # --ignore-vuln=CVE-... with a citation when the finding is
50
50
  # explicitly accepted per GOV-009 §"Vulnerability Response".
51
51
  #
52
- # CVE-2026-3219: vulnerability in `pip` itself (the package
52
+ # CVE-2026-3219 / PYSEC-2026-196: vulnerability in `pip` itself (the package
53
53
  # manager), not a project dependency. The runner's pip is
54
54
  # supplied by GitHub's setup-python image and is not something
55
55
  # ZettelForge's pyproject can pin or upgrade. Risk-accepted
@@ -57,25 +57,59 @@ jobs:
57
57
  # install, not at runtime; CI builds in ephemeral runners with
58
58
  # no persistent state. Re-evaluate when GitHub's images ship a
59
59
  # patched pip.
60
- pip-audit --strict --vulnerability-service=osv \
61
- --ignore-vuln=CVE-2026-3219
60
+ #
61
+ # CVE-2023-36464 / GHSA-4vvm-4w3v-6mr8: medium-severity
62
+ # infinite-loop DoS in PyPDF2 3.0.1, introduced transitively by
63
+ # Maigret. PyPDF2 has no patched release under that package name
64
+ # (upstream recommends migrating to pypdf>=3.9.0), and ZettelForge's
65
+ # AGE-120 username collector does not parse attacker-supplied PDFs or
66
+ # invoke Maigret report generation. Accepted for AGE-120 because the
67
+ # GOV-009 blocking threshold is HIGH/CRITICAL and the collector
68
+ # lazy-imports/fails closed.
69
+ #
70
+ # --skip-editable: do not audit the editable local zettelforge
71
+ # install. It is the project itself, not a third-party dependency,
72
+ # and on a release-prep PR its version is bumped ahead of PyPI
73
+ # (e.g. 2.8.0 before publish), so pip-audit cannot resolve it.
74
+ #
75
+ # --strict is intentionally NOT used: it fails the run whenever a
76
+ # distribution can't be audited, which includes the editable
77
+ # package we deliberately skip ("distribution marked as editable").
78
+ # pip-audit still exits non-zero on any real reported vulnerability,
79
+ # so the "any reported vuln blocks" gate is preserved; every
80
+ # third-party dependency (and the runner's pip) is still audited.
81
+ pip-audit --skip-editable \
82
+ --ignore-vuln=CVE-2026-3219 \
83
+ --ignore-vuln=PYSEC-2026-196 \
84
+ --ignore-vuln=CVE-2023-36464
62
85
 
63
86
  test:
64
87
  runs-on: ubuntu-latest
65
88
  needs: lint
66
89
  strategy:
67
90
  fail-fast: false
91
+ # The fastembed model download is shared across Python versions. Running
92
+ # these jobs in parallel can double-hit HuggingFace and trigger 429s.
93
+ max-parallel: 1
68
94
  matrix:
69
95
  python-version: ['3.12', '3.13']
70
96
 
71
97
  steps:
72
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
98
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
73
99
 
74
100
  - name: Set up Python ${{ matrix.python-version }}
75
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
101
+ uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
76
102
  with:
77
103
  python-version: ${{ matrix.python-version }}
78
104
 
105
+ - name: Cache fastembed model
106
+ uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830
107
+ with:
108
+ path: |
109
+ ~/.cache/fastembed
110
+ ~/.cache/huggingface
111
+ key: fastembed-nomic-embed-text-v1.5-Q-${{ runner.os }}
112
+
79
113
  - name: Install dependencies
80
114
  run: |
81
115
  python -m pip install --upgrade pip
@@ -102,19 +136,21 @@ jobs:
102
136
 
103
137
  - name: Upload coverage
104
138
  if: matrix.python-version == '3.12'
105
- uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354
106
- with:
107
- file: ./coverage.xml
108
- fail_ci_if_error: false
139
+ continue-on-error: true
140
+ env:
141
+ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
142
+ run: |
143
+ python -m pip install codecov-cli
144
+ codecovcli upload-process -f ./coverage.xml
109
145
 
110
146
  governance:
111
147
  runs-on: ubuntu-latest
112
148
  needs: lint
113
149
  steps:
114
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
150
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
115
151
 
116
152
  - name: Set up Python
117
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
153
+ uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
118
154
  with:
119
155
  python-version: '3.12'
120
156
 
@@ -139,10 +175,10 @@ jobs:
139
175
  needs: [test, governance]
140
176
 
141
177
  steps:
142
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
178
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
143
179
 
144
180
  - name: Set up Python
145
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
181
+ uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
146
182
  with:
147
183
  python-version: '3.12'
148
184
 
@@ -14,8 +14,8 @@ jobs:
14
14
  deploy:
15
15
  runs-on: ubuntu-latest
16
16
  steps:
17
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
18
- - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
17
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
18
+ - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
19
19
  with:
20
20
  python-version: '3.12'
21
21
  - run: pip install mkdocs-material
@@ -6,15 +6,19 @@ on:
6
6
 
7
7
  jobs:
8
8
  publish:
9
+ # Package-only releases (for example packages-v0.1.0) are not
10
+ # Python package releases and must not attempt a PyPI upload.
11
+ if: startsWith(github.event.release.tag_name, 'v')
9
12
  runs-on: ubuntu-latest
10
13
  permissions:
14
+ contents: read
11
15
  id-token: write # trusted publishing
12
16
 
13
17
  steps:
14
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
18
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
15
19
 
16
20
  - name: Set up Python
17
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
21
+ uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
18
22
  with:
19
23
  python-version: '3.12'
20
24
 
@@ -17,10 +17,10 @@ jobs:
17
17
  actions: read
18
18
  runs-on: ubuntu-latest
19
19
  steps:
20
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
20
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
21
21
 
22
22
  - name: Set up Python
23
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
23
+ uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
24
24
  with:
25
25
  python-version: '3.12'
26
26
 
@@ -65,7 +65,7 @@ jobs:
65
65
 
66
66
  - name: Upload SARIF to GitHub
67
67
  if: steps.check_token.outputs.has_token == 'true'
68
- uses: github/codeql-action/upload-sarif@b25d0ebf40e5b63ee81e1bd6e5d2a12b7c2aeb61
68
+ uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e
69
69
  with:
70
70
  sarif_file: snyk-code.sarif
71
71
  continue-on-error: true
@@ -3,6 +3,19 @@
3
3
  Visual diagram: [`docs/architecture-diagram.mmd`](docs/architecture-diagram.mmd)
4
4
  Deep explanation: [`docs/explanation/architecture.md`](docs/explanation/architecture.md)
5
5
 
6
+ ## Foundational Model: Two Hemispheres
7
+
8
+ ZettelForge is modeled on the brain. A symbolic left hemisphere (the typed,
9
+ STIX-aligned knowledge graph with schema enforcement and rule-based inference) is
10
+ the precise, explainable authority of record. An associative right hemisphere
11
+ (blended vector and graph retrieval with GraphRAG and agentic synthesis) explores
12
+ and proposes. Underneath both, knowledge is atomic facts connected by directed,
13
+ time-stamped, pairwise associations, with time and provenance as first-class
14
+ primitives (Cognitive Data Model, Pieris 2025). The components below implement
15
+ this model. See [design philosophy](docs/explanation/design-philosophy-dual-hemisphere.md)
16
+ for the rationale and research basis, and [GOVERNANCE.md](GOVERNANCE.md) for the
17
+ binding design commitment.
18
+
6
19
  ## Storage
7
20
 
8
21
  ZettelForge uses a `StorageBackend` ABC (33 methods) with pluggable
@@ -6,6 +6,54 @@ Versioning follows [Semantic Versioning](https://semver.org/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [2.8.0] - 2026-06-26
10
+
11
+ Feature release. Extends the RFC-016 OSINT layer with passive ingest and
12
+ live AGE-120 enrichers, adds AGE-127 prompt-injection /
13
+ retrieval-poisoning guardrails across the memory pipeline, and lays the
14
+ RFC-018 Phase 0 enrichment job ledger foundation, alongside configurable
15
+ LLM generation budgets and bulk detection-ingest hardening. No data
16
+ migration is required.
17
+
18
+ ### Added
19
+
20
+ - **Prompt-injection and retrieval-poisoning guardrails** (AGE-127). A
21
+ deterministic guard for untrusted memory and CTI text, wired into the
22
+ remember, recall, synthesis, fact-extraction, entity-indexing,
23
+ note-construction, intent-classification, memory-update, and evolution
24
+ boundaries. LLM prompts are hardened to treat query and context text as
25
+ untrusted evidence. (#166)
26
+ - **Passive OSINT executor** (RFC-016 Phase 1.5). Validates collector
27
+ tuples and persists knowledge-graph writes; scopes OSINT alias caching to
28
+ each `KnowledgeGraph` instance; canonicalizes Organization values to
29
+ avoid WHOIS/RDAP duplicates; treats an explicit empty collector
30
+ allow-list as empty; adds user-facing passive OSINT docs and mkdocs
31
+ navigation. (#163)
32
+ - **Live OSINT enrichers** (AGE-120). Native RFC-016 collectors that feed
33
+ the graph backend: WHOIS/DNS, maigret/sherlock username discovery, HIBP
34
+ breach lookup, and blockchain wallet transactions, with OSINT
35
+ ontology / executor / entity-resolution / graph-persistence extensions.
36
+ The network collectors are an opt-in `[osint]` extra, key-gated and
37
+ fail-closed, and are never auto-triggered by `remember()`. Known
38
+ fast-follow robustness items are tracked in #176. (#167)
39
+ - **RFC-018 Phase 0 enrichment job ledger.** Durable SQLite job-metadata
40
+ foundation with `MemoryManager` queue-state recording and regression
41
+ tests, plus the RFC-018 spec for the next ingestion, enrichment,
42
+ parser-safety, metadata-policy, operator-health, and release-train
43
+ improvements. (#178)
44
+ - Configurable LLM generation budgets for causal extraction, synthesis,
45
+ fact extraction, NER, and memory evolution, plus `reasoning_model` scaling
46
+ floors and `<think>` / `<thinking>` JSON parse stripping. (#153)
47
+ - Bulk Sigma/YARA ingest can defer enrichment and drain once via
48
+ `MemoryManager.flush()`. (#153)
49
+
50
+ ### Fixed
51
+
52
+ - Hardened CCCS YARA metadata validation against multiline regex injection
53
+ and overly permissive author values. (#153)
54
+ - `MemoryManager.flush()` now waits for in-flight enrichment work, not only
55
+ queued work; cached `Plyara` parsing is serialized for concurrent callers. (#153)
56
+
9
57
  ## [2.7.0] - 2026-05-26
10
58
 
11
59
  Security and OSINT release. Adds the RFC-016 OSINT layer and the first
@@ -0,0 +1,49 @@
1
+ # Governance
2
+
3
+ ## Maintainer
4
+
5
+ ZettelForge is maintained by Patrick Roland (@rolandpg).
6
+
7
+ ## Licensing Commitment
8
+
9
+ ZettelForge is MIT licensed. This will not change.
10
+
11
+ ## Design Commitment: Brain-Inspired Memory Architecture
12
+
13
+ ZettelForge is modeled on the brain by deliberate decision, not metaphor. This
14
+ commitment is binding on the architecture and will not be quietly abandoned.
15
+
16
+ The memory engine is dual-hemisphere. A symbolic left hemisphere (a typed,
17
+ STIX/ATT&CK-aligned knowledge graph with schema enforcement and rule-based
18
+ inference) is the authority of record: precise, constraint-checked, and
19
+ explainable. An associative right hemisphere (blended vector and graph retrieval,
20
+ GraphRAG, and agentic synthesis) explores and proposes, and writes back into the
21
+ symbolic layer only through validated paths. The two layers stay distinct.
22
+
23
+ Underneath both, knowledge is represented as atomic facts connected by directed,
24
+ time-stamped, pairwise associations (the Cognitive Data Model, Pieris 2025). Time
25
+ and provenance are first-class primitives: every note and claim carries its
26
+ source, markings (TLP), confidence, and time validity, and every retrieval result
27
+ carries provenance. Ingested content is untrusted and is sanitized or isolated
28
+ before any model call. Quantitative quality or performance claims are reproduced
29
+ on the project's own CTI benchmark suite before being stated as fact.
30
+
31
+ Changes to storage, retrieval, the knowledge graph, or ingestion MUST preserve
32
+ this model, and design decisions SHOULD cite the relevant hemisphere or layer.
33
+ The full rationale and research basis are in
34
+ [docs/explanation/design-philosophy-dual-hemisphere.md](docs/explanation/design-philosophy-dual-hemisphere.md).
35
+
36
+ ## Extension Boundary
37
+
38
+ The extension package (`zettelforge-enterprise`) provides features
39
+ that require external infrastructure (TypeDB, OpenCTI, multi-tenant
40
+ OAuth). The open source project will never be degraded to create
41
+ commercial incentive.
42
+
43
+ Rule: if a feature works with JSONL + local embeddings, it belongs
44
+ in this repo.
45
+
46
+ ## Contributions
47
+
48
+ Community contributions are reviewed on their technical merits.
49
+ All contributions to this repository remain MIT licensed.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zettelforge
3
- Version: 2.7.0
3
+ Version: 2.8.0
4
4
  Summary: ZettelForge: Agentic Memory System with vector search, knowledge graph, and synthesis
5
5
  Project-URL: Homepage, https://github.com/rolandpg/zettelforge
6
6
  Project-URL: Documentation, https://docs.threatrecall.ai
@@ -39,10 +39,11 @@ Provides-Extra: crewai
39
39
  Requires-Dist: crewai>=1.14.0; extra == 'crewai'
40
40
  Provides-Extra: dev
41
41
  Requires-Dist: dnspython>=2.4.0; extra == 'dev'
42
- Requires-Dist: fastapi>=0.100.0; extra == 'dev'
42
+ Requires-Dist: fastapi<0.138.2,>=0.100.0; extra == 'dev'
43
43
  Requires-Dist: ipwhois>=1.2.0; extra == 'dev'
44
44
  Requires-Dist: jinja2>=3.0.0; extra == 'dev'
45
45
  Requires-Dist: langchain-core>=0.2.0; extra == 'dev'
46
+ Requires-Dist: maigret>=0.4.0; extra == 'dev'
46
47
  Requires-Dist: mypy>=1.0.0; extra == 'dev'
47
48
  Requires-Dist: psutil>=5.9.0; extra == 'dev'
48
49
  Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
@@ -50,6 +51,7 @@ Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
50
51
  Requires-Dist: pytest>=7.0.0; extra == 'dev'
51
52
  Requires-Dist: python-whois>=0.9.0; extra == 'dev'
52
53
  Requires-Dist: ruff>=0.4.0; extra == 'dev'
54
+ Requires-Dist: sherlock-project>=0.14.0; extra == 'dev'
53
55
  Requires-Dist: uvicorn>=0.20.0; extra == 'dev'
54
56
  Provides-Extra: extensions
55
57
  Requires-Dist: zettelforge-enterprise>=2.1.0; extra == 'extensions'
@@ -67,13 +69,15 @@ Requires-Dist: onnxruntime-genai>=0.4.0; extra == 'local-onnx'
67
69
  Provides-Extra: osint
68
70
  Requires-Dist: dnspython>=2.4.0; extra == 'osint'
69
71
  Requires-Dist: ipwhois>=1.2.0; extra == 'osint'
72
+ Requires-Dist: maigret>=0.4.0; extra == 'osint'
70
73
  Requires-Dist: python-whois>=0.9.0; extra == 'osint'
74
+ Requires-Dist: sherlock-project>=0.14.0; extra == 'osint'
71
75
  Provides-Extra: pii
72
76
  Requires-Dist: presidio-analyzer>=2.2.0; extra == 'pii'
73
77
  Requires-Dist: presidio-anonymizer>=2.2.0; extra == 'pii'
74
78
  Requires-Dist: spacy>=3.5.0; extra == 'pii'
75
79
  Provides-Extra: web
76
- Requires-Dist: fastapi>=0.100.0; extra == 'web'
80
+ Requires-Dist: fastapi<0.138.2,>=0.100.0; extra == 'web'
77
81
  Requires-Dist: jinja2>=3.0.0; extra == 'web'
78
82
  Requires-Dist: psutil>=5.9.0; extra == 'web'
79
83
  Requires-Dist: uvicorn>=0.20.0; extra == 'web'
@@ -28,6 +28,79 @@ ZettelForge was evaluated across five benchmark suites. The system runs with zer
28
28
 
29
29
  ---
30
30
 
31
+ ## 0. Performance session 2026-06-09 (v2.8.0-dev, branch perf/cti-memory-40)
32
+
33
+ All numbers below are same-machine (DGX Spark GB10), same-day, deterministic
34
+ config: enrichment disabled (`ZETTELFORGE_ENRICHMENT_ENABLED=false`), keyword
35
+ judge, heuristic answer extraction (no synthesis LLM installed). The clean
36
+ baseline was measured first on unmodified v2.7.0 source after repairing the
37
+ rotted harnesses (dead `disable_enrichment` kwarg, removed `remember_chunked`
38
+ API). Raw logs: `benchmarks/results/session_2026-06-09/`.
39
+
40
+ | Metric | v2.7.0 baseline | optimized | delta |
41
+ |--------|-----------------|-----------|-------|
42
+ | LoCoMo accuracy (keyword judge) | 7.0% | 11.0% | +57% relative |
43
+ | LoCoMo p50 / p95 latency | 336ms / 387ms | 170ms / 193ms | -49% / -50% |
44
+ | LoCoMo ingest (272 sessions) | 262.5s (1.0/s) | 33.8s (8.0/s) | 7.8x |
45
+ | CTI retrieval accuracy | 75.0% | 75.0% | held |
46
+ | CTI p50 latency (idle machine) | 79ms | 39ms | -51% |
47
+ | recall p95 (profiled, 60 calls) | 258ms | 93ms | -64% |
48
+ | recall mean (profiled) | 117.6ms | 54.8ms | -53% |
49
+
50
+ Note on LoCoMo baselines: the published 22% (v2.1.1) used a local synthesis
51
+ LLM (qwen2.5:3b) that is not installed on this host; both columns above use
52
+ the same deterministic heuristic-extraction path, so the comparison is
53
+ apples to apples. Latency includes harness overhead (keyword boost scan and
54
+ synthesis fallback), not just `recall()`.
55
+
56
+ ### What changed
57
+
58
+ 1. **Scoped knowledge graph reads.** `_recall_inner` traversed the
59
+ process-global JSONL KG (109MB on this host, mixing every store) while
60
+ writes went to the per-store SQLite KG. Isolated stores saw up to ~2000
61
+ phantom note IDs per entity query and never saw their own graph. Recall
62
+ now reads the store's KG via `StoreGraphSource`.
63
+ 2. **MemSAD gate vectorized.** The write-time anomaly gate was 93% of
64
+ remember() latency at 50 references (~1.1s/ingest): O(n^2) pure-Python
65
+ cosines plus n^2 n-gram recounts per ingest. numpy pairwise scoring,
66
+ content-hash counter cache, and a bounded reference fetch
67
+ (`get_recent_notes_by_domain`) brought warm evaluate() to ~3.4ms with
68
+ scores pinned to the original math at 1e-9 by characterization tests.
69
+ 3. **Rerank policy.** Cross-encoder rerank is the dominant read cost and is
70
+ worth +15pp CTI accuracy (75% vs 60% without it). Grid-tuned bounds:
71
+ 8 candidates, 256 chars/doc (accuracy holds from 50x512 down to 8x128;
72
+ collapses below 8 candidates). `rerank_model` is configurable; the
73
+ model grid kept ms-marco-MiniLM-L-6-v2.
74
+ 4. **ONNX thread pinning.** 20-core default oversubscribed small batches:
75
+ 8 threads cut rerank 23.7ms to 11.5ms and query embedding 5.9ms to 4.5ms.
76
+ 5. **Embedding LRU cache** keyed by (model, sha256(text)) — first
77
+ integration of the dormant cache.py.
78
+ 6. **Entity fan-out gate.** Query entities whose KG out-degree exceeds
79
+ `retrieval.entity_max_fanout` (default 25) are skipped by graph and
80
+ entity-augmentation stages (conversational speaker names map to every
81
+ session and flood blended recall).
82
+ 7. **Enrichment off-switch** (`ZETTELFORGE_ENRICHMENT_ENABLED`) restoring
83
+ deterministic benchmark ingestion; `remember_chunked()` restored.
84
+
85
+ ### Chunked-ingestion configuration (recorded, not default)
86
+
87
+ `LOCOMO_CHUNK_SIZE=800` stores each session as ~800-char chunks
88
+ (MemPalace granularity, no 4000-char truncation): 13.0% accuracy at
89
+ p50 347ms / p95 418ms on a ~1400-note store. Compared to the v2.7.0
90
+ baseline at effectively the same latency (336ms), that is +86%
91
+ relative accuracy; compared to the default optimized config it trades
92
+ 2x latency for +2pp. Default stays full-session (11.0% at 170ms).
93
+
94
+ ### Negative result (recorded)
95
+
96
+ Free-text person extraction (capitalized tokens in running text) dropped
97
+ LoCoMo from 11% to 5% by reshuffling supersession chains at ingest, with no
98
+ single-hop or multi-hop gain. Reverted same day; regression-locked in
99
+ `tests/test_conversational_entities.py`. Conversational NER should come via
100
+ the RFC-001 LLM path, not regex.
101
+
102
+ ---
103
+
31
104
  ## 1. CTI Retrieval Benchmark (Domain Benchmark)
32
105
 
33
106
  **Date:** 2026-04-10 | **Corpus:** 8 real-world-style CTI reports | **Queries:** 20
@@ -22,6 +22,8 @@ from datetime import datetime
22
22
  from typing import List, Dict, Tuple
23
23
 
24
24
  os.environ["ZETTELFORGE_BACKEND"] = "jsonl"
25
+ # Deterministic ingestion: no background LLM enrichment during benchmarks.
26
+ os.environ.setdefault("ZETTELFORGE_ENRICHMENT_ENABLED", "false")
25
27
 
26
28
  from zettelforge import MemoryManager
27
29
 
@@ -1,78 +1,78 @@
1
1
  {
2
2
  "meta": {
3
- "date": "2026-04-10T08:05:55.405026",
3
+ "date": "2026-06-09T13:56:15.802128",
4
4
  "reports": 8,
5
5
  "queries": 20
6
6
  },
7
7
  "full_session": {
8
8
  "strategy": "full_session",
9
9
  "notes": 8,
10
- "ingest_time_s": 69.1,
10
+ "ingest_time_s": 3.4,
11
11
  "accuracy": 75.0,
12
- "avg_score": 0.875,
13
- "p50_latency_ms": 620.0,
14
- "p95_latency_ms": 2732.0,
12
+ "avg_score": 0.85,
13
+ "p50_latency_ms": 39.0,
14
+ "p95_latency_ms": 159.0,
15
15
  "by_category": {
16
16
  "tool-attribution": {
17
17
  "accuracy": 40.0,
18
18
  "avg_score": 0.7,
19
- "p50_latency_ms": 1343.0
19
+ "p50_latency_ms": 42.0
20
20
  },
21
21
  "cve-linkage": {
22
22
  "accuracy": 75.0,
23
- "avg_score": 0.875,
24
- "p50_latency_ms": 794.0
23
+ "avg_score": 0.75,
24
+ "p50_latency_ms": 38.0
25
25
  },
26
26
  "attribution": {
27
27
  "accuracy": 100.0,
28
28
  "avg_score": 1.0,
29
- "p50_latency_ms": 611.0
29
+ "p50_latency_ms": 59.0
30
30
  },
31
31
  "temporal": {
32
32
  "accuracy": 66.7,
33
33
  "avg_score": 0.833,
34
- "p50_latency_ms": 569.0
34
+ "p50_latency_ms": 41.0
35
35
  },
36
36
  "multi-hop": {
37
37
  "accuracy": 100.0,
38
38
  "avg_score": 1.0,
39
- "p50_latency_ms": 644.0
39
+ "p50_latency_ms": 38.0
40
40
  }
41
41
  }
42
42
  },
43
43
  "chunked_800": {
44
44
  "strategy": "chunked_800",
45
45
  "notes": 8,
46
- "ingest_time_s": 56.5,
46
+ "ingest_time_s": 0.1,
47
47
  "accuracy": 75.0,
48
- "avg_score": 0.875,
49
- "p50_latency_ms": 706.0,
50
- "p95_latency_ms": 2729.0,
48
+ "avg_score": 0.85,
49
+ "p50_latency_ms": 52.0,
50
+ "p95_latency_ms": 59.0,
51
51
  "by_category": {
52
52
  "tool-attribution": {
53
53
  "accuracy": 40.0,
54
54
  "avg_score": 0.7,
55
- "p50_latency_ms": 1299.0
55
+ "p50_latency_ms": 50.0
56
56
  },
57
57
  "cve-linkage": {
58
58
  "accuracy": 75.0,
59
- "avg_score": 0.875,
60
- "p50_latency_ms": 795.0
59
+ "avg_score": 0.75,
60
+ "p50_latency_ms": 52.0
61
61
  },
62
62
  "attribution": {
63
63
  "accuracy": 100.0,
64
64
  "avg_score": 1.0,
65
- "p50_latency_ms": 535.0
65
+ "p50_latency_ms": 52.0
66
66
  },
67
67
  "temporal": {
68
68
  "accuracy": 66.7,
69
69
  "avg_score": 0.833,
70
- "p50_latency_ms": 772.0
70
+ "p50_latency_ms": 54.0
71
71
  },
72
72
  "multi-hop": {
73
73
  "accuracy": 100.0,
74
74
  "avg_score": 1.0,
75
- "p50_latency_ms": 741.0
75
+ "p50_latency_ms": 33.0
76
76
  }
77
77
  }
78
78
  }
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env python3
2
+ """Instrument note-lookup volume per recall stage.
3
+
4
+ Counts store.get_note_by_id calls (total vs unique ids) and graph result
5
+ sizes per query to locate the redundant-lookup source the profiler exposed
6
+ (~476 lookups/query on an 8-note corpus).
7
+
8
+ Usage:
9
+ python benchmarks/instrument_lookups.py
10
+ """
11
+ import os
12
+ import tempfile
13
+
14
+ os.environ.setdefault('ZETTELFORGE_ENRICHMENT_ENABLED', 'false')
15
+
16
+ from cti_retrieval_benchmark import CTI_QUERIES, CTI_REPORTS
17
+
18
+ from zettelforge import MemoryManager
19
+ from zettelforge.graph_retriever import GraphRetriever
20
+
21
+
22
+ def main() -> None:
23
+ tmpdir = tempfile.mkdtemp(prefix='instr_lookups_')
24
+ mm = MemoryManager(jsonl_path=f'{tmpdir}/notes.jsonl', lance_path=f'{tmpdir}/vectordb')
25
+ for report in CTI_REPORTS:
26
+ mm.remember(report['content'], source_type='threat_report', source_ref=report['id'], domain='cti')
27
+
28
+ # Wrap get_note_by_id with a counter
29
+ calls = {'total': 0, 'ids': []}
30
+ orig = mm.store.get_note_by_id
31
+
32
+ def counting(nid):
33
+ calls['total'] += 1
34
+ calls['ids'].append(nid)
35
+ return orig(nid)
36
+
37
+ mm.store.get_note_by_id = counting
38
+
39
+ # Wrap graph retrieval to report result sizes
40
+ orig_retrieve = GraphRetriever.retrieve_note_ids
41
+ graph_sizes = []
42
+
43
+ def counting_retrieve(self, query_entities, max_depth=2):
44
+ res = orig_retrieve(self, query_entities, max_depth=max_depth)
45
+ graph_sizes.append(len(res))
46
+ return res
47
+
48
+ GraphRetriever.retrieve_note_ids = counting_retrieve
49
+
50
+ print(f'{"query":<48} {"lookups":>8} {"unique":>7} {"graph_n":>8}')
51
+ for qa in CTI_QUERIES:
52
+ calls['total'] = 0
53
+ calls['ids'] = []
54
+ graph_sizes.clear()
55
+ mm.recall(qa['question'], k=10, exclude_superseded=False)
56
+ uniq = len(set(calls['ids']))
57
+ gsz = graph_sizes[0] if graph_sizes else 0
58
+ print(f'{qa["question"][:46]:<48} {calls["total"]:>8} {uniq:>7} {gsz:>8}')
59
+
60
+
61
+ if __name__ == '__main__':
62
+ main()