zettelforge 2.5.2__tar.gz → 2.6.2__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 (360) hide show
  1. zettelforge-2.6.2/.github/workflows/stale.yml +47 -0
  2. {zettelforge-2.5.2 → zettelforge-2.6.2}/.gitignore +13 -0
  3. {zettelforge-2.5.2 → zettelforge-2.6.2}/CHANGELOG.md +110 -0
  4. {zettelforge-2.5.2 → zettelforge-2.6.2}/CONTRIBUTING.md +43 -0
  5. zettelforge-2.6.2/CONTRIBUTORS.md +17 -0
  6. {zettelforge-2.5.2 → zettelforge-2.6.2}/PKG-INFO +9 -2
  7. {zettelforge-2.5.2 → zettelforge-2.6.2}/README.md +2 -1
  8. zettelforge-2.6.2/ROADMAP.md +94 -0
  9. {zettelforge-2.5.2 → zettelforge-2.6.2}/config.default.yaml +55 -1
  10. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/THREAT_MODEL.md +7 -5
  11. zettelforge-2.6.2/docs/explanation/llm-budgets-and-timeouts.md +101 -0
  12. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/troubleshoot.md +99 -8
  13. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/upgrade.md +67 -3
  14. zettelforge-2.6.2/docs/how-to/use-web-interface.md +177 -0
  15. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/index.md +4 -2
  16. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/reference/configuration.md +111 -6
  17. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/reference/module-inventory.md +40 -1
  18. zettelforge-2.6.2/docs/reference/web-api.md +415 -0
  19. zettelforge-2.6.2/docs/rfcs/RFC-014-content-limits.md +143 -0
  20. zettelforge-2.6.2/docs/rfcs/RFC-015-zettelforge-web-gui.md +624 -0
  21. {zettelforge-2.5.2 → zettelforge-2.6.2}/mkdocs.yml +1 -0
  22. zettelforge-2.6.2/pr-body.md +41 -0
  23. {zettelforge-2.5.2 → zettelforge-2.6.2}/pyproject.toml +9 -1
  24. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/__init__.py +1 -1
  25. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/config.py +59 -0
  26. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/governance_validator.py +17 -1
  27. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/lance_maintenance.py +8 -8
  28. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/memory_manager.py +60 -0
  29. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_config.py +27 -0
  30. zettelforge-2.6.2/tests/test_governance.py +125 -0
  31. zettelforge-2.6.2/tests/test_web_api.py +493 -0
  32. zettelforge-2.6.2/web/app.py +1005 -0
  33. zettelforge-2.6.2/web/static/css/design_tokens.css +114 -0
  34. zettelforge-2.6.2/web/templates/base.html +201 -0
  35. zettelforge-2.6.2/web/templates/components/remember_panel.html +72 -0
  36. zettelforge-2.6.2/web/templates/components/result_card.html +98 -0
  37. zettelforge-2.6.2/web/templates/components/search_bar.html +11 -0
  38. zettelforge-2.6.2/web/templates/components/synthesis_block.html +58 -0
  39. zettelforge-2.6.2/web/templates/components/tab_bar.html +42 -0
  40. zettelforge-2.6.2/web/templates/config_editor.html +799 -0
  41. zettelforge-2.6.2/web/templates/index.html +279 -0
  42. zettelforge-2.6.2/web/ui/assets/favicon-16.png +0 -0
  43. zettelforge-2.6.2/web/ui/assets/favicon-32.png +0 -0
  44. zettelforge-2.6.2/web/ui/assets/favicon-512.png +0 -0
  45. zettelforge-2.6.2/web/ui/assets/favicon-apple-touch.png +0 -0
  46. zettelforge-2.6.2/web/ui/assets/logo.svg +42 -0
  47. zettelforge-2.6.2/web/ui/assets/threatrecall-lockup.svg +39 -0
  48. zettelforge-2.6.2/web/ui/assets/zettelforge_architecture.svg +104 -0
  49. zettelforge-2.6.2/web/ui/colors_and_type.css +286 -0
  50. zettelforge-2.6.2/web/ui/favicon.svg +33 -0
  51. zettelforge-2.6.2/web/ui/fonts/Neuropol.otf +0 -0
  52. zettelforge-2.6.2/web/ui/index.html +55 -0
  53. zettelforge-2.6.2/web/ui/js/app.js +144 -0
  54. zettelforge-2.6.2/web/ui/js/components/header.js +95 -0
  55. zettelforge-2.6.2/web/ui/js/components/result-card.js +69 -0
  56. zettelforge-2.6.2/web/ui/js/components/sidebar.js +57 -0
  57. zettelforge-2.6.2/web/ui/js/components/spinner.js +8 -0
  58. zettelforge-2.6.2/web/ui/js/components/tabs.js +17 -0
  59. zettelforge-2.6.2/web/ui/js/components/toast.js +35 -0
  60. zettelforge-2.6.2/web/ui/js/lib/api.js +83 -0
  61. zettelforge-2.6.2/web/ui/js/lib/state.js +50 -0
  62. zettelforge-2.6.2/web/ui/js/views/configuration.js +784 -0
  63. zettelforge-2.6.2/web/ui/js/views/dashboard.js +251 -0
  64. zettelforge-2.6.2/web/ui/js/views/entities.js +415 -0
  65. zettelforge-2.6.2/web/ui/js/views/history.js +296 -0
  66. zettelforge-2.6.2/web/ui/js/views/ingest.js +425 -0
  67. zettelforge-2.6.2/web/ui/js/views/knowledge-graph.js +606 -0
  68. zettelforge-2.6.2/web/ui/js/views/logs.js +256 -0
  69. zettelforge-2.6.2/web/ui/js/views/search.js +343 -0
  70. zettelforge-2.5.2/docs/superpowers/research/2026-04-09-ctibench-ragas-benchmarks.md +0 -31
  71. zettelforge-2.5.2/docs/superpowers/research/2026-04-09-fastembed-local-embeddings.md +0 -460
  72. zettelforge-2.5.2/docs/superpowers/research/2026-04-09-hybrid-typedb-lancedb-architecture.md +0 -855
  73. zettelforge-2.5.2/docs/superpowers/research/2026-04-09-local-llm-llama-cpp.md +0 -17
  74. zettelforge-2.5.2/docs/superpowers/research/2026-04-15-anti-aversion-cleanup.md +0 -763
  75. zettelforge-2.5.2/docs/superpowers/research/2026-04-15-causal-graph.md +0 -409
  76. zettelforge-2.5.2/docs/superpowers/research/2026-04-15-ctibench-ate-fix.md +0 -211
  77. zettelforge-2.5.2/docs/superpowers/research/2026-04-15-format-stability.md +0 -1029
  78. zettelforge-2.5.2/docs/superpowers/research/2026-04-15-memory-evolution.md +0 -428
  79. zettelforge-2.5.2/docs/superpowers/research/2026-04-15-merge-consolidation.md +0 -372
  80. zettelforge-2.5.2/docs/superpowers/research/2026-04-15-persistence-semantics.md +0 -422
  81. zettelforge-2.5.2/docs/superpowers/research/2026-04-15-sqlite-migration.md +0 -636
  82. zettelforge-2.5.2/docs/superpowers/research/2026-04-17-test-suite-audit.md +0 -178
  83. zettelforge-2.5.2/docs/superpowers/research/2026-04-24-phase-0.5-attribution-prelim.md +0 -123
  84. zettelforge-2.5.2/docs/superpowers/research/2026-04-25-graph-retriever-silence.md +0 -117
  85. zettelforge-2.5.2/docs/superpowers/research/2026-04-25-phase-0.5-attribution.md +0 -230
  86. zettelforge-2.5.2/docs/superpowers/research/README.md +0 -40
  87. zettelforge-2.5.2/docs/superpowers/specs/2026-04-15-p1-features-prd.md +0 -990
  88. zettelforge-2.5.2/tests/test_governance.py +0 -32
  89. zettelforge-2.5.2/web/app.py +0 -530
  90. {zettelforge-2.5.2 → zettelforge-2.6.2}/.github/CODEOWNERS +0 -0
  91. {zettelforge-2.5.2 → zettelforge-2.6.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  92. {zettelforge-2.5.2 → zettelforge-2.6.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  93. {zettelforge-2.5.2 → zettelforge-2.6.2}/.github/SECURITY.md +0 -0
  94. {zettelforge-2.5.2 → zettelforge-2.6.2}/.github/dependabot.yml +0 -0
  95. {zettelforge-2.5.2 → zettelforge-2.6.2}/.github/pull_request_template.md +0 -0
  96. {zettelforge-2.5.2 → zettelforge-2.6.2}/.github/workflows/ci.yml +0 -0
  97. {zettelforge-2.5.2 → zettelforge-2.6.2}/.github/workflows/docs.yml +0 -0
  98. {zettelforge-2.5.2 → zettelforge-2.6.2}/.github/workflows/publish.yml +0 -0
  99. {zettelforge-2.5.2 → zettelforge-2.6.2}/.github/workflows/snyk-security.yml +0 -0
  100. {zettelforge-2.5.2 → zettelforge-2.6.2}/ARCHITECTURE.md +0 -0
  101. {zettelforge-2.5.2 → zettelforge-2.6.2}/CODEOWNERS +0 -0
  102. {zettelforge-2.5.2 → zettelforge-2.6.2}/CODE_OF_CONDUCT.md +0 -0
  103. {zettelforge-2.5.2 → zettelforge-2.6.2}/Dockerfile +0 -0
  104. {zettelforge-2.5.2 → zettelforge-2.6.2}/GOVERNANCE.md +0 -0
  105. {zettelforge-2.5.2 → zettelforge-2.6.2}/LICENSE +0 -0
  106. {zettelforge-2.5.2 → zettelforge-2.6.2}/MANIFEST.in +0 -0
  107. {zettelforge-2.5.2 → zettelforge-2.6.2}/SECURITY.md +0 -0
  108. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/BENCHMARK_REPORT.md +0 -0
  109. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/LOCOMO_BENCHMARK_COMPARISON.md +0 -0
  110. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/auto_ralph.py +0 -0
  111. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/benchmark_harness.py +0 -0
  112. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/cti_benchmark_v2.py +0 -0
  113. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/cti_retrieval_benchmark.py +0 -0
  114. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/cti_retrieval_results.json +0 -0
  115. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/cti_v2_results.json +0 -0
  116. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/ctibench_benchmark.py +0 -0
  117. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/ctibench_results.json +0 -0
  118. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/dataset.json +0 -0
  119. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/enterprise-attack.json +0 -0
  120. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/evolve_benchmark.py +0 -0
  121. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/evolve_results.json +0 -0
  122. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/graph_test.py +0 -0
  123. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/locomo_benchmark.py +0 -0
  124. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/locomo_results.json +0 -0
  125. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/locomo_results_v1.3.0_baseline.json +0 -0
  126. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/memoryagentbench.py +0 -0
  127. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/memoryagentbench_results.json +0 -0
  128. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/mempalace_benchmark.py +0 -0
  129. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/mempalace_results.json +0 -0
  130. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/naive_memory.py +0 -0
  131. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/opencti_benchmark.py +0 -0
  132. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/ragas_benchmark.py +0 -0
  133. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/ragas_cti_results.json +0 -0
  134. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/ragas_results.json +0 -0
  135. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/results/benchmark_report.md +0 -0
  136. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/results/ralph_optimization_log.json +0 -0
  137. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/scale_benchmark.py +0 -0
  138. {zettelforge-2.5.2 → zettelforge-2.6.2}/benchmarks/scale_results.json +0 -0
  139. {zettelforge-2.5.2 → zettelforge-2.6.2}/config.example.yaml +0 -0
  140. {zettelforge-2.5.2 → zettelforge-2.6.2}/docker/docker-compose.yml +0 -0
  141. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/.well-known/security.txt +0 -0
  142. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/CNAME +0 -0
  143. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/architecture-diagram.mmd +0 -0
  144. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/archive/PACKAGE_SUMMARY.md +0 -0
  145. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/archive/README.md +0 -0
  146. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/archive/SKILL.md +0 -0
  147. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/ZettelForge_Architecture.mmd +0 -0
  148. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/architecture-overview.mmd +0 -0
  149. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/architecture-read-path.mmd +0 -0
  150. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/architecture-write-path.mmd +0 -0
  151. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/cf-analytics.js +0 -0
  152. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/demo.gif +0 -0
  153. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/favicon-16.png +0 -0
  154. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/favicon-32.png +0 -0
  155. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/favicon-512.png +0 -0
  156. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/favicon-64.png +0 -0
  157. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/favicon-apple-touch.png +0 -0
  158. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/favicon-old.svg +0 -0
  159. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/favicon.svg +0 -0
  160. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/logo.svg +0 -0
  161. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/social-preview.png +0 -0
  162. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/threatrecall-lockup-monogram.svg +0 -0
  163. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/threatrecall-lockup.svg +0 -0
  164. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/threatrecall-logo-flat.svg +0 -0
  165. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/threatrecall-logo-philosophy.md +0 -0
  166. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/threatrecall-logo.png +0 -0
  167. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/threatrecall-mark.png +0 -0
  168. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/zettelforge_architecture-light.svg +0 -0
  169. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/assets/zettelforge_architecture.svg +0 -0
  170. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/brand/brandIdentity.md +0 -0
  171. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/brand/colors_and_type.css +0 -0
  172. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/explanation/architecture.md +0 -0
  173. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/explanation/epistemic-tiers.md +0 -0
  174. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/explanation/stix-in-zettelforge.md +0 -0
  175. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/explanation/two-phase-pipeline.md +0 -0
  176. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/explanation/zettelkasten-philosophy.md +0 -0
  177. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/configure-lancedb.md +0 -0
  178. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/configure-opencti.md +0 -0
  179. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/configure-pii.md +0 -0
  180. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/configure-typedb.md +0 -0
  181. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/ingest-news-report.md +0 -0
  182. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/integrate-llm-agent.md +0 -0
  183. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/migrate-jsonl-to-sqlite.md +0 -0
  184. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/query-apt-tools.md +0 -0
  185. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/reproduce-benchmarks.md +0 -0
  186. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/resolve-aliases.md +0 -0
  187. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/run-temporal-query.md +0 -0
  188. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/how-to/store-threat-actor.md +0 -0
  189. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/human-evaluation-rubric.md +0 -0
  190. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/llms.txt +0 -0
  191. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/narrative/2026-04-16-the-memory-problem.md +0 -0
  192. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/overrides/main.html +0 -0
  193. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/reference/architecture-deep-dive.md +0 -0
  194. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/reference/governance-controls.md +0 -0
  195. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/reference/memory-manager-api.md +0 -0
  196. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/reference/retrieval-policies.md +0 -0
  197. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/reference/stix-schema.md +0 -0
  198. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/rfcs/RFC-001-conversational-entity-extractor.md +0 -0
  199. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/rfcs/RFC-002-universal-llm-provider.md +0 -0
  200. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/rfcs/RFC-003-adversarial-review.md +0 -0
  201. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/rfcs/RFC-003-read-path-depth-routing.md +0 -0
  202. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/rfcs/RFC-007-operational-telemetry.md +0 -0
  203. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/rfcs/RFC-009-enrichment-pipeline-v2.md +0 -0
  204. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/rfcs/RFC-010-enrichment-hotfix.md +0 -0
  205. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/rfcs/RFC-011-local-llm-backend-config.md +0 -0
  206. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/rfcs/RFC-012-litellm-unified-provider.md +0 -0
  207. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/rfcs/RFC-013-presidio-pii-detection.md +0 -0
  208. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/stylesheets/brand-tokens.css +0 -0
  209. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/stylesheets/extra.css +0 -0
  210. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/stylesheets/fonts/Neuropol.otf +0 -0
  211. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/tutorials/01-quickstart.md +0 -0
  212. {zettelforge-2.5.2 → zettelforge-2.6.2}/docs/tutorials/02-first-cti-report.md +0 -0
  213. {zettelforge-2.5.2 → zettelforge-2.6.2}/examples/athf_bridge.py +0 -0
  214. {zettelforge-2.5.2 → zettelforge-2.6.2}/examples/mcp_claude_code.md +0 -0
  215. {zettelforge-2.5.2 → zettelforge-2.6.2}/examples/quickstart.py +0 -0
  216. {zettelforge-2.5.2 → zettelforge-2.6.2}/governance/controls.yaml +0 -0
  217. {zettelforge-2.5.2 → zettelforge-2.6.2}/scripts/migrate_jsonl_to_sqlite.py +0 -0
  218. {zettelforge-2.5.2 → zettelforge-2.6.2}/scripts/rebuild_index.py +0 -0
  219. {zettelforge-2.5.2 → zettelforge-2.6.2}/scripts/record-demo.sh +0 -0
  220. {zettelforge-2.5.2 → zettelforge-2.6.2}/scripts/typedb-setup.sh +0 -0
  221. {zettelforge-2.5.2 → zettelforge-2.6.2}/scripts/zettelforge-rebuild.service +0 -0
  222. {zettelforge-2.5.2 → zettelforge-2.6.2}/scripts/zettelforge-rebuild.timer +0 -0
  223. {zettelforge-2.5.2 → zettelforge-2.6.2}/server.json +0 -0
  224. {zettelforge-2.5.2 → zettelforge-2.6.2}/skills/claude-code-skill.md +0 -0
  225. {zettelforge-2.5.2 → zettelforge-2.6.2}/skills/openclaw-skill.md +0 -0
  226. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/__main__.py +0 -0
  227. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/alias_resolver.py +0 -0
  228. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/backend_factory.py +0 -0
  229. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/blended_retriever.py +0 -0
  230. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/cache.py +0 -0
  231. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/consolidation.py +0 -0
  232. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/demo.py +0 -0
  233. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/detection/__init__.py +0 -0
  234. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/detection/base.py +0 -0
  235. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/detection/consumers.py +0 -0
  236. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/detection/explainer.py +0 -0
  237. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/edition.py +0 -0
  238. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/entity_indexer.py +0 -0
  239. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/extensions.py +0 -0
  240. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/fact_extractor.py +0 -0
  241. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/graph_retriever.py +0 -0
  242. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/integrations/__init__.py +0 -0
  243. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/integrations/langchain_retriever.py +0 -0
  244. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/intent_classifier.py +0 -0
  245. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/json_parse.py +0 -0
  246. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/knowledge_graph.py +0 -0
  247. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/llm_client.py +0 -0
  248. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/llm_providers/__init__.py +0 -0
  249. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/llm_providers/base.py +0 -0
  250. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/llm_providers/litellm_provider.py +0 -0
  251. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/llm_providers/local_provider.py +0 -0
  252. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/llm_providers/mock_provider.py +0 -0
  253. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/llm_providers/ollama_provider.py +0 -0
  254. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/llm_providers/registry.py +0 -0
  255. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/log.py +0 -0
  256. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/mcp/__init__.py +0 -0
  257. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/mcp/__main__.py +0 -0
  258. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/mcp/server.py +0 -0
  259. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/memory_evolver.py +0 -0
  260. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/memory_store.py +0 -0
  261. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/memory_updater.py +0 -0
  262. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/note_constructor.py +0 -0
  263. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/note_schema.py +0 -0
  264. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/observability.py +0 -0
  265. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/ocsf.py +0 -0
  266. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/ontology.py +0 -0
  267. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/pii_validator.py +0 -0
  268. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/retry.py +0 -0
  269. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/scripts/compact_lance.py +0 -0
  270. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/scripts/human_eval_sampler.py +0 -0
  271. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/scripts/telemetry_aggregator.py +0 -0
  272. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/scripts/telemetry_dashboard.py +0 -0
  273. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sigma/__init__.py +0 -0
  274. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sigma/cli.py +0 -0
  275. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sigma/entities.py +0 -0
  276. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sigma/ingest.py +0 -0
  277. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sigma/parser.py +0 -0
  278. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sigma/schemas/NOTICE.md +0 -0
  279. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sigma/schemas/__init__.py +0 -0
  280. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sigma/schemas/sigma-correlation-rules-schema.json +0 -0
  281. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sigma/schemas/sigma-detection-rule-schema.json +0 -0
  282. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sigma/schemas/sigma-filters-schema.json +0 -0
  283. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sigma/tags.py +0 -0
  284. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/sqlite_backend.py +0 -0
  285. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/storage_backend.py +0 -0
  286. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/synthesis_generator.py +0 -0
  287. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/synthesis_validator.py +0 -0
  288. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/telemetry.py +0 -0
  289. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/vector_memory.py +0 -0
  290. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/vector_retriever.py +0 -0
  291. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/yara/__init__.py +0 -0
  292. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/yara/cccs_metadata.py +0 -0
  293. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/yara/cli.py +0 -0
  294. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/yara/entities.py +0 -0
  295. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/yara/ingest.py +0 -0
  296. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/yara/parser.py +0 -0
  297. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/yara/schemas/CCCS_YARA.yml +0 -0
  298. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/yara/schemas/CCCS_YARA_values.yml +0 -0
  299. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/yara/schemas/NOTICE.md +0 -0
  300. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/yara/schemas/__init__.py +0 -0
  301. {zettelforge-2.5.2 → zettelforge-2.6.2}/src/zettelforge/yara/tags.py +0 -0
  302. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/__init__.py +0 -0
  303. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/benchmark_scale.py +0 -0
  304. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/conftest.py +0 -0
  305. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/fixtures/sigma/cloud_example.yml +0 -0
  306. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/fixtures/sigma/correlation_example.yml +0 -0
  307. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/fixtures/sigma/process_creation_example.yml +0 -0
  308. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/fixtures/sigma/tagged_example.yml +0 -0
  309. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/fixtures/yara/malware_hash.yar +0 -0
  310. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/fixtures/yara/technique_loader.yar +0 -0
  311. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/fixtures/yara/webshell.yar +0 -0
  312. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_basic.py +0 -0
  313. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_blended_retriever.py +0 -0
  314. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_causal_extraction.py +0 -0
  315. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_consolidation.py +0 -0
  316. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_conversational_entities.py +0 -0
  317. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_core.py +0 -0
  318. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_cti_integration.py +0 -0
  319. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_detection_explainer.py +0 -0
  320. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_detection_rule_entities.py +0 -0
  321. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_edition.py +0 -0
  322. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_embedding.py +0 -0
  323. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_entity_indexer_races.py +0 -0
  324. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_extensions.py +0 -0
  325. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_fact_extractor.py +0 -0
  326. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_governance_spec_drift.py +0 -0
  327. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_graph_retriever.py +0 -0
  328. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_human_eval_sampler.py +0 -0
  329. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_intent_classifier.py +0 -0
  330. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_json_parse.py +0 -0
  331. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_kg_edge_schema.py +0 -0
  332. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_lance_maintenance.py +0 -0
  333. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_langchain_retriever.py +0 -0
  334. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_llm_client.py +0 -0
  335. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_llm_providers.py +0 -0
  336. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_logging_compliance.py +0 -0
  337. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_mcp_server.py +0 -0
  338. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_memory_evolver.py +0 -0
  339. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_memory_updater.py +0 -0
  340. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_performance.py +0 -0
  341. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_pii_validator.py +0 -0
  342. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_recall_integration.py +0 -0
  343. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_sigma_entities.py +0 -0
  344. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_sigma_ingest.py +0 -0
  345. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_sigma_parser.py +0 -0
  346. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_sqlite_backend.py +0 -0
  347. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_sqlite_integration.py +0 -0
  348. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_storage_backend.py +0 -0
  349. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_telemetry_aggregator.py +0 -0
  350. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_telemetry_collector.py +0 -0
  351. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_telemetry_dashboard.py +0 -0
  352. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_telemetry_integration.py +0 -0
  353. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_temporal_graph.py +0 -0
  354. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_two_phase_e2e.py +0 -0
  355. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_typedb_client.py +0 -0
  356. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_yara_entities.py +0 -0
  357. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_yara_ingest.py +0 -0
  358. {zettelforge-2.5.2 → zettelforge-2.6.2}/tests/test_yara_parser.py +0 -0
  359. {zettelforge-2.5.2 → zettelforge-2.6.2}/web/auth.py +0 -0
  360. {zettelforge-2.5.2 → zettelforge-2.6.2}/web/mcp_server.py +0 -0
@@ -0,0 +1,47 @@
1
+ name: Stale issue and PR management
2
+
3
+ on:
4
+ schedule:
5
+ - cron: "0 9 * * 1" # Every Monday at 09:00 UTC
6
+
7
+ permissions:
8
+ issues: write
9
+ pull-requests: write
10
+
11
+ jobs:
12
+ stale:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/stale@v9
16
+ with:
17
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
18
+
19
+ # Issues
20
+ stale-issue-message: >
21
+ This issue has been inactive for 60 days. It will be closed
22
+ in 14 days unless there is new activity. If this is still
23
+ relevant, please comment to keep it open.
24
+ close-issue-message: >
25
+ This issue has been automatically closed after 14 days of
26
+ inactivity. It can be reopened at any time.
27
+ days-before-issue-stale: 60
28
+ days-before-issue-close: 14
29
+ stale-issue-label: stale
30
+ exempt-issue-labels: "p0,planned,help wanted,good first issue"
31
+
32
+ # PRs
33
+ stale-pr-message: >
34
+ This pull request has been inactive for 60 days. It will be
35
+ closed in 14 days unless there is new activity.
36
+ close-pr-message: >
37
+ This pull request has been automatically closed after 14 days
38
+ of inactivity.
39
+ days-before-pr-stale: 60
40
+ days-before-pr-close: 14
41
+ stale-pr-label: stale
42
+ exempt-pr-labels: "draft,work-in-progress"
43
+
44
+ # General
45
+ operations-per-run: 100
46
+ remove-stale-when-updated: true
47
+ delete-branch: false
@@ -12,6 +12,17 @@ tasks/
12
12
  docs/.ralph/
13
13
  agentsync.md
14
14
 
15
+ # Internal research/specs/plans — workspace scratch that contains
16
+ # unredacted incident detail, customer references, and intermediate
17
+ # reasoning. Kept on disk for the maintainer; not published.
18
+ docs/superpowers/
19
+
20
+ # Claude Code session artifacts — agent worktrees, transcripts, and
21
+ # scratch state created by the maintainer's local tooling. Belt-and-
22
+ # suspenders: these were never tracked, but listing them prevents an
23
+ # accidental `git add -A` from sweeping them in.
24
+ .claude/
25
+
15
26
  # MkDocs build output (regenerated from docs/)
16
27
  site/
17
28
 
@@ -28,6 +39,8 @@ downloads/
28
39
  eggs/
29
40
  .eggs/
30
41
  lib/
42
+ !web/ui/js/lib/
43
+ !web/ui/js/lib/*.js
31
44
  lib64/
32
45
  parts/
33
46
  sdist/
@@ -6,6 +6,116 @@ Versioning follows [Semantic Versioning](https://semver.org/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [2.6.2] - 2026-04-27
10
+
11
+ UI/UX release. Fixes the `/config` page so the Apply button actually works
12
+ and surfaces enum-style settings as dropdowns instead of free-text inputs.
13
+ No data migration. No config changes. No API contract changes.
14
+
15
+ ### Fixed
16
+
17
+ - **`/config` "Save Changes" button is no longer dead.** The Quick Settings
18
+ panel called `saveConfigForm()` and `reloadConfig()` — neither function
19
+ was defined anywhere, so the button silently no-op'd and the panel
20
+ rendered "Loading schema..." forever. Replaced with a real form-based
21
+ editor whose Apply button PUTs a nested payload to `/api/config` and
22
+ reloads from server on success.
23
+
24
+ ### Added
25
+
26
+ - **Form-based config editor with dropdowns.** `/config` now renders a
27
+ grouped settings form alongside the YAML editor. Known enum fields
28
+ (`backend`, `embedding.provider`, `llm.provider`, `llm.local_backend`,
29
+ `logging.level`, `synthesis.default_format`, `governance.pii.action`)
30
+ render as `<select>` controls instead of free-text inputs. Restart-
31
+ required leaves get a "restart required" badge sourced from the same
32
+ set the server uses, so the UI warning is never out of sync with the
33
+ server's classification.
34
+ - **Pending-changes counter and Revert button.** The form tracks dirty
35
+ fields by dotted path, builds a single nested payload on Apply, and
36
+ shows `N pending change(s) (M need restart)` next to the buttons.
37
+ - **YAML editor accepts both YAML and JSON** (was JSON-only despite the
38
+ label) and skips redacted `***` secrets so they aren't PUT back as
39
+ literal strings.
40
+
41
+ ### Tests
42
+
43
+ - 4 new tests in `tests/test_web_api.py`: dropdown enum round-trip for
44
+ `logging.level` (restart-required) and `synthesis.default_format` (live);
45
+ multi-section nested payload from a single Apply; and a regression guard
46
+ on the `/config` HTML structure (form tab, dropdown enum declarations,
47
+ restart-leaf flags, and proof the dead `saveConfigForm`/`reloadConfig`
48
+ handlers are gone). 28 passed, 2 skipped (was 24 + 2).
49
+
50
+ ## [2.6.1] - 2026-04-25
51
+
52
+ Hotfix release. Resolves three blockers found in code review of the
53
+ RFC-015 web GUI shipped in v2.6.0. No data migration. No config changes.
54
+
55
+ ### Fixed
56
+
57
+ - **`/config` HTML page now renders.** `_to_dict` was defined as a closure
58
+ inside `get_config_endpoint`, so every render of `/config` raised
59
+ `NameError`, was silently swallowed by a bare `except`, and left the
60
+ YAML body blank on initial server-side render. Promoted to a module-level
61
+ `_config_to_dict` helper used by both routes. (PR #131)
62
+ - **`PUT /api/config` correctly reports nested restart-required fields.**
63
+ The check compared top-level payload keys against a set of dotted-path
64
+ fields, so payloads like `{"embedding": {"provider": "x"}}` were
65
+ reported as `applied: ["embedding"]`, `pending_restart: []`, telling
66
+ operators a restart-required change had taken effect when it had not.
67
+ Added `_flatten_keys` to walk nested payloads to dotted leaf paths;
68
+ `applied` and `pending_restart` now contain accurate dotted paths.
69
+ (PR #131)
70
+ - **`/config` HTML route is now auth-gated.** `/api/config` was protected,
71
+ but the HTML shell (and once the `_to_dict` bug was fixed, its
72
+ server-rendered YAML body) was reachable without an API key. Added
73
+ `Depends(require_api_guard)` and made the YAML body redact secrets
74
+ before serialization. (PR #131)
75
+
76
+ ### Tests
77
+
78
+ - Added four regression tests in `tests/test_web_api.py` covering all
79
+ three fixes. 24 passed, 2 skipped (was 20 + 2).
80
+
81
+ ## [2.6.0] - 2026-04-25
82
+
83
+ Feature release. Adds configurable content-size limits for DoS mitigation
84
+ (RFC-014) and moves per-call-site LLM token budgets out of hardcoded
85
+ literals into `LLMConfig`, making them overridable at deploy time.
86
+
87
+ ### Added
88
+
89
+ - **Configurable content size limits** (`GovernanceConfig.limits.max_content_length`,
90
+ default 50 MB). `remember()` calls with content exceeding the limit are
91
+ rejected with a clear error message. Set to `0` to disable the check.
92
+ Environment override: `ZETTELFORGE_LIMITS_MAX_CONTENT_LENGTH`.
93
+ (RFC-014, PR #123)
94
+ - **Per-call-site `max_tokens` budgets configurable via `LLMConfig`**.
95
+ Five new fields: `max_tokens_causal` (8000), `max_tokens_synthesis` (2500),
96
+ `max_tokens_fact` (2500), `max_tokens_ner` (2500), `max_tokens_evolution` (2500).
97
+ Defaults match v2.5.2 values. No behavioral change for existing configs.
98
+ (PR #126, issue #125)
99
+
100
+ ### Changed
101
+
102
+ - **Docs: config reconciliation for v2.5.2** — `config.default.yaml` gained
103
+ the `lance:` section (RFC-009 Phase 1.5), `docs/reference/configuration.md`
104
+ now covers all v2.5.2 knobs (`lance`, `pii`, per-call-site budgets), and
105
+ `docs/explanation/llm-budgets-and-timeouts.md` explains the reasoning-model
106
+ token-budget tradeoffs. (PR #126)
107
+ - **`_apply_yaml()` now handles `lance:` section** — previously the
108
+ `lance.cleanup_interval_minutes` and `lance.cleanup_older_than_seconds` YAML
109
+ knobs were silently ignored (regression from RFC-009 Phase 1.5 landing in
110
+ v2.4.x without the `_apply_yaml` branch). (PR #126, code review finding)
111
+
112
+ ### Internal
113
+
114
+ - **Removed `docs/superpowers/` from version control**. The directory holds
115
+ workspace scratch — internal research and notes with unredacted incident
116
+ detail. 18 files (7319 lines) untracked. No published content lost.
117
+ (PR #128)
118
+
9
119
  ## [2.5.2] - 2026-04-25
10
120
 
11
121
  Hotfix release. Restores end-to-end functionality of synthesis, causal
@@ -37,6 +37,49 @@ If your contribution needs TypeDB or OpenCTI, open an issue to discuss.
37
37
  We keep the extension boundary clear so contributors know their work
38
38
  will always remain open source.
39
39
 
40
+ ### For major features: Start with an RFC
41
+
42
+ If you're proposing a significant new feature (new subsystem, new backend,
43
+ breaking API change), open an RFC before writing code. RFCs live in
44
+ `docs/rfcs/` and follow the template from the existing RFCs in that
45
+ directory. Open a Discussion first to socialize the idea, then file a
46
+ draft RFC as a PR. This prevents wasted effort on work that won't be
47
+ accepted.
48
+
49
+ See [ROADMAP.md](ROADMAP.md) for the current priorities and what's
50
+ planned for upcoming releases.
51
+
52
+ ### Good first issues
53
+
54
+ Issues tagged [good first issue](https://github.com/rolandpg/zettelforge/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
55
+ have structured acceptance criteria in the issue body. Check the issue
56
+ for: which files to edit, test expectations, and example input/output.
57
+ If an issue is unclear, ask in the issue comments.
58
+
59
+ ## Issue triage
60
+
61
+ This project is maintained by a solo developer (per GOV-006). Here is
62
+ what you can expect:
63
+
64
+ - **New issues**: triaged within 7 days. You will get a response (even
65
+ if it's "not planned, closing").
66
+ - **Bug reports**: severity assessed within 7 days. P0 (crash, data
67
+ loss, security) gets a same-day response.
68
+ - **Feature requests**: tagged with `enhancement` on creation. The
69
+ maintainer will add `planned`, `deferred`, or `won't fix` within 7
70
+ days.
71
+ - **PR reviews**: first review within 14 days of submission. Smaller
72
+ PRs get reviewed faster.
73
+ - **Stale issues**: issues with no activity for 60 days are tagged
74
+ `stale` and closed after 14 more days without response. This keeps
75
+ the tracker manageable for a solo maintainer.
76
+
77
+ ## Contributor recognition
78
+
79
+ Every contributor is listed in [CONTRIBUTORS.md](CONTRIBUTORS.md),
80
+ regardless of contribution size. If you submit a PR that gets merged,
81
+ you will be added. If your name is missing, open a PR.
82
+
40
83
  ## Code Style
41
84
 
42
85
  - Follow PEP 8
@@ -0,0 +1,17 @@
1
+ # Contributors
2
+
3
+ Thank you to everyone who has contributed to ZettelForge.
4
+
5
+ ## Maintainers
6
+
7
+ - **Patrick G. Roland II** — creator and lead maintainer
8
+
9
+ ## Contributors
10
+
11
+ This file tracks individuals who have contributed code, documentation, design, or other improvements. The maintainer updates it with each release.
12
+
13
+ If you have contributed and your name is missing, please open a PR or issue.
14
+
15
+ ---
16
+
17
+ *This project uses a "thank you, next" acknowledgment model. All contributors are listed regardless of contribution size. A typo fix counts.*
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zettelforge
3
- Version: 2.5.2
3
+ Version: 2.6.2
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
@@ -36,12 +36,16 @@ Requires-Dist: requests>=2.31.0
36
36
  Requires-Dist: structlog>=24.0.0
37
37
  Requires-Dist: tantivy>=0.11.0
38
38
  Provides-Extra: dev
39
+ Requires-Dist: fastapi>=0.100.0; extra == 'dev'
40
+ Requires-Dist: jinja2>=3.0.0; extra == 'dev'
39
41
  Requires-Dist: langchain-core>=0.2.0; extra == 'dev'
40
42
  Requires-Dist: mypy>=1.0.0; extra == 'dev'
43
+ Requires-Dist: psutil>=5.9.0; extra == 'dev'
41
44
  Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
42
45
  Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
43
46
  Requires-Dist: pytest>=7.0.0; extra == 'dev'
44
47
  Requires-Dist: ruff>=0.4.0; extra == 'dev'
48
+ Requires-Dist: uvicorn>=0.20.0; extra == 'dev'
45
49
  Provides-Extra: extensions
46
50
  Requires-Dist: zettelforge-enterprise>=2.1.0; extra == 'extensions'
47
51
  Provides-Extra: langchain
@@ -61,6 +65,8 @@ Requires-Dist: presidio-anonymizer>=2.2.0; extra == 'pii'
61
65
  Requires-Dist: spacy>=3.5.0; extra == 'pii'
62
66
  Provides-Extra: web
63
67
  Requires-Dist: fastapi>=0.100.0; extra == 'web'
68
+ Requires-Dist: jinja2>=3.0.0; extra == 'web'
69
+ Requires-Dist: psutil>=5.9.0; extra == 'web'
64
70
  Requires-Dist: uvicorn>=0.20.0; extra == 'web'
65
71
  Description-Content-Type: text/markdown
66
72
 
@@ -79,8 +85,9 @@ It extracts CVEs, threat actors, IOCs, and ATT&CK techniques from analyst notes
79
85
  [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/downloads/)
80
86
  [![License: MIT](https://img.shields.io/badge/license-MIT-green)](https://opensource.org/licenses/MIT)
81
87
  [![CI](https://github.com/rolandpg/zettelforge/actions/workflows/ci.yml/badge.svg)](https://github.com/rolandpg/zettelforge/actions)
88
+ [![Open Issues](https://img.shields.io/github/issues/rolandpg/zettelforge?color=blue)](https://github.com/rolandpg/zettelforge/issues)
82
89
 
83
- **[⭐ Star](https://github.com/rolandpg/zettelforge) · [📦 `pip install zettelforge`](https://pypi.org/project/zettelforge/) · [📖 Docs](https://docs.threatrecall.ai/) · [🧪 Hosted beta](https://threatrecall.ai)**
90
+ **[⭐ Star](https://github.com/rolandpg/zettelforge) · [📦 `pip install zettelforge`](https://pypi.org/project/zettelforge/) · [📖 Docs](https://docs.threatrecall.ai/) · [🧪 Hosted beta](https://threatrecall.ai) · [🗺️ Roadmap](ROADMAP.md)**
84
91
 
85
92
  <p align="center">
86
93
  <a href="https://www.buymeacoffee.com/xypher22pr0" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-green.png" alt="Buy Me a Coffee" style="height: 60px !important;width: 217px !important;" ></a>
@@ -13,8 +13,9 @@ It extracts CVEs, threat actors, IOCs, and ATT&CK techniques from analyst notes
13
13
  [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/downloads/)
14
14
  [![License: MIT](https://img.shields.io/badge/license-MIT-green)](https://opensource.org/licenses/MIT)
15
15
  [![CI](https://github.com/rolandpg/zettelforge/actions/workflows/ci.yml/badge.svg)](https://github.com/rolandpg/zettelforge/actions)
16
+ [![Open Issues](https://img.shields.io/github/issues/rolandpg/zettelforge?color=blue)](https://github.com/rolandpg/zettelforge/issues)
16
17
 
17
- **[⭐ Star](https://github.com/rolandpg/zettelforge) · [📦 `pip install zettelforge`](https://pypi.org/project/zettelforge/) · [📖 Docs](https://docs.threatrecall.ai/) · [🧪 Hosted beta](https://threatrecall.ai)**
18
+ **[⭐ Star](https://github.com/rolandpg/zettelforge) · [📦 `pip install zettelforge`](https://pypi.org/project/zettelforge/) · [📖 Docs](https://docs.threatrecall.ai/) · [🧪 Hosted beta](https://threatrecall.ai) · [🗺️ Roadmap](ROADMAP.md)**
18
19
 
19
20
  <p align="center">
20
21
  <a href="https://www.buymeacoffee.com/xypher22pr0" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-green.png" alt="Buy Me a Coffee" style="height: 60px !important;width: 217px !important;" ></a>
@@ -0,0 +1,94 @@
1
+ # ZettelForge Roadmap
2
+
3
+ Last updated: 2026-04-25
4
+
5
+ This document communicates what the maintainer is building, what is on hold, and what is out of scope. It is updated when priorities shift.
6
+
7
+ ---
8
+
9
+ ## Current release: v2.6.1
10
+
11
+ Shipped: 2026-04-25. See `CHANGELOG.md` for details.
12
+
13
+ The v2.6 series (RFC-013 through RFC-015) delivered PII detection (Presidio), configurable content size limits, and the Web Management Interface. The v2.5 series (RFC-009 through RFC-012) delivered the enrichment pipeline v2, local LLM backends, and unified provider config.
14
+
15
+ ---
16
+
17
+ ## v2.7.0 targets (next release)
18
+
19
+ Target: 2026-05-09. Scope is frozen at the items below. Everything else defers to v2.8.0 unless marked **P0**.
20
+
21
+ ### Must ship (P0)
22
+
23
+ - [ ] **Issue #125: Harden reasoning-model LLM budget plumbing.** Regression tests for `max_tokens` at each call site, config-overridable budgets, `thinking` tag stripping in `json_parse.py`, and a `reasoning_model: bool` auto-scaling flag. Post-#124 follow-up. (Est: 2-3 days)
24
+ - [ ] **Issue #73: Tighten CCCS metadata regexes (SEC-6 / SEC-7).** Low hanging security hardening. (Est: 0.5 day)
25
+ - [ ] **Issue #72: MemoryManager.remember(sync=True) dominates bulk ingest.** The YARA p95 plyara tail needs a timeout or chunked processing path. (Est: 1 day)
26
+
27
+ ### Nice to have (P1)
28
+
29
+ - [ ] **Issue #71: Add typed DetectionMeta extension to MemoryNote.Metadata.** Paves the way for richer entity metadata downstream. (Est: 0.5 day)
30
+ - [ ] **Issue #51: Ratchet governance coverage threshold from 67% toward 80%.** Incremental. (Est: 1 day)
31
+
32
+ ### Community items (open for contribution)
33
+
34
+ See the [good first issue](https://github.com/rolandpg/zettelforge/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) label. These are pre-scoped with acceptance criteria:
35
+
36
+ - #47 — IPv6 address extraction
37
+ - #46 — YARA rule reference extraction
38
+ - #45 — Sigma rule ID extraction
39
+ - #39 — Threat actor alias mappings for Chinese APT groups
40
+ - #36 — Architecture decision records (ADRs)
41
+ - #44 — Example: MISP JSON feed ingestion
42
+ - #43 — Example: Slack bot for CTI queries
43
+ - #41 — Example: Jupyter notebook CTI analysis workflow
44
+
45
+ ---
46
+
47
+ ## Next release (v2.7.1+)
48
+
49
+ Target: approximately 2026-05-23. Provisional scope; will be finalized after v2.7.0 ships.
50
+
51
+ - [ ] **CrewAI tool wrapper** (#40). Integration path for CrewAI agents to use ZettelForge as a memory backend.
52
+ - [ ] **OpenCTI sync overhaul**. Improving the bidirectional sync reliability from the initial implementation.
53
+ - [ ] **Detection rules as first-class entities** (#feat/detection-rules-first-class branch). Sigma/YARA rules stored and searchable as knowledge graph nodes.
54
+
55
+ ---
56
+
57
+ ## Backlog / on hold
58
+
59
+ These are tracked but not actively scheduled:
60
+
61
+ - **MCP registry publish** (feat/mcp-registry-publish branch). Publishing the MCP server to the official MCP registry.
62
+ - **Enterprise split**. Separating the current monolithic package into community + enterprise tiers. Governance, license boundary, and packaging work. Not blocked, but deferred until community adoption justifies the overhead.
63
+ - **TypeDB read-path hardening**. Depth routing and schema versioning for the TypeDB backend. Most users run on JSONL/SQLite; TypeDB is a small fraction of the install base.
64
+ - **Conversational entity extractor** (stash: feature/RFC-001-conversational-entity-extractor). Interactive refinement of extracted entities by analyst chat. Requires UX thinking and a frontend update.
65
+ - **Pydantic v3 upgrade prep** (stash: test-fix/pydantic-v3-prep). Preparing internal models for Pydantic v3 migration. Low urgency; no upstream pressure yet.
66
+
67
+ ---
68
+
69
+ ## Out of scope (not building)
70
+
71
+ These have been proposed or discussed and explicitly decided against:
72
+
73
+ - **UI framework migration** (React, Vue, Svelte, etc.). The web GUI is a vanilla JS SPA using the ZettelForge Design System. No npm build step, no JS framework. This is intentional: the SPA must remain maintainable by a solo developer and installable with `pip install zettelforge[web]` without a separate build step. Not changing.
74
+ - **Docker containerization**. Deferred to v2.x post-v1.0 per the tech stack decision. The in-process architecture already makes deployment trivial.
75
+ - **Cloud-hosted memory backend**. ZettelForge is designed for local-first, air-gapped, and on-prem deployments. A cloud sync layer would compromise the security model and is not on the roadmap.
76
+ - **LangChain / LangGraph integration as a default**. Out of scope per the CLAUDE.md rules in this repo. Community wraps are welcome (see the CrewAI issue for how integration should work).
77
+
78
+ ---
79
+
80
+ ## Release cadence
81
+
82
+ - **Minor releases (v2.x.0)**: roughly every 2-3 weeks, bundling feature work and hardening.
83
+ - **Patch releases (v2.x.y)**: as needed for P0 bugs, security fixes, and regressions. No pre-scheduled date.
84
+ - **Major releases (v3.0.0+)**: not yet planned. The API is still evolving. A major version bump will come with a public deprecation notice and migration guide.
85
+
86
+ ## Commitment
87
+
88
+ This roadmap is a best-effort forecast, not a contract. Priorities shift based on user feedback, security findings, and the maintainer's availability (solo maintainer, GOV-006 declared).
89
+
90
+ Issues tagged with a target release are actively planned. Issues without a release tag are candidates for the next roadmap refresh.
91
+
92
+ ---
93
+
94
+ [Back to README](README.md)
@@ -374,6 +374,8 @@ synthesis:
374
374
  # Env overrides:
375
375
  # ZETTELFORGE_PII_ENABLED=true
376
376
  # ZETTELFORGE_PII_ACTION=redact
377
+ # ZETTELFORGE_LIMITS_MAX_CONTENT_LENGTH=104857600
378
+ # ZETTELFORGE_LIMITS_RECALL_TIMEOUT=60
377
379
  #
378
380
  governance:
379
381
  enabled: true
@@ -385,9 +387,32 @@ governance:
385
387
  entities: []
386
388
  language: en
387
389
  nlp_model: en_core_web_sm
390
+ limits:
391
+ max_content_length: 52428800 # 50 MB, 0 = unlimited
392
+ recall_timeout_seconds: 30.0 # seconds, 0 = unlimited
388
393
 
389
394
 
390
- # ── Cache ───────────────────────────────────────────────────────────────────
395
+ # ── LanceDB Maintenance (RFC-009 Phase 1.5) ─────────────────────────────────
396
+ # Background daemon that prunes stale LanceDB version-history per shard.
397
+ #
398
+ # Examples:
399
+ # # Default — clean up every hour, keep versions < 1 hour old
400
+ # cleanup_interval_minutes: 60
401
+ # cleanup_older_than_seconds: 3600
402
+ #
403
+ # # Aggressive cleanup (high-ingestion environments)
404
+ # cleanup_interval_minutes: 15
405
+ # cleanup_older_than_seconds: 600
406
+ #
407
+ # # Disable cleanup entirely
408
+ # cleanup_interval_minutes: 0
409
+ #
410
+ lance:
411
+ cleanup_interval_minutes: 60
412
+ cleanup_older_than_seconds: 3600
413
+
414
+
415
+ # ── Cache
391
416
  # In-memory cache for TypeDB query results. Reduces round-trips for
392
417
  # frequently accessed entities and relationships.
393
418
  #
@@ -434,3 +459,32 @@ logging:
434
459
  level: INFO
435
460
  log_intents: true
436
461
  log_causal: true
462
+
463
+
464
+ # ── Web UI (RFC-015) ─────────────────────────────────────────────────────────
465
+ # ZettelForge Web Management Interface — SPA served at GET /.
466
+ #
467
+ # Examples:
468
+ # # Default — enabled on 0.0.0.0:8088
469
+ # enabled: true
470
+ # host: 0.0.0.0
471
+ # port: 8088
472
+ #
473
+ # # Custom port (e.g., behind nginx reverse proxy)
474
+ # enabled: true
475
+ # host: 127.0.0.1
476
+ # port: 9000
477
+ #
478
+ # # Disable web UI entirely (library-only mode)
479
+ # enabled: false
480
+ #
481
+ # Env overrides:
482
+ # ZETTELFORGE_WEB_ENABLED=true
483
+ # ZETTELFORGE_WEB_PORT=8088
484
+ # ZETTELFORGE_WEB_UI_DIR=/path/to/ui
485
+ #
486
+ web:
487
+ enabled: true
488
+ host: 0.0.0.0
489
+ port: 8088
490
+
@@ -137,9 +137,9 @@ TB-1 ─────────────────────────
137
137
 
138
138
  | ID | Threat | Component | Risk | Mitigation |
139
139
  |----|--------|-----------|------|------------|
140
- | D-01 | Large content in `remember()` exhausts memory or blocks the enrichment queue | MemoryManager (P1) | **Medium** — degraded performance | `remember_report()` chunks long documents. No explicit size limit on `remember()` content. Enrichment queue has `maxsize=500` backpressure. |
140
+ | D-01 | Large content in `remember()` exhausts memory or blocks the enrichment queue | MemoryManager (P1) | **Low** — gracefully rejected | `governance.limits.max_content_length` (RFC-014, default 50 MB) blocks oversized content with a clear error. `remember_report()` chunks long documents. Enrichment queue has `maxsize=500` backpressure. |
141
141
  | D-02 | LLM provider (ollama, litellm) hangs and blocks `remember()` | LLM Provider (TB-4) | **High** — operation blocks | OllamaProvider has timeout (RFC-010, default 60s). LitellmProvider has timeout + num_retries. `generate()` returns empty string on recoverable failure. Fallback provider (e.g., local -> ollama) gives alternative path. |
142
- | D-03 | Malicious query triggers deep graph traversal exhausting time/resources | BlendedRetriever | **Medium** — slow recall | `max_graph_depth` config (default 2) limits BFS hops. `default_k` (default 10) limits results. No timeout on recall queries. |
142
+ | D-03 | Malicious query triggers deep graph traversal exhausting time/resources | BlendedRetriever | **Medium** — bounded, but timeout may still block | `governance.limits.recall_timeout_seconds` (RFC-014, default 30s) applies a wall-clock timeout to the recall pipeline, but the current `ThreadPoolExecutor`-based approach must not be treated as guaranteeing prompt return on timeout. `max_graph_depth` (default 2) limits BFS hops. `default_k` (default 10) limits results. Reclassify to **Low** only after the timeout path is verified to return promptly and log `recall_timed_out` without waiting for the running task to finish. |
143
143
  | D-04 | spaCy model download blocks first `remember()` when PII is enabled | PIIValidator (lazy load) | **Low** — delayed first call (~2-3 seconds) | One-time download cost. Matching fastembed pattern. Can be pre-downloaded for air-gapped deployments. |
144
144
 
145
145
  ### 2.6 Elevation of Privilege
@@ -158,8 +158,8 @@ TB-1 ─────────────────────────
158
158
  |------------|-------|--------------|
159
159
  | **Critical** | 2 | T-01 (storage tampering), I-01 (unencrypted data at rest), E-02 (governance bypass via filesystem) |
160
160
  | **High** | 7 | S-01 (spoofed MCP client), S-03 (config tampering), T-02 (config security downgrade), R-01 (repudiation without audit), I-02 (PII in stored notes), D-02 (LLM provider hang), E-01 (cross-tenant data access) |
161
- | **Medium** | 9 | S-02 (fake LLM provider), T-04 (retrieval poisoning), R-02, R-03, I-04 (error message leakage), D-01, D-03, E-03 |
162
- | **Low** | 1 | D-04 (PII model download delay) |
161
+ | **Medium** | 7 | S-02 (fake LLM provider), T-04 (retrieval poisoning), R-02, R-03, I-04 (error message leakage), E-03 |
162
+ | **Low** | 3 | D-01, D-03, D-04 (PII model download delay) |
163
163
 
164
164
  ### Top 5 Mitigations (Priority Order)
165
165
 
@@ -181,6 +181,8 @@ TB-1 ─────────────────────────
181
181
  | API key redaction | I-03 | `LLMConfig.__repr__` redacts api_key and sensitive extra keys | Unit tests in `test_llm_providers.py` |
182
182
  | PII detection + redaction | I-02 | PIIValidator (RFC-013): log/redact/block | Unit tests in `test_pii_validator.py` |
183
183
  | LLM provider timeout | D-02 | `OllamaProvider` timeout=60s, `LiteLLMProvider` timeout + num_retries | Unit tests (RFC-010, RFC-012) |
184
+ | Content size limit | D-01 | `governance.limits.max_content_length` (RFC-014, default 50 MB) blocks oversized content | Unit tests in `test_governance.py` |
185
+ | Recall timeout | D-03 | `governance.limits.recall_timeout_seconds` (RFC-014, default 30s) wraps recall in ThreadPoolExecutor with wall-clock timeout | Unit tests in `test_governance.py` |
184
186
  | Config env-var resolution | I-03 | `${ENV_VAR}` syntax prevents raw secrets in YAML | Unit tests |
185
187
  | Configurable model provider | S-02, E-03 | `provider` key selects backend; no implicit unauthenticated outbound calls | Config validation |
186
188
  | Enrichment queue backpressure | D-01 | `maxsize=500` bounded queue | Code review |
@@ -189,7 +191,6 @@ TB-1 ─────────────────────────
189
191
 
190
192
  | Recommendation | Threat(s) | Effort | Priority |
191
193
  |---------------|-----------|--------|----------|
192
- | Add content size limit to `remember()` | D-01 | Small | P3 |
193
194
  | Add global exception handler that sanitizes error output | I-04 | Medium | P2 |
194
195
  | Add TLS verification option for self-hosted LLM endpoints | S-02 | Small | P2 |
195
196
  | Add config file integrity check (SHA-256 of default vs. loaded) | T-02, S-03 | Medium | P3 |
@@ -231,6 +232,7 @@ Per GOV-021, the following data types exist in the system:
231
232
 
232
233
  | Change | RFC/PR | Date | Threat Model Impact |
233
234
  |--------|--------|------|---------------------|
235
+ | Content size limits + recall timeout | RFC-014 | 2026-04-25 | Mitigation for D-01 (content size limit, default 50 MB); partial mitigation for D-03 (timeout) |
234
236
  | PII detection and redaction | RFC-013 (PR #118) | 2026-04-25 | New control for I-02; new attack surface (D-04); PII text logging fixed |
235
237
  | LiteLLM unified provider | RFC-012 (PR #108) | 2026-04-25 | New provider for I-03 (API keys); new outbound traffic pattern (TB-4) |
236
238
  | Local LLM backend selection | RFC-011 (PR #104) | 2026-04-25 | No new threat surface — extends existing local provider |
@@ -0,0 +1,101 @@
1
+ ---
2
+ title: "LLM budgets, timeouts, and what they cost you"
3
+ description: "How ZettelForge's LLM token budgets and HTTP timeouts trade latency for end-to-end correctness — what each knob controls, what it costs, and when to override."
4
+ diataxis_type: explanation
5
+ audience: "Operator / Developer"
6
+ tags: [llm, configuration, performance, reasoning-models, ollama]
7
+ last_updated: "2026-04-25"
8
+ version: "2.6.0"
9
+ ---
10
+
11
+ # LLM budgets, timeouts, and what they cost you
12
+
13
+ ZettelForge makes five distinct kinds of LLM calls (causal triple extraction, synthesis, fact extraction, conversational NER, neighbor evolution). Each has a hardcoded `max_tokens` budget and shares a single configurable `llm.timeout`. The defaults trade ingest latency for end-to-end correctness on a reference reasoning model (`qwen3.5:9b`, Q4_K_M). This page explains why the defaults look the way they do and when you should override them.
14
+
15
+ If you just want the table of values, see the [Configuration Reference §Per-call-site `max_tokens` budgets](../reference/configuration.md#per-call-site-max_tokens-budgets-hardcoded-v252). This page is the *why*.
16
+
17
+ ## The hidden-thinking-token problem
18
+
19
+ Modern reasoning models — qwen3.5+, qwen3.6, nemotron-3, deepseek-r1, gemini-thinking — generate two streams of tokens for any prompt:
20
+
21
+ 1. **Reasoning tokens.** Wrapped in `<think>...</think>`, these are the model's internal scratch work. Ollama hides them from the `response` field by default but **they still count against `num_predict`**.
22
+ 2. **Answer tokens.** What the model actually emits as the final user-visible output. These appear in `response`.
23
+
24
+ If `num_predict` is 300 tokens and the model uses 280 of them reasoning, you get 20 tokens of answer — usually not enough for valid JSON. If it uses all 300, you get an empty string and Ollama returns `done_reason: "length" eval_count: 300 response: ""`. The pre-2.5.2 budgets (300/400/800/1024) were sized for non-reasoning models and silently failed every call on the reasoning model that ZettelForge defaults to. v2.5.2 raised the per-call-site caps to give reasoning room *and* answer room on the same generation.
25
+
26
+ ## Per-call-site budgets — and why each one is what it is
27
+
28
+ ### Causal triple extraction (`note_constructor.py`, **8000 tokens**)
29
+
30
+ The largest budget anywhere in the codebase. The prompt asks the model to enumerate *every* causal relation in a passage of up to 2000 characters, validating each relation against an allowlist. Empirical: `qwen3.5:9b` at 4000 tokens succeeded only ~70% of the time (eval_count varied 2.8k–4k+, with the longer reasoning chains hitting the budget cap). 8000 keeps the success rate above 95% on the same model. Wall-clock cost: 60–140 s per call.
31
+
32
+ ### Synthesis (`synthesis_generator.py`, **2500 tokens**)
33
+
34
+ Single-answer prompts converge faster than enumerate-everything prompts. 2500 covers reasoning + a paragraph of JSON answer. Wall-clock: 20–50 s per query.
35
+
36
+ ### Fact extraction (`fact_extractor.py`, **2500 tokens**)
37
+
38
+ Similar profile to synthesis — bounded JSON output. The pre-2.5.2 cap was 400, which left this silently no-opping on every reasoning-model call.
39
+
40
+ ### Conversational NER (`entity_indexer.py`, **2500 tokens**)
41
+
42
+ The regex fast-path covers CTI types (CVE, ATT&CK, IOCs); LLM NER fills in `person`, `location`, `organization`, `event`, `activity`, `temporal`. Output is a small JSON object so 2500 is generous. The retry path uses the same budget.
43
+
44
+ ### Neighbor evolution (`memory_evolver.py`, **2500 tokens** × 2)
45
+
46
+ Two-note comparison + ADD/UPDATE/DELETE/NOOP decision. Both the first call and the parse-retry call use 2500. Parse-retry exists because reasoning models occasionally emit prose preamble before the JSON; the second call reasserts JSON-only and usually gets it.
47
+
48
+ ## The shared timeout
49
+
50
+ `llm.timeout` (default **180 s** in v2.5.2; was 60 s pre-fix) governs the HTTP read deadline on every Ollama call.
51
+
52
+ The 60 s default fired before causal extraction at 8000 tokens could complete on a 9B model. The fix had to come at both ends: bigger budget *and* longer timeout. If you lower one, lower the other together — bumping the budget without bumping the timeout just trades empty-response failures for ReadTimeout failures.
53
+
54
+ ## When to override
55
+
56
+ The defaults are calibrated for `qwen3.5:9b` Q4_K_M on a single GPU. You want to override if:
57
+
58
+ ### You're on faster hardware (H100, multi-GPU, large batch)
59
+
60
+ You can lower `llm.timeout` to e.g. 60 s without losing correctness, since each generation completes faster. The budgets themselves are about *how much room the model needs to think*, not how fast it thinks — leave them alone unless you've measured.
61
+
62
+ ### You're on a non-reasoning model (gemma4, llama-3.x base, qwen2.5)
63
+
64
+ These don't emit ` thinking` tokens. Your budgets only need to cover the actual answer length, not reasoning + answer. You can set `max_tokens` values to ~25% of the v2.5.2 defaults in config (v2.6.0 moved them to `LLMConfig` — see [issue #125](https://github.com/rolandpg/zettelforge/issues/125)). `llm.timeout: 60` is then enough.
65
+
66
+ ### You're on a much larger model (70B, 120B, cloud)
67
+
68
+ Reasoning depth often *increases* with model size. The 8000-token causal cap may not be enough on a 120B reasoning model. Watch the OCSF log for `event=llm_call_empty_response done_reason=length eval_count=8000` — if you see it, raise the `max_tokens=8000` literal inside `NoteConstructor.extract_causal_triples` (in `src/zettelforge/note_constructor.py`) and re-test.
69
+
70
+ ### You're triggering `sync=True` or doing bulk ingestion
71
+
72
+ The default async path moves causal extraction off the write hot path; `remember()` returns in ~50 ms while extraction happens later in the enrichment worker. **`sync=True` blocks the caller until the worker finishes.** With v2.5.2 budgets on a 9B reasoning model that's 1–3 minutes per note. For bulk ingestion (1000+ notes), prefer async and let the queue drain at its own pace; for sync use cases (test fixtures, small one-shots), accept the latency or downgrade to a non-reasoning model.
73
+
74
+ ## Verifying your budgets are right
75
+
76
+ The OCSF log at `~/.amem/logs/zettelforge.log` carries every LLM call as a structured event. Two events to know:
77
+
78
+ - **`llm_call_empty_response`** — `WARNING` level, fires whenever an Ollama call returns an empty `response`. Always visible at the default `INFO` log level.
79
+ - **`llm_call_complete`** — `DEBUG` level, fires on every successful call with `eval_count`, `response_chars`, `max_tokens`, `duration_ms`, etc. Only visible when `logging.level: DEBUG` is set in `config.yaml` (or via `ZETTELFORGE_LOG_LEVEL=DEBUG`).
80
+
81
+ To spot too-small budgets at the default log level:
82
+
83
+ ```bash
84
+ grep '"event":"llm_call_empty_response"' ~/.amem/logs/zettelforge.log \
85
+ | jq -r '"\(.model) eval=\(.eval_count) of max=\(.max_tokens) dur_ms=\(.duration_ms)"' \
86
+ | tail -20
87
+ ```
88
+
89
+ `eval == max_tokens` with `done_reason: length` is the canonical token-starvation signature — raise the budget for that call site.
90
+
91
+ To verify budgets aren't *too* generous (free wall-clock to claw back), enable DEBUG logging and grep `llm_call_complete` instead. `eval_count << max_tokens` with non-empty `response_chars` means you could safely lower the cap on faster hardware.
92
+
93
+ ## Background
94
+
95
+ - v2.5.2 hotfix CHANGELOG entry — full root-cause writeup and per-file diffs.
96
+ - [Issue #125](https://github.com/rolandpg/zettelforge/issues/125) — v2.6.0 plan to make these budgets config-overridable per call site, add `<think>`-tag stripping as a post-processing guard, and a `reasoning_model: bool` auto-scale flag.
97
+
98
+ ## Related
99
+
100
+ - [Configuration Reference](../reference/configuration.md)
101
+ - [Troubleshoot ZettelForge](../how-to/troubleshoot.md)