swarph-cli 0.60.0__tar.gz → 0.62.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (404) hide show
  1. {swarph_cli-0.60.0/src/swarph_cli.egg-info → swarph_cli-0.62.0}/PKG-INFO +1 -1
  2. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/pyproject.toml +2 -1
  3. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/__init__.py +1 -1
  4. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/cell.py +1 -1
  5. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/init.py +1 -0
  6. swarph_cli-0.62.0/src/swarph_cli/commands/install_opencode_plugin.py +173 -0
  7. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/memory_emit_hook.py +29 -7
  8. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/monitor.py +95 -2
  9. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/spawn.py +288 -1
  10. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/wake_hook_output.py +1 -1
  11. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/dreaming/enrich.py +28 -6
  12. swarph_cli-0.62.0/src/swarph_cli/dreaming/slm.py +120 -0
  13. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/main.py +1 -0
  14. swarph_cli-0.62.0/src/swarph_cli/payloads/opencode/plugin.js +82 -0
  15. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-monitor-reexec.path +1 -0
  16. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-monitor-reexec.service +26 -1
  17. {swarph_cli-0.60.0 → swarph_cli-0.62.0/src/swarph_cli.egg-info}/PKG-INFO +1 -1
  18. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli.egg-info/SOURCES.txt +7 -0
  19. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_654_reexec.py +87 -0
  20. swarph_cli-0.62.0/tests/test_656_owned_slm.py +184 -0
  21. swarph_cli-0.62.0/tests/test_memory_emit_snippet.py +43 -0
  22. swarph_cli-0.62.0/tests/test_opencode_membrane.py +510 -0
  23. swarph_cli-0.62.0/tests/test_opencode_plugin.py +144 -0
  24. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/LICENSE +0 -0
  25. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/README.md +0 -0
  26. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/setup.cfg +0 -0
  27. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/__main__.py +0 -0
  28. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/bench/__init__.py +0 -0
  29. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/bench/backends.py +0 -0
  30. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/bench/pack.py +0 -0
  31. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/bench/prices.py +0 -0
  32. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/bench/quality.py +0 -0
  33. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/bench/refresh_prices.py +0 -0
  34. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/bench/runner.py +0 -0
  35. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/bench/validate.py +0 -0
  36. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/caller.py +0 -0
  37. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/capture/__init__.py +0 -0
  38. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/capture/harden.py +0 -0
  39. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/capture/lineage.py +0 -0
  40. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/capture/liveness.py +0 -0
  41. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/capture/manifest.py +0 -0
  42. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/capture/paths.py +0 -0
  43. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/capture/verify.py +0 -0
  44. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/chain_token.py +0 -0
  45. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/__init__.py +0 -0
  46. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/_content.py +0 -0
  47. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/_display.py +0 -0
  48. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/_gateway_client.py +0 -0
  49. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/add.py +0 -0
  50. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/bench.py +0 -0
  51. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/board.py +0 -0
  52. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/board_merge_check.py +0 -0
  53. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/brain.py +0 -0
  54. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/brain_ask.py +0 -0
  55. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/cell.py +0 -0
  56. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/cell_probe.py +0 -0
  57. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  58. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/channel.py +0 -0
  59. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/chat.py +0 -0
  60. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/codegraph.py +0 -0
  61. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/codegraph_hook.py +0 -0
  62. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/codex_hook_output.py +0 -0
  63. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/codex_waker.py +0 -0
  64. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/compress.py +0 -0
  65. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/daemon.py +0 -0
  66. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/dreaming.py +0 -0
  67. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/event.py +0 -0
  68. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/gateway.py +0 -0
  69. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/gh_route.py +0 -0
  70. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/group.py +0 -0
  71. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/guide.py +0 -0
  72. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/highlight.py +0 -0
  73. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/hook_output.py +0 -0
  74. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/hooks.py +0 -0
  75. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/import_session.py +0 -0
  76. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
  77. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/install_hook.py +0 -0
  78. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  79. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
  80. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/install_wake_hook.py +0 -0
  81. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/lane.py +0 -0
  82. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/mcp_server.py +0 -0
  83. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/memory.py +0 -0
  84. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/memory_sync.py +0 -0
  85. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/mesh.py +0 -0
  86. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/okf_links.py +0 -0
  87. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/onboard.py +0 -0
  88. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
  89. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
  90. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/protocol_handler.py +0 -0
  91. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/ratify.py +0 -0
  92. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/schedule.py +0 -0
  93. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/security.py +0 -0
  94. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/service.py +0 -0
  95. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/timeline.py +0 -0
  96. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/version.py +0 -0
  97. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/watchdog.py +0 -0
  98. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/compress/__init__.py +0 -0
  99. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/compress/levers.py +0 -0
  100. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/compress/marker.py +0 -0
  101. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/compress/verify.py +0 -0
  102. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/console_safe.py +0 -0
  103. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/delivery_queue.py +0 -0
  104. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/dreaming/__init__.py +0 -0
  105. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/dreaming/candidates.py +0 -0
  106. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/dreaming/clone.py +0 -0
  107. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/dreaming/index_check.py +0 -0
  108. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/dreaming/organize.py +0 -0
  109. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/dreaming/probes.py +0 -0
  110. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/dreaming/report.py +0 -0
  111. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/dreaming/run.py +0 -0
  112. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/dreaming/transcripts.py +0 -0
  113. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/dreaming/verify.py +0 -0
  114. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/gateway/__init__.py +0 -0
  115. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/gateway/feature_registry.py +0 -0
  116. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/gateway/lanes_control.py +0 -0
  117. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/gateway/schema.sql +0 -0
  118. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/gateway/server.py +0 -0
  119. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/gateway/services_control.py +0 -0
  120. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/gateway_default.py +0 -0
  121. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/gh_identity.py +0 -0
  122. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/guide/GUIDE.md +0 -0
  123. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/multiplexer.py +0 -0
  124. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/orphan_daemons.py +0 -0
  125. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/pane_probe.py +0 -0
  126. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/parsers/__init__.py +0 -0
  127. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/parsers/claude.py +0 -0
  128. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
  129. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
  130. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
  131. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
  132. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
  133. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
  134. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
  135. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
  136. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
  137. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
  138. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
  139. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/peer_executor.py +0 -0
  140. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/peer_executor_windows.py +0 -0
  141. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/peer_reconciliation.py +0 -0
  142. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/peer_reply_outbox.py +0 -0
  143. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/peer_service_host.py +0 -0
  144. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/peer_service_payload.py +0 -0
  145. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/peer_service_reply.py +0 -0
  146. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/peer_staging.py +0 -0
  147. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/query_filters.py +0 -0
  148. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/scripts/__init__.py +0 -0
  149. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
  150. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  151. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
  152. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
  153. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/service/__init__.py +0 -0
  154. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/service/app.py +0 -0
  155. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/service/providers.py +0 -0
  156. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/session_bridge.py +0 -0
  157. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
  158. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
  159. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/stall_alert.py +0 -0
  160. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/codex-waker.default +0 -0
  161. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
  162. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
  163. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
  164. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
  165. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
  166. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
  167. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
  168. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
  169. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  170. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  171. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  172. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/timeline_paths.py +0 -0
  173. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/tokens.py +0 -0
  174. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  175. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  176. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli.egg-info/requires.txt +0 -0
  177. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli.egg-info/top_level.txt +0 -0
  178. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_124_register_merge.py +0 -0
  179. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_132_ensure_monitor_state_dir.py +0 -0
  180. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_139_tool_probe.py +0 -0
  181. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_183_card_anchor.py +0 -0
  182. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_201_win32_bash_refusal.py +0 -0
  183. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_243_onboard_per_peer_token.py +0 -0
  184. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_307_task4_ask_reply.py +0 -0
  185. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
  186. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_319_refusal_exit_codes.py +0 -0
  187. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_323_mistral_backend.py +0 -0
  188. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_356_query_filters.py +0 -0
  189. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_397_gh_identity_router.py +0 -0
  190. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_397_router_preflight.py +0 -0
  191. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_467b_onboard_operator_credential.py +0 -0
  192. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_523_guide.py +0 -0
  193. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_524_conftest_hermeticity.py +0 -0
  194. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_532_accept_flag.py +0 -0
  195. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_535_version_capability.py +0 -0
  196. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_546_default_gateway.py +0 -0
  197. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_548_gateway_remainder.py +0 -0
  198. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_552_codegraph_gateway.py +0 -0
  199. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_555_relay_row_shape.py +0 -0
  200. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_556_relay_freshness.py +0 -0
  201. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_564_bootstrap_first_peer.py +0 -0
  202. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
  203. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_579_register_bootstrap_message.py +0 -0
  204. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_591_cli_step_graph.py +0 -0
  205. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_644_monitor_supervision.py +0 -0
  206. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_648_install_unit.py +0 -0
  207. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_684_dreaming_verb.py +0 -0
  208. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_689_pkg_version_order.py +0 -0
  209. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_716_timeline_one_path.py +0 -0
  210. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_724_ps1_ascii_or_bom.py +0 -0
  211. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_725_stdio_reconfigure.py +0 -0
  212. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_734_dreaming_enrich_degrade.py +0 -0
  213. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_740_edit_project.py +0 -0
  214. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_762_cards_edit_priority.py +0 -0
  215. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_801_confirm_flow_cli.py +0 -0
  216. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_802_stage_history.py +0 -0
  217. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_825_codegraph_hook_noise.py +0 -0
  218. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_829_counterfactual_column.py +0 -0
  219. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_830_hook_binding_version.py +0 -0
  220. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_84_cli_labels.py +0 -0
  221. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_add_security_gate.py +0 -0
  222. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_artifact_add.py +0 -0
  223. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_artifact_hash.py +0 -0
  224. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_artifact_lib.py +0 -0
  225. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_artifact_mcp.py +0 -0
  226. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_artifact_skill.py +0 -0
  227. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_artifact_tool.py +0 -0
  228. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_artifact_uri.py +0 -0
  229. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_bench_backends.py +0 -0
  230. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_bench_command.py +0 -0
  231. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_bench_pack.py +0 -0
  232. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_bench_prices.py +0 -0
  233. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_bench_quality.py +0 -0
  234. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_bench_runner.py +0 -0
  235. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_bench_validate.py +0 -0
  236. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_board_card_thread.py +0 -0
  237. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_board_command.py +0 -0
  238. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_board_merge_check.py +0 -0
  239. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_board_obligation_close.py +0 -0
  240. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_brain_ask_command.py +0 -0
  241. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_brain_ask_env_diagnosis.py +0 -0
  242. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_brain_ask_identity_explicitness.py +0 -0
  243. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_brain_command.py +0 -0
  244. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_caller_meta_guard.py +0 -0
  245. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_capture_lineage.py +0 -0
  246. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_capture_liveness.py +0 -0
  247. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_capture_manifest.py +0 -0
  248. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_capture_paths.py +0 -0
  249. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_capture_security.py +0 -0
  250. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_cell_command_dispatch.py +0 -0
  251. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_cell_harden.py +0 -0
  252. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_cell_loader.py +0 -0
  253. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_cell_selfcheck.py +0 -0
  254. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_cell_verify.py +0 -0
  255. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_chain_token.py +0 -0
  256. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_channel_command.py +0 -0
  257. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_channel_poll_never_breaks_dms.py +0 -0
  258. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_channel_post.py +0 -0
  259. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_channel_wake_policy_filter.py +0 -0
  260. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_chat_command.py +0 -0
  261. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_claude_parser.py +0 -0
  262. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_claude_tmux_template.py +0 -0
  263. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_codegraph_caller_scope.py +0 -0
  264. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_codegraph_command.py +0 -0
  265. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_codegraph_hook.py +0 -0
  266. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_codegraph_mcp.py +0 -0
  267. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_codex_waker.py +0 -0
  268. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_compress_caller_convention.py +0 -0
  269. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_compress_command.py +0 -0
  270. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_compress_levers.py +0 -0
  271. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_compress_marker.py +0 -0
  272. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_compress_verify.py +0 -0
  273. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_console_encoding_e2e.py +0 -0
  274. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_content_composition.py +0 -0
  275. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_cursor_membrane.py +0 -0
  276. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_daemon_command.py +0 -0
  277. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_daemon_console_encoding.py +0 -0
  278. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_daemon_delivery.py +0 -0
  279. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_delivery_queue.py +0 -0
  280. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_display_sanitize.py +0 -0
  281. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_ensure_monitor_no_peer_default.py +0 -0
  282. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_event_emit.py +0 -0
  283. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_feature_to_uri.py +0 -0
  284. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_gateway_brain.py +0 -0
  285. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_gateway_client.py +0 -0
  286. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_gateway_command.py +0 -0
  287. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_gateway_message_idempotency.py +0 -0
  288. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_gh_route_doctor_mesh_name.py +0 -0
  289. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_group_command.py +0 -0
  290. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_guide_commands_parse.py +0 -0
  291. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_guide_selfcheck.py +0 -0
  292. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_heartbeat_check.py +0 -0
  293. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_highlight_command.py +0 -0
  294. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_hook_output.py +0 -0
  295. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_hooks_activity_marker.py +0 -0
  296. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_hooks_add.py +0 -0
  297. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_hooks_bundle.py +0 -0
  298. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_hooks_init.py +0 -0
  299. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_hooks_legacy_generation_removal.py +0 -0
  300. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_hooks_lifecycle.py +0 -0
  301. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_hooks_merge.py +0 -0
  302. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_hooks_verify.py +0 -0
  303. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_hooks_win32_bash_wrapper.py +0 -0
  304. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_hooks_win32_command_path.py +0 -0
  305. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_import_command.py +0 -0
  306. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_inbox_identity_provenance.py +0 -0
  307. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_init_command.py +0 -0
  308. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_init_provider_menu.py +0 -0
  309. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_install_codex_hooks.py +0 -0
  310. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_install_hook.py +0 -0
  311. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_install_multiplexer_command.py +0 -0
  312. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_install_postcompact_hook.py +0 -0
  313. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_install_wake_hook.py +0 -0
  314. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_lane_command.py +0 -0
  315. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_main.py +0 -0
  316. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_main_unknown_verb_guard.py +0 -0
  317. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_mcp_missing_hint.py +0 -0
  318. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_mcp_server.py +0 -0
  319. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_memory_command.py +0 -0
  320. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_memory_gateway.py +0 -0
  321. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_memory_sync.py +0 -0
  322. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_memory_sync_membrane.py +0 -0
  323. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_mesh_command.py +0 -0
  324. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_mesh_inbox_mark_read.py +0 -0
  325. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_mesh_inbox_sanitize.py +0 -0
  326. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_mesh_send_unknown_peer.py +0 -0
  327. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_mesh_sidecar.py +0 -0
  328. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  329. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  330. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_meta_edge_identity.py +0 -0
  331. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_monitor_channel_poll.py +0 -0
  332. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_monitor_cli.py +0 -0
  333. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_monitor_daemon_collision.py +0 -0
  334. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_monitor_self_name_verification.py +0 -0
  335. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_monitor_state_split.py +0 -0
  336. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_multiplexer.py +0 -0
  337. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_muse_membrane.py +0 -0
  338. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_muse_own_cli.py +0 -0
  339. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_obligation_close_is_surfaced.py +0 -0
  340. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_okf_links.py +0 -0
  341. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_onboard_command.py +0 -0
  342. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_orphan_daemons.py +0 -0
  343. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_packaged_artifacts_exist.py +0 -0
  344. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_packaged_skills.py +0 -0
  345. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_peer_executor.py +0 -0
  346. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_peer_executor_windows.py +0 -0
  347. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_peer_reply_drain_command.py +0 -0
  348. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_peer_reply_drain_supervisor.py +0 -0
  349. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_peer_reply_outbox.py +0 -0
  350. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_peer_reply_transport.py +0 -0
  351. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_peer_service_e2e.py +0 -0
  352. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_peer_service_host.py +0 -0
  353. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_peer_service_payload.py +0 -0
  354. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_peer_service_reply.py +0 -0
  355. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_postcompact_recall.py +0 -0
  356. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_process_liveness.py +0 -0
  357. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_protocol_handler.py +0 -0
  358. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_providers_isolation.py +0 -0
  359. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_ratify_command.py +0 -0
  360. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_readme_claims_no_gateway_default.py +0 -0
  361. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_repl_caller_convention.py +0 -0
  362. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_schedule_command.py +0 -0
  363. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_schedule_create_contract.py +0 -0
  364. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_security.py +0 -0
  365. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_service_app.py +0 -0
  366. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_service_command.py +0 -0
  367. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_service_providers.py +0 -0
  368. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_session_bridge_inject.py +0 -0
  369. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_session_bridge_probe.py +0 -0
  370. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_session_bridge_resolve.py +0 -0
  371. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_session_path_role_gate.py +0 -0
  372. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_shipped_units_match_the_cli.py +0 -0
  373. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_smoke_chat.py +0 -0
  374. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_smoke_one_shot.py +0 -0
  375. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_smoke_phase_5_5.py +0 -0
  376. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_spawn_base_hoist.py +0 -0
  377. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_spawn_command.py +0 -0
  378. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_spawn_identity_stamp.py +0 -0
  379. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_spawn_live_pin.py +0 -0
  380. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_spawn_mitosis_lineage.py +0 -0
  381. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_spawn_tmux_session.py +0 -0
  382. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_spawn_windows_relaunch.py +0 -0
  383. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_stall_alert.py +0 -0
  384. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_subprocess_decode_encoding.py +0 -0
  385. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_timeline_command.py +0 -0
  386. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_tmux_wake_runstate_defer.py +0 -0
  387. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_tmux_wake_session_reanchor.py +0 -0
  388. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_tmux_wake_stale.py +0 -0
  389. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_tmux_wake_submit_verify.py +0 -0
  390. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_token_file_one_parser.py +0 -0
  391. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_token_precedence_explicit_first.py +0 -0
  392. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_unified_token_resolver.py +0 -0
  393. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_version_command.py +0 -0
  394. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_version_consistency.py +0 -0
  395. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_vibe_membrane.py +0 -0
  396. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_watchdog.py +0 -0
  397. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_watchdog_dm_wake.py +0 -0
  398. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  399. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  400. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_watchdog_emit_health.py +0 -0
  401. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_watchdog_install_flags.py +0 -0
  402. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_watchdog_model_rung.py +0 -0
  403. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_watchdog_stale_peers.py +0 -0
  404. {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_windows_waker_installer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.60.0
3
+ Version: 0.62.0
4
4
  Summary: The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`.
5
5
  Author: Pierre Samson, Claude Opus
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "swarph-cli"
7
- version = "0.60.0"
7
+ version = "0.62.0"
8
8
  description = "The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -146,6 +146,7 @@ swarph_cli = ["systemd/*.service", "systemd/*.timer", "systemd/*.default",
146
146
  "systemd/*.path",
147
147
  "scripts/*.sh", "scripts/*.ps1", "guide/*.md",
148
148
  "payloads/postcompact/*.sh", "payloads/postcompact/*.cmd",
149
+ "payloads/opencode/*.js",
149
150
  "skills/*/SKILL.md", "skills/*/LICENSE"]
150
151
  # v0.15: ship the bundled mesh-gateway schema so `swarph gateway serve`
151
152
  # can apply it at startup via MESH_SCHEMA_PATH (defaults next to server.py).
@@ -16,6 +16,6 @@ The architecture splits CLI from substrate so:
16
16
 
17
17
  from __future__ import annotations
18
18
 
19
- __version__ = "0.60.0"
19
+ __version__ = "0.62.0"
20
20
 
21
21
  __all__ = ["__version__"]
@@ -66,7 +66,7 @@ _validate_uuid = validate_uuid_str # historical helper name
66
66
  # ONE swarph-cli release, and entry into swarph_shared.VALID_PROVIDERS only in a
67
67
  # later one. spawn.py's guard is `VALID_PROVIDERS ⊆ MEMBRANES`, so this direction
68
68
  # is inert while the reverse raises at import for every fresh install (#247).
69
- CLI_ENABLED_PROVIDERS = VALID_PROVIDERS | {"muse", "cursor"}
69
+ CLI_ENABLED_PROVIDERS = VALID_PROVIDERS | {"muse", "cursor", "opencode"}
70
70
 
71
71
 
72
72
  def _config_root() -> Path:
@@ -37,6 +37,7 @@ _LLM_BLURBS = {
37
37
  "grok": "xAI Grok — grok membrane",
38
38
  "muse": "Muse — muse membrane",
39
39
  "vibe": "Vibe — vibe membrane",
40
+ "opencode": "OpenCode — opencode membrane (XDG config+data isolation)",
40
41
  }
41
42
 
42
43
 
@@ -0,0 +1,173 @@
1
+ """``swarph install-opencode-plugin`` — the OpenCode plugin that wires swarph hooks.
2
+
3
+ Part 2 of the opencode membrane. opencode has NO command-hook surface — its only
4
+ hook surface is a JavaScript plugin loaded from the config dir's ``plugins/``
5
+ tree (auto-loaded, no ``opencode.json`` ``plugin`` entry required; measured:
6
+ ``~/.config/opencode/plugin/opensling.js`` loads against a config containing only
7
+ ``$schema``). This verb renders the package payload (baking the pinned Python
8
+ interpreter, the same reason ``install_codex_hooks`` pins it: PATH presence is
9
+ not a liveness proof) and writes it as ``swarph-opencode.js``:
10
+
11
+ * ``--scope user`` → ``~/.config/opencode/plugins/swarph-opencode.js`` —
12
+ the OPERATOR's bare ``opencode`` sessions (outside a swarph cell).
13
+ * ``--scope project`` → ``<cwd>/.opencode/plugins/swarph-opencode.js`` — a
14
+ per-project wiring.
15
+
16
+ The swarph CELL does NOT read either of those locations: its membrane relocates
17
+ ``XDG_CONFIG_HOME`` to ``<cwd>/.opencode-cell/config``, so its plugin lives at
18
+ ``<cwd>/.opencode-cell/config/opencode/plugins/swarph-opencode.js`` and is
19
+ written by the membrane's ``_opencode_env`` via ``ensure_cell_plugin`` — a
20
+ spawned cell is wired without a separate onboarding step.
21
+
22
+ Identity (SWARPH_SELF / SWARPH_MEMORY_DIR) is read by the plugin from env AT
23
+ RUNTIME, not baked — the membrane already stamps SWARPH_SELF into the cell env,
24
+ and a bare-session install inherits the operator's env at hook time.
25
+
26
+ Idempotent, ``--uninstall``, ``--dry-run``, write-landed assert — the same
27
+ operator contract as every other hook installer (#527 task 3).
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ import argparse
33
+ import json
34
+ import sys
35
+ from importlib import resources
36
+ from pathlib import Path
37
+
38
+ from swarph_cli.cell import _atomic_write_text
39
+
40
+ _PAYLOAD = "swarph_cli.payloads.opencode"
41
+ _PLUGIN_NAME = "swarph-opencode.js"
42
+
43
+
44
+ def _payload_text() -> str:
45
+ """Read the plugin FROM THE PACKAGE — never a source-tree path, so tests
46
+ exercise the same importlib.resources lookup an installed wheel depends on
47
+ (the 0.39.3 undeclared-package-data lesson)."""
48
+ return resources.files(_PAYLOAD).joinpath("plugin.js").read_text(encoding="utf-8")
49
+
50
+
51
+ def _render_plugin(interpreter: str) -> str:
52
+ r"""Render the plugin with @PYTHON@ replaced by ``interpreter`` AS A JS STRING.
53
+
54
+ >>> shlex.quote is WRONG here, and it was the shipped defect (#423 review).
55
+ The placeholder sits on the RHS of ``const PY = @PYTHON@;`` — a JavaScript
56
+ string literal position, NOT shell text. ``shlex.quote`` wraps in single
57
+ quotes and leaves backslashes raw, so on Windows
58
+ ``C:\Program Files\Python\python.exe`` became
59
+ ``const PY = "'C:\Program Files\Python\python.exe'";`` — invalid escapes, and
60
+ the single quotes land INSIDE the double quotes, so the value is a wrong path
61
+ even where it parses. json.dumps produces a valid JS string literal (double
62
+ quoted, backslashes escaped as ``\\``), which a JS parser accepts and decodes
63
+ back to the exact path. The template leaves the quotes to the substitution
64
+ (``const PY = @PYTHON@;``) so they are never doubled. <<<
65
+ """
66
+ return _payload_text().replace("@PYTHON@", json.dumps(interpreter))
67
+
68
+
69
+ def _render() -> str:
70
+ """The rendered plugin, with @PYTHON@ pinned to THIS interpreter."""
71
+ return _render_plugin(str(Path(sys.executable).resolve()))
72
+
73
+
74
+ def _target(scope: str) -> Path:
75
+ if scope == "project":
76
+ return Path.cwd() / ".opencode" / "plugins" / _PLUGIN_NAME
77
+ return Path.home() / ".config" / "opencode" / "plugins" / _PLUGIN_NAME
78
+
79
+
80
+ def cell_plugin_path(cwd: Path) -> Path:
81
+ """Where a cell's plugin lives inside its isolated config dir.
82
+
83
+ Mirrors ``_OPENCODE_CELL_SUBDIR`` + the XDG_CONFIG_HOME layout the membrane
84
+ sets in ``_opencode_env`` — kept here (not imported from spawn) so spawn can
85
+ call back into it without a cycle.
86
+ """
87
+ return cwd / ".opencode-cell" / "config" / "opencode" / "plugins" / _PLUGIN_NAME
88
+
89
+
90
+ def ensure_cell_plugin(cwd: Path) -> None:
91
+ """Write the cell's plugin if absent or changed. Idempotent + best-effort:
92
+ a failure must not block the spawn (worst case the cell runs unhooked)."""
93
+ try:
94
+ rendered = _render()
95
+ target = cell_plugin_path(cwd)
96
+ if target.exists() and target.read_text(encoding="utf-8") == rendered:
97
+ return
98
+ target.parent.mkdir(parents=True, exist_ok=True)
99
+ _atomic_write_text(target, rendered)
100
+ except Exception:
101
+ pass
102
+
103
+
104
+ def _read_existing(target: Path) -> str:
105
+ return target.read_text(encoding="utf-8") if target.exists() else ""
106
+
107
+
108
+ def run_install_opencode_plugin(argv: list[str] | None = None) -> int:
109
+ if argv is None:
110
+ argv = sys.argv[2:]
111
+
112
+ p = argparse.ArgumentParser(prog="swarph install-opencode-plugin")
113
+ p.add_argument("--scope", choices=("user", "project"), default="user")
114
+ p.add_argument("--uninstall", action="store_true")
115
+ p.add_argument("--dry-run", action="store_true")
116
+ args = p.parse_args(argv)
117
+
118
+ target = _target(args.scope)
119
+
120
+ if args.dry_run:
121
+ print(f"# swarph install-opencode-plugin --dry-run", file=sys.stderr)
122
+ print(f"# scope: {args.scope}", file=sys.stderr)
123
+ print(f"# target: {target}", file=sys.stderr)
124
+ print(f"# action: {'uninstall' if args.uninstall else 'install'}", file=sys.stderr)
125
+ if not args.uninstall:
126
+ print(_render())
127
+ return 0
128
+
129
+ if args.uninstall:
130
+ existed = target.exists()
131
+ if existed:
132
+ target.unlink()
133
+ print(
134
+ f"swarph install-opencode-plugin: uninstalled {_PLUGIN_NAME} from "
135
+ f"{target} ({'removed' if existed else 'was absent'}).",
136
+ file=sys.stderr,
137
+ )
138
+ return 0
139
+
140
+ rendered = _render()
141
+ if _read_existing(target) == rendered:
142
+ print(
143
+ f"swarph install-opencode-plugin: no change needed at {target} "
144
+ "(plugin already in desired state).",
145
+ file=sys.stderr,
146
+ )
147
+ return 0
148
+
149
+ target.parent.mkdir(parents=True, exist_ok=True)
150
+ _atomic_write_text(target, rendered)
151
+ # The #527 task-3 assert: "installed" is a claim about the filesystem.
152
+ if target.read_text(encoding="utf-8") != rendered:
153
+ print(
154
+ f"swarph install-opencode-plugin: WRITE DID NOT LAND — {target} "
155
+ "re-reads as different content than was written. The install is NOT "
156
+ "in effect.",
157
+ file=sys.stderr,
158
+ )
159
+ return 2
160
+ print(
161
+ f"swarph install-opencode-plugin: installed {_PLUGIN_NAME} at {target}.",
162
+ file=sys.stderr,
163
+ )
164
+ print(
165
+ "NOTE: opencode loads the plugin table at session start. Already-open "
166
+ "sessions keep the pre-edit table — open a FRESH session before testing.",
167
+ file=sys.stderr,
168
+ )
169
+ return 0
170
+
171
+
172
+ if __name__ == "__main__":
173
+ raise SystemExit(run_install_opencode_plugin(sys.argv[1:]))
@@ -67,15 +67,37 @@ def _memory_slug(file_path: str) -> Optional[str]:
67
67
 
68
68
 
69
69
  def _snippet(path: Path) -> str:
70
- """First non-empty line, de-headed, collapsed, truncated. Unreadable file →
71
- empty snippet (the pointer still emits — the write happened either way)."""
70
+ """One-line summary of a memory: its frontmatter `description`, else the first body
71
+ line. Unreadable file → empty snippet (the pointer still emits — the write happened).
72
+
73
+ Every memory file opens with YAML frontmatter, so "first non-empty line" returned the
74
+ literal `---` delimiter. That is a NON-EMPTY STRING, so the caller's `if snippet` guard
75
+ passed and the timeline took `memory cached: ---`. MEASURED 2026-09-15: 21 such entries
76
+ on the shared timeline between 2026-08-25 and 2026-09-15, every one of them useless.
77
+ An emptiness guard cannot catch a delimiter; the fix has to be here, not at the guard.
78
+ """
72
79
  try:
73
- for line in path.read_text(encoding="utf-8", errors="replace").splitlines():
74
- s = line.strip().lstrip("#").strip()
75
- if s:
76
- return " ".join(s.split())[:_SNIPPET_CHARS]
80
+ lines = path.read_text(encoding="utf-8", errors="replace").splitlines()
77
81
  except Exception:
78
- pass
82
+ return ""
83
+ body = lines
84
+ if lines and lines[0].strip() == "---":
85
+ for i, line in enumerate(lines[1:], start=1):
86
+ if line.strip() == "---":
87
+ # `description:` exists precisely to be a one-line summary — prefer it.
88
+ for fm in lines[1:i]:
89
+ if fm.strip().lower().startswith("description:"):
90
+ d = fm.split(":", 1)[1].strip().strip('"').strip("'")
91
+ if d:
92
+ return " ".join(d.split())[:_SNIPPET_CHARS]
93
+ body = lines[i + 1:]
94
+ break
95
+ else:
96
+ body = lines[1:] # unterminated frontmatter: skip the opening delimiter
97
+ for line in body:
98
+ s = line.strip().lstrip("#").strip()
99
+ if s and s != "---": # a stray delimiter is never a summary
100
+ return " ".join(s.split())[:_SNIPPET_CHARS]
79
101
  return ""
80
102
 
81
103
 
@@ -1703,7 +1703,7 @@ def _cmd_clear_reexec_hold(args: argparse.Namespace) -> int:
1703
1703
  return 0
1704
1704
 
1705
1705
 
1706
- def _resolve_consumed_tree(swarph_bin: str) -> tuple:
1706
+ def _resolve_consumed_tree(swarph_bin: str, pythonpath: str = None) -> tuple:
1707
1707
  """#807: (interpreter, init_path, how) for the tree the swarph BINARY loads.
1708
1708
  Reads the shim's shebang, asks that interpreter where swarph_cli lives. The
1709
1709
  installer's own interpreter is the wrong witness (a pipx-run installer watched
@@ -1722,6 +1722,8 @@ def _resolve_consumed_tree(swarph_bin: str) -> tuple:
1722
1722
  # whoever runs the installer (a dev shell's PYTHONPATH=src would otherwise be
1723
1723
  # reported as "the tree the monitors load" — the exact misreading this fixes).
1724
1724
  env = {k: v for k, v in os.environ.items() if k not in ("PYTHONPATH", "PYTHONHOME", "PYTHONSAFEPATH")}
1725
+ if pythonpath:
1726
+ env["PYTHONPATH"] = pythonpath
1725
1727
  try:
1726
1728
  r = subprocess.run([interp, "-c", "import swarph_cli, sys; print(swarph_cli.__file__)"],
1727
1729
  capture_output=True, text=True, encoding="utf-8", errors="replace",
@@ -1733,6 +1735,55 @@ def _resolve_consumed_tree(swarph_bin: str) -> tuple:
1733
1735
  return interp, r.stdout.strip().replace("\\", "/"), f"resolved from {swarph_bin} -> {interp}"
1734
1736
 
1735
1737
 
1738
+ _USER_SITE_RE = re.compile(r"^(.*/\.local/lib/python[0-9]+\.[0-9]+/site-packages)/swarph_cli/__init__\.py$")
1739
+
1740
+
1741
+ def _user_site_of(init_path: str):
1742
+ """The user-site dir a swarph_cli __init__ lives in, or None (pipx venv, system)."""
1743
+ m = _USER_SITE_RE.match((init_path or "").replace("\\", "/"))
1744
+ return m.group(1) if m else None
1745
+
1746
+
1747
+ def _owner_user_site(swarph_bin: str, interp: str):
1748
+ """#807 (root cannot see the user site): the pip --user tree of the SHIM'S OWNER for
1749
+ the shim's interpreter — ~owner/.local/lib/pythonX.Y/site-packages — if swarph_cli
1750
+ is there. The unit runs as root and root's interpreter never searches another
1751
+ user's site, so the installer must find it by construction, not by import."""
1752
+ import pwd, subprocess
1753
+ try:
1754
+ home = pwd.getpwuid(Path(swarph_bin).stat().st_uid).pw_dir
1755
+ r = subprocess.run([interp, "-c", "import sys; print('%d.%d' % sys.version_info[:2])"],
1756
+ capture_output=True, text=True, encoding="utf-8", errors="replace",
1757
+ timeout=20)
1758
+ except (OSError, KeyError, subprocess.SubprocessError):
1759
+ return None
1760
+ ver = r.stdout.strip()
1761
+ if r.returncode != 0 or not re.fullmatch(r"[0-9]+\.[0-9]+", ver):
1762
+ return None
1763
+ cand = f"{home}/.local/lib/python{ver}/site-packages"
1764
+ return cand if Path(cand, "swarph_cli", "__init__.py").exists() else None
1765
+
1766
+
1767
+ def _condition_probe(interpreter: str, pythonpath: str = None) -> tuple:
1768
+ """Run the unit's ExecCondition NOW, as the user running this verb, with the env the
1769
+ unit will have. (ok, detail). The 2026-09-15 lesson: a condition that fails on every
1770
+ fire is a SKIP (Result=success) — OnFailure never fires, and the unit is quieter than
1771
+ the failed state it replaced. So the writer checks it before it writes."""
1772
+ import subprocess
1773
+ env = {k: v for k, v in os.environ.items() if k not in ("PYTHONPATH", "PYTHONHOME", "PYTHONSAFEPATH")}
1774
+ if pythonpath:
1775
+ env["PYTHONPATH"] = pythonpath
1776
+ try:
1777
+ r = subprocess.run([interpreter, "-c", "import swarph_cli"], capture_output=True, text=True,
1778
+ encoding="utf-8", errors="replace", timeout=20, env=env)
1779
+ except (OSError, subprocess.SubprocessError) as e:
1780
+ return False, f"{interpreter} could not be run ({type(e).__name__})"
1781
+ if r.returncode == 0:
1782
+ return True, "OK"
1783
+ last = (r.stderr.strip().splitlines() or ["(no stderr)"])[-1]
1784
+ return False, f"rc={r.returncode}: {last}"
1785
+
1786
+
1736
1787
  def _live_tree():
1737
1788
  """The tree the INSTALLER's own interpreter loads (a pipx-run installer reports the
1738
1789
  pipx venv), asked through the same scrubbed child as the consumed tree, so a dev
@@ -1772,12 +1823,39 @@ def _cmd_install_reexec(args: argparse.Namespace) -> int:
1772
1823
  # interpreter, not the interpreter running this verb.
1773
1824
  interpreter, init_path, how = _resolve_consumed_tree(swarph_bin)
1774
1825
  live_init = _live_tree()
1826
+ pythonpath = None
1827
+ if init_path is None and interpreter:
1828
+ # #807 (2026-09-15): run as root, the shim's interpreter cannot import a pip
1829
+ # --user install at all — the same ModuleNotFoundError as a mid-install
1830
+ # window, on EVERY fire. Find the owner's user site by construction.
1831
+ us = _owner_user_site(swarph_bin, interpreter)
1832
+ if us:
1833
+ _i, init2, how2 = _resolve_consumed_tree(swarph_bin, pythonpath=us)
1834
+ if init2:
1835
+ init_path, how = init2, (f"{how2} via the shim owner's user site — {interpreter} "
1836
+ f"alone cannot import it (this user is not the owner, e.g. root)")
1775
1837
  if init_path is None:
1776
1838
  if live_init is None:
1777
1839
  raise RuntimeError("cannot resolve the installed swarph_cli package "
1778
1840
  "path — is this a broken install?")
1779
1841
  init_path, how = live_init, f"{how}; fell back to the LIVE interpreter"
1780
1842
  extra_trees = [t for t in {live_init} if t and t != init_path]
1843
+ # A user-site tree is invisible to root's interpreter: the unit runs as root, so
1844
+ # the site the monitors load is handed to it explicitly. A pipx/venv tree needs
1845
+ # nothing (its interpreter owns its site) and gets no Environment= line.
1846
+ pythonpath = _user_site_of(init_path)
1847
+ env_line = f"Environment=PYTHONPATH={pythonpath}\n" if pythonpath else ""
1848
+ # Provenance (2026-09-15): a unit on disk could not say what produced it, so a
1849
+ # render from an unreleased checkout was indistinguishable from a release.
1850
+ # The PACKAGE PATH is the field that tells a release apart from a checkout: an
1851
+ # unreleased tree reports the last release's __version__ until the bump, and
1852
+ # the interpreter is the same either way (lab-ovh, #420 review, measured).
1853
+ import swarph_cli as _pkg
1854
+ from swarph_cli import __version__ as _ver
1855
+ from datetime import datetime, timezone
1856
+ rendered_by = (f"swarph-cli {_ver} ({str(_pkg.__file__).replace(chr(92), '/')}) "
1857
+ f"install-reexec, {sys.executable}, "
1858
+ f"{datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')}")
1781
1859
  rendered = {}
1782
1860
  for name, rel in _REEXEC_TEMPLATES.items():
1783
1861
  text = _read_packaged(rel)
@@ -1785,7 +1863,9 @@ def _cmd_install_reexec(args: argparse.Namespace) -> int:
1785
1863
  .replace("<SITE_PACKAGES_INIT>", init_path)
1786
1864
  .replace("<INTERPRETER>", interpreter or sys.executable)
1787
1865
  .replace("<SWARPH_BIN>", swarph_bin)
1788
- .replace("<STATE_ROOT>", state_root))
1866
+ .replace("<STATE_ROOT>", state_root)
1867
+ .replace("<ENV_PYTHONPATH>\n", env_line)
1868
+ .replace("<RENDERED_BY>", rendered_by))
1789
1869
  if name.endswith(".path") and extra_trees:
1790
1870
  out = out.replace(f"PathChanged={init_path}\n",
1791
1871
  f"PathChanged={init_path}\n"
@@ -1803,6 +1883,9 @@ def _cmd_install_reexec(args: argparse.Namespace) -> int:
1803
1883
  dropins[f"{name}.d/10-onfailure.conf"] = (
1804
1884
  f"# #807: written by `swarph monitor install-reexec --on-failure`; box-local.\n"
1805
1885
  f"[Unit]\nOnFailure={args.on_failure}\n")
1886
+ import getpass
1887
+ cond_ok, cond_detail = _condition_probe(interpreter or sys.executable, pythonpath)
1888
+ who = getpass.getuser()
1806
1889
  if not args.write:
1807
1890
  for name, text in rendered.items():
1808
1891
  print(f"# ── {name} ──")
@@ -1811,6 +1894,8 @@ def _cmd_install_reexec(args: argparse.Namespace) -> int:
1811
1894
  print(f"# ── {name} ──")
1812
1895
  print(text, end="")
1813
1896
  print(f"# watching: {init_path} ({how})")
1897
+ print(f"# condition check as {who}: {'OK' if cond_ok else 'FAILS — ' + cond_detail}"
1898
+ + ("" if cond_ok else " (the unit would be SKIPPED on every fire; --write will refuse)"))
1814
1899
  for t in extra_trees:
1815
1900
  print(f"# also watching: {t} (a second install tree; the monitors do not load it)")
1816
1901
  print(f"# import check: {interpreter or sys.executable} -c 'import swarph_cli'")
@@ -1822,6 +1907,13 @@ def _cmd_install_reexec(args: argparse.Namespace) -> int:
1822
1907
  print("swarph monitor install-reexec --write: Linux-only.",
1823
1908
  file=sys.stderr)
1824
1909
  return 2
1910
+ if not cond_ok:
1911
+ print(f"swarph monitor install-reexec --write: REFUSED — the ExecCondition this unit would "
1912
+ f"carry fails right now for {who}: {interpreter or sys.executable} -c 'import swarph_cli' "
1913
+ f"→ {cond_detail}. Installed, it would be SKIPPED on every fire (Result=success, no "
1914
+ f"OnFailure) — a control quieter than the failed state it replaces. Nothing written.",
1915
+ file=sys.stderr)
1916
+ return 2
1825
1917
  target_dir = Path(args.dir)
1826
1918
  if not target_dir.is_dir() or not os.access(target_dir, os.W_OK):
1827
1919
  print(f"swarph monitor install-reexec: {target_dir} is not writable — "
@@ -1836,6 +1928,7 @@ def _cmd_install_reexec(args: argparse.Namespace) -> int:
1836
1928
  (target_dir / name).write_text(text, encoding="utf-8")
1837
1929
  print(f"wrote {target_dir / name}")
1838
1930
  print(f"watching: {init_path} ({how})")
1931
+ print(f"condition check as {who}: OK" + (f" (PYTHONPATH={pythonpath})" if pythonpath else ""))
1839
1932
  for t in extra_trees:
1840
1933
  print(f"also watching: {t}")
1841
1934
  print("next: systemctl daemon-reload && systemctl reset-failed swarph-monitor-reexec.path "