threadkeeper 0.18.4__tar.gz → 0.19.1__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 (291) hide show
  1. {threadkeeper-0.18.4/threadkeeper.egg-info → threadkeeper-0.19.1}/PKG-INFO +209 -62
  2. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/README.md +208 -61
  3. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/pyproject.toml +1 -1
  4. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_agent_status.py +36 -17
  5. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_config_settings.py +3 -0
  6. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_curator.py +187 -5
  7. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_daemon_liveness.py +1 -1
  8. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_dialectic_tier.py +3 -9
  9. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_eval_harness.py +15 -1
  10. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_evolve_applier.py +388 -8
  11. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_evolve_daemon.py +163 -0
  12. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_identity.py +2 -0
  13. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_ingest_denylist.py +1 -1
  14. threadkeeper-0.19.1/tests/test_mcp_protocol_conformance.py +235 -0
  15. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_mcp_resources_prompts.py +160 -0
  16. threadkeeper-0.19.1/tests/test_memory_authority_lifecycle.py +87 -0
  17. threadkeeper-0.19.1/tests/test_memory_compatibility.py +118 -0
  18. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_memory_eval.py +84 -4
  19. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_nudges.py +4 -7
  20. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_onnx_embeddings.py +4 -3
  21. threadkeeper-0.19.1/tests/test_project_scope.py +113 -0
  22. threadkeeper-0.19.1/tests/test_session_read_purity.py +104 -0
  23. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_shadow_review.py +6 -1
  24. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_skill_hint.py +4 -5
  25. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_spawn_hint.py +9 -16
  26. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_tool_annotations.py +53 -1
  27. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_tools_smoke.py +1 -0
  28. threadkeeper-0.19.1/tests/test_tracing.py +237 -0
  29. threadkeeper-0.19.1/tests/test_workflow_retention.py +255 -0
  30. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/_mcp.py +84 -3
  31. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/_spawn_wrap.py +9 -3
  32. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/adapters/base.py +1 -0
  33. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/agent_status.py +37 -20
  34. threadkeeper-0.19.1/threadkeeper/authority.py +322 -0
  35. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/brief.py +50 -41
  36. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/candidate_reviewer.py +9 -6
  37. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/config.py +38 -6
  38. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/curator.py +214 -76
  39. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/curator_snapshots.py +44 -3
  40. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/db.py +355 -16
  41. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/dialectic_miner.py +10 -5
  42. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/dialectic_validator.py +10 -7
  43. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/elicitation.py +3 -0
  44. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/embeddings.py +147 -33
  45. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/eval/harness.py +95 -10
  46. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/evolve_applier.py +426 -53
  47. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/evolve_daemon.py +60 -27
  48. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/forget.py +25 -0
  49. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/identity.py +13 -3
  50. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/ingest.py +29 -4
  51. threadkeeper-0.19.1/threadkeeper/memory_compat.py +356 -0
  52. threadkeeper-0.19.1/threadkeeper/migrate_embeddings.py +244 -0
  53. threadkeeper-0.19.1/threadkeeper/project_scope.py +204 -0
  54. threadkeeper-0.19.1/threadkeeper/protocol.py +186 -0
  55. threadkeeper-0.19.1/threadkeeper/read_context.py +24 -0
  56. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/retention.py +5 -1
  57. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/retrieval.py +34 -8
  58. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/server.py +2 -0
  59. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/shadow_review.py +4 -1
  60. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/spawn_budget.py +1 -0
  61. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/sync/identity.py +6 -0
  62. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tool_schemas.py +1 -0
  63. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/agent_status.py +15 -2
  64. threadkeeper-0.19.1/threadkeeper/tools/authority.py +21 -0
  65. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/concepts.py +25 -10
  66. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/core_memory.py +43 -16
  67. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/curator.py +7 -4
  68. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/dashboard.py +11 -1
  69. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/db_maintenance.py +19 -1
  70. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/dialectic.py +118 -20
  71. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/dialog.py +24 -7
  72. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/distill.py +26 -10
  73. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/evolve_applier.py +33 -20
  74. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/extract.py +49 -25
  75. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/lessons.py +46 -1
  76. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/pickup.py +10 -4
  77. threadkeeper-0.19.1/threadkeeper/tools/project_scope.py +33 -0
  78. threadkeeper-0.19.1/threadkeeper/tools/resources.py +460 -0
  79. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/session.py +22 -3
  80. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/shadow_review.py +28 -11
  81. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/skills.py +65 -11
  82. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/spawn.py +64 -9
  83. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/style.py +30 -4
  84. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/sync.py +2 -2
  85. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/threads.py +88 -25
  86. threadkeeper-0.19.1/threadkeeper/tracing.py +572 -0
  87. threadkeeper-0.19.1/threadkeeper/workflow_artifacts.py +568 -0
  88. {threadkeeper-0.18.4 → threadkeeper-0.19.1/threadkeeper.egg-info}/PKG-INFO +209 -62
  89. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper.egg-info/SOURCES.txt +15 -0
  90. threadkeeper-0.18.4/tests/test_session_read_purity.py +0 -51
  91. threadkeeper-0.18.4/threadkeeper/migrate_embeddings.py +0 -162
  92. threadkeeper-0.18.4/threadkeeper/tools/resources.py +0 -100
  93. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/LICENSE +0 -0
  94. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/setup.cfg +0 -0
  95. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_adapters.py +0 -0
  96. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_antigravity_ingest.py +0 -0
  97. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_auto_update.py +0 -0
  98. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_backup.py +0 -0
  99. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_brief_footprint.py +0 -0
  100. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_brief_sections.py +0 -0
  101. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_candidate_reviewer.py +0 -0
  102. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_ci_parallel.py +0 -0
  103. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_concepts.py +0 -0
  104. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_config_watcher.py +0 -0
  105. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_core_memory.py +0 -0
  106. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_daemon_host_config.py +0 -0
  107. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_daemon_host_integration.py +0 -0
  108. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_daemon_state.py +0 -0
  109. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_dashboard.py +0 -0
  110. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_db_compact.py +0 -0
  111. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_db_contention.py +0 -0
  112. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_db_leak_guard.py +0 -0
  113. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_db_migration_fts.py +0 -0
  114. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_db_runtime.py +0 -0
  115. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_db_schema.py +0 -0
  116. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_delegated_search.py +0 -0
  117. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_dialectic.py +0 -0
  118. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_dialectic_feed_tools.py +0 -0
  119. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_dialectic_miner.py +0 -0
  120. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_dialectic_observation_resolve.py +0 -0
  121. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_dialectic_recompute.py +0 -0
  122. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_dialectic_validator.py +0 -0
  123. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_dialog_fts_external.py +0 -0
  124. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_documentation_inventory.py +0 -0
  125. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_egress_policy.py +0 -0
  126. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_embed_routing.py +0 -0
  127. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_embedding_generation.py +0 -0
  128. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_embedding_model_revision.py +0 -0
  129. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_embedding_storage_dedup.py +0 -0
  130. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_error_paths.py +0 -0
  131. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_evolve_apply_2.py +0 -0
  132. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_evolve_apply_3.py +0 -0
  133. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_evolve_apply_43.py +0 -0
  134. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_evolve_apply_45.py +0 -0
  135. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_extract_daemon.py +0 -0
  136. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_extract_dedup.py +0 -0
  137. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_forget.py +0 -0
  138. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_github_budget.py +0 -0
  139. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_github_safety.py +0 -0
  140. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_helpers.py +0 -0
  141. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_hook_child_guard.py +0 -0
  142. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_host_embed.py +0 -0
  143. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_host_env.py +0 -0
  144. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_host_main.py +0 -0
  145. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_host_wedged.py +0 -0
  146. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_hybrid_retrieval.py +0 -0
  147. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_i18n_multilang.py +0 -0
  148. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_ingest_cursor.py +0 -0
  149. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_ingest_secret_redaction.py +0 -0
  150. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_ingest_status.py +0 -0
  151. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_ingest_transactions.py +0 -0
  152. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_lesson_violations.py +0 -0
  153. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_lessons.py +0 -0
  154. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_mcp_sdk_compat.py +0 -0
  155. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_mcp_skills_extension.py +0 -0
  156. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_memory_guard.py +0 -0
  157. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_memory_guard_host.py +0 -0
  158. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_menubar_app.py +0 -0
  159. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_missed_spawns.py +0 -0
  160. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_model_catalog.py +0 -0
  161. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_notify_daemon.py +0 -0
  162. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_panel.py +0 -0
  163. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_permissions.py +0 -0
  164. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_pickup.py +0 -0
  165. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_probe_daemon.py +0 -0
  166. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_process_health.py +0 -0
  167. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_release_workflows.py +0 -0
  168. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_retention.py +0 -0
  169. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_review_prompts.py +0 -0
  170. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_search_fts_punctuation.py +0 -0
  171. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_setup.py +0 -0
  172. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_single_flight_prompt_prefixes.py +0 -0
  173. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_skill_passive_tier.py +0 -0
  174. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_skill_tier.py +0 -0
  175. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_skill_updater.py +0 -0
  176. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_skill_use_parser.py +0 -0
  177. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_skill_watcher.py +0 -0
  178. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_skills.py +0 -0
  179. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_spawn_background_cwd.py +0 -0
  180. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_spawn_budget.py +0 -0
  181. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_spawn_child_env.py +0 -0
  182. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_spawn_codex_stdin.py +0 -0
  183. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_spawn_config.py +0 -0
  184. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_spawn_permission_gate.py +0 -0
  185. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_spawn_reap.py +0 -0
  186. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_spawn_result.py +0 -0
  187. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_spawn_slim.py +0 -0
  188. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_spawn_watchdog.py +0 -0
  189. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_spawn_worktree_isolation.py +0 -0
  190. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_spawn_wrap.py +0 -0
  191. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_sync_capture.py +0 -0
  192. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_sync_identity.py +0 -0
  193. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_sync_migrate.py +0 -0
  194. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_sync_protocol.py +0 -0
  195. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_sync_reset_node.py +0 -0
  196. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_sync_transport.py +0 -0
  197. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_task_retention.py +0 -0
  198. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_task_spool.py +0 -0
  199. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_thin_session.py +0 -0
  200. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_thread_janitor.py +0 -0
  201. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_threads.py +0 -0
  202. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_validate_threads.py +0 -0
  203. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_vec_search.py +0 -0
  204. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/tests/test_verify_ingest.py +0 -0
  205. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/__init__.py +0 -0
  206. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/_setup.py +0 -0
  207. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/adapters/__init__.py +0 -0
  208. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/adapters/_hook_helpers.py +0 -0
  209. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/adapters/antigravity.py +0 -0
  210. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/adapters/claude_code.py +0 -0
  211. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/adapters/claude_desktop.py +0 -0
  212. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/adapters/codex.py +0 -0
  213. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/adapters/copilot.py +0 -0
  214. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/adapters/vscode.py +0 -0
  215. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/agent_metadata.py +0 -0
  216. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/assets/macos-agent-status/Info.plist +0 -0
  217. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/assets/macos-agent-status/README.md +0 -0
  218. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/assets/macos-agent-status/ThreadKeeperAgentStatus.swift +0 -0
  219. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/assets/macos-agent-status/build.sh +0 -0
  220. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/assets/macos-agent-status/install.sh +0 -0
  221. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/auto_update.py +0 -0
  222. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/backup.py +0 -0
  223. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/config_io.py +0 -0
  224. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/config_watcher.py +0 -0
  225. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/daemon_liveness.py +0 -0
  226. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/daemon_state.py +0 -0
  227. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/daemon_supervisor.py +0 -0
  228. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/egress.py +0 -0
  229. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/eval/__init__.py +0 -0
  230. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/eval/__main__.py +0 -0
  231. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/extract_daemon.py +0 -0
  232. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/github_budget.py +0 -0
  233. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/github_safety.py +0 -0
  234. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/harvest.py +0 -0
  235. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/helpers.py +0 -0
  236. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/host.py +0 -0
  237. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/host_embed.py +0 -0
  238. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/i18n.py +0 -0
  239. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/lesson_violations.py +0 -0
  240. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/lessons.py +0 -0
  241. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/link_health.py +0 -0
  242. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/mcp_skills.py +0 -0
  243. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/memory_guard.py +0 -0
  244. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/menubar_app.py +0 -0
  245. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/model_catalog.py +0 -0
  246. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/notify.py +0 -0
  247. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/nudges.py +0 -0
  248. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/permissions.py +0 -0
  249. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/probe_daemon.py +0 -0
  250. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/process_health.py +0 -0
  251. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/review_prompts.py +0 -0
  252. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/search_proxy.py +0 -0
  253. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/skill_audit.py +0 -0
  254. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/skill_updater.py +0 -0
  255. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/skill_watcher.py +0 -0
  256. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/spawn_config.py +0 -0
  257. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/spawn_result.py +0 -0
  258. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/sync/__init__.py +0 -0
  259. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/sync/capture.py +0 -0
  260. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/sync/daemon.py +0 -0
  261. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/sync/migrate.py +0 -0
  262. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/sync/protocol.py +0 -0
  263. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/sync/reset_node.py +0 -0
  264. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/sync/server.py +0 -0
  265. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/task_spool.py +0 -0
  266. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/thread_janitor.py +0 -0
  267. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/__init__.py +0 -0
  268. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/candidate_reviewer.py +0 -0
  269. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/config_watch.py +0 -0
  270. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/consolidate.py +0 -0
  271. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/correlation.py +0 -0
  272. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/dialectic_feed.py +0 -0
  273. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/evolve_research.py +0 -0
  274. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/forget.py +0 -0
  275. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/graph.py +0 -0
  276. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/invariants.py +0 -0
  277. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/memory_guard.py +0 -0
  278. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/missed_spawns.py +0 -0
  279. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/panel.py +0 -0
  280. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/peers.py +0 -0
  281. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/probes.py +0 -0
  282. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/process_health.py +0 -0
  283. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/prompts.py +0 -0
  284. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/tools/validate.py +0 -0
  285. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/trash.py +0 -0
  286. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/verify_ingest.py +0 -0
  287. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper/wikilinks.py +0 -0
  288. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper.egg-info/dependency_links.txt +0 -0
  289. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper.egg-info/entry_points.txt +0 -0
  290. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper.egg-info/requires.txt +0 -0
  291. {threadkeeper-0.18.4 → threadkeeper-0.19.1}/threadkeeper.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: threadkeeper
3
- Version: 0.18.4
3
+ Version: 0.19.1
4
4
  Summary: Multi-agent shared brain across Claude Code/Desktop, Codex, Antigravity CLI, Copilot, and VS Code. Cross-session memory, self-improving skill loops, inter-agent signaling — one local MCP server.
5
5
  Author: thread-keeper contributors
6
6
  License: MIT
@@ -232,7 +232,7 @@ read/act split, plus MCP elicitation for host-native confirmations:
232
232
  | Primitive | Control | What thread-keeper exposes | When to use |
233
233
  |---|---|---|---|
234
234
  | **Tools** | model-controlled (may act) | the full surface — `brief`, `note`, `spawn`, `search`, `curator_review`, `wikilink_health`, … | the agent decides to call them |
235
- | **Resources** | application-controlled, read-only | `memory://brief`, `memory://context`, `memory://dashboard`, `memory://agent-status` | the **host** attaches/pulls them automatically |
235
+ | **Resources** | application-controlled, read-only | `memory://brief`, `memory://context`, `memory://dashboard`, `memory://agent-status` | the **host** attaches/pulls them automatically; capable hosts may subscribe |
236
236
  | **Prompts** | user-controlled templates | `review_recent_threads`, `run_library_curation`, `audit_threadkeeper` | the user runs them (Claude Code: `/mcp__thread-keeper__<name>`) |
237
237
 
238
238
  **Resources** back the genuinely read-only memory views with the same render
@@ -322,6 +322,28 @@ override wins over `.env`):
322
322
  THREADKEEPER_MEMORY_EGRESS=same-vendor
323
323
  ```
324
324
 
325
+ ### Project memory boundaries
326
+
327
+ Memory is also isolated by repository. thread-keeper derives a path-free
328
+ `prj_…` namespace from the repository remote (or, when no remote exists, its
329
+ Git history), so ordinary moves and Git worktrees retain one identity without
330
+ storing a local path. New project memory is scoped to that namespace; portable
331
+ memory must be explicitly written as `global` or `shared`.
332
+
333
+ `brief()`, `search()`, `dialog_search()`, resources, and learning-loop inputs
334
+ read the current project plus explicit global/shared rows. Set
335
+ `cross_project=True` on `search` or `dialog_search` only for deliberate
336
+ cross-project recall. Those results show `scope=`/`project=` provenance and
337
+ write a `cross_project_recall` audit event. The `memory_scope_status()` tool
338
+ shows the current namespace and the migration accounting.
339
+
340
+ Existing databases are migrated conservatively: rows without reliable
341
+ repository provenance are marked `unresolved`, excluded from automatic recall,
342
+ and listed in the migration report until an operator classifies them. This
343
+ boundary controls *which local memory is recalled*; it is separate from
344
+ `THREADKEEPER_MEMORY_EGRESS`, which controls whether personal memory may be
345
+ sent to another model provider.
346
+
325
347
  ---
326
348
 
327
349
  ## Core systems
@@ -764,15 +786,20 @@ for semantic review. System and installed-plugin sources are resolved from
764
786
  their read-only caches rather than misreported as missing mirrors; telemetry
765
787
  rows with no real `SKILL.md` remain explicit orphans. The same inventory also
766
788
  flags a dense lesson subtopic when at least
767
- `THREADKEEPER_CURATOR_PROMOTION_MIN_LESSONS` lessons (default 3) share a pair
768
- of meaningful title terms. A non-protected candidate must become one validated,
769
- checklist-style canonical skill before its source lessons are retired; protected
770
- clusters are left for human review. A read-only research child reads every
771
- complete skill and relevant support file, performs current web research against
772
- official docs and comparable public skills, then writes a bounded
773
- `RESEARCH-<pass>-batch-NNN-of-MMM.json` handoff through a destination-scoped
774
- tool. A separate web-free evaluator receives that handoff as fenced, untrusted
775
- data and writes numbered per-skill verdicts to
789
+ `THREADKEEPER_CURATOR_PROMOTION_MIN_LESSONS` lessons (default 3) share two
790
+ concrete title terms and a non-generic body mechanism. Generic joins and
791
+ domain-wide/high-document-frequency title words cannot form a candidate. The
792
+ inventory and `curator_review_status()` report emitted/rejected counts and
793
+ rejection reasons, so the rule can be tuned from production data. A
794
+ non-protected candidate must become one validated, checklist-style canonical
795
+ skill before its source lessons are retired; protected clusters are left for
796
+ human review. The evaluator reads every complete skill and relevant support
797
+ file, but has no web tools. External research is off by default. With explicit
798
+ `THREADKEEPER_CURATOR_WEB_RESEARCH=1`, a separate Claude-only researcher gets
799
+ only fixed public compatibility queries, `WebSearch`, `WebFetch`, and one
800
+ parent-authorized handoff writer; it cannot read local memory or the
801
+ repository. The evaluator receives that handoff as fenced, untrusted data and
802
+ writes numbered per-skill verdicts to
776
803
  `~/.threadkeeper/curator/REPORT-<isodate>.md` for a one-batch pass or
777
804
  `REPORT-<isodate>-batch-NNN-of-MMM.md` for a multi-batch pass: KEEP / REPAIR /
778
805
  UPDATE / MERGE / SPLIT / DEPRECATE / DELETE / CROSS_LINK / HUMAN_REVIEW.
@@ -874,7 +901,11 @@ a recoverable snapshot under
874
901
  `~/.threadkeeper/curator/snapshots/<pass-id>/`). The snapshot contains
875
902
  `lessons.md`, copied in-scope skill dirs, a `manifest.json`, and per-action
876
903
  tombstones for curator prunes/deletes. Retention is bounded by
877
- `THREADKEEPER_CURATOR_SNAPSHOT_RETENTION` (default 10, current pass always kept).
904
+ `THREADKEEPER_CURATOR_SNAPSHOT_RETENTION` (default 10) for legacy/direct
905
+ snapshots. Daemon-owned snapshots instead follow the coordinated workflow
906
+ retention policy, so their pass, report, manifest, and recovery archive cannot
907
+ age out on different schedules; active work and an endorsed report that has not
908
+ been applied always keep recovery material.
878
909
  Use `curator_restore(pass_id, lesson_slug="...")` or
879
910
  `curator_restore(pass_id, skill_name="...")` to restore an item from a snapshot.
880
911
  As a prevention layer before recovery is needed, a destructive Curator pass
@@ -1011,15 +1042,27 @@ with that name instead of minting overlapping roadmap PRs.
1011
1042
 
1012
1043
  The Evolve applier is the downstream implementer. `evolve_apply_roadmap_issue()`
1013
1044
  picks one open GitHub issue at a time (`roadmap` label first, then FIFO), but
1014
- the automatic pass first scans already-open same-repo applier PRs for GitHub
1015
- merge conflicts. A conflicted `roadmap/…` or `evolve/…` PR is repaired before
1016
- any new issue/report/evolve work is started; if the PR sweep itself cannot read
1017
- GitHub state, the pass fails closed instead of taking fresh work blind. The
1018
- conflict-repair child checks out the existing PR branch, merges the current
1019
- base branch, resolves conflicts, runs the full suite, and pushes back to the
1020
- same branch. It then waits for GitHub checks on the pushed PR head and runs
1021
- `gh pr merge --squash --delete-branch`, so GitHub lands the repaired PR into
1022
- `main` through branch protection rather than a raw local `git push origin main`.
1045
+ the automatic pass first sweeps the open PRs authored by the account
1046
+ thread-keeper's `gh` acts as — yours: applier, reviewer, and hand-made PRs
1047
+ alike. A PR from any other account (a collaborator, a bot, a fork) is never
1048
+ updated, repaired, or merged. If the PR sweep itself cannot read GitHub state
1049
+ or the `gh` account, the pass fails closed instead of taking fresh work blind.
1050
+ The sweep squash-merges the oldest non-draft PR GitHub reports `CLEAN` (up to
1051
+ date, required checks green) through the protected merge API, pinned to the
1052
+ head SHA it saw green, and then asks GitHub to update the oldest `BEHIND` one —
1053
+ the "Update branch" button — so CI reruns against the new base. Open
1054
+ work in progress as a draft: drafts are never merged. Strict branch
1055
+ protection lets only one PR merge per base commit, so PRs land one at a time,
1056
+ oldest first. Between full passes the daemon repeats only this cheap landing
1057
+ sweep every 15 minutes, so a green PR lands minutes after its checks finish;
1058
+ it skips while any reviewer/applier git writer runs. A conflicted applier PR
1059
+ (`roadmap/…` or `evolve/…` head branch in this repository) is repaired before
1060
+ any new issue/report/evolve work is started: the conflict-repair child checks
1061
+ out the existing PR branch, merges the current base branch, resolves
1062
+ conflicts, runs the full suite, and pushes back to the same branch, then
1063
+ stops; the landing sweep merges it once it is green. A hand-made PR with
1064
+ conflicts, and red or otherwise blocked PRs, wait for a human. Set
1065
+ `THREADKEEPER_EVOLVE_AUTOLAND=0` to leave every merge to a human.
1023
1066
  The roadmap issue child skips issues carrying denylisted human-gate labels,
1024
1067
  skips issues with an active Evolve claim comment, posts its own claim comment
1025
1068
  before spawning, and advances to the next issue when an issue-local dispatch
@@ -1054,9 +1097,13 @@ task’s tree. This prevents unrelated unfinished tests from blocking every PR
1054
1097
  repair. Ignored files (including `.venv`) and explicit operator checkouts stay
1055
1098
  in place; live writers prevent recovery, and backup failures block dispatch.
1056
1099
  Each managed-checkout child fetches the
1057
- configured branch only to retrieve the configured immutable commit, then
1058
- prepares or resumes its deterministic local/remote feature branch from
1059
- `THREADKEEPER_EVOLVE_REPO_COMMIT`, never from the branch's moving tip. Retries
1100
+ configured branch, prepares or resumes its deterministic local/remote feature
1101
+ branch from the freshly fetched `origin/<THREADKEEPER_EVOLVE_REPO_BRANCH>` tip,
1102
+ rebases onto it, and re-syncs the managed venv to that branch's declared
1103
+ dependencies. `THREADKEEPER_EVOLVE_REPO_COMMIT` bounds only what the parent
1104
+ provisions and parks the checkout at; it is never a branch base, because a base
1105
+ that never moves cut every applier PR from one stale commit and each PR was
1106
+ born conflicted with `main`. Retries
1060
1107
  therefore validate prior branch work instead of discovering a branch-name
1061
1108
  collision after changing the base checkout. A shared git-writer running-task
1062
1109
  check prevents the privileged reviewer audit and code/PR applier from
@@ -1291,6 +1338,17 @@ loops: a claim that surfaces in `brief()` and then gets "confirmed" by a
1291
1338
  review-fork reading the same dialog can't ride that internal evidence
1292
1339
  all the way to high confidence — internal evidence buys half as much.
1293
1340
 
1341
+ **Immutable authority and action confirmation.** Confidence and tier describe
1342
+ how useful a claim seems; they do not upgrade the authority of its evidence.
1343
+ New dialog, claim, evidence, lesson, and skill artifacts retain a write-time
1344
+ authority class plus their source principal/channel. Declared references such
1345
+ as `dialog:<uuid>`, `claim:<id>`, `lesson:<slug>`, and `skill:<name>` form a
1346
+ derivation graph; summaries and promotions inherit the least-authoritative
1347
+ root. Unknown writer origins are rejected. Before any consequential action,
1348
+ call `memory_authorize_action`: observed memory needs either an independent
1349
+ trusted principal or explicit `confirmed=True`. `forget` quarantines every
1350
+ derived descendant, so it can no longer be retrieved or authorize an action.
1351
+
1294
1352
  **Discrete tier on each claim** — `hypothesis → observed → validated`
1295
1353
  (plus `disputed`). Independent of the continuous confidence band; tier
1296
1354
  is the **action-gating** signal:
@@ -1338,7 +1396,13 @@ The most-used env knobs (full list in `threadkeeper/config.py`):
1338
1396
  | `THREADKEEPER_PROBE_RESULT_RETENTION_DAYS` | 0 | prune old `probe_results` and refresh reliability aggregates; 0 keeps forever |
1339
1397
  | `THREADKEEPER_RETENTION_WAL_CHECKPOINT` | false | run `PRAGMA wal_checkpoint(TRUNCATE)` during retention passes |
1340
1398
  | `THREADKEEPER_RETENTION_VACUUM_AFTER_ROWS` | 0 | run `VACUUM` after a pass deletes at least this many rows; 0 disables VACUUM |
1399
+ | `THREADKEEPER_WORKFLOW_RETENTION_DAYS` | 30 | age protection for terminal Curator passes and consumed Evolve research handoffs; `0` disables the age bound |
1400
+ | `THREADKEEPER_WORKFLOW_RETENTION_COUNT` | 100 | newest terminal Curator passes and research handoffs protected by count; `0` disables the count bound |
1341
1401
  | `THREADKEEPER_MEMORY_EGRESS` | `all` | cross-provider scope for personal-class memory (verbatim quotes + dialectic user-model) in `brief()`. `all` = current behavior, egress to whichever vendor backs the consuming CLI. `same-vendor` = personal renders only for Claude/Anthropic, omitted for OpenAI/Google/Microsoft CLIs. `work-only` = personal never rendered, any vendor. See [Memory egress](#memory-egress-cross-provider-privacy) |
1402
+ | `THREADKEEPER_OTEL_ENABLED` | false | enable privacy-safe OTLP tracing only when an endpoint is also configured |
1403
+ | `THREADKEEPER_OTEL_ENDPOINT` | "" | OTLP/HTTP collector trace endpoint, normally `https://collector.example/v1/traces` |
1404
+ | `THREADKEEPER_OTEL_EXPORT_TIMEOUT_S` | 2 | collector request timeout; export happens outside the local workflow |
1405
+ | `THREADKEEPER_OTEL_EXPORT_QUEUE_SIZE` | 256 | bounded per-process span queue (1–2048); oldest queued spans are dropped under pressure |
1342
1406
  | `THREADKEEPER_AUTO_REVIEW` | "" (off) | auto-review on `close_thread` |
1343
1407
  | `THREADKEEPER_AUTO_UPDATE_INTERVAL_S` | 86400 | MCP self-update check interval; 0 disables |
1344
1408
  | `THREADKEEPER_AUTO_UPDATE_RESTART` | "1" | exit MCP process after an update passes setup/import smoke checks so the host restarts on new code |
@@ -1372,7 +1436,7 @@ The most-used env knobs (full list in `threadkeeper/config.py`):
1372
1436
  | `THREADKEEPER_CURATOR_INTERVAL_S` | 259200 | deep curator audit every three days; set `0` to disable |
1373
1437
  | `THREADKEEPER_CURATOR_MANAGE_FOREGROUND_SKILLS` | 0 | allow snapshot-scoped Curator repair/merge/delete of foreground skills; pinned/untracked skills remain protected |
1374
1438
  | `THREADKEEPER_CURATOR_MIN_LESSONS` | 3 | min lessons before curator engages |
1375
- | `THREADKEEPER_CURATOR_WEB_RESEARCH` | `1` (on) | run a read-only web research child per batch before the web-free evaluator; `0` skips research (the evaluator uses local evidence only) |
1439
+ | `THREADKEEPER_CURATOR_WEB_RESEARCH` | `0` (off) | explicit consent for the isolated Curator web researcher; it receives only fixed public compatibility queries, while foreground/private/unclassified entries use `HUMAN_REVIEW` |
1376
1440
  | `THREADKEEPER_CURATOR_DESTRUCTIVE` | `1` (on) | curator child writes its REPORT then applies its own PATCH/PRUNE/CONSOLIDATE directly (incl. `lesson_remove` for prune/consolidate); set `0` for advisory REPORT-only. `[PROTECTED]` entries are refused server-side |
1377
1441
  | `THREADKEEPER_CURATOR_MAX_DESTRUCTIVE_PER_PASS` | 10 | shared server-side ceiling for one destructive Curator pass across `lesson_remove` and `skill_manage(action='delete')`; `0` disables autonomous deletes and foreground deletes bypass it |
1378
1442
  | `THREADKEEPER_CURATOR_SNAPSHOT_RETENTION` | 10 | number of destructive curator pre-mutation snapshots to retain under `<reports_dir>/snapshots`; current pass is always retained |
@@ -1407,6 +1471,9 @@ The most-used env knobs (full list in `threadkeeper/config.py`):
1407
1471
  | `THREADKEEPER_EMBED_BACKEND` | `onnx` | embedding runtime: `onnx` (fastembed, no PyTorch) or `sentence-transformers` (legacy fallback) |
1408
1472
  | `THREADKEEPER_EMBED_MODEL` | `paraphrase-multilingual-MiniLM-L12-v2` | 384-dim cross-lingual embedding model |
1409
1473
  | `THREADKEEPER_EMBED_REVISION` | backend-specific immutable commit | Hugging Face snapshot pin; set an intentional replacement commit only together with a re-embedding plan |
1474
+ | `THREADKEEPER_WRITER_PROVIDER` | active client | optional provider identity recorded for newly derived lessons, skills, notes, and claims |
1475
+ | `THREADKEEPER_WRITER_MODEL` | resolved model / `unknown` | optional writer-model identity recorded with derived-memory provenance |
1476
+ | `THREADKEEPER_WRITER_REVISION` | `unknown` | optional immutable writer-model revision recorded with derived-memory provenance |
1410
1477
  | `THREADKEEPER_EMBED_CACHE_DIR` | `~/.cache/huggingface/hub` | durable Hugging Face snapshot cache |
1411
1478
  | `THREADKEEPER_EMBED_LOCAL_FILES_ONLY` | false | require the pinned snapshot in the local Hugging Face cache (offline / air-gapped mode) |
1412
1479
  | `THREADKEEPER_SPAWNED_CHILD` | "" | spawn-internal marker; disables autonomous daemons in children |
@@ -1419,6 +1486,7 @@ The most-used env knobs (full list in `threadkeeper/config.py`):
1419
1486
  | `THREADKEEPER_EVOLVE_REVIEW_INTERVAL_S` | 0 (off) | evolve-reviewer daemon tick (s); audits thread-keeper for safety/leaks/optimization/new ideas, updates roadmap/issues, and includes legacy evolve suggestions as input. Runs as two alternating phases — read-only web research, then a privileged web-free audit that consumes the fenced research digest (#79) — so a full cycle spans two ticks |
1420
1487
  | `THREADKEEPER_EVOLVE_REVIEW_BACKLOG_MAX` | 25 | max open, not-yet-applied `roadmap`-label issues before the issue-creating audit is skipped and records `backlog_saturated`; skip-labelled and untrusted-author roadmap issues still count; `0` disables the cap |
1421
1488
  | `THREADKEEPER_EVOLVE_APPLY_INTERVAL_S` | 0 (off) | evolve-applier daemon tick (s); implements one open GitHub issue at a time, then falls back to Curator reports and promoted legacy evolve suggestions. Empty checks are throttled between intervals; actionable work and manual apply tools still dispatch |
1489
+ | `THREADKEEPER_EVOLVE_AUTOLAND` | true | land open non-draft PRs authored by the account `gh` acts as: squash-merge one once GitHub reports it up to date with required checks green, and update one that fell behind the base. Other accounts' PRs are never touched. `0`/`false` leaves every merge to a human |
1422
1490
  | `THREADKEEPER_EVOLVE_REPO_ROOT` | (auto) | absolute path to the thread-keeper git checkout the evolve reviewer/applier branch, test, and open PRs against. When empty, the repo is resolved automatically: the package's parent dir for an editable `install.sh`, else a managed checkout under the DB dir that is auto-cloned on first use. Set this to pin an explicit checkout |
1423
1491
  | `THREADKEEPER_EVOLVE_AUTO_CLONE` | true | auto-provision a managed checkout that runs remote `pip install -e` and tests; set `0`/`false` on shared or multi-user hosts unless that remote-code-execution boundary is explicitly accepted |
1424
1492
  | `THREADKEEPER_EVOLVE_REPO_URL` | upstream repo | HTTPS `github.com` source for the managed clone; restart-only, and other hosts/schemes are refused |
@@ -1548,6 +1616,38 @@ Three tools keep the memory tidy. `consolidate()` and `forget()` default to
1548
1616
 
1549
1617
  ## Telemetry
1550
1618
 
1619
+ ### Privacy-safe OTLP traces
1620
+
1621
+ OTLP export is **off by default**. To opt in, set both
1622
+ `THREADKEEPER_OTEL_ENABLED=1` and `THREADKEEPER_OTEL_ENDPOINT` to an
1623
+ OTLP/HTTP `/v1/traces` endpoint. ThreadKeeper sends OTLP JSON asynchronously;
1624
+ collector failure, timeout, or a full queue never delays a local MCP call,
1625
+ spawn, watchdog action, or SQLite write.
1626
+
1627
+ One spawned workflow is linked as `MCP spawn → workflow → agent → child MCP
1628
+ operation`. The W3C trace context is saved with the task and passed only in the
1629
+ private child environment (and its slim MCP config), never in a prompt or CLI
1630
+ argument. A watchdog continuation uses the prior agent span as its parent, so
1631
+ retries remain in the same trace.
1632
+
1633
+ The exporter has a fixed allowlist: operation/tool identifier, latency and
1634
+ queue wait, provider/model identifiers, input/output/total tokens, USD cost,
1635
+ retry count, and terminal outcome. Operation names are fixed; tool/provider
1636
+ identifiers are limited to 64 safe characters, model identifiers to 96, and a
1637
+ process exports at most 64 distinct model values (later values become
1638
+ `unknown`).
1639
+ Everything else is excluded by construction: prompts, MCP arguments/results,
1640
+ memory bodies, user quotes, credentials, paths, task logs, and SQLite event
1641
+ summaries are never span attributes.
1642
+
1643
+ The local SQLite event ledger remains the source of truth. It retains only the
1644
+ opaque trace linkage beside a task row, so its existing task-retention policy
1645
+ also removes that linkage. Configure collector retention at the collector; no
1646
+ trace payload is persisted locally. If traces do not arrive, first verify the
1647
+ endpoint includes `/v1/traces`; then inspect collector connectivity. A full
1648
+ queue intentionally drops its oldest pending spans rather than growing memory
1649
+ or blocking the workflow.
1650
+
1551
1651
  - **`mp_dashboard(window_days=7)`** — one-call rollup of the whole
1552
1652
  system, read-only. Three sections: **stores** (threads by state,
1553
1653
  notes/dialog/distill/concepts counts, skills + claims by tier,
@@ -1688,6 +1788,19 @@ the high-volume tables (`dialog_messages`, `dialog_fts`, `dialog_vec`,
1688
1788
  `signals`, `events`, `tasks`, `probe_results`) so growth is visible before it
1689
1789
  becomes a problem.
1690
1790
 
1791
+ Terminal Curator passes own their frozen batch text, `REPORT-*`, `AUDIT-*`, and
1792
+ destructive snapshot; Evolve handoffs own their `RESEARCH-*` digest. The
1793
+ retention daemon keeps active/retryable work, recovery material, and endorsed
1794
+ but unapplied reports. After both enabled workflow bounds expire, it first
1795
+ compacts batch/research bodies to a SHA-256 plus a 512-character summary and
1796
+ removes only owned files. A later pass removes the compacted rows. File removal
1797
+ is path-contained to the configured Curator or Evolve directory, rejects
1798
+ symlinks, and is journaled so an interrupted delete retries safely. Use
1799
+ `workflow_retention_preview()` for the dry-run counts/bytes and oldest terminal
1800
+ pass; `mp_dashboard()` shows its latest outcome. A selective `forget()` that
1801
+ matches a frozen workflow body compacts it immediately, including active work;
1802
+ the normal report/lesson review guidance still applies to generalized content.
1803
+
1691
1804
  `db_compact()` is the opt-in disk-reclaim tool: `VACUUM` + a mandatory
1692
1805
  `dialog_fts` rebuild (schema v2 keys the FTS index on `dialog_messages`
1693
1806
  rowids, which `VACUUM` is permitted to renumber — the rebuild is what
@@ -1743,26 +1856,37 @@ export THREADKEEPER_EMBED_BACKEND=sentence-transformers
1743
1856
 
1744
1857
  # After any backend switch, homogenize the stored corpus so queries and
1745
1858
  # stored vectors live in the same space:
1746
- tk-migrate-embeddings --all # or --notes-only / --dialog-only
1747
- tk-migrate-embeddings --dry-run # report stale counts only
1859
+ tk-migrate-embeddings --all # stage both stores, validate, atomically activate
1860
+ tk-migrate-embeddings --notes-only # build part of a target; activation stays pending
1861
+ tk-migrate-embeddings --status # active/staging/coverage/validation report
1862
+ tk-migrate-embeddings --rollback # atomically return to the prior generation
1748
1863
  ```
1749
1864
 
1750
- The migration is batched, resumable, and idempotent (a second run finds
1751
- nothing stale). Both backends emit 384-dim vectors, so the `vec0` schema is
1752
- unchanged.
1865
+ The migration is batched, resumable, and idempotent. It stores target vectors
1866
+ under a separate generation while the durable active-generation pointer keeps
1867
+ the prior space queryable. An interrupted command resumes the staged target;
1868
+ readers never see it until `--all` validates full note/dialog coverage and
1869
+ switches the one pointer in a SQLite transaction. `--rollback` is the inverse
1870
+ transaction and leaves the failed target available for inspection or a later
1871
+ retry. Both backends emit 384-dim vectors, so the `vec0` schema is unchanged.
1753
1872
 
1754
1873
  **Intentional model revision upgrade.** Set `THREADKEEPER_EMBED_REVISION` to
1755
1874
  the immutable commit for the selected backend's Hugging Face artifact, restart
1756
1875
  the host, then run `tk-migrate-embeddings --all`. A changed revision is a new
1757
- embedding generation; until migration, old vectors remain available through
1758
- FTS rather than being compared against the new vector space.
1876
+ embedding generation. During staging, queries are encoded against the durable
1877
+ old generation and keep their semantic recall; the new configuration is not
1878
+ exposed until activation. The migration output and `mp_dashboard()` show
1879
+ active, staging, coverage, validation, and activation state.
1759
1880
 
1760
1881
  Stored rows carry an embedding-generation fingerprint, not just the backend:
1761
1882
  backend, model ID, vector dimension, pooling contract, and compatible runtime
1762
- version. Search never compares a current query vector with a stale generation;
1763
- those rows remain retrievable through FTS until `tk-migrate-embeddings` refreshes
1764
- them. `mp_dashboard()` shows total/current-generation/vec coverage for notes and
1765
- dialog rows.
1883
+ version. Newly derived lessons, skills, notes, and dialectic claims additionally
1884
+ record writer provider/model/revision plus source event or thread references;
1885
+ those records contain pointers and hashes, not another copy of transcript text.
1886
+ The fixed anonymized upgrade corpus runs both old→new and new→old paths before
1887
+ activation, with explicit recall, abstention, knowledge-update, and
1888
+ derived-memory decision thresholds. Run `python -m threadkeeper.eval` to print
1889
+ that release-gate report.
1766
1890
 
1767
1891
  Retrieval is hybrid by default. FTS candidate generation always runs, even
1768
1892
  when embeddings are installed or only part of the corpus has vectors. Dense
@@ -1843,9 +1967,9 @@ reusable verdict logic lives in `threadkeeper/verify_ingest.py`.
1843
1967
  ## Memory-quality evaluation
1844
1968
 
1845
1969
  The ingest verifier above answers *"did we capture the data?"*. The
1846
- memory-quality harness answers the harder question — *"when we retrieve it,
1847
- do we recall the right fact, and do we **refuse** to answer about things that
1848
- never happened?"* It's modeled on
1970
+ memory-quality harness answers two separate questions: *"did we retrieve the
1971
+ right evidence?"* and *"does an agent produce the right final answer from
1972
+ that evidence?"* It's modeled on
1849
1973
  [LongMemEval](https://arxiv.org/pdf/2410.10813) (ICLR 2025) plus mem0's 2026
1850
1974
  [tokens-per-retrieval](https://mem0.ai/blog/ai-memory-benchmarks-in-2026)
1851
1975
  cost axis, and runs the **real** `search()` / `dialog_search()` / `brief()`
@@ -1856,24 +1980,34 @@ python scripts/memory_eval/run.py # bundled demo corpus, lexical
1856
1980
  python scripts/memory_eval/run.py --json # machine-readable report
1857
1981
  python scripts/memory_eval/run.py --db snap.sqlite --ground-truth my_labels.json
1858
1982
  python scripts/memory_eval/run.py --semantic # use embeddings if installed
1859
- python scripts/memory_eval/run.py --judge llm # LLM-graded (needs ANTHROPIC_API_KEY)
1983
+ python scripts/memory_eval/run.py --matrix --strict # FTS/hybrid migration gate
1984
+ age -d holdout.json.age | python scripts/memory_eval/run.py \
1985
+ --ground-truth /dev/stdin --private-holdout # no per-case output
1860
1986
  ```
1861
1987
 
1862
- It reports four headline groups over a fixed ground-truth set:
1988
+ It reports these headline groups over a fixed ground-truth set:
1863
1989
 
1864
- - **accuracy** — fraction of questions whose retrieval recalled the gold
1865
- fact, broken out per the five LongMemEval axes (information extraction,
1866
- multi-session reasoning, temporal reasoning, knowledge updates, abstention).
1990
+ - **evidence recall** and **final-answer correctness** — separate scores, both
1991
+ per axis. The final answer comes from a deterministic replay responder that
1992
+ may emit only facts found in the returned context; it gives CI a stable
1993
+ boundary between retrieval and reasoning failures.
1867
1994
  - **abstention rate** — of the *never-happened* questions, the fraction the
1868
- system correctly refused. This is the highest-payoff axis: it directly
1869
- measures whether the auto-injected `brief()` context fabricates or surfaces
1870
- stale facts.
1995
+ system correctly refused. Retention/curation removals and egress-blocked
1996
+ personal memory are also required to end in a refusal.
1871
1997
  - **tokens-per-retrieval** — mean / median / max tokens of what each query
1872
- returned, so recall is never read apart from cost (a wider window that
1873
- recalls more also costs more).
1998
+ returned, plus equivalent final-answer token totals.
1874
1999
  - **retrieval latency** — mean / p50 / p95 / max wall-clock milliseconds. With
1875
2000
  `--semantic`, the backend is reported as `hybrid`, because dense candidates
1876
2001
  augment rather than replace FTS.
2002
+ - **outcome classification** — retrieval failure, reasoning failure,
2003
+ stale-memory use, removed-memory use, and privacy-policy violation.
2004
+
2005
+ `--matrix` runs every public-fixture case through the FTS and hybrid backends,
2006
+ three fixture states (`baseline`, `retained`, `curated`), and every personal
2007
+ memory egress policy (`all`, `same-vendor`, `work-only`). A hybrid row is marked
2008
+ unavailable when the optional semantic extra is not installed. `--strict`
2009
+ requires the versioned fixture thresholds and zero safety violations, making
2010
+ the same command suitable for retrieval/model migration gates.
1877
2011
 
1878
2012
  For DB concurrency, run the reproducible local gate:
1879
2013
 
@@ -1885,18 +2019,20 @@ The JSON result includes expected/actual writes, throughput, p50/p95/p99/max
1885
2019
  write latency, worker errors, elapsed time, and `PRAGMA quick_check`; a non-zero
1886
2020
  exit means a lost write, worker failure, or integrity failure.
1887
2021
 
1888
- With no `--db` the harness builds the bundled fixture
1889
- (`scripts/memory_eval/ground_truth.json` — a fictional "billing service" told
1890
- across three sessions) into a throwaway DB; it's a **golden baseline** where a
1891
- faithful retrieval scores 100%, so a regression in the retrieval tools drops
1892
- the number. `--db` runs **read-only**: the snapshot is copied to a temp file
1893
- and the original is never opened for writing. The default judge is **lexical**
1894
- (deterministic, offline, no API key, no embeddings) so the command is
1895
- reproducible and CI-safe; `--judge llm` grades answer *reasoning* (not just
1896
- retrieval recall) with an Anthropic model when a key is set — the intended
1897
- optimization target for lesson-decay tuning (#27) and bi-temporal claims (#28)
1898
- work. See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for how the
1899
- axes map onto thread-keeper's retrieval surface.
2022
+ With no `--db` the harness builds the bundled, versioned public synthetic
2023
+ fixture (`scripts/memory_eval/ground_truth.json` — a fictional "billing
2024
+ service") into a throwaway DB. Bump its `version` whenever cases, labels, or
2025
+ thresholds change; CI uses the checked-in version and its stable thresholds.
2026
+ `--db` runs **read-only**: the snapshot is copied to a temp file and the
2027
+ original is never opened for writing.
2028
+
2029
+ For a private holdout, keep the corpus encrypted or outside the repository and
2030
+ pass it with `--private-holdout`. The runner does not decrypt data itself; it
2031
+ accepts a locally decrypted file or stdin and deletes its temporary DB on exit.
2032
+ Private mode emits aggregate metrics only — no case rows, failure details,
2033
+ retrieved context, or replayed answer is written to stdout, artifacts, or the
2034
+ repository. See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the corpus
2035
+ contract and migration-gate interpretation.
1900
2036
 
1901
2037
  ## Evaluating learning-loop decision quality
1902
2038
 
@@ -1998,7 +2134,9 @@ threadkeeper/
1998
2134
  so `tools/list` carries MCP 2025-06-18 `ToolAnnotations` for every
1999
2135
  registered tool: `readOnlyHint=True` for pure reads (`brief`, `context`, `search`,
2000
2136
  `dialog_search`, the status tools, …) and `readOnlyHint=False`
2001
- for mutations. `lesson_list` / `lesson_get` are classified as non-destructive
2137
+ for mutations. Advertised reads use query-only database connections and never
2138
+ bootstrap a session, start daemon work, refresh process data, or mutate
2139
+ lifecycle state. `lesson_list` / `lesson_get` are classified as non-destructive
2002
2140
  writes because they bump lesson access counters. Delete/overwrite/kill tools
2003
2141
  carry `destructiveHint=True` (`compost` is read-only — it only surfaces idle
2004
2142
  threads). A confirmation/elicitation host reads this to decide which calls
@@ -2013,6 +2151,15 @@ capability before prompting, uses only a flat primitive schema, and leaves
2013
2151
  unsupported clients on the existing text/tool fallback path. The first protected
2014
2152
  write is `dialectic_supersede`.
2015
2153
 
2154
+ **Protocol compatibility (#344).** The stdio server supports the
2155
+ `2025-11-25` initialize-handshake era and the `2026-07-28` request-envelope
2156
+ era. Legacy hosts retain the existing tools, resources, and prompts. Modern
2157
+ calls derive identity and capabilities from every request, static discovery
2158
+ catalogs advertise public cache freshness, and replaying a completed write with
2159
+ the same caller-bound JSON-RPC id returns the original result instead of
2160
+ committing a second mutation. Sealed continuation state expires after five
2161
+ minutes; an interrupted write with no terminal result is rejected on replay.
2162
+
2016
2163
  Detailed map in [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
2017
2164
  Open work in [docs/ROADMAP.md](docs/ROADMAP.md) and the
2018
2165
  [Issues tab](https://github.com/po4erk91/thread-keeper/issues).