thread-archive 0.0.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 (250) hide show
  1. thread_archive-0.0.2/.gitignore +39 -0
  2. thread_archive-0.0.2/.mcp.json.example +13 -0
  3. thread_archive-0.0.2/CHANGELOG.md +328 -0
  4. thread_archive-0.0.2/LICENSE +21 -0
  5. thread_archive-0.0.2/PKG-INFO +296 -0
  6. thread_archive-0.0.2/README.md +257 -0
  7. thread_archive-0.0.2/claude-install.md +144 -0
  8. thread_archive-0.0.2/docs/format.md +130 -0
  9. thread_archive-0.0.2/docs/plans/reindex-atomic-swap.md +120 -0
  10. thread_archive-0.0.2/frontend/.gitignore +1 -0
  11. thread_archive-0.0.2/frontend/index.html +12 -0
  12. thread_archive-0.0.2/frontend/package-lock.json +4599 -0
  13. thread_archive-0.0.2/frontend/package.json +34 -0
  14. thread_archive-0.0.2/frontend/src/App.tsx +24 -0
  15. thread_archive-0.0.2/frontend/src/api.ts +103 -0
  16. thread_archive-0.0.2/frontend/src/components/Landing.tsx +7 -0
  17. thread_archive-0.0.2/frontend/src/components/Markdown.tsx +20 -0
  18. thread_archive-0.0.2/frontend/src/components/Message.tsx +242 -0
  19. thread_archive-0.0.2/frontend/src/components/RawMode.tsx +8 -0
  20. thread_archive-0.0.2/frontend/src/components/SearchView.tsx +80 -0
  21. thread_archive-0.0.2/frontend/src/components/Sidebar.tsx +77 -0
  22. thread_archive-0.0.2/frontend/src/components/StatusBar.tsx +21 -0
  23. thread_archive-0.0.2/frontend/src/components/ThreadView.tsx +99 -0
  24. thread_archive-0.0.2/frontend/src/main.tsx +14 -0
  25. thread_archive-0.0.2/frontend/src/modelColor.ts +63 -0
  26. thread_archive-0.0.2/frontend/src/styles.css +222 -0
  27. thread_archive-0.0.2/frontend/src/test/Message.test.tsx +147 -0
  28. thread_archive-0.0.2/frontend/src/test/SearchView.test.tsx +100 -0
  29. thread_archive-0.0.2/frontend/src/test/ThreadView.test.tsx +111 -0
  30. thread_archive-0.0.2/frontend/src/test/setup.ts +10 -0
  31. thread_archive-0.0.2/frontend/tsconfig.json +21 -0
  32. thread_archive-0.0.2/frontend/vite.config.ts +25 -0
  33. thread_archive-0.0.2/pyproject.toml +118 -0
  34. thread_archive-0.0.2/scripts/coverage_gate.py +94 -0
  35. thread_archive-0.0.2/scripts/golden_from_usage.py +173 -0
  36. thread_archive-0.0.2/scripts/librarian_backfill.py +160 -0
  37. thread_archive-0.0.2/scripts/retrieval_eval.py +170 -0
  38. thread_archive-0.0.2/src/thread_archive/__init__.py +34 -0
  39. thread_archive-0.0.2/src/thread_archive/_api.py +2235 -0
  40. thread_archive-0.0.2/src/thread_archive/_config.py +126 -0
  41. thread_archive-0.0.2/src/thread_archive/_importers/__init__.py +81 -0
  42. thread_archive-0.0.2/src/thread_archive/_importers/_continuation.py +136 -0
  43. thread_archive-0.0.2/src/thread_archive/_importers/_cursor.py +103 -0
  44. thread_archive-0.0.2/src/thread_archive/_importers/_events.py +244 -0
  45. thread_archive-0.0.2/src/thread_archive/_importers/_line_stream.py +193 -0
  46. thread_archive-0.0.2/src/thread_archive/_importers/_read.py +82 -0
  47. thread_archive-0.0.2/src/thread_archive/_importers/_result.py +17 -0
  48. thread_archive-0.0.2/src/thread_archive/_importers/_sidecar.py +113 -0
  49. thread_archive-0.0.2/src/thread_archive/_importers/_state.py +240 -0
  50. thread_archive-0.0.2/src/thread_archive/_importers/_titles.py +179 -0
  51. thread_archive-0.0.2/src/thread_archive/_importers/antigravity.py +254 -0
  52. thread_archive-0.0.2/src/thread_archive/_importers/claude_code.py +293 -0
  53. thread_archive-0.0.2/src/thread_archive/_importers/claude_science.py +330 -0
  54. thread_archive-0.0.2/src/thread_archive/_importers/cloth.py +20 -0
  55. thread_archive-0.0.2/src/thread_archive/_importers/codex.py +324 -0
  56. thread_archive-0.0.2/src/thread_archive/_importers/cowork.py +107 -0
  57. thread_archive-0.0.2/src/thread_archive/_importers/cursor.py +432 -0
  58. thread_archive-0.0.2/src/thread_archive/_importers/exports.py +389 -0
  59. thread_archive-0.0.2/src/thread_archive/_importers/grok.py +600 -0
  60. thread_archive-0.0.2/src/thread_archive/_importers/opencode.py +538 -0
  61. thread_archive-0.0.2/src/thread_archive/_knowledge/__init__.py +67 -0
  62. thread_archive-0.0.2/src/thread_archive/_knowledge/_claims.py +116 -0
  63. thread_archive-0.0.2/src/thread_archive/_knowledge/_community.py +75 -0
  64. thread_archive-0.0.2/src/thread_archive/_knowledge/graph.py +208 -0
  65. thread_archive-0.0.2/src/thread_archive/_knowledge/materialize.py +212 -0
  66. thread_archive-0.0.2/src/thread_archive/_knowledge/write.py +438 -0
  67. thread_archive-0.0.2/src/thread_archive/_launchd.py +167 -0
  68. thread_archive-0.0.2/src/thread_archive/_mcp/__init__.py +1 -0
  69. thread_archive-0.0.2/src/thread_archive/_mcp/librarian.py +151 -0
  70. thread_archive-0.0.2/src/thread_archive/_mcp/server.py +307 -0
  71. thread_archive-0.0.2/src/thread_archive/_retrieval/__init__.py +280 -0
  72. thread_archive-0.0.2/src/thread_archive/_retrieval/_classify.py +80 -0
  73. thread_archive-0.0.2/src/thread_archive/_retrieval/_codex.py +157 -0
  74. thread_archive-0.0.2/src/thread_archive/_retrieval/_context.py +123 -0
  75. thread_archive-0.0.2/src/thread_archive/_retrieval/_extract.py +184 -0
  76. thread_archive-0.0.2/src/thread_archive/_retrieval/embed.py +186 -0
  77. thread_archive-0.0.2/src/thread_archive/_retrieval/format.py +149 -0
  78. thread_archive-0.0.2/src/thread_archive/_retrieval/fts.py +538 -0
  79. thread_archive-0.0.2/src/thread_archive/_retrieval/rank.py +228 -0
  80. thread_archive-0.0.2/src/thread_archive/_retrieval/read.py +908 -0
  81. thread_archive-0.0.2/src/thread_archive/_retrieval/rerank.py +143 -0
  82. thread_archive-0.0.2/src/thread_archive/_retrieval/vectors.py +611 -0
  83. thread_archive-0.0.2/src/thread_archive/_scripts/__init__.py +1 -0
  84. thread_archive-0.0.2/src/thread_archive/_scripts/backfill_recompute.py +328 -0
  85. thread_archive-0.0.2/src/thread_archive/_scripts/backfill_reconcile.py +296 -0
  86. thread_archive-0.0.2/src/thread_archive/_scripts/denamespace_dedup_keys.py +120 -0
  87. thread_archive-0.0.2/src/thread_archive/_scripts/recover_dropped_events.py +190 -0
  88. thread_archive-0.0.2/src/thread_archive/_scripts/repair_grok_tool_names.py +118 -0
  89. thread_archive-0.0.2/src/thread_archive/_scripts/repair_grok_tool_names_backup_20260704T155625Z.json +275 -0
  90. thread_archive-0.0.2/src/thread_archive/_scripts/repair_grok_tool_names_plan_20260704.json +435 -0
  91. thread_archive-0.0.2/src/thread_archive/_setup/__init__.py +12 -0
  92. thread_archive-0.0.2/src/thread_archive/_setup/clients.py +89 -0
  93. thread_archive-0.0.2/src/thread_archive/_setup/wizard.py +474 -0
  94. thread_archive-0.0.2/src/thread_archive/_store/__init__.py +45 -0
  95. thread_archive-0.0.2/src/thread_archive/_store/_base.py +173 -0
  96. thread_archive-0.0.2/src/thread_archive/_store/_defaults.py +57 -0
  97. thread_archive-0.0.2/src/thread_archive/_store/_types.py +38 -0
  98. thread_archive-0.0.2/src/thread_archive/_store/models.py +370 -0
  99. thread_archive-0.0.2/src/thread_archive/_store/schema.py +84 -0
  100. thread_archive-0.0.2/src/thread_archive/_thread_import/__init__.py +40 -0
  101. thread_archive-0.0.2/src/thread_archive/_thread_import/api.py +78 -0
  102. thread_archive-0.0.2/src/thread_archive/_thread_import/event_builder.py +810 -0
  103. thread_archive-0.0.2/src/thread_archive/_thread_import/exporters/__init__.py +9 -0
  104. thread_archive-0.0.2/src/thread_archive/_thread_import/exporters/_cursor_kv_mixin.py +166 -0
  105. thread_archive-0.0.2/src/thread_archive/_thread_import/exporters/_cursor_parse_mixin.py +149 -0
  106. thread_archive-0.0.2/src/thread_archive/_thread_import/exporters/cursor.py +582 -0
  107. thread_archive-0.0.2/src/thread_archive/_thread_import/exporters/cursor_parse.py +145 -0
  108. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/__init__.py +191 -0
  109. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/base.py +698 -0
  110. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/chatgpt.py +722 -0
  111. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/chatgpt_content.py +332 -0
  112. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/claude.py +443 -0
  113. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/claude_code.py +1035 -0
  114. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/claude_code_blocks.py +415 -0
  115. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/claude_code_ide.py +122 -0
  116. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/claude_code_sessions.py +90 -0
  117. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/config/__init__.py +26 -0
  118. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/config/base.py +220 -0
  119. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/cursor.py +538 -0
  120. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/cursor_blocks.py +365 -0
  121. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/pipeline/__init__.py +29 -0
  122. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/pipeline/base.py +251 -0
  123. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/pipeline/interfaces.py +191 -0
  124. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/transformers/__init__.py +22 -0
  125. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/transformers/active_path.py +87 -0
  126. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/transformers/coalescing.py +389 -0
  127. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/transformers/ide_context.py +158 -0
  128. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/transformers/thinking_merge.py +68 -0
  129. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/types/__init__.py +56 -0
  130. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/types/chatgpt.py +99 -0
  131. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/types/claude.py +120 -0
  132. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/types/claude_code.py +139 -0
  133. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/types/cursor.py +109 -0
  134. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/validators/__init__.py +83 -0
  135. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/validators/base.py +168 -0
  136. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/validators/content.py +80 -0
  137. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/validators/referential.py +57 -0
  138. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/validators/thinking.py +143 -0
  139. thread_archive-0.0.2/src/thread_archive/_thread_import/parsers/validators/types.py +87 -0
  140. thread_archive-0.0.2/src/thread_archive/_thread_import/schemas/__init__.py +231 -0
  141. thread_archive-0.0.2/src/thread_archive/_thread_import/schemas/chatgpt/v1.json +197 -0
  142. thread_archive-0.0.2/src/thread_archive/_thread_import/schemas/chatgpt/v2.json +203 -0
  143. thread_archive-0.0.2/src/thread_archive/_thread_import/schemas/claude/v1.json +188 -0
  144. thread_archive-0.0.2/src/thread_archive/_thread_import/schemas/claude_code/v1.json +183 -0
  145. thread_archive-0.0.2/src/thread_archive/_thread_import/schemas/cursor/v1.json +143 -0
  146. thread_archive-0.0.2/src/thread_archive/_thread_import/schemas/cursor/v2.json +200 -0
  147. thread_archive-0.0.2/src/thread_archive/_thread_import/timestamps.py +57 -0
  148. thread_archive-0.0.2/src/thread_archive/_thread_import/tool_names.py +48 -0
  149. thread_archive-0.0.2/src/thread_archive/_truth/__init__.py +40 -0
  150. thread_archive-0.0.2/src/thread_archive/_truth/jsonl_log.py +2340 -0
  151. thread_archive-0.0.2/src/thread_archive/_truth/repair.py +322 -0
  152. thread_archive-0.0.2/src/thread_archive/_watcher/__init__.py +57 -0
  153. thread_archive-0.0.2/src/thread_archive/_watcher/base.py +65 -0
  154. thread_archive-0.0.2/src/thread_archive/_watcher/daemon.py +289 -0
  155. thread_archive-0.0.2/src/thread_archive/_watcher/export_drop.py +203 -0
  156. thread_archive-0.0.2/src/thread_archive/_watcher/exthost.py +316 -0
  157. thread_archive-0.0.2/src/thread_archive/_watcher/lazy.py +117 -0
  158. thread_archive-0.0.2/src/thread_archive/_watcher/sources.py +616 -0
  159. thread_archive-0.0.2/src/thread_archive/_web/__init__.py +15 -0
  160. thread_archive-0.0.2/src/thread_archive/_web/server.py +299 -0
  161. thread_archive-0.0.2/src/thread_archive/_web/static/assets/index-DECSH1Mz.css +10 -0
  162. thread_archive-0.0.2/src/thread_archive/_web/static/assets/index-Djq0FK0y.js +101 -0
  163. thread_archive-0.0.2/src/thread_archive/_web/static/index.html +13 -0
  164. thread_archive-0.0.2/src/thread_archive/cli.py +746 -0
  165. thread_archive-0.0.2/tests/__init__.py +0 -0
  166. thread_archive-0.0.2/tests/conftest.py +68 -0
  167. thread_archive-0.0.2/tests/goldens/providers/antigravity.json +134 -0
  168. thread_archive-0.0.2/tests/goldens/providers/claude-code.json +258 -0
  169. thread_archive-0.0.2/tests/goldens/providers/cloth.json +120 -0
  170. thread_archive-0.0.2/tests/goldens/providers/codex.json +83 -0
  171. thread_archive-0.0.2/tests/goldens/providers/cursor.json +184 -0
  172. thread_archive-0.0.2/tests/goldens/providers/grok.json +177 -0
  173. thread_archive-0.0.2/tests/goldens/providers/opencode.json +86 -0
  174. thread_archive-0.0.2/tests/helpers.py +74 -0
  175. thread_archive-0.0.2/tests/install/Dockerfile +32 -0
  176. thread_archive-0.0.2/tests/install/README.md +56 -0
  177. thread_archive-0.0.2/tests/install/e2e_check.py +102 -0
  178. thread_archive-0.0.2/tests/install/make_fixtures.py +176 -0
  179. thread_archive-0.0.2/tests/install/obfuscate_fixtures.py +204 -0
  180. thread_archive-0.0.2/tests/install/run_in_container.sh +26 -0
  181. thread_archive-0.0.2/tests/install/run_install_test.sh +24 -0
  182. thread_archive-0.0.2/tests/mp_child.py +91 -0
  183. thread_archive-0.0.2/tests/test_api.py +75 -0
  184. thread_archive-0.0.2/tests/test_backfill_dedup_key.py +156 -0
  185. thread_archive-0.0.2/tests/test_backup_mirror.py +300 -0
  186. thread_archive-0.0.2/tests/test_chatgpt_timestamps.py +65 -0
  187. thread_archive-0.0.2/tests/test_cli_smoke.py +69 -0
  188. thread_archive-0.0.2/tests/test_codex_render.py +151 -0
  189. thread_archive-0.0.2/tests/test_continuation.py +149 -0
  190. thread_archive-0.0.2/tests/test_dedup_collapse_and_deep_verify.py +241 -0
  191. thread_archive-0.0.2/tests/test_drain_intent.py +186 -0
  192. thread_archive-0.0.2/tests/test_durability_and_homes.py +161 -0
  193. thread_archive-0.0.2/tests/test_event_builder.py +376 -0
  194. thread_archive-0.0.2/tests/test_event_builder_fidelity.py +100 -0
  195. thread_archive-0.0.2/tests/test_export_drop.py +204 -0
  196. thread_archive-0.0.2/tests/test_exports.py +85 -0
  197. thread_archive-0.0.2/tests/test_exthost.py +142 -0
  198. thread_archive-0.0.2/tests/test_import_ratchet.py +103 -0
  199. thread_archive-0.0.2/tests/test_import_state_watermarks.py +99 -0
  200. thread_archive-0.0.2/tests/test_importer_claude_code.py +537 -0
  201. thread_archive-0.0.2/tests/test_importer_claude_science.py +205 -0
  202. thread_archive-0.0.2/tests/test_importer_cowork.py +69 -0
  203. thread_archive-0.0.2/tests/test_importer_providers.py +319 -0
  204. thread_archive-0.0.2/tests/test_ingest_cursor_hardening.py +173 -0
  205. thread_archive-0.0.2/tests/test_integrity_hardening.py +343 -0
  206. thread_archive-0.0.2/tests/test_kg_materialize.py +131 -0
  207. thread_archive-0.0.2/tests/test_kg_write.py +278 -0
  208. thread_archive-0.0.2/tests/test_knowledge.py +86 -0
  209. thread_archive-0.0.2/tests/test_launchd.py +41 -0
  210. thread_archive-0.0.2/tests/test_lazy_ingest.py +97 -0
  211. thread_archive-0.0.2/tests/test_librarian_claims.py +113 -0
  212. thread_archive-0.0.2/tests/test_librarian_gate.py +107 -0
  213. thread_archive-0.0.2/tests/test_manifest.py +53 -0
  214. thread_archive-0.0.2/tests/test_mcp.py +140 -0
  215. thread_archive-0.0.2/tests/test_mcp_librarian.py +75 -0
  216. thread_archive-0.0.2/tests/test_migration_scripts.py +303 -0
  217. thread_archive-0.0.2/tests/test_multiprocess_durability.py +192 -0
  218. thread_archive-0.0.2/tests/test_nightly.py +270 -0
  219. thread_archive-0.0.2/tests/test_package_artifact.py +269 -0
  220. thread_archive-0.0.2/tests/test_preserve_antigravity.py +96 -0
  221. thread_archive-0.0.2/tests/test_preserve_cc_builder.py +122 -0
  222. thread_archive-0.0.2/tests/test_preserve_codex.py +88 -0
  223. thread_archive-0.0.2/tests/test_preserve_cursor_import.py +242 -0
  224. thread_archive-0.0.2/tests/test_preserve_exports.py +200 -0
  225. thread_archive-0.0.2/tests/test_preserve_grok.py +119 -0
  226. thread_archive-0.0.2/tests/test_preserve_opencode.py +254 -0
  227. thread_archive-0.0.2/tests/test_provider_goldens.py +300 -0
  228. thread_archive-0.0.2/tests/test_public_api.py +85 -0
  229. thread_archive-0.0.2/tests/test_read.py +400 -0
  230. thread_archive-0.0.2/tests/test_read_queued_slotting.py +117 -0
  231. thread_archive-0.0.2/tests/test_read_unknown_events.py +77 -0
  232. thread_archive-0.0.2/tests/test_recover_dropped_events.py +83 -0
  233. thread_archive-0.0.2/tests/test_reindex_fail_closed.py +255 -0
  234. thread_archive-0.0.2/tests/test_rerank.py +170 -0
  235. thread_archive-0.0.2/tests/test_retrieval_meta_and_filters.py +163 -0
  236. thread_archive-0.0.2/tests/test_search.py +321 -0
  237. thread_archive-0.0.2/tests/test_setup_wizard.py +256 -0
  238. thread_archive-0.0.2/tests/test_shard_twins.py +107 -0
  239. thread_archive-0.0.2/tests/test_store.py +193 -0
  240. thread_archive-0.0.2/tests/test_structured_model_split.py +196 -0
  241. thread_archive-0.0.2/tests/test_transformer_coalescing.py +287 -0
  242. thread_archive-0.0.2/tests/test_truth.py +543 -0
  243. thread_archive-0.0.2/tests/test_truth_manifest.py +112 -0
  244. thread_archive-0.0.2/tests/test_truth_repair.py +248 -0
  245. thread_archive-0.0.2/tests/test_vector_sidecar.py +84 -0
  246. thread_archive-0.0.2/tests/test_vectors.py +230 -0
  247. thread_archive-0.0.2/tests/test_vendor_thread_import.py +38 -0
  248. thread_archive-0.0.2/tests/test_verify.py +424 -0
  249. thread_archive-0.0.2/tests/test_watch.py +308 -0
  250. thread_archive-0.0.2/tests/test_web.py +348 -0
@@ -0,0 +1,39 @@
1
+ # venvs
2
+ .venv/
3
+ venv/
4
+
5
+ # python
6
+ __pycache__/
7
+ *.py[cod]
8
+ *.egg-info/
9
+ /dist/
10
+ .pytest_cache/
11
+ .ruff_cache/
12
+ .mypy_cache/
13
+
14
+ # Archive data lives in the user's home (~/.thread/archive), NEVER in the repo.
15
+ # These are belt-and-suspenders in case a run is pointed at the repo by mistake.
16
+ # All anchored to the repo root so they can't shadow the source tree (e.g. the
17
+ # `src/thread_archive/truth/` module must stay tracked).
18
+ /data/
19
+ /index.db
20
+ /index.db-*
21
+ /truth/
22
+ /*.sqlite
23
+
24
+ # Machine-specific MCP client config — generated at install time with absolute paths
25
+ # to this clone's venv (see claude-install.md). The tracked template is .mcp.json.example.
26
+ /.mcp.json
27
+
28
+ # Obfuscated corpus built from real local stores by tests/install/obfuscate_fixtures.py.
29
+ # Private even after obfuscation — never commit it.
30
+ tests/install/fixtures-real/
31
+
32
+ # coverage data (pytest --cov; the CI pytest row reports coverage per run,
33
+ # and writes coverage.json for the coverage-gate row)
34
+ /.coverage
35
+ /.coverage.*
36
+ /coverage.json
37
+
38
+ # os
39
+ .DS_Store
@@ -0,0 +1,13 @@
1
+ {
2
+ "_comment": "Template. Copy to .mcp.json and replace ABSOLUTE_REPO_PATH with this clone's absolute path (the install step does this for you). The two servers split read from write: thread-archive is read-only (search/read); thread-archive-librarian carries the curation writes + the librarian skill's reads.",
3
+ "mcpServers": {
4
+ "thread-archive": {
5
+ "command": "ABSOLUTE_REPO_PATH/.venv/bin/archive-mcp",
6
+ "env": { "THREAD_ARCHIVE_HOME": "~/.thread/archive" }
7
+ },
8
+ "thread-archive-librarian": {
9
+ "command": "ABSOLUTE_REPO_PATH/.venv/bin/archive-librarian-mcp",
10
+ "env": { "THREAD_ARCHIVE_HOME": "~/.thread/archive" }
11
+ }
12
+ }
13
+ }
@@ -0,0 +1,328 @@
1
+ # Changelog
2
+
3
+ ## 0.0.2 — 2026-07-11
4
+
5
+ First release published to PyPI: `pip install thread-archive`.
6
+
7
+ - **`thread_archive` — first-run setup and the human status view (2026-07-11).**
8
+ The consumer front door the pip story was missing: `pip install
9
+ thread-archive` then `thread_archive` runs an interactive setup that
10
+ *discovers* the machine's conversation stores (stat-only dry run: counts,
11
+ sizes, date ranges — new `SourceWatcher.discover()`), shows what it found
12
+ and where copies will live before touching anything, imports with per-source
13
+ narration (importer log noise routed to `<home>/logs/setup.log`), then
14
+ offers the launchd watcher and MCP wiring (`claude mcp add` run for you at
15
+ user scope, or the JSON block printed for any other client). Every step is
16
+ skippable; choices persist in the new `<home>/config.json`, and disabled
17
+ sources are honored by every ingest path (daemon, lazy MCP catch-up,
18
+ `archive watch`) via `enabled_watchers()`. Re-running lands on a status
19
+ view; `thread_archive setup` re-enters the flow; `--yes` is the
20
+ non-interactive twin (a non-TTY run without it only prints guidance — and
21
+ checking "is this set up?" no longer scaffolds an empty home as a side
22
+ effect). Both `thread_archive` and `thread-archive` console scripts ship.
23
+ `archive` stays the operator seam.
24
+
25
+ - **Verify's index self-check no longer false-alarms "malformed inverted index"
26
+ (2026-07-11).** Both of today's nightly `verify` failures — the
27
+ highest-severity alarm the system has, fired twice on a healthy archive —
28
+ came from running `PRAGMA quick_check` on a pooled connection: FTS5's
29
+ integrity check consults per-connection segment-structure state, and a
30
+ connection that lives across the watcher's continuous `event_search` rewrites
31
+ can report a healthy index as malformed (reproduced 3-of-5 on a warm pool
32
+ while a simultaneous fresh connection said `ok` every time; likely an
33
+ upstream SQLite 3.51 bug in the FTS5 xIntegrity path). The pragma now runs on
34
+ a private, just-opened connection to the engine's own database file
35
+ (`_api.verify`). Corruption that surfaces as a *raised* `DatabaseError`
36
+ (rather than result rows) is now also captured as a red `quick_check`
37
+ verdict instead of crashing verify; locked/can't-open stays an error so
38
+ environmental trouble can't impersonate corruption. A new test corrupts an
39
+ index page on disk and requires verify to name `quick_check` red.
40
+
41
+ - **The package lane now drives the installed `archive-mcp` binary the way a
42
+ consumer's client does (2026-07-11):** JSON-RPC over stdio against the
43
+ clean-venv install — tools/list, the first `thread_search` on a virgin home
44
+ (the regression shape of the first-open schema race below), and search+read
45
+ over imported data. The lane's CLI lifecycle test also caught up with the
46
+ retrieval-verb removal (it still called `archive search`/`read`; it now
47
+ exercises ingest + the durability kit, with retrieval covered via MCP).
48
+
49
+ - **First search on a virgin archive no longer dies with "table already exists"
50
+ (2026-07-11).** `init_db`'s `create_all` existence check isn't atomic with its
51
+ CREATEs, so the MCP server's warm-models search (a background thread at
52
+ startup) racing the first tool call on an empty home made one of them lose the
53
+ CREATE and error — breaking the very first `thread_search` of a fresh install.
54
+ `init_db` now serializes openers in-process and retries a lost cross-process
55
+ race (`_store/schema.py`).
56
+
57
+ - **`archive-mcp` cohosts lazy catch-up ingest, and the watcher LaunchAgent
58
+ installs from the package (2026-07-11).** The zero-daemon install path:
59
+ `pip install thread-archive` + `claude mcp add thread-archive -- archive-mcp`
60
+ is now the whole setup — the MCP server runs a throttled background ingest
61
+ pass at startup and around tool calls (`_watcher/lazy.py`;
62
+ `THREAD_ARCHIVE_MCP_INGEST=0` disables), so search sees current
63
+ conversations with nothing else installed. Cross-process safety is a new
64
+ ingest-owner flock (`<home>/.ingest-owner.lock`): a lazy pass runs only
65
+ while holding it exclusively, and the watcher daemon holds it for its whole
66
+ lifetime — so with the daemon alive (or several MCP servers racing), exactly
67
+ one process ingests and the rest skip, losing nothing (sources replay from
68
+ import state; dedup_key collapses any overlap).
69
+ The always-fresh upgrade is now `archive daemon install|uninstall|restart|
70
+ status` (`_launchd.py`, macOS-only): the plist is generated in-package,
71
+ pointing at the installed `archive` console script — no repo checkout, no
72
+ sed. `host/Makefile` delegates its watcher targets to the verb (its template
73
+ plist is deleted; the Makefile's remaining value-add is the thread-family
74
+ manifest and the backup agent). Mac-only is a deliberate product decision.
75
+
76
+ - **The public API is narrowed to exactly two things (2026-07-11): the
77
+ retrieval MCP tools (`thread_search` / `thread_read`, served by
78
+ `archive-mcp`) and the on-disk truth format (docs/format.md).** Everything
79
+ else is now declared private support machinery — the `archive` CLI, the
80
+ librarian MCP server, the web viewer, and all Python modules. Not ready ≠
81
+ not shipped: the private pieces keep running (launchd, cron, and the /ci
82
+ skill drive the CLI), they just carry no external stability promise; more
83
+ surface gets exposed deliberately as it matures.
84
+ As part of the same narrowing, `archive search`, `archive read`, and
85
+ `archive web` are removed (with `_web.serve`, the foreground server only
86
+ `web` called): retrieval traffic belongs to the public MCP tools, and the
87
+ viewer's persistent URL was already the watcher's (`archive watch --web`),
88
+ so the standalone verbs were extra doors onto the same surface. The CLI
89
+ keeps only ingest (`import`, `import-export`, `watch`, `embed`) and the
90
+ durability kit (`backup`, `verify`, `restore-drill`, `reindex`, `repair`,
91
+ `status`, `nightly`). `tests/test_public_api.py` pins the verb set — as
92
+ internal-wiring coordination (plists, cron, skills reference these verbs),
93
+ not as public API — and pins the *absence* of retrieval verbs.
94
+
95
+ - **A proven-fixed stage retires its own failure (2026-07-11).** The only thing
96
+ that could clear a failed nightly was another full nightly (~1h, restore-drill
97
+ dominated), so an archive that had been fixed *and re-verified at full strength*
98
+ went on announcing itself unprotected until 04:00 came around. `nightly` now
99
+ publishes a **verdict** rather than a transcript: `_pipeline_verdict` takes the
100
+ last nightly's `failed_stages` and subtracts every stage a later, at-least-as-
101
+ strong run has since proven good, and `_stamp_heartbeat` republishes it — called
102
+ from `verify` / `backup` / `restore_drill` as well as `nightly`, so re-running a
103
+ stage on its own clears the board.
104
+ The strength comparison is the guard, and it matters for verify alone: the
105
+ nightly escalates verify on age gates (`deep`, `hashes`, and the mirror
106
+ parse-scan, which rides `--backup` exactly when deep is due), so a later *basic*
107
+ verify passing says nothing about a deep tier that failed. `verify_last` now
108
+ records its tier (`deep` / `hashes` / `backup`) for that comparison to read;
109
+ without it, a cheap green would launder an expensive red. A rerun weaker than
110
+ the check that failed retires nothing — by design.
111
+ The heartbeat gained `nightly_at` (when the pipeline last *ran*) alongside `at`
112
+ (when the file was last written), because those stopped being the same fact the
113
+ moment a lone stage rerun could rewrite it: thread-monitor anchors its
114
+ "backups have stopped happening" staleness check on `nightly_at`, so a hand-run
115
+ verify can no longer mask a dead 04:00 job.
116
+
117
+ - The test suite grew the boundaries a review flagged as untested — the places
118
+ where reality differs from the in-process synthetic environment:
119
+ - A **package lane** (`tests/test_package_artifact.py`, `-m package`,
120
+ deselected from the default run) builds the wheel + sdist, asserts their
121
+ contents (vendored parsers, provider JSON schemas, pre-built web assets, no
122
+ stray top-level packages), installs the wheel into a clean venv, and runs
123
+ the real `archive` CLI lifecycle from it. The Docker install test now
124
+ builds and installs the wheel instead of `pip install -e`.
125
+ - A **multiprocess durability suite** (`tests/test_multiprocess_durability.py`)
126
+ exercises the flock/fsync/drain-intent protocol with real processes:
127
+ writers SIGKILLed at each drain window (via `tests/mp_child.py`), four
128
+ concurrent importers, and a same-session import race — each converging to a
129
+ verified, single-copy store.
130
+ - **Provider goldens** (`tests/test_provider_goldens.py` +
131
+ `tests/goldens/providers/`): every importer's full normalized truth output
132
+ is locked against a reviewed golden file (regenerate with
133
+ `UPDATE_GOLDENS=1`), so an importer change shows as a reviewable diff.
134
+ - **Migration/repair script tests** (`tests/test_migration_scripts.py`) cover
135
+ the run()/apply/backup/idempotence paths of the `_scripts` migrations,
136
+ including `repair_grok_tool_names` (previously 0% — its committed plan file
137
+ doubles as the fixture).
138
+ - **Frontend component tests** (Vitest + Testing Library, `npm test` /
139
+ the `frontend-test` CI row): block rendering, loading/error/empty states,
140
+ search grouping, uuid→thread redirects, model grouping. Previously the SPA
141
+ only had a typecheck.
142
+ - **Coverage became a gate**: the CI pytest row measures branch coverage and
143
+ `scripts/coverage_gate.py` (its own CI row) enforces per-package floors — a
144
+ regression ratchet calibrated just under measured coverage, instead of one
145
+ global threshold that dormant vendored parser code would render meaningless.
146
+ - Pytest markers with `--strict-markers`: `integration` (real sockets /
147
+ subprocesses; deselectable for hermetic sandboxes) and `package`.
148
+
149
+ - The family-manifest writer left the package: `thread_archive/manifest.py` is
150
+ now `host/write-manifest.py`. It was the one member of the public surface that
151
+ made no sense to a `pip install` consumer — a thread-family integration point,
152
+ and one that resolved its MCP command paths from a repo checkout (meaningless
153
+ under `site-packages`, where it silently emitted a manifest with no `mcp`
154
+ block). `host/` is installer machinery and is never packaged, which is exactly
155
+ what this is; `make install-agent` and `make manifest` (add `WEB=0` for
156
+ `--no-web`) call it there. `cli` is now the only public module name.
157
+
158
+ - The public Python API is gone — deliberately. `api.py` became `_api.py`, a
159
+ private coordination layer the CLI, MCP servers, and web viewer call into;
160
+ `thread_archive.__all__` shrank to `__version__`. Nothing outside the repo
161
+ imported the Python surface, and the durability promise was always the
162
+ on-disk truth format, not function signatures. The supported surface is now
163
+ exactly: the `archive` CLI, the two MCP servers, and the truth format
164
+ (docs/format.md). Programmatic read access goes through the
165
+ documented stores (index.db is plain SQLite; truth is documented JSONL).
166
+ The ratchet test now pins the surface at empty.
167
+
168
+ - The public surface is now locked down to what's deliberately advertised:
169
+ every internal subpackage is underscore-private (`_store`, `_truth`,
170
+ `_retrieval`, `_knowledge`, `_importers`, `_watcher`, `_mcp`, `_web`,
171
+ `_scripts`, `_config`), leaving only `api`, `cli`, and `manifest` at public
172
+ names. `api.embed` (the `archive embed` backend) joined `__all__` — it was
173
+ the one public-named api function not re-exported. A ratchet test
174
+ (`tests/test_public_api.py`) pins `__all__`, the api-module surface, and the
175
+ set of public module names, so widening the API is an edit to a pinned list,
176
+ never a naming accident. README gained a Stability section stating the
177
+ supported surface.
178
+
179
+ - The truth storage format is now versioned and specified: `docs/format.md`
180
+ documents the truth directory (manifest, per-thread files, sharding, record
181
+ shapes, dedup-key form, overlays, kg log, import cursors), the manifest's
182
+ existing `version: 1` is declared as the format version with a bump policy
183
+ (only for changes an existing reader would misinterpret), and readers now
184
+ *refuse* a truth directory declaring a newer version (`TruthFormatError`)
185
+ instead of guessing at unknown layout semantics.
186
+
187
+ - The vendored provider-parser island moved from a public top-level
188
+ `thread_import` package to `thread_archive._thread_import` — a pip install
189
+ no longer plants a second, generically named public package in
190
+ site-packages, and the parser API stays private until it's deliberately
191
+ exposed.
192
+
193
+ - PyPI release readiness: version single-sourced from `thread_archive.__version__`
194
+ (pyproject declares it dynamic); `[project.urls]` added;
195
+ sdist contents pinned via `[tool.hatch.build.targets.sdist]` `only-include`
196
+ (hatchling only reads the root `.gitignore`, so `frontend/node_modules` —
197
+ ignored only by the nested `frontend/.gitignore` — was ballooning the sdist
198
+ to 17 MB; it and repo-local dirs like `host/` and `.claude/` are now
199
+ excluded). README gains a `pip install thread-archive` path and the real
200
+ clone URL. Built distributions land in `dist/` (gitignored) for
201
+ `twine upload`.
202
+
203
+ - Integrity hardening (from the self-review in thread 3716422), four gates at
204
+ the transaction/content seams: (1) the backup mirror traversal now runs under
205
+ the truth-write mutex, so a copy can never capture a mid-drain partial batch
206
+ or a pre-rollback append that the shrink guard would then pin in the mirror;
207
+ (2) `verify --hashes --backup` now *fails* on a mirror hash-mismatch count
208
+ above the previous run's for that destination (it was report-only), with the
209
+ same fails-once baseline absorption as the live scan (`backup_hashes`
210
+ component, `backup_hashes_last` in health); (3) `rebuild_truth_from_store`
211
+ grew two content pre-flights behind the existing containment gate — it
212
+ refuses when any store payload fails the content hash in its own dedup_key
213
+ (a corrupted index row that kept its id/key must not replace the good truth
214
+ line) and when truth records carry fields the running code's models don't map
215
+ (an older binary must not lossily re-emit newer truth); `force=True` remains
216
+ the deliberate override; (4) `repair_truth` self-validates restore candidates
217
+ the same way — a failing payload is still restored (it's the only copy left)
218
+ but counted (`restored_hash_mismatches`) and logged, and the next
219
+ `verify --hashes` reports it. Plus a fifth, report-only seam: reindex counts
220
+ and logs same-id event content it is about to overwrite in the index
221
+ (`content_overwrites` + sample ids in its result, salvage or not). A blocking
222
+ content gate was rejected (below), but for an *unkeyed* event the index row
223
+ can be the last good copy of a truth line rotted in place, and once the swap
224
+ lands the two stores agree — cross-store parity can never see it again; the
225
+ publication report is the last observable moment of the overwrite.
226
+ Reviewed but deliberately not adopted: a durable per-drain transaction ledger
227
+ (truth-ahead-of-index is the designed safe direction; dedup collapses
228
+ resurrections), a *blocking* content-equality reindex gate (would invert
229
+ truth's authority over the index; cross-store parity in `verify --hashes` is
230
+ the detector, and the report-only counter above covers the laundering
231
+ window), and sticky-red-until-acknowledged hash semantics (the fails-once
232
+ baseline + failure ledger is the documented tradeoff).
233
+
234
+ - Ingest hardening (from the self-review in thread 3716420): the line-stream
235
+ cursor no longer assumes its source is append-only. The watermark carries a
236
+ sha256 of the bytes it was computed over (`import_state.last_content_hash`), and
237
+ each poll re-hashes the file's prefix to prove the imported lines are still the
238
+ file's first lines — a mismatch rewinds to line 0 and re-imports, dedup_key
239
+ collapsing what's already held. This closes three silent-loss paths that all
240
+ looked like "nothing changed" to the old size-equality check: a line rewritten
241
+ to the same serialized length, a truncate-and-regrow between polls, and — the
242
+ sharp one — a malformed *interior* line later repaired, which shifted every
243
+ later line's index out from under a cursor that counts parsed, not physical,
244
+ lines. A torn final line still reads as the append it is, not a rewrite.
245
+ - A turn split across polls stays one turn: the assistant reply whose user line
246
+ landed in an earlier poll now inherits that turn's `stream_id` instead of
247
+ opening its own.
248
+ - Per-item failures inside the Cursor / OpenCode / Claude Science DB scans reach
249
+ the watcher's health instead of only the log — a caught-and-logged failure left
250
+ the scan looking like a clean "nothing new" while a conversation was missing.
251
+ - The import loop asks the index only about the dedup keys it built this pass,
252
+ rather than loading every key in the thread — a poll of a long session no longer
253
+ pays for the whole session's history.
254
+ - `init_db` ALTERs in columns added after a table shipped (`create_all` only ever
255
+ issues `CREATE TABLE`, so a live index never grew one). Missing *indexes* stay
256
+ with verify/reindex.
257
+ - A red verify names its cause and keeps its evidence: results carry
258
+ `failed_components`, the full result of any failing run is appended to
259
+ `<home>/verify-failures.jsonl`, and the deep/hashes health records now carry
260
+ their own tier's verdict (prompted by a nightly that went red with nothing
261
+ persisted to say why).
262
+ - `verify --hashes` grew cross-store payload parity: truth and index payloads
263
+ are fingerprint-compared per id, so rot in an *unkeyed* payload (most
264
+ pre-dedup-key history) is detectable instead of being silently promoted over
265
+ the good index row by the next reindex. Deep verify does the same for the kg
266
+ log's content.
267
+ - The curatorial log joined the daily tier: shallow verify parse-scans
268
+ `kg_events.jsonl` and count-checks it against the table (it previously waited
269
+ up to a week for the deep pass), and the deep kg id-diff is now
270
+ watermark-bounded so a live librarian write can't false-alarm it.
271
+ - `verify --backup` fails on a mirror whose effective count dropped since the
272
+ previous scan (the drill's coverage floor only sees drops ≥2%).
273
+ - Watcher poll errors surface in health.json / `archive status` instead of
274
+ living only in stderr logs.
275
+
276
+ ## 0.0.1 — 2026-07-11
277
+
278
+ - Retrieval correctness pass (from the self-review in thread 3716414): the
279
+ semantic arm now sits out of `tool_name`-scoped, count, and oldest searches
280
+ (tool docs aren't embedded, so every fused hit violated the filter);
281
+ `exclude_content_type` narrows the KNN scope itself instead of discarding
282
+ candidates post-top-k; vector chunk max-pooling moved *before* the top-k cut
283
+ so one long doc can't eat candidate slots; natural-language queries get a
284
+ bm25 OR-fallback tier over meaningful terms when the strict all-terms MATCH
285
+ under-fills (fixes hard zero-recall on conversational queries, lexical-only
286
+ installs especially); `sort='oldest'` scans the index chronologically so the
287
+ pool holds true first mentions; the vector matrix cache is canonicalized by
288
+ scope and bounded.
289
+ - Crash-safe truth appends: drain intent journal, all-or-nothing batches,
290
+ torn-tail repair, exclusive cross-process truth-write locking, and dedup
291
+ enforced as a DB constraint (`(thread_id, dedup_key)` unique).
292
+ - Reindex fails closed: committed-content regression gates, `quick_check` +
293
+ foreign-key checks before the index swap, citations repointed across dedup
294
+ collapse, and vectors survive a plain reindex.
295
+ - `verify` grew tiers: shallow daily parity, `--deep` truth↔index diffs and
296
+ search-surface checks, `--hashes` content self-validation with baselines,
297
+ `--backup` mirror scans, and declared-schema parity.
298
+ - Backup hardened: atomic publishes, shrink guard, bounded deletions, per-run
299
+ hardlink generations, and restore drills; `archive nightly` runs
300
+ backup → verify → drill with per-stage health records and a heartbeat stamp
301
+ for monitoring.
302
+ - `archive repair`: quarantines unparseable truth lines and restores committed
303
+ content — the sanctioned path from a red verify back to green.
304
+ - One-time duplicate repair: ~27k duplicated turns collapsed, ~237k dedup keys
305
+ backfilled, and rebuilds now reproduce the repair instead of undoing it.
306
+ - Retrieval overhaul: thread titles/summaries indexed as searchable docs,
307
+ long documents chunked into the vector space, canonical time-bound format,
308
+ identifier-recall fixes, auto-widening MCP search scope, and an eval harness
309
+ (MRR / recall@k).
310
+ - Test suite pinned model-free (~2 min → ~8 s), integrity tests renamed by
311
+ behavior, coverage measured on every CI sweep, librarian MCP covered.
312
+ - `GET /api/archive-link` resolves a candidate list of ids, first real one
313
+ wins.
314
+
315
+ ## 0.0.0 — initial release
316
+
317
+ - Serverless local archive over `~/.thread/archive`: append-only JSONL truth
318
+ plus a rebuildable SQLite index.
319
+ - Multi-provider importers: Claude Code, Cursor, OpenCode, ChatGPT/Anthropic
320
+ exports, Grok, cloth, and friends.
321
+ - Watcher daemon (`archive watch`) tails harness stores and ingests
322
+ continuously.
323
+ - Retrieval MCP server: `thread_search` (FTS + optional semantic/rerank arms)
324
+ and `thread_read`.
325
+ - Librarian MCP: topics, citations, thread links, and a review queue for
326
+ knowledge curation.
327
+ - Read-only web viewer cohosted at `:8787` (`archive watch --web`).
328
+ - CLI: import/export, reindex, checkpoint, backup, verify.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ella
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.