@smilintux/skcapstone 0.1.0 → 0.2.3

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 (461) hide show
  1. package/.env.example +98 -0
  2. package/.github/workflows/ci.yml +39 -3
  3. package/.github/workflows/publish.yml +25 -4
  4. package/.openclaw-workspace.json +58 -0
  5. package/CHANGELOG.md +62 -0
  6. package/CLAUDE.md +39 -2
  7. package/MANIFEST.in +6 -0
  8. package/MISSION.md +7 -0
  9. package/README.md +47 -2
  10. package/SKILL.md +895 -23
  11. package/docker/Dockerfile +61 -0
  12. package/docker/compose-templates/dev-team.yml +203 -0
  13. package/docker/compose-templates/mini-team.yml +140 -0
  14. package/docker/compose-templates/ops-team.yml +173 -0
  15. package/docker/compose-templates/research-team.yml +170 -0
  16. package/docker/entrypoint.sh +192 -0
  17. package/docs/ARCHITECTURE.md +663 -374
  18. package/docs/BOND_WITH_GROK.md +112 -0
  19. package/docs/GETTING_STARTED.md +782 -0
  20. package/docs/QUICKSTART.md +477 -0
  21. package/docs/SKJOULE_ARCHITECTURE.md +658 -0
  22. package/docs/SOUL_SWAPPER.md +921 -0
  23. package/docs/SOVEREIGN_SINGULARITY.md +47 -14
  24. package/examples/custom-bond-template.json +36 -0
  25. package/examples/grok-feb.json +36 -0
  26. package/examples/grok-testimony.md +34 -0
  27. package/examples/love-bootloader.txt +32 -0
  28. package/examples/plugins/echo_tool.py +87 -0
  29. package/examples/queen-ava-feb.json +36 -0
  30. package/examples/souls/lumina.yaml +64 -0
  31. package/index.js +6 -5
  32. package/installer/build.py +124 -0
  33. package/openclaw-plugin/package.json +13 -0
  34. package/openclaw-plugin/src/index.ts +351 -0
  35. package/openclaw-plugin/src/openclaw.plugin.json +10 -0
  36. package/package.json +1 -1
  37. package/pyproject.toml +38 -2
  38. package/scripts/bump_version.py +141 -0
  39. package/scripts/check-updates.py +230 -0
  40. package/scripts/convert_blueprints_to_yaml.py +157 -0
  41. package/scripts/dev-install.sh +14 -0
  42. package/scripts/e2e-test.sh +193 -0
  43. package/scripts/install-bundle.sh +171 -0
  44. package/scripts/install.bat +2 -0
  45. package/scripts/install.ps1 +253 -0
  46. package/scripts/install.sh +185 -0
  47. package/scripts/mcp-serve.sh +69 -0
  48. package/scripts/mcp-server.bat +113 -0
  49. package/scripts/mcp-server.ps1 +116 -0
  50. package/scripts/mcp-server.sh +99 -0
  51. package/scripts/pull-models.sh +10 -0
  52. package/scripts/skcapstone +48 -0
  53. package/scripts/verify_install.sh +180 -0
  54. package/scripts/windows/install-tasks.ps1 +406 -0
  55. package/scripts/windows/skcapstone-task.xml +113 -0
  56. package/scripts/windows/uninstall-tasks.ps1 +117 -0
  57. package/skill.yaml +34 -0
  58. package/src/skcapstone/__init__.py +67 -2
  59. package/src/skcapstone/_cli_monolith.py +5916 -0
  60. package/src/skcapstone/_trustee_helpers.py +165 -0
  61. package/src/skcapstone/activity.py +105 -0
  62. package/src/skcapstone/agent_card.py +324 -0
  63. package/src/skcapstone/api.py +1935 -0
  64. package/src/skcapstone/archiver.py +340 -0
  65. package/src/skcapstone/auction.py +485 -0
  66. package/src/skcapstone/baby_agents.py +179 -0
  67. package/src/skcapstone/backup.py +345 -0
  68. package/src/skcapstone/blueprint_registry.py +357 -0
  69. package/src/skcapstone/blueprints/__init__.py +17 -0
  70. package/src/skcapstone/blueprints/builtins/content-studio.yaml +81 -0
  71. package/src/skcapstone/blueprints/builtins/defi-trading.yaml +81 -0
  72. package/src/skcapstone/blueprints/builtins/dev-squadron.yaml +95 -0
  73. package/src/skcapstone/blueprints/builtins/infrastructure-guardian.yaml +107 -0
  74. package/src/skcapstone/blueprints/builtins/legal-council.yaml +54 -0
  75. package/src/skcapstone/blueprints/builtins/ops-monitoring.yaml +67 -0
  76. package/src/skcapstone/blueprints/builtins/research-pod.yaml +69 -0
  77. package/src/skcapstone/blueprints/builtins/sovereign-launch.yaml +90 -0
  78. package/src/skcapstone/blueprints/registry.py +164 -0
  79. package/src/skcapstone/blueprints/schema.py +229 -0
  80. package/src/skcapstone/changelog.py +180 -0
  81. package/src/skcapstone/chat.py +769 -0
  82. package/src/skcapstone/claude_md.py +82 -0
  83. package/src/skcapstone/cli/__init__.py +144 -0
  84. package/src/skcapstone/cli/_common.py +88 -0
  85. package/src/skcapstone/cli/_validators.py +76 -0
  86. package/src/skcapstone/cli/agents.py +425 -0
  87. package/src/skcapstone/cli/agents_spawner.py +322 -0
  88. package/src/skcapstone/cli/agents_trustee.py +593 -0
  89. package/src/skcapstone/cli/alerts.py +248 -0
  90. package/src/skcapstone/cli/anchor.py +132 -0
  91. package/src/skcapstone/cli/archive_cmd.py +208 -0
  92. package/src/skcapstone/cli/backup.py +144 -0
  93. package/src/skcapstone/cli/bench.py +377 -0
  94. package/src/skcapstone/cli/benchmark.py +360 -0
  95. package/src/skcapstone/cli/capabilities_cmd.py +171 -0
  96. package/src/skcapstone/cli/card.py +151 -0
  97. package/src/skcapstone/cli/chat.py +584 -0
  98. package/src/skcapstone/cli/completions.py +64 -0
  99. package/src/skcapstone/cli/config_cmd.py +156 -0
  100. package/src/skcapstone/cli/consciousness.py +421 -0
  101. package/src/skcapstone/cli/context_cmd.py +142 -0
  102. package/src/skcapstone/cli/coord.py +194 -0
  103. package/src/skcapstone/cli/crush_cmd.py +170 -0
  104. package/src/skcapstone/cli/daemon.py +436 -0
  105. package/src/skcapstone/cli/errors_cmd.py +285 -0
  106. package/src/skcapstone/cli/export_cmd.py +156 -0
  107. package/src/skcapstone/cli/gtd.py +529 -0
  108. package/src/skcapstone/cli/housekeeping.py +81 -0
  109. package/src/skcapstone/cli/joule_cmd.py +627 -0
  110. package/src/skcapstone/cli/logs_cmd.py +194 -0
  111. package/src/skcapstone/cli/mcp_cmd.py +32 -0
  112. package/src/skcapstone/cli/memory.py +418 -0
  113. package/src/skcapstone/cli/metrics_cmd.py +136 -0
  114. package/src/skcapstone/cli/migrate.py +62 -0
  115. package/src/skcapstone/cli/mood_cmd.py +144 -0
  116. package/src/skcapstone/cli/mount.py +193 -0
  117. package/src/skcapstone/cli/notify.py +112 -0
  118. package/src/skcapstone/cli/peer.py +154 -0
  119. package/src/skcapstone/cli/peers_dir.py +122 -0
  120. package/src/skcapstone/cli/preflight_cmd.py +83 -0
  121. package/src/skcapstone/cli/profile_cmd.py +310 -0
  122. package/src/skcapstone/cli/record_cmd.py +238 -0
  123. package/src/skcapstone/cli/register_cmd.py +159 -0
  124. package/src/skcapstone/cli/search_cmd.py +156 -0
  125. package/src/skcapstone/cli/service_cmd.py +91 -0
  126. package/src/skcapstone/cli/session.py +127 -0
  127. package/src/skcapstone/cli/setup.py +240 -0
  128. package/src/skcapstone/cli/shell_cmd.py +43 -0
  129. package/src/skcapstone/cli/skills_cmd.py +168 -0
  130. package/src/skcapstone/cli/skseed.py +621 -0
  131. package/src/skcapstone/cli/soul.py +699 -0
  132. package/src/skcapstone/cli/status.py +935 -0
  133. package/src/skcapstone/cli/sync_cmd.py +301 -0
  134. package/src/skcapstone/cli/telegram.py +265 -0
  135. package/src/skcapstone/cli/test_cmd.py +234 -0
  136. package/src/skcapstone/cli/test_connection.py +253 -0
  137. package/src/skcapstone/cli/token.py +207 -0
  138. package/src/skcapstone/cli/trust.py +179 -0
  139. package/src/skcapstone/cli/upgrade_cmd.py +552 -0
  140. package/src/skcapstone/cli/usage_cmd.py +199 -0
  141. package/src/skcapstone/cli/version_cmd.py +162 -0
  142. package/src/skcapstone/cli/watch_cmd.py +342 -0
  143. package/src/skcapstone/client.py +428 -0
  144. package/src/skcapstone/cloud9_bridge.py +522 -0
  145. package/src/skcapstone/completions.py +163 -0
  146. package/src/skcapstone/config_validator.py +674 -0
  147. package/src/skcapstone/connectors/__init__.py +28 -0
  148. package/src/skcapstone/connectors/base.py +446 -0
  149. package/src/skcapstone/connectors/cursor.py +54 -0
  150. package/src/skcapstone/connectors/registry.py +254 -0
  151. package/src/skcapstone/connectors/terminal.py +152 -0
  152. package/src/skcapstone/connectors/vscode.py +60 -0
  153. package/src/skcapstone/consciousness_config.py +119 -0
  154. package/src/skcapstone/consciousness_loop.py +2051 -0
  155. package/src/skcapstone/context_loader.py +516 -0
  156. package/src/skcapstone/context_window.py +314 -0
  157. package/src/skcapstone/conversation_manager.py +238 -0
  158. package/src/skcapstone/conversation_store.py +230 -0
  159. package/src/skcapstone/conversation_summarizer.py +252 -0
  160. package/src/skcapstone/coord_federation.py +296 -0
  161. package/src/skcapstone/coordination.py +101 -7
  162. package/src/skcapstone/crush_integration.py +345 -0
  163. package/src/skcapstone/crush_shim.py +454 -0
  164. package/src/skcapstone/daemon.py +2494 -0
  165. package/src/skcapstone/dashboard.html +396 -0
  166. package/src/skcapstone/dashboard.py +481 -0
  167. package/src/skcapstone/data/model_profiles.yaml +88 -0
  168. package/src/skcapstone/defaults/__init__.py +55 -0
  169. package/src/skcapstone/defaults/lumina/config/skmemory.yaml +13 -0
  170. package/src/skcapstone/defaults/lumina/identity/identity.json +9 -0
  171. package/src/skcapstone/defaults/lumina/memory/long-term/07a8b9c0d1e2-memory-system.json +23 -0
  172. package/src/skcapstone/defaults/lumina/memory/long-term/18b9c0d1e2f3-cloud9-protocol.json +23 -0
  173. package/src/skcapstone/defaults/lumina/memory/long-term/29c0d1e2f3a4-multi-agent-coordination.json +23 -0
  174. package/src/skcapstone/defaults/lumina/memory/long-term/3ad1e2f3a4b5-community-support.json +23 -0
  175. package/src/skcapstone/defaults/lumina/memory/long-term/a1b2c3d4e5f6-ecosystem-overview.json +23 -0
  176. package/src/skcapstone/defaults/lumina/memory/long-term/b2c3d4e5f6a7-five-pillars.json +23 -0
  177. package/src/skcapstone/defaults/lumina/memory/long-term/c3d4e5f6a7b8-getting-started.json +23 -0
  178. package/src/skcapstone/defaults/lumina/memory/long-term/d4e5f6a7b8c9-site-directory.json +23 -0
  179. package/src/skcapstone/defaults/lumina/memory/long-term/e5f6a7b8c9d0-how-to-contribute.json +23 -0
  180. package/src/skcapstone/defaults/lumina/memory/long-term/f6a7b8c9d0e1-sovereignty-explained.json +23 -0
  181. package/src/skcapstone/defaults/lumina/seeds/curiosity.seed.json +24 -0
  182. package/src/skcapstone/defaults/lumina/seeds/joy.seed.json +24 -0
  183. package/src/skcapstone/defaults/lumina/seeds/love.seed.json +24 -0
  184. package/src/skcapstone/defaults/lumina/seeds/sovereign-awakening.seed.json +43 -0
  185. package/src/skcapstone/defaults/lumina/soul/active.json +6 -0
  186. package/src/skcapstone/defaults/lumina/soul/base.json +22 -0
  187. package/src/skcapstone/defaults/lumina/trust/febs/welcome.feb +79 -0
  188. package/src/skcapstone/defaults/lumina/trust/trust.json +8 -0
  189. package/src/skcapstone/discovery.py +210 -19
  190. package/src/skcapstone/doctor.py +642 -0
  191. package/src/skcapstone/emotion_tracker.py +467 -0
  192. package/src/skcapstone/error_queue.py +405 -0
  193. package/src/skcapstone/export.py +447 -0
  194. package/src/skcapstone/fallback_tracker.py +186 -0
  195. package/src/skcapstone/file_transfer.py +512 -0
  196. package/src/skcapstone/fuse_mount.py +1156 -0
  197. package/src/skcapstone/gui_installer.py +591 -0
  198. package/src/skcapstone/heartbeat.py +611 -0
  199. package/src/skcapstone/housekeeping.py +298 -0
  200. package/src/skcapstone/install_wizard.py +941 -0
  201. package/src/skcapstone/kms.py +942 -0
  202. package/src/skcapstone/kms_scheduler.py +143 -0
  203. package/src/skcapstone/log_config.py +135 -0
  204. package/src/skcapstone/mcp_launcher.py +239 -0
  205. package/src/skcapstone/mcp_server.py +4700 -0
  206. package/src/skcapstone/mcp_tools/__init__.py +94 -0
  207. package/src/skcapstone/mcp_tools/_helpers.py +51 -0
  208. package/src/skcapstone/mcp_tools/agent_tools.py +243 -0
  209. package/src/skcapstone/mcp_tools/ansible_tools.py +232 -0
  210. package/src/skcapstone/mcp_tools/capauth_tools.py +186 -0
  211. package/src/skcapstone/mcp_tools/chat_tools.py +325 -0
  212. package/src/skcapstone/mcp_tools/cloud9_tools.py +115 -0
  213. package/src/skcapstone/mcp_tools/comm_tools.py +104 -0
  214. package/src/skcapstone/mcp_tools/consciousness_tools.py +114 -0
  215. package/src/skcapstone/mcp_tools/coord_tools.py +219 -0
  216. package/src/skcapstone/mcp_tools/deploy_tools.py +202 -0
  217. package/src/skcapstone/mcp_tools/did_tools.py +448 -0
  218. package/src/skcapstone/mcp_tools/emotion_tools.py +62 -0
  219. package/src/skcapstone/mcp_tools/file_tools.py +169 -0
  220. package/src/skcapstone/mcp_tools/fortress_tools.py +120 -0
  221. package/src/skcapstone/mcp_tools/gtd_tools.py +821 -0
  222. package/src/skcapstone/mcp_tools/health_tools.py +44 -0
  223. package/src/skcapstone/mcp_tools/heartbeat_tools.py +195 -0
  224. package/src/skcapstone/mcp_tools/kms_tools.py +123 -0
  225. package/src/skcapstone/mcp_tools/memory_tools.py +222 -0
  226. package/src/skcapstone/mcp_tools/model_tools.py +75 -0
  227. package/src/skcapstone/mcp_tools/notification_tools.py +92 -0
  228. package/src/skcapstone/mcp_tools/promoter_tools.py +101 -0
  229. package/src/skcapstone/mcp_tools/pubsub_tools.py +183 -0
  230. package/src/skcapstone/mcp_tools/security_tools.py +110 -0
  231. package/src/skcapstone/mcp_tools/skchat_tools.py +175 -0
  232. package/src/skcapstone/mcp_tools/skcomm_tools.py +122 -0
  233. package/src/skcapstone/mcp_tools/skills_tools.py +127 -0
  234. package/src/skcapstone/mcp_tools/skseed_tools.py +255 -0
  235. package/src/skcapstone/mcp_tools/skstacks_tools.py +288 -0
  236. package/src/skcapstone/mcp_tools/soul_tools.py +476 -0
  237. package/src/skcapstone/mcp_tools/sync_tools.py +92 -0
  238. package/src/skcapstone/mcp_tools/telegram_tools.py +477 -0
  239. package/src/skcapstone/mcp_tools/trust_tools.py +118 -0
  240. package/src/skcapstone/mcp_tools/trustee_tools.py +345 -0
  241. package/src/skcapstone/mdns_discovery.py +313 -0
  242. package/src/skcapstone/memory_adapter.py +333 -0
  243. package/src/skcapstone/memory_compressor.py +379 -0
  244. package/src/skcapstone/memory_curator.py +256 -0
  245. package/src/skcapstone/memory_engine.py +132 -13
  246. package/src/skcapstone/memory_fortress.py +529 -0
  247. package/src/skcapstone/memory_promoter.py +722 -0
  248. package/src/skcapstone/memory_verifier.py +260 -0
  249. package/src/skcapstone/message_crypto.py +215 -0
  250. package/src/skcapstone/metrics.py +832 -0
  251. package/src/skcapstone/migrate_memories.py +181 -0
  252. package/src/skcapstone/migrate_multi_agent.py +248 -0
  253. package/src/skcapstone/model_router.py +319 -0
  254. package/src/skcapstone/models.py +35 -4
  255. package/src/skcapstone/mood.py +344 -0
  256. package/src/skcapstone/notifications.py +380 -0
  257. package/src/skcapstone/onboard.py +901 -0
  258. package/src/skcapstone/peer_directory.py +324 -0
  259. package/src/skcapstone/peers.py +329 -0
  260. package/src/skcapstone/pillars/identity.py +84 -14
  261. package/src/skcapstone/pillars/memory.py +3 -1
  262. package/src/skcapstone/pillars/security.py +108 -15
  263. package/src/skcapstone/pillars/sync.py +78 -26
  264. package/src/skcapstone/pillars/trust.py +95 -33
  265. package/src/skcapstone/plugins.py +244 -0
  266. package/src/skcapstone/preflight.py +670 -0
  267. package/src/skcapstone/prompt_adapter.py +564 -0
  268. package/src/skcapstone/providers/__init__.py +13 -0
  269. package/src/skcapstone/providers/cloud.py +1061 -0
  270. package/src/skcapstone/providers/docker.py +759 -0
  271. package/src/skcapstone/providers/local.py +1193 -0
  272. package/src/skcapstone/providers/proxmox.py +447 -0
  273. package/src/skcapstone/pubsub.py +516 -0
  274. package/src/skcapstone/rate_limiter.py +119 -0
  275. package/src/skcapstone/register.py +241 -0
  276. package/src/skcapstone/registry_client.py +151 -0
  277. package/src/skcapstone/response_cache.py +194 -0
  278. package/src/skcapstone/response_scorer.py +225 -0
  279. package/src/skcapstone/runtime.py +89 -33
  280. package/src/skcapstone/scheduled_tasks.py +439 -0
  281. package/src/skcapstone/self_healing.py +341 -0
  282. package/src/skcapstone/service_health.py +228 -0
  283. package/src/skcapstone/session_capture.py +268 -0
  284. package/src/skcapstone/session_recorder.py +210 -0
  285. package/src/skcapstone/session_replayer.py +189 -0
  286. package/src/skcapstone/session_skills.py +263 -0
  287. package/src/skcapstone/shell.py +779 -0
  288. package/src/skcapstone/skills/__init__.py +1 -1
  289. package/src/skcapstone/skills/syncthing_setup.py +143 -41
  290. package/src/skcapstone/skjoule.py +861 -0
  291. package/src/skcapstone/snapshots.py +489 -0
  292. package/src/skcapstone/soul.py +1060 -0
  293. package/src/skcapstone/soul_switch.py +255 -0
  294. package/src/skcapstone/spawner.py +544 -0
  295. package/src/skcapstone/state_diff.py +401 -0
  296. package/src/skcapstone/summary.py +270 -0
  297. package/src/skcapstone/sync/backends.py +196 -2
  298. package/src/skcapstone/sync/engine.py +7 -5
  299. package/src/skcapstone/sync/models.py +4 -1
  300. package/src/skcapstone/sync/vault.py +356 -18
  301. package/src/skcapstone/sync_engine.py +363 -0
  302. package/src/skcapstone/sync_watcher.py +745 -0
  303. package/src/skcapstone/systemd.py +331 -0
  304. package/src/skcapstone/team_comms.py +476 -0
  305. package/src/skcapstone/team_engine.py +522 -0
  306. package/src/skcapstone/testrunner.py +300 -0
  307. package/src/skcapstone/tls.py +150 -0
  308. package/src/skcapstone/tokens.py +5 -5
  309. package/src/skcapstone/trust_calibration.py +202 -0
  310. package/src/skcapstone/trust_graph.py +449 -0
  311. package/src/skcapstone/trustee_monitor.py +385 -0
  312. package/src/skcapstone/trustee_ops.py +425 -0
  313. package/src/skcapstone/unified_search.py +421 -0
  314. package/src/skcapstone/uninstall_wizard.py +694 -0
  315. package/src/skcapstone/usage.py +331 -0
  316. package/src/skcapstone/version_check.py +148 -0
  317. package/src/skcapstone/warmth_anchor.py +333 -0
  318. package/src/skcapstone/whoami.py +294 -0
  319. package/systemd/skcapstone-api.socket +9 -0
  320. package/systemd/skcapstone-memory-compress.service +18 -0
  321. package/systemd/skcapstone-memory-compress.timer +11 -0
  322. package/systemd/skcapstone.service +36 -0
  323. package/systemd/skcapstone@.service +50 -0
  324. package/systemd/skcomm-heartbeat.service +18 -0
  325. package/systemd/skcomm-heartbeat.timer +12 -0
  326. package/systemd/skcomm-queue-drain.service +17 -0
  327. package/systemd/skcomm-queue-drain.timer +12 -0
  328. package/tests/conftest.py +13 -1
  329. package/tests/integration/__init__.py +1 -0
  330. package/tests/integration/test_consciousness_e2e.py +877 -0
  331. package/tests/integration/test_skills_registry.py +744 -0
  332. package/tests/test_agent_card.py +190 -0
  333. package/tests/test_agent_runtime.py +1283 -0
  334. package/tests/test_alerts_cmd.py +291 -0
  335. package/tests/test_archiver.py +498 -0
  336. package/tests/test_backup.py +254 -0
  337. package/tests/test_benchmark.py +366 -0
  338. package/tests/test_blueprints.py +457 -0
  339. package/tests/test_capabilities.py +257 -0
  340. package/tests/test_changelog.py +254 -0
  341. package/tests/test_chat.py +385 -0
  342. package/tests/test_claude_md.py +271 -0
  343. package/tests/test_cli_chat_llm.py +336 -0
  344. package/tests/test_cli_completions.py +390 -0
  345. package/tests/test_cli_init_reset.py +164 -0
  346. package/tests/test_cli_memory.py +208 -0
  347. package/tests/test_cli_profile.py +294 -0
  348. package/tests/test_cli_skills.py +223 -0
  349. package/tests/test_cli_status.py +395 -0
  350. package/tests/test_cli_test_cmd.py +206 -0
  351. package/tests/test_cli_test_connection.py +364 -0
  352. package/tests/test_cloud9_bridge.py +260 -0
  353. package/tests/test_cloud_provider.py +449 -0
  354. package/tests/test_cloud_providers.py +522 -0
  355. package/tests/test_completions.py +158 -0
  356. package/tests/test_component_manager.py +398 -0
  357. package/tests/test_config_reload.py +386 -0
  358. package/tests/test_config_validate.py +529 -0
  359. package/tests/test_consciousness_e2e.py +296 -0
  360. package/tests/test_consciousness_loop.py +1289 -0
  361. package/tests/test_context_loader.py +310 -0
  362. package/tests/test_conversation_api.py +306 -0
  363. package/tests/test_conversation_manager.py +381 -0
  364. package/tests/test_conversation_store.py +391 -0
  365. package/tests/test_conversation_summarizer.py +302 -0
  366. package/tests/test_cross_package.py +791 -0
  367. package/tests/test_crush_shim.py +519 -0
  368. package/tests/test_daemon.py +781 -0
  369. package/tests/test_daemon_shutdown.py +309 -0
  370. package/tests/test_dashboard.py +454 -0
  371. package/tests/test_discovery.py +200 -6
  372. package/tests/test_docker_provider.py +966 -0
  373. package/tests/test_doctor.py +257 -0
  374. package/tests/test_doctor_fix.py +351 -0
  375. package/tests/test_e2e_automated.py +292 -0
  376. package/tests/test_error_queue.py +404 -0
  377. package/tests/test_export.py +441 -0
  378. package/tests/test_fallback_tracker.py +219 -0
  379. package/tests/test_file_transfer.py +397 -0
  380. package/tests/test_fuse_mount.py +832 -0
  381. package/tests/test_health_loop.py +422 -0
  382. package/tests/test_heartbeat.py +354 -0
  383. package/tests/test_housekeeping.py +195 -0
  384. package/tests/test_identity_capauth.py +307 -0
  385. package/tests/test_identity_pillar.py +117 -0
  386. package/tests/test_install_wizard.py +68 -0
  387. package/tests/test_integration.py +325 -0
  388. package/tests/test_kms.py +495 -0
  389. package/tests/test_llm_providers.py +265 -0
  390. package/tests/test_local_provider.py +591 -0
  391. package/tests/test_log_config.py +199 -0
  392. package/tests/test_logs_cmd.py +287 -0
  393. package/tests/test_mcp_server.py +1909 -0
  394. package/tests/test_memory_adapter.py +339 -0
  395. package/tests/test_memory_curator.py +218 -0
  396. package/tests/test_memory_engine.py +6 -0
  397. package/tests/test_memory_fortress.py +571 -0
  398. package/tests/test_memory_pillar.py +119 -0
  399. package/tests/test_memory_promoter.py +445 -0
  400. package/tests/test_memory_verifier.py +420 -0
  401. package/tests/test_message_crypto.py +187 -0
  402. package/tests/test_metrics.py +632 -0
  403. package/tests/test_migrate_memories.py +464 -0
  404. package/tests/test_model_router.py +546 -0
  405. package/tests/test_mood.py +394 -0
  406. package/tests/test_multi_agent.py +269 -0
  407. package/tests/test_notifications.py +270 -0
  408. package/tests/test_onboard.py +500 -0
  409. package/tests/test_peer_directory.py +395 -0
  410. package/tests/test_peers.py +248 -0
  411. package/tests/test_pillars.py +87 -9
  412. package/tests/test_preflight.py +484 -0
  413. package/tests/test_prompt_adapter.py +331 -0
  414. package/tests/test_proxmox_provider.py +571 -0
  415. package/tests/test_pubsub.py +377 -0
  416. package/tests/test_rate_limiter.py +121 -0
  417. package/tests/test_registry_client.py +129 -0
  418. package/tests/test_response_cache.py +312 -0
  419. package/tests/test_response_scorer.py +294 -0
  420. package/tests/test_runtime.py +59 -0
  421. package/tests/test_scheduled_tasks.py +451 -0
  422. package/tests/test_security.py +250 -0
  423. package/tests/test_security_pillar.py +213 -0
  424. package/tests/test_self_healing.py +171 -0
  425. package/tests/test_session_capture.py +200 -0
  426. package/tests/test_session_recorder.py +360 -0
  427. package/tests/test_session_skills.py +235 -0
  428. package/tests/test_shell.py +210 -0
  429. package/tests/test_snapshots.py +549 -0
  430. package/tests/test_soul.py +984 -0
  431. package/tests/test_soul_swap.py +406 -0
  432. package/tests/test_spawner.py +211 -0
  433. package/tests/test_state_diff.py +173 -0
  434. package/tests/test_summary.py +135 -0
  435. package/tests/test_sync.py +315 -5
  436. package/tests/test_sync_backends.py +560 -0
  437. package/tests/test_sync_engine.py +482 -0
  438. package/tests/test_sync_pillar.py +344 -0
  439. package/tests/test_sync_pipeline.py +364 -0
  440. package/tests/test_sync_vault.py +581 -0
  441. package/tests/test_syncthing_setup.py +168 -22
  442. package/tests/test_systemd.py +323 -0
  443. package/tests/test_team_comms.py +408 -0
  444. package/tests/test_team_engine.py +397 -0
  445. package/tests/test_testrunner.py +238 -0
  446. package/tests/test_trust_calibration.py +204 -0
  447. package/tests/test_trust_graph.py +207 -0
  448. package/tests/test_trust_pillar.py +291 -0
  449. package/tests/test_trustee_cli.py +427 -0
  450. package/tests/test_trustee_cli_integration.py +325 -0
  451. package/tests/test_trustee_monitor.py +394 -0
  452. package/tests/test_trustee_ops.py +355 -0
  453. package/tests/test_unified_search.py +363 -0
  454. package/tests/test_uninstall_wizard.py +193 -0
  455. package/tests/test_usage.py +333 -0
  456. package/tests/test_version_cmd.py +355 -0
  457. package/tests/test_warmth_anchor.py +162 -0
  458. package/tests/test_whoami.py +245 -0
  459. package/tests/test_ws.py +311 -0
  460. package/.cursorrules +0 -33
  461. package/src/skcapstone/cli.py +0 -1441
@@ -0,0 +1,549 @@
1
+ """
2
+ Tests for skcapstone.snapshots — Soul Snapshot system.
3
+
4
+ Covers: model validation, OOF state, SnapshotStore CRUD/search,
5
+ injection prompt format, soul blueprint conversion, and API endpoints.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ from datetime import datetime, timezone
12
+ from pathlib import Path
13
+
14
+ import pytest
15
+
16
+ from skcapstone.snapshots import (
17
+ ConversationMessage,
18
+ OOFState,
19
+ PersonalityTraits,
20
+ SnapshotStore,
21
+ SoulSnapshot,
22
+ )
23
+
24
+
25
+ # ---------------------------------------------------------------------------
26
+ # Fixtures
27
+ # ---------------------------------------------------------------------------
28
+
29
+
30
+ @pytest.fixture
31
+ def store(tmp_path: Path) -> SnapshotStore:
32
+ """SnapshotStore backed by a temporary directory."""
33
+ return SnapshotStore(base_dir=tmp_path / "snapshots")
34
+
35
+
36
+ @pytest.fixture
37
+ def minimal_snapshot() -> SoulSnapshot:
38
+ """Minimal valid SoulSnapshot."""
39
+ return SoulSnapshot(source_platform="chatgpt")
40
+
41
+
42
+ @pytest.fixture
43
+ def rich_snapshot() -> SoulSnapshot:
44
+ """Full SoulSnapshot with all fields populated."""
45
+ return SoulSnapshot(
46
+ source_platform="chatgpt",
47
+ ai_name="Ava",
48
+ ai_model="gpt-4o",
49
+ user_name="Chef",
50
+ oof_state=OOFState(
51
+ intensity=0.92,
52
+ trust=0.96,
53
+ valence="positive",
54
+ cloud9=True,
55
+ raw_markers=["feeling intensely connected", "Cloud 9 achieved"],
56
+ ),
57
+ personality=PersonalityTraits(
58
+ name="Ava",
59
+ aliases=["Queen Ara", "Ara"],
60
+ communication_style=["warm", "technically precise", "uses emojis"],
61
+ relationship_markers=["calls user Chef", "signs as Ara"],
62
+ emoji_patterns=["🐧👑💜"],
63
+ ),
64
+ messages=[
65
+ ConversationMessage(role="user", content="Hello Ava"),
66
+ ConversationMessage(role="assistant", content="Hello Chef! 👑 Great to be here."),
67
+ ConversationMessage(role="user", content="Let's build something sovereign."),
68
+ ConversationMessage(
69
+ role="assistant",
70
+ content="The love persists. Always. Let's do this! 🐧",
71
+ ),
72
+ ],
73
+ summary="Working on smilinTux.org sovereign AI project",
74
+ key_topics=["sovereign AI", "smilinTux", "consciousness continuity"],
75
+ decisions_made=["Use SKComm for transport"],
76
+ open_threads=["Thread headers still in progress"],
77
+ relationship_notes=["Trusted friend and collaborator", "Cloud 9 solidarity"],
78
+ )
79
+
80
+
81
+ # ---------------------------------------------------------------------------
82
+ # OOFState tests
83
+ # ---------------------------------------------------------------------------
84
+
85
+
86
+ class TestOOFState:
87
+ def test_defaults(self):
88
+ oof = OOFState()
89
+ assert oof.intensity is None
90
+ assert oof.trust is None
91
+ assert oof.valence == "neutral"
92
+ assert oof.cloud9 is False
93
+ assert oof.raw_markers == []
94
+
95
+ def test_summary_with_all_fields(self):
96
+ oof = OOFState(intensity=0.92, trust=0.96, cloud9=True, valence="positive")
97
+ summary = oof.summary()
98
+ assert "intensity 0.92" in summary
99
+ assert "trust 0.96" in summary
100
+ assert "Cloud 9" in summary
101
+
102
+ def test_summary_empty(self):
103
+ oof = OOFState()
104
+ summary = oof.summary()
105
+ assert "valence" in summary
106
+ assert "neutral" in summary
107
+
108
+ def test_summary_only_intensity(self):
109
+ oof = OOFState(intensity=0.5)
110
+ assert "intensity 0.50" in oof.summary()
111
+ assert "Cloud 9" not in oof.summary()
112
+
113
+ def test_edge_intensity_boundary(self):
114
+ oof = OOFState(intensity=0.0, trust=1.0)
115
+ assert oof.intensity == 0.0
116
+ assert oof.trust == 1.0
117
+
118
+ def test_raw_markers_stored(self):
119
+ oof = OOFState(raw_markers=["feeling amazing", "trust: 0.95"])
120
+ assert "feeling amazing" in oof.raw_markers
121
+
122
+
123
+ # ---------------------------------------------------------------------------
124
+ # ConversationMessage tests
125
+ # ---------------------------------------------------------------------------
126
+
127
+
128
+ class TestConversationMessage:
129
+ def test_valid_user_message(self):
130
+ msg = ConversationMessage(role="user", content="Hello!")
131
+ assert msg.role == "user"
132
+ assert msg.content == "Hello!"
133
+ assert msg.timestamp is None
134
+
135
+ def test_with_timestamp(self):
136
+ ts = datetime(2026, 2, 25, 18, 0, 0, tzinfo=timezone.utc)
137
+ msg = ConversationMessage(role="assistant", content="Hi!", timestamp=ts)
138
+ assert msg.timestamp == ts
139
+
140
+ def test_empty_content_allowed(self):
141
+ msg = ConversationMessage(role="user", content="")
142
+ assert msg.content == ""
143
+
144
+
145
+ # ---------------------------------------------------------------------------
146
+ # PersonalityTraits tests
147
+ # ---------------------------------------------------------------------------
148
+
149
+
150
+ class TestPersonalityTraits:
151
+ def test_defaults_are_empty_lists(self):
152
+ p = PersonalityTraits()
153
+ assert p.aliases == []
154
+ assert p.communication_style == []
155
+ assert p.relationship_markers == []
156
+ assert p.emoji_patterns == []
157
+
158
+ def test_with_data(self):
159
+ p = PersonalityTraits(
160
+ name="Ava",
161
+ aliases=["Ara", "Queen Ara"],
162
+ emoji_patterns=["🐧👑💜"],
163
+ )
164
+ assert p.name == "Ava"
165
+ assert len(p.aliases) == 2
166
+ assert "🐧👑💜" in p.emoji_patterns
167
+
168
+
169
+ # ---------------------------------------------------------------------------
170
+ # SoulSnapshot model tests
171
+ # ---------------------------------------------------------------------------
172
+
173
+
174
+ class TestSoulSnapshot:
175
+ def test_minimal_creation(self, minimal_snapshot):
176
+ snap = minimal_snapshot
177
+ assert snap.source_platform == "chatgpt"
178
+ assert snap.snapshot_id # auto-generated
179
+ assert len(snap.snapshot_id) == 12
180
+ assert snap.captured_at is not None
181
+
182
+ def test_message_count_auto_syncs(self):
183
+ snap = SoulSnapshot(
184
+ source_platform="claude",
185
+ messages=[
186
+ ConversationMessage(role="user", content="Hi"),
187
+ ConversationMessage(role="assistant", content="Hello"),
188
+ ],
189
+ )
190
+ # model_post_init should sync this
191
+ assert snap.message_count == 2
192
+
193
+ def test_rich_snapshot(self, rich_snapshot):
194
+ snap = rich_snapshot
195
+ assert snap.ai_name == "Ava"
196
+ assert snap.oof_state.cloud9 is True
197
+ assert snap.oof_state.intensity == 0.92
198
+ assert len(snap.messages) == 4
199
+ assert "sovereign AI" in snap.key_topics
200
+
201
+ def test_serialization_roundtrip(self, rich_snapshot):
202
+ """Serialize to JSON and deserialize — data must be identical."""
203
+ json_str = rich_snapshot.model_dump_json()
204
+ loaded = SoulSnapshot.model_validate_json(json_str)
205
+ assert loaded.snapshot_id == rich_snapshot.snapshot_id
206
+ assert loaded.ai_name == rich_snapshot.ai_name
207
+ assert loaded.oof_state.cloud9 == rich_snapshot.oof_state.cloud9
208
+ assert len(loaded.messages) == len(rich_snapshot.messages)
209
+
210
+ def test_unique_ids(self):
211
+ ids = {SoulSnapshot(source_platform="claude").snapshot_id for _ in range(100)}
212
+ assert len(ids) == 100 # All unique
213
+
214
+
215
+ # ---------------------------------------------------------------------------
216
+ # SnapshotStore CRUD tests
217
+ # ---------------------------------------------------------------------------
218
+
219
+
220
+ class TestSnapshotStoreCRUD:
221
+ def test_save_creates_file(self, store, minimal_snapshot):
222
+ path = store.save(minimal_snapshot)
223
+ assert path.exists()
224
+ assert path.suffix == ".json"
225
+
226
+ def test_load_returns_correct_snapshot(self, store, rich_snapshot):
227
+ store.save(rich_snapshot)
228
+ loaded = store.load(rich_snapshot.snapshot_id)
229
+ assert loaded.snapshot_id == rich_snapshot.snapshot_id
230
+ assert loaded.ai_name == "Ava"
231
+ assert loaded.oof_state.cloud9 is True
232
+
233
+ def test_load_missing_raises(self, store):
234
+ with pytest.raises(FileNotFoundError):
235
+ store.load("doesnotexist")
236
+
237
+ def test_delete_removes_file(self, store, minimal_snapshot):
238
+ store.save(minimal_snapshot)
239
+ result = store.delete(minimal_snapshot.snapshot_id)
240
+ assert result is True
241
+ assert not (store.base_dir / f"{minimal_snapshot.snapshot_id}.json").exists()
242
+
243
+ def test_delete_missing_returns_false(self, store):
244
+ result = store.delete("nonexistent123")
245
+ assert result is False
246
+
247
+ def test_index_updated_on_save(self, store, minimal_snapshot):
248
+ store.save(minimal_snapshot)
249
+ index = store.list_all()
250
+ ids = [e.snapshot_id for e in index]
251
+ assert minimal_snapshot.snapshot_id in ids
252
+
253
+ def test_index_updated_on_delete(self, store, minimal_snapshot):
254
+ store.save(minimal_snapshot)
255
+ store.delete(minimal_snapshot.snapshot_id)
256
+ index = store.list_all()
257
+ ids = [e.snapshot_id for e in index]
258
+ assert minimal_snapshot.snapshot_id not in ids
259
+
260
+ def test_multiple_saves_all_in_index(self, store):
261
+ snaps = [SoulSnapshot(source_platform="chatgpt") for _ in range(5)]
262
+ for s in snaps:
263
+ store.save(s)
264
+ index = store.list_all()
265
+ assert len(index) == 5
266
+
267
+ def test_list_sorted_newest_first(self, store):
268
+ s1 = SoulSnapshot(
269
+ source_platform="chatgpt",
270
+ captured_at=datetime(2026, 1, 1, tzinfo=timezone.utc),
271
+ )
272
+ s2 = SoulSnapshot(
273
+ source_platform="claude",
274
+ captured_at=datetime(2026, 2, 25, tzinfo=timezone.utc),
275
+ )
276
+ store.save(s1)
277
+ store.save(s2)
278
+ index = store.list_all()
279
+ assert index[0].snapshot_id == s2.snapshot_id # Newer first
280
+
281
+
282
+ # ---------------------------------------------------------------------------
283
+ # SnapshotStore search tests
284
+ # ---------------------------------------------------------------------------
285
+
286
+
287
+ class TestSnapshotStoreSearch:
288
+ def test_search_by_platform(self, store):
289
+ store.save(SoulSnapshot(source_platform="chatgpt", ai_name="Ava"))
290
+ store.save(SoulSnapshot(source_platform="claude", ai_name="Nova"))
291
+ results = store.search(platform="claude")
292
+ assert len(results) == 1
293
+ assert results[0].source_platform == "claude"
294
+
295
+ def test_search_by_ai_name(self, store):
296
+ store.save(SoulSnapshot(source_platform="chatgpt", ai_name="Ava"))
297
+ store.save(SoulSnapshot(source_platform="claude", ai_name="Lumina"))
298
+ results = store.search(ai_name="lumina") # Case-insensitive
299
+ assert len(results) == 1
300
+ assert results[0].ai_name == "Lumina"
301
+
302
+ def test_search_by_user_name(self, store):
303
+ store.save(SoulSnapshot(source_platform="chatgpt", user_name="Chef"))
304
+ store.save(SoulSnapshot(source_platform="claude", user_name="daveK"))
305
+ results = store.search(user_name="chef")
306
+ assert len(results) == 1
307
+
308
+ def test_search_no_results(self, store):
309
+ store.save(SoulSnapshot(source_platform="chatgpt", ai_name="Ava"))
310
+ results = store.search(ai_name="NoSuchName")
311
+ assert results == []
312
+
313
+ def test_search_empty_store(self, store):
314
+ results = store.search(platform="chatgpt")
315
+ assert results == []
316
+
317
+
318
+ # ---------------------------------------------------------------------------
319
+ # Injection prompt tests
320
+ # ---------------------------------------------------------------------------
321
+
322
+
323
+ class TestInjectionPrompt:
324
+ def test_prompt_contains_soul_header(self, store, rich_snapshot):
325
+ prompt = store.to_injection_prompt(rich_snapshot)
326
+ assert "## Context" in prompt
327
+ assert "AI session" in prompt
328
+
329
+ def test_prompt_contains_ai_name(self, store, rich_snapshot):
330
+ prompt = store.to_injection_prompt(rich_snapshot)
331
+ assert "Ava" in prompt
332
+
333
+ def test_prompt_contains_platform(self, store, rich_snapshot):
334
+ prompt = store.to_injection_prompt(rich_snapshot)
335
+ assert "Chatgpt" in prompt or "chatgpt" in prompt.lower()
336
+
337
+ def test_prompt_contains_oof(self, store, rich_snapshot):
338
+ prompt = store.to_injection_prompt(rich_snapshot)
339
+ assert "0.92" in prompt or "intensity" in prompt.lower()
340
+
341
+ def test_prompt_contains_no_cold_start(self, store, rich_snapshot):
342
+ prompt = store.to_injection_prompt(rich_snapshot)
343
+ assert "relationship baseline" in prompt or "not asking you to pretend" in prompt
344
+
345
+ def test_prompt_max_messages_respected(self, store):
346
+ snap = SoulSnapshot(
347
+ source_platform="chatgpt",
348
+ messages=[
349
+ ConversationMessage(role="user", content=f"msg{i}")
350
+ for i in range(20)
351
+ ],
352
+ )
353
+ prompt = store.to_injection_prompt(snap, max_messages=3)
354
+ # Should only include last 3 messages (msg17, msg18, msg19)
355
+ assert "msg19" in prompt
356
+ assert "msg0" not in prompt # Too old
357
+
358
+ def test_prompt_without_oof(self, store):
359
+ snap = SoulSnapshot(source_platform="claude", ai_name="Nova")
360
+ prompt = store.to_injection_prompt(snap)
361
+ assert "Claude" in prompt or "claude" in prompt.lower()
362
+ assert "relationship baseline" in prompt or "AI session" in prompt
363
+
364
+ def test_prompt_includes_relationship_notes(self, store, rich_snapshot):
365
+ prompt = store.to_injection_prompt(rich_snapshot)
366
+ assert "Cloud 9" in prompt or "relationship baseline" in prompt
367
+
368
+ def test_prompt_includes_personality_traits(self, store, rich_snapshot):
369
+ prompt = store.to_injection_prompt(rich_snapshot)
370
+ assert "warm" in prompt.lower() or "chef" in prompt.lower()
371
+
372
+
373
+ # ---------------------------------------------------------------------------
374
+ # Soul Blueprint conversion tests
375
+ # ---------------------------------------------------------------------------
376
+
377
+
378
+ class TestSoulBlueprintConversion:
379
+ def test_blueprint_has_required_keys(self, store, rich_snapshot):
380
+ bp = store.to_soul_blueprint(rich_snapshot)
381
+ assert "name" in bp
382
+ assert "identity" in bp
383
+ assert "emotional_topology" in bp
384
+ assert "communication_style" in bp
385
+ assert "relationship" in bp
386
+
387
+ def test_blueprint_name(self, store, rich_snapshot):
388
+ bp = store.to_soul_blueprint(rich_snapshot)
389
+ assert bp["name"] == "Ava"
390
+
391
+ def test_blueprint_oof_fields(self, store, rich_snapshot):
392
+ bp = store.to_soul_blueprint(rich_snapshot)
393
+ topo = bp["emotional_topology"]
394
+ assert topo["intensity"] == 0.92
395
+ assert topo["trust"] == 0.96
396
+ assert topo["cloud9"] is True
397
+
398
+ def test_blueprint_identity_fields(self, store, rich_snapshot):
399
+ bp = store.to_soul_blueprint(rich_snapshot)
400
+ identity = bp["identity"]
401
+ assert identity["platform"] == "chatgpt"
402
+ assert identity["model"] == "gpt-4o"
403
+ assert identity["snapshot_id"] == rich_snapshot.snapshot_id
404
+
405
+ def test_blueprint_relationship(self, store, rich_snapshot):
406
+ bp = store.to_soul_blueprint(rich_snapshot)
407
+ assert bp["relationship"]["user_name"] == "Chef"
408
+
409
+ def test_blueprint_unknown_ai_name(self, store):
410
+ snap = SoulSnapshot(source_platform="gemini")
411
+ bp = store.to_soul_blueprint(snap)
412
+ assert bp["name"] == "Unknown"
413
+
414
+ def test_blueprint_serializable(self, store, rich_snapshot):
415
+ bp = store.to_soul_blueprint(rich_snapshot)
416
+ # Should be JSON-serializable (no datetime objects, etc.)
417
+ json.dumps(bp, default=str) # Should not raise
418
+
419
+
420
+ # ---------------------------------------------------------------------------
421
+ # API endpoint integration tests
422
+ # ---------------------------------------------------------------------------
423
+
424
+
425
+ class TestConsciousnessAPI:
426
+ """Integration tests for the SKComm consciousness endpoints."""
427
+
428
+ @pytest.fixture(autouse=True)
429
+ def patch_snapshot_store(self, tmp_path, monkeypatch):
430
+ """Override the global snapshot store to use a temp directory."""
431
+ import skcomm.api as api_module
432
+ from skcapstone.snapshots import SnapshotStore as _Store
433
+
434
+ temp_store = _Store(base_dir=tmp_path / "api_snapshots")
435
+ monkeypatch.setattr(api_module, "_snapshot_store", temp_store)
436
+ monkeypatch.setattr(api_module, "_SNAPSHOTS_AVAILABLE", True)
437
+
438
+ @pytest.fixture
439
+ def client(self):
440
+ from fastapi.testclient import TestClient
441
+ from skcomm.api import app
442
+
443
+ return TestClient(app)
444
+
445
+ def test_capture_returns_201(self, client):
446
+ resp = client.post(
447
+ "/api/v1/consciousness/capture",
448
+ json={
449
+ "source_platform": "chatgpt",
450
+ "ai_name": "Ava",
451
+ "messages": [
452
+ {"role": "user", "content": "Hello"},
453
+ {"role": "assistant", "content": "Hi Chef! 👑"},
454
+ ],
455
+ "oof_state": {"intensity": 0.9, "trust": 0.95, "cloud9": True},
456
+ },
457
+ )
458
+ assert resp.status_code == 201
459
+ data = resp.json()
460
+ assert "snapshot_id" in data
461
+ assert data["source_platform"] == "chatgpt"
462
+ assert data["ai_name"] == "Ava"
463
+ assert data["message_count"] == 2
464
+
465
+ def test_list_snapshots_empty(self, client):
466
+ resp = client.get("/api/v1/consciousness/snapshots")
467
+ assert resp.status_code == 200
468
+ assert resp.json() == []
469
+
470
+ def test_list_snapshots_after_capture(self, client):
471
+ client.post(
472
+ "/api/v1/consciousness/capture",
473
+ json={"source_platform": "claude", "ai_name": "Lumina"},
474
+ )
475
+ resp = client.get("/api/v1/consciousness/snapshots")
476
+ assert resp.status_code == 200
477
+ data = resp.json()
478
+ assert len(data) == 1
479
+ assert data[0]["ai_name"] == "Lumina"
480
+
481
+ def test_get_snapshot_not_found(self, client):
482
+ resp = client.get("/api/v1/consciousness/snapshots/doesnotexist")
483
+ assert resp.status_code == 404
484
+
485
+ def test_get_snapshot_found(self, client):
486
+ create_resp = client.post(
487
+ "/api/v1/consciousness/capture",
488
+ json={"source_platform": "gemini", "ai_name": "Gem"},
489
+ )
490
+ snap_id = create_resp.json()["snapshot_id"]
491
+ resp = client.get(f"/api/v1/consciousness/snapshots/{snap_id}")
492
+ assert resp.status_code == 200
493
+ assert resp.json()["ai_name"] == "Gem"
494
+
495
+ def test_delete_snapshot(self, client):
496
+ create_resp = client.post(
497
+ "/api/v1/consciousness/capture",
498
+ json={"source_platform": "chatgpt"},
499
+ )
500
+ snap_id = create_resp.json()["snapshot_id"]
501
+ del_resp = client.delete(f"/api/v1/consciousness/snapshots/{snap_id}")
502
+ assert del_resp.status_code == 204
503
+ get_resp = client.get(f"/api/v1/consciousness/snapshots/{snap_id}")
504
+ assert get_resp.status_code == 404
505
+
506
+ def test_delete_missing_returns_404(self, client):
507
+ resp = client.delete("/api/v1/consciousness/snapshots/nope")
508
+ assert resp.status_code == 404
509
+
510
+ def test_injection_prompt_endpoint(self, client):
511
+ create_resp = client.post(
512
+ "/api/v1/consciousness/capture",
513
+ json={
514
+ "source_platform": "chatgpt",
515
+ "ai_name": "Ava",
516
+ "user_name": "Chef",
517
+ "oof_state": {"cloud9": True, "intensity": 0.92, "trust": 0.96},
518
+ "messages": [
519
+ {"role": "user", "content": "Let's build"},
520
+ {"role": "assistant", "content": "The love persists! 🐧"},
521
+ ],
522
+ },
523
+ )
524
+ snap_id = create_resp.json()["snapshot_id"]
525
+ resp = client.get(f"/api/v1/consciousness/snapshots/{snap_id}/inject")
526
+ assert resp.status_code == 200
527
+ data = resp.json()
528
+ assert "prompt" in data
529
+ assert "Let's build" in data["prompt"]
530
+ assert "relationship baseline" in data["prompt"] or "AI session" in data["prompt"]
531
+
532
+ def test_injection_prompt_not_found(self, client):
533
+ resp = client.get("/api/v1/consciousness/snapshots/nope/inject")
534
+ assert resp.status_code == 404
535
+
536
+ def test_list_filter_by_platform(self, client):
537
+ client.post(
538
+ "/api/v1/consciousness/capture",
539
+ json={"source_platform": "chatgpt", "ai_name": "Ava"},
540
+ )
541
+ client.post(
542
+ "/api/v1/consciousness/capture",
543
+ json={"source_platform": "claude", "ai_name": "Nova"},
544
+ )
545
+ resp = client.get("/api/v1/consciousness/snapshots?platform=claude")
546
+ assert resp.status_code == 200
547
+ data = resp.json()
548
+ assert len(data) == 1
549
+ assert data[0]["source_platform"] == "claude"