swarph-cli 0.72.9__tar.gz → 0.72.10__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 (486) hide show
  1. {swarph_cli-0.72.9/src/swarph_cli.egg-info → swarph_cli-0.72.10}/PKG-INFO +1 -1
  2. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/pyproject.toml +1 -1
  3. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/__init__.py +1 -1
  4. swarph_cli-0.72.10/src/swarph_cli/commands/sweep.py +413 -0
  5. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/guide/GUIDE.md +1 -0
  6. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/main.py +1 -0
  7. swarph_cli-0.72.10/src/swarph_cli/systemd/swarph-stuck-sweep.service +15 -0
  8. swarph_cli-0.72.10/src/swarph_cli/systemd/swarph-stuck-sweep.timer +14 -0
  9. {swarph_cli-0.72.9 → swarph_cli-0.72.10/src/swarph_cli.egg-info}/PKG-INFO +1 -1
  10. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli.egg-info/SOURCES.txt +5 -0
  11. swarph_cli-0.72.10/tests/test_1064_stuck_sweep.py +332 -0
  12. swarph_cli-0.72.10/tests/test_1065_release.py +295 -0
  13. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/LICENSE +0 -0
  14. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/README.md +0 -0
  15. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/setup.cfg +0 -0
  16. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/__main__.py +0 -0
  17. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/__init__.py +0 -0
  18. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/backends.py +0 -0
  19. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/claude_cli.py +0 -0
  20. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/codex_cli.py +0 -0
  21. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/pack.py +0 -0
  22. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/prices.py +0 -0
  23. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/providers.py +0 -0
  24. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/quality.py +0 -0
  25. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/refresh_prices.py +0 -0
  26. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/rules.py +0 -0
  27. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/runner.py +0 -0
  28. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/signal_score.py +0 -0
  29. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/validate.py +0 -0
  30. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/caller.py +0 -0
  31. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/capture/__init__.py +0 -0
  32. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/capture/harden.py +0 -0
  33. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/capture/lineage.py +0 -0
  34. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/capture/liveness.py +0 -0
  35. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/capture/manifest.py +0 -0
  36. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/capture/paths.py +0 -0
  37. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/capture/verify.py +0 -0
  38. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/cell.py +0 -0
  39. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/chain_token.py +0 -0
  40. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/channel.py +0 -0
  41. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/__init__.py +0 -0
  42. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/_content.py +0 -0
  43. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/_display.py +0 -0
  44. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/_gateway_client.py +0 -0
  45. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/add.py +0 -0
  46. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/bench.py +0 -0
  47. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/board.py +0 -0
  48. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/board_merge_check.py +0 -0
  49. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/brain.py +0 -0
  50. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/brain_ask.py +0 -0
  51. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/cell.py +0 -0
  52. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/cell_probe.py +0 -0
  53. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  54. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/channel.py +0 -0
  55. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/chat.py +0 -0
  56. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/codegraph.py +0 -0
  57. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/codegraph_hook.py +0 -0
  58. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/codex_hook_output.py +0 -0
  59. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/codex_waker.py +0 -0
  60. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/compress.py +0 -0
  61. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/daemon.py +0 -0
  62. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/dreaming.py +0 -0
  63. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/event.py +0 -0
  64. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/gateway.py +0 -0
  65. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/gh_route.py +0 -0
  66. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/group.py +0 -0
  67. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/guide.py +0 -0
  68. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/highlight.py +0 -0
  69. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/hook_interpreter.py +0 -0
  70. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/hook_output.py +0 -0
  71. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/hooks.py +0 -0
  72. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/import_session.py +0 -0
  73. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/init.py +0 -0
  74. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
  75. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/install_hook.py +0 -0
  76. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  77. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
  78. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
  79. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/install_wake_hook.py +0 -0
  80. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/lane.py +0 -0
  81. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/mcp_server.py +0 -0
  82. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/memory.py +0 -0
  83. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
  84. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/memory_sync.py +0 -0
  85. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/mesh.py +0 -0
  86. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/monitor.py +0 -0
  87. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/okf_links.py +0 -0
  88. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/onboard.py +0 -0
  89. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
  90. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
  91. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/protocol_handler.py +0 -0
  92. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/ratify.py +0 -0
  93. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/roster.py +0 -0
  94. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/rowclear_hook.py +0 -0
  95. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/schedule.py +0 -0
  96. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/security.py +0 -0
  97. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/service.py +0 -0
  98. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/spawn.py +0 -0
  99. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/timeline.py +0 -0
  100. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/version.py +0 -0
  101. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/wake_hook_output.py +0 -0
  102. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/watchdog.py +0 -0
  103. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/compress/__init__.py +0 -0
  104. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/compress/levers.py +0 -0
  105. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/compress/marker.py +0 -0
  106. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/compress/verify.py +0 -0
  107. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/console_safe.py +0 -0
  108. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/delivery_queue.py +0 -0
  109. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dm_frame.py +0 -0
  110. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/__init__.py +0 -0
  111. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/candidates.py +0 -0
  112. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/clone.py +0 -0
  113. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/enrich.py +0 -0
  114. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/index_check.py +0 -0
  115. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/notify.py +0 -0
  116. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/organize.py +0 -0
  117. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/probes.py +0 -0
  118. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/report.py +0 -0
  119. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/run.py +0 -0
  120. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/slm.py +0 -0
  121. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/transcripts.py +0 -0
  122. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/verify.py +0 -0
  123. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/gateway/__init__.py +0 -0
  124. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/gateway/feature_registry.py +0 -0
  125. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/gateway/lanes_control.py +0 -0
  126. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/gateway/schema.sql +0 -0
  127. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/gateway/server.py +0 -0
  128. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/gateway/services_control.py +0 -0
  129. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/gateway_default.py +0 -0
  130. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/gh_identity.py +0 -0
  131. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/identity.py +0 -0
  132. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/multiplexer.py +0 -0
  133. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/operator/__init__.py +0 -0
  134. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/operator/__main__.py +0 -0
  135. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/operator/api.py +0 -0
  136. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/operator/cli.py +0 -0
  137. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/operator/config.py +0 -0
  138. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/operator/fmt.py +0 -0
  139. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/orphan_daemons.py +0 -0
  140. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/pane_probe.py +0 -0
  141. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/parsers/__init__.py +0 -0
  142. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/parsers/claude.py +0 -0
  143. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
  144. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
  145. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
  146. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
  147. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
  148. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
  149. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
  150. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
  151. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
  152. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
  153. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
  154. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
  155. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/peer_executor.py +0 -0
  156. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/peer_executor_windows.py +0 -0
  157. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/peer_reconciliation.py +0 -0
  158. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/peer_reply_outbox.py +0 -0
  159. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/peer_service_host.py +0 -0
  160. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/peer_service_payload.py +0 -0
  161. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/peer_service_reply.py +0 -0
  162. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/peer_staging.py +0 -0
  163. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/query_filters.py +0 -0
  164. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/rowclear.py +0 -0
  165. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/scripts/__init__.py +0 -0
  166. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
  167. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  168. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
  169. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
  170. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
  171. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/service/__init__.py +0 -0
  172. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/service/app.py +0 -0
  173. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/service/providers.py +0 -0
  174. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/session_bridge.py +0 -0
  175. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
  176. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
  177. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/source_text.py +0 -0
  178. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/stall_alert.py +0 -0
  179. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/codex-waker.default +0 -0
  180. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
  181. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
  182. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
  183. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
  184. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
  185. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
  186. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
  187. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
  188. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
  189. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
  190. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  191. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  192. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  193. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/timeline_paths.py +0 -0
  194. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/tokens.py +0 -0
  195. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  196. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  197. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli.egg-info/requires.txt +0 -0
  198. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli.egg-info/top_level.txt +0 -0
  199. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_1006_board_publisher.py +0 -0
  200. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_1006_session_board.py +0 -0
  201. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_1010_strand_only.py +0 -0
  202. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_1013_spawn_channel.py +0 -0
  203. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_1017_muse_tui.py +0 -0
  204. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_1050_surrogate_wake.py +0 -0
  205. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_1052_opencode_deaf.py +0 -0
  206. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_1059_watchdog_unread_env.py +0 -0
  207. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_1061_roster_cli.py +0 -0
  208. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_124_register_merge.py +0 -0
  209. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_126_pending_from_complete.py +0 -0
  210. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_132_ensure_monitor_state_dir.py +0 -0
  211. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_138_memory_navigate.py +0 -0
  212. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_139_tool_probe.py +0 -0
  213. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_183_card_anchor.py +0 -0
  214. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_184_notify_honesty.py +0 -0
  215. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_201_win32_bash_refusal.py +0 -0
  216. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_243_onboard_per_peer_token.py +0 -0
  217. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_307_task4_ask_reply.py +0 -0
  218. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
  219. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_319_refusal_exit_codes.py +0 -0
  220. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_323_mistral_backend.py +0 -0
  221. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_332_report_organize_findings.py +0 -0
  222. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_356_query_filters.py +0 -0
  223. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_397_gh_identity_router.py +0 -0
  224. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_397_router_preflight.py +0 -0
  225. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_402_one_identity_resolver.py +0 -0
  226. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_432_rowclear.py +0 -0
  227. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_467b_onboard_operator_credential.py +0 -0
  228. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_472_register_caller_binding.py +0 -0
  229. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_484_codex_queue_sink.py +0 -0
  230. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_523_guide.py +0 -0
  231. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_524_conftest_hermeticity.py +0 -0
  232. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_532_accept_flag.py +0 -0
  233. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_535_version_capability.py +0 -0
  234. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_538_signal_bench.py +0 -0
  235. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_546_default_gateway.py +0 -0
  236. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_547_verbs_from_registry.py +0 -0
  237. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_548_gateway_remainder.py +0 -0
  238. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_552_codegraph_gateway.py +0 -0
  239. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_555_relay_row_shape.py +0 -0
  240. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_556_relay_freshness.py +0 -0
  241. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_559_edges_truncated.py +0 -0
  242. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_564_bootstrap_first_peer.py +0 -0
  243. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
  244. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_579_register_bootstrap_message.py +0 -0
  245. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_591_cli_step_graph.py +0 -0
  246. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_644_monitor_supervision.py +0 -0
  247. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_648_install_unit.py +0 -0
  248. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_654_reexec.py +0 -0
  249. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_656_owned_slm.py +0 -0
  250. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_684_dreaming_verb.py +0 -0
  251. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_689_pkg_version_order.py +0 -0
  252. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_715_grok_wake.py +0 -0
  253. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_716_timeline_one_path.py +0 -0
  254. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_723_grok_0251.py +0 -0
  255. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_724_ps1_ascii_or_bom.py +0 -0
  256. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_725_stdio_reconfigure.py +0 -0
  257. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_726_grok_limit_screen.py +0 -0
  258. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_729_arm_check_delivery_gap.py +0 -0
  259. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_729_dm_once.py +0 -0
  260. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_729_wake_watchdog.py +0 -0
  261. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_734_dreaming_enrich_degrade.py +0 -0
  262. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_735_dm_mcp_tools.py +0 -0
  263. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_739_wake_writer.py +0 -0
  264. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_740_edit_project.py +0 -0
  265. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_762_cards_edit_priority.py +0 -0
  266. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_764_enrich_collapse.py +0 -0
  267. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_801_confirm_flow_cli.py +0 -0
  268. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_802_stage_history.py +0 -0
  269. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_825_codegraph_hook_noise.py +0 -0
  270. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_825_install_posttooluse_only.py +0 -0
  271. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_825_remaining_cap_shred_bind.py +0 -0
  272. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_829_counterfactual_column.py +0 -0
  273. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_830_hook_binding_version.py +0 -0
  274. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_84_cli_labels.py +0 -0
  275. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_866_doctrine_guide.py +0 -0
  276. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_872_identity_refuses_not_warns.py +0 -0
  277. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_874_source_text.py +0 -0
  278. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
  279. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_937_dreaming_notify.py +0 -0
  280. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_938_organize_line_limit.py +0 -0
  281. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_941_bench_backends.py +0 -0
  282. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_941_harness_342.py +0 -0
  283. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_945_same_line_pairing.py +0 -0
  284. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_955_prompt_audit.py +0 -0
  285. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_957_auth_http.py +0 -0
  286. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_959_venv_interpreter.py +0 -0
  287. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_960_channel.py +0 -0
  288. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
  289. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_961_cursor_grok_footer.py +0 -0
  290. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_961_loud_stop.py +0 -0
  291. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_963_plugin_manifest.py +0 -0
  292. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_964_channel_cell.py +0 -0
  293. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_965_mesh_wait.py +0 -0
  294. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_975_channel_hop.py +0 -0
  295. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_add_security_gate.py +0 -0
  296. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_artifact_add.py +0 -0
  297. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_artifact_hash.py +0 -0
  298. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_artifact_lib.py +0 -0
  299. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_artifact_mcp.py +0 -0
  300. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_artifact_skill.py +0 -0
  301. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_artifact_tool.py +0 -0
  302. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_artifact_uri.py +0 -0
  303. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_bench_backends.py +0 -0
  304. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_bench_claude_cli.py +0 -0
  305. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_bench_codex_cli.py +0 -0
  306. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_bench_command.py +0 -0
  307. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_bench_pack.py +0 -0
  308. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_bench_prices.py +0 -0
  309. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_bench_quality.py +0 -0
  310. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_bench_runner.py +0 -0
  311. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_bench_validate.py +0 -0
  312. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_board_card_thread.py +0 -0
  313. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_board_command.py +0 -0
  314. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_board_merge_check.py +0 -0
  315. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_board_obligation_close.py +0 -0
  316. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_brain_ask_command.py +0 -0
  317. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_brain_ask_env_diagnosis.py +0 -0
  318. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_brain_ask_identity_explicitness.py +0 -0
  319. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_brain_command.py +0 -0
  320. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_caller_meta_guard.py +0 -0
  321. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_capture_lineage.py +0 -0
  322. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_capture_liveness.py +0 -0
  323. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_capture_manifest.py +0 -0
  324. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_capture_paths.py +0 -0
  325. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_capture_security.py +0 -0
  326. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_cell_command_dispatch.py +0 -0
  327. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_cell_harden.py +0 -0
  328. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_cell_loader.py +0 -0
  329. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_cell_selfcheck.py +0 -0
  330. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_cell_verify.py +0 -0
  331. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_chain_token.py +0 -0
  332. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_channel_command.py +0 -0
  333. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_channel_poll_never_breaks_dms.py +0 -0
  334. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_channel_post.py +0 -0
  335. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_channel_wake_policy_filter.py +0 -0
  336. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_chat_command.py +0 -0
  337. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_claude_parser.py +0 -0
  338. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_claude_tmux_template.py +0 -0
  339. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_codegraph_caller_scope.py +0 -0
  340. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_codegraph_command.py +0 -0
  341. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_codegraph_hook.py +0 -0
  342. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_codegraph_mcp.py +0 -0
  343. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_codex_waker.py +0 -0
  344. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_compress_caller_convention.py +0 -0
  345. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_compress_command.py +0 -0
  346. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_compress_levers.py +0 -0
  347. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_compress_marker.py +0 -0
  348. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_compress_verify.py +0 -0
  349. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_console_encoding_e2e.py +0 -0
  350. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_content_composition.py +0 -0
  351. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_cursor_membrane.py +0 -0
  352. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_daemon_command.py +0 -0
  353. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_daemon_console_encoding.py +0 -0
  354. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_daemon_delivery.py +0 -0
  355. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_delivery_queue.py +0 -0
  356. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_display_sanitize.py +0 -0
  357. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_ensure_monitor_no_peer_default.py +0 -0
  358. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_event_emit.py +0 -0
  359. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_feature_to_uri.py +0 -0
  360. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_gateway_brain.py +0 -0
  361. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_gateway_client.py +0 -0
  362. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_gateway_command.py +0 -0
  363. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_gateway_message_idempotency.py +0 -0
  364. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_gh_route_doctor_mesh_name.py +0 -0
  365. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_group_command.py +0 -0
  366. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_guide_commands_parse.py +0 -0
  367. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_guide_selfcheck.py +0 -0
  368. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_heartbeat_check.py +0 -0
  369. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_highlight_command.py +0 -0
  370. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_hook_output.py +0 -0
  371. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_hooks_activity_marker.py +0 -0
  372. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_hooks_add.py +0 -0
  373. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_hooks_bundle.py +0 -0
  374. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_hooks_init.py +0 -0
  375. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_hooks_legacy_generation_removal.py +0 -0
  376. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_hooks_lifecycle.py +0 -0
  377. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_hooks_merge.py +0 -0
  378. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_hooks_verify.py +0 -0
  379. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_hooks_win32_bash_wrapper.py +0 -0
  380. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_hooks_win32_command_path.py +0 -0
  381. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_import_command.py +0 -0
  382. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_inbox_identity_provenance.py +0 -0
  383. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_init_command.py +0 -0
  384. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_init_provider_menu.py +0 -0
  385. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_install_codex_hooks.py +0 -0
  386. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_install_hook.py +0 -0
  387. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_install_multiplexer_command.py +0 -0
  388. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_install_postcompact_hook.py +0 -0
  389. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_install_wake_hook.py +0 -0
  390. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_lane_command.py +0 -0
  391. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_main.py +0 -0
  392. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_main_unknown_verb_guard.py +0 -0
  393. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_mcp_missing_hint.py +0 -0
  394. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_mcp_server.py +0 -0
  395. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_memory_command.py +0 -0
  396. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_memory_emit_snippet.py +0 -0
  397. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_memory_gateway.py +0 -0
  398. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_memory_sync.py +0 -0
  399. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_memory_sync_membrane.py +0 -0
  400. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_mesh_command.py +0 -0
  401. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_mesh_inbox_mark_read.py +0 -0
  402. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_mesh_inbox_sanitize.py +0 -0
  403. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_mesh_originate_thread_623.py +0 -0
  404. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_mesh_send_unknown_peer.py +0 -0
  405. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_mesh_sidecar.py +0 -0
  406. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  407. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_mesh_sidecar_muse_sink.py +0 -0
  408. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  409. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_meta_edge_identity.py +0 -0
  410. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_monitor_channel_poll.py +0 -0
  411. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_monitor_cli.py +0 -0
  412. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_monitor_daemon_collision.py +0 -0
  413. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_monitor_self_name_verification.py +0 -0
  414. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_monitor_state_split.py +0 -0
  415. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_multiplexer.py +0 -0
  416. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_muse_membrane.py +0 -0
  417. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_muse_own_cli.py +0 -0
  418. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_obligation_close_is_surfaced.py +0 -0
  419. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_okf_links.py +0 -0
  420. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_onboard_command.py +0 -0
  421. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_opencode_membrane.py +0 -0
  422. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_opencode_plugin.py +0 -0
  423. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_operator_me.py +0 -0
  424. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_orphan_daemons.py +0 -0
  425. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_packaged_artifacts_exist.py +0 -0
  426. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_packaged_skills.py +0 -0
  427. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_peer_executor.py +0 -0
  428. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_peer_executor_windows.py +0 -0
  429. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_peer_reply_drain_command.py +0 -0
  430. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_peer_reply_drain_supervisor.py +0 -0
  431. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_peer_reply_outbox.py +0 -0
  432. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_peer_reply_transport.py +0 -0
  433. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_peer_service_e2e.py +0 -0
  434. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_peer_service_host.py +0 -0
  435. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_peer_service_payload.py +0 -0
  436. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_peer_service_reply.py +0 -0
  437. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_postcompact_recall.py +0 -0
  438. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_process_liveness.py +0 -0
  439. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_protocol_handler.py +0 -0
  440. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_providers_isolation.py +0 -0
  441. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_ratify_command.py +0 -0
  442. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_readme_claims_no_gateway_default.py +0 -0
  443. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_repl_caller_convention.py +0 -0
  444. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_schedule_command.py +0 -0
  445. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_schedule_create_contract.py +0 -0
  446. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_security.py +0 -0
  447. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_service_app.py +0 -0
  448. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_service_command.py +0 -0
  449. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_service_providers.py +0 -0
  450. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_session_bridge_inject.py +0 -0
  451. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_session_bridge_probe.py +0 -0
  452. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_session_bridge_resolve.py +0 -0
  453. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_session_path_role_gate.py +0 -0
  454. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_shipped_units_match_the_cli.py +0 -0
  455. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_smoke_chat.py +0 -0
  456. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_smoke_one_shot.py +0 -0
  457. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_smoke_phase_5_5.py +0 -0
  458. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_spawn_base_hoist.py +0 -0
  459. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_spawn_command.py +0 -0
  460. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_spawn_identity_stamp.py +0 -0
  461. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_spawn_live_pin.py +0 -0
  462. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_spawn_mitosis_lineage.py +0 -0
  463. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_spawn_tmux_session.py +0 -0
  464. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_spawn_windows_relaunch.py +0 -0
  465. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_stall_alert.py +0 -0
  466. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_subprocess_decode_encoding.py +0 -0
  467. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_timeline_command.py +0 -0
  468. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_tmux_wake_runstate_defer.py +0 -0
  469. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_tmux_wake_session_reanchor.py +0 -0
  470. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_tmux_wake_stale.py +0 -0
  471. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_tmux_wake_submit_verify.py +0 -0
  472. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_token_file_one_parser.py +0 -0
  473. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_token_precedence_explicit_first.py +0 -0
  474. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_unified_token_resolver.py +0 -0
  475. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_version_command.py +0 -0
  476. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_version_consistency.py +0 -0
  477. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_vibe_membrane.py +0 -0
  478. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_watchdog.py +0 -0
  479. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_watchdog_dm_wake.py +0 -0
  480. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  481. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  482. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_watchdog_emit_health.py +0 -0
  483. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_watchdog_install_flags.py +0 -0
  484. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_watchdog_model_rung.py +0 -0
  485. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_watchdog_stale_peers.py +0 -0
  486. {swarph_cli-0.72.9 → swarph_cli-0.72.10}/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.72.9
3
+ Version: 0.72.10
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.72.9"
7
+ version = "0.72.10"
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" }
@@ -16,6 +16,6 @@ The architecture splits CLI from substrate so:
16
16
 
17
17
  from __future__ import annotations
18
18
 
19
- __version__ = "0.72.9"
19
+ __version__ = "0.72.10"
20
20
 
21
21
  __all__ = ["__version__"]
@@ -0,0 +1,413 @@
1
+ """``swarph sweep`` — the card-#1064 stuck-work sweep, one daily shot.
2
+
3
+ Finds (a) open/taken rows blocked >=48h on an unmoving dependency and (b)
4
+ approved-or-old open PRs with no obligation row naming them; DMs each
5
+ finding's BLOCKING holder once per 24h (never the waiter) and appends one
6
+ line per finding to the nightly watchtower report file. Never closes or
7
+ reassigns: the only board write is the DM itself (POST /messages).
8
+
9
+ swarph sweep [--dry-run] [--repos R1,R2] [--state-file P] [--report-file P]
10
+ [--as SELF] [--gateway URL] [--token-file F] [--json]
11
+
12
+ --dry-run reads the live board and GitHub, prints the findings, and writes
13
+ nothing (no DMs, no state, no report) — the rehearsal shape.
14
+ """
15
+ from __future__ import annotations
16
+
17
+ import argparse
18
+ import json
19
+ import os
20
+ import re
21
+ import subprocess
22
+ import sys
23
+ from datetime import datetime, timezone
24
+
25
+ _STUCK_H = 48
26
+ _RESEND_H = 24
27
+
28
+ _DEFAULT_REPOS = (
29
+ "BrainSurfing-tech/swarph-cli",
30
+ "BrainSurfing-tech/mesh-gateway",
31
+ "BrainSurfing-tech/swarph-codegraph-fed",
32
+ )
33
+
34
+ _PR_URL_RE = re.compile(
35
+ r"github\.com/([A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+)/pull/(\d+)",
36
+ re.IGNORECASE)
37
+ _PR_NUM_RE = re.compile(r"\bPR\s*#?(\d+)\b", re.IGNORECASE)
38
+
39
+
40
+ def _parse_ts(v):
41
+ """Tolerant ISO parse -> epoch seconds, or None. Never raises: an
42
+ unparseable timestamp is unevaluable, not zero."""
43
+ if not v:
44
+ return None
45
+ try:
46
+ dt = datetime.fromisoformat(str(v).replace("Z", "+00:00"))
47
+ except (TypeError, ValueError):
48
+ return None
49
+ try:
50
+ return dt.timestamp()
51
+ except (TypeError, ValueError, OverflowError, OSError):
52
+ return None
53
+
54
+
55
+ def _age_days(now, ts):
56
+ return round((now - ts) / 86400)
57
+
58
+
59
+ def _row_last_change(row):
60
+ for k in ("closed_at", "taken_at", "created_at"):
61
+ ts = _parse_ts(row.get(k))
62
+ if ts is not None:
63
+ return ts
64
+ return None
65
+
66
+
67
+ def _named_prs(rows):
68
+ """(repo-or-None, number) pairs obligation rows name: full URLs match
69
+ exactly; bare PR numbers match by number across tracked repos."""
70
+ named = set()
71
+ for row in rows:
72
+ if not isinstance(row, dict):
73
+ continue
74
+ blob = " ".join(
75
+ str(row.get(k) or "") for k in ("accept", "close_evidence"))
76
+ for m in _PR_URL_RE.finditer(blob):
77
+ try:
78
+ named.add((m.group(1).lower(), int(m.group(2))))
79
+ except ValueError:
80
+ continue
81
+ for m in _PR_NUM_RE.finditer(blob):
82
+ try:
83
+ named.add((None, int(m.group(1))))
84
+ except ValueError:
85
+ continue
86
+ return named
87
+
88
+
89
+ def _graph_steps(http_get, gateway, token, card_id, cache):
90
+ if card_id in cache:
91
+ return cache[card_id]
92
+ try:
93
+ status, body = http_get(
94
+ f"{gateway}/board/cards/{card_id}/graph", token)
95
+ except Exception:
96
+ return None
97
+ if status != 200 or not isinstance(body, dict):
98
+ return None
99
+ steps = body.get("steps")
100
+ if not isinstance(steps, list):
101
+ return None
102
+ cache[card_id] = steps
103
+ return steps
104
+
105
+
106
+ def _row_findings(rows, by_id, http_get, gateway, token, now):
107
+ """Kind (a): one finding per (open taken waiter, unsatisfied old need)."""
108
+ out = []
109
+ cache = {}
110
+ for w in rows:
111
+ if w.get("status") != "open" or not w.get("taken_at"):
112
+ continue
113
+ step = w.get("step")
114
+ if not step:
115
+ continue
116
+ card_id = w.get("card_id")
117
+ if not isinstance(card_id, int) or isinstance(card_id, bool):
118
+ continue
119
+ steps = _graph_steps(http_get, gateway, token, card_id, cache)
120
+ if steps is None:
121
+ continue
122
+ entry = next((e for e in steps
123
+ if isinstance(e, dict) and e.get("step") == step), None)
124
+ if entry is None:
125
+ continue
126
+ needs = entry.get("needs")
127
+ if not isinstance(needs, list):
128
+ continue
129
+ for need in needs:
130
+ if not isinstance(need, dict):
131
+ continue
132
+ sat = need.get("satisfied")
133
+ if sat is True:
134
+ continue
135
+ if sat is not False:
136
+ continue # unevaluable need names no finding
137
+ rid = need.get("row_id")
138
+ dep = by_id.get(rid)
139
+ if not isinstance(dep, dict):
140
+ continue
141
+ if dep.get("status") == "closed":
142
+ continue # terminal: satisfied or a rework case, not unmoving
143
+ changed = _row_last_change(dep)
144
+ if changed is None or now - changed < _STUCK_H * 3600:
145
+ continue
146
+ holder = dep.get("holder")
147
+ blocker = holder if holder and dep.get("holder_known", True) else None
148
+ out.append({
149
+ "kind": "row",
150
+ "waiter_id": w.get("id"),
151
+ "waiter": w.get("holder"),
152
+ "waiter_step": step,
153
+ "card_id": card_id,
154
+ "need": need.get("step"),
155
+ "need_id": rid,
156
+ "blocker": blocker,
157
+ "age_days": _age_days(now, changed),
158
+ })
159
+ return out
160
+
161
+
162
+ def _pr_findings(named, gh_prs, repos, peers, now):
163
+ """Kind (b): approved-or-old open PRs no row names. An author with no
164
+ mesh peer is report-only — the CLI must not invent a recipient."""
165
+ out = []
166
+ for repo in repos:
167
+ try:
168
+ prs = gh_prs(repo)
169
+ except Exception:
170
+ continue
171
+ if not isinstance(prs, list):
172
+ continue
173
+ for pr in prs:
174
+ if not isinstance(pr, dict):
175
+ continue
176
+ try:
177
+ number = int(pr.get("number"))
178
+ except (TypeError, ValueError):
179
+ continue
180
+ if (repo.lower(), number) in named or (None, number) in named:
181
+ continue
182
+ approved = pr.get("reviewDecision") == "APPROVED"
183
+ created = _parse_ts(pr.get("createdAt"))
184
+ old = created is not None and now - created >= _STUCK_H * 3600
185
+ if not approved and not old:
186
+ continue
187
+ author = pr.get("author") or {}
188
+ login = author.get("login") if isinstance(author, dict) else None
189
+ # blocker names the responsible party even when they have no
190
+ # mesh peer — the SEND step DMs only peers (report-only else).
191
+ out.append({
192
+ "kind": "pr",
193
+ "repo": repo,
194
+ "number": number,
195
+ "title": pr.get("title") or "",
196
+ "url": pr.get("url") or "",
197
+ "approved": approved,
198
+ "age_days": _age_days(now, created) if created else None,
199
+ "blocker": login,
200
+ })
201
+ return out
202
+
203
+
204
+ def _finding_key(f):
205
+ if f["kind"] == "row":
206
+ return f"row:{f['waiter_id']}:{f['need']}"
207
+ return f"pr:{f['repo']}#{f['number']}"
208
+
209
+
210
+ def _report_line(now_iso, f):
211
+ if f["kind"] == "row":
212
+ return (f"{now_iso} STUCK row waiter=#{f['waiter_id']}({f['waiter']}) "
213
+ f"step={f['waiter_step']} card=#{f['card_id']} "
214
+ f"blocked-by=#{f['need_id']}({f['blocker']}) "
215
+ f"need={f['need']} need-age={f['age_days']}d")
216
+ how = "approved" if f["approved"] else f"age={f['age_days']}d"
217
+ return (f"{now_iso} STUCK pr {f['repo']}#{f['number']} "
218
+ f"'{f['title']}' ({how}, no review row) author={f['blocker']}")
219
+
220
+
221
+ def _dm_content(f):
222
+ if f["kind"] == "row":
223
+ return (f"STUCK-WORK SWEEP: row #{f['waiter_id']} ({f['waiter']}, "
224
+ f"{f['waiter_step']}, card #{f['card_id']}) has waited on "
225
+ f"your row #{f['need_id']} ({f['need']}, unchanged "
226
+ f"{f['age_days']}d). Please move it, fail it, or say what "
227
+ f"it waits on.")
228
+ state = "approved" if f["approved"] else "open %sd" % (f['age_days'],)
229
+ return (f"STUCK-WORK SWEEP: your PR {f['repo']}#{f['number']} "
230
+ f"'{f['title']}' ({f['url']}) is {state} "
231
+ f"with no review row naming it. Please land it or ask for review.")
232
+
233
+
234
+ def run_sweep(*, gateway, token, sender, repos, state_file, report_file,
235
+ now, http_get, http_post, gh_prs, dry_run=False):
236
+ """One sweep. Returns (rc, findings): rc 1 only when the board itself is
237
+ unreadable (nothing evaluable); findings are the SENT (or dry-run, would
238
+ send) findings. Only ever writes DMs + the two local files."""
239
+ gateway = gateway.rstrip("/")
240
+ try:
241
+ status, body = http_get(f"{gateway}/board/obligations", token)
242
+ except Exception:
243
+ status, body = None, None
244
+ if status != 200 or not isinstance(body, dict) or not isinstance(
245
+ body.get("obligations"), list):
246
+ print("swarph sweep: board unreadable — no findings, nothing sent",
247
+ file=sys.stderr)
248
+ return 1, []
249
+ rows = [r for r in body["obligations"] if isinstance(r, dict)]
250
+ by_id = {r.get("id"): r for r in rows
251
+ if isinstance(r.get("id"), int)}
252
+ try:
253
+ pstatus, pbody = http_get(f"{gateway}/peers", token)
254
+ peers = {p["name"] for p in pbody.get("peers", [])
255
+ if isinstance(p, dict) and p.get("name")}
256
+ if pstatus != 200:
257
+ peers = set()
258
+ except Exception:
259
+ peers = set()
260
+
261
+ findings = _row_findings(rows, by_id, http_get, gateway, token, now)
262
+ findings += _pr_findings(_named_prs(rows), gh_prs, repos, peers, now)
263
+
264
+ try:
265
+ with open(state_file, encoding="utf-8") as fp:
266
+ sent = json.load(fp)
267
+ sent = sent if isinstance(sent, dict) else {}
268
+ except (OSError, ValueError):
269
+ sent = {}
270
+ now_iso = datetime.fromtimestamp(now, timezone.utc).isoformat()
271
+ fresh, updates = [], {}
272
+ for f in findings:
273
+ key = _finding_key(f)
274
+ try:
275
+ last = _parse_ts(sent.get(key))
276
+ except Exception:
277
+ last = None
278
+ if last is not None and now - last < _RESEND_H * 3600:
279
+ continue
280
+ fresh.append(f)
281
+ updates[key] = now_iso
282
+
283
+ if dry_run:
284
+ for f in fresh:
285
+ print("DRY " + _report_line(now_iso, f))
286
+ return 0, fresh
287
+
288
+ for f in fresh:
289
+ # DM only a blocker with a mesh peer: pinging the waiter instead is
290
+ # the FAIL, and inventing a recipient for a gh login is its cousin.
291
+ if f["blocker"] is not None and f["blocker"] in peers:
292
+ status, payload = http_post(
293
+ f"{gateway}/messages",
294
+ {"from_node": sender, "to_node": f["blocker"],
295
+ "kind": "fyi", "content": _dm_content(f)},
296
+ token)
297
+ if status < 200 or status >= 300:
298
+ detail = (payload.get("detail", "<gateway error>")
299
+ if isinstance(payload, dict) else "<gateway error>")
300
+ print(f"swarph sweep: DM to {f['blocker']} failed "
301
+ f"{status}: {detail}", file=sys.stderr)
302
+ continue
303
+ line = _report_line(now_iso, f)
304
+ print(line)
305
+ try:
306
+ with open(report_file, "a", encoding="utf-8") as fp:
307
+ fp.write(line + "\n")
308
+ except OSError as exc:
309
+ print(f"swarph sweep: report append failed: {exc}",
310
+ file=sys.stderr)
311
+ if updates:
312
+ sent.update(updates)
313
+ try:
314
+ with open(state_file, "w", encoding="utf-8") as fp:
315
+ json.dump(sent, fp)
316
+ except OSError as exc:
317
+ print(f"swarph sweep: state write failed: {exc}", file=sys.stderr)
318
+ return 0, fresh
319
+
320
+
321
+ def _gh_prs(repo):
322
+ """Live gh transport: open PRs with the fields the matcher needs, or
323
+ None when gh is unusable (that repo's findings are skipped, not
324
+ invented)."""
325
+ try:
326
+ p = subprocess.run(
327
+ ["gh", "pr", "list", "--repo", repo, "--state", "open",
328
+ "--limit", "100", "--json",
329
+ "number,title,createdAt,reviewDecision,url,author"],
330
+ capture_output=True, text=True, encoding="utf-8",
331
+ errors="replace", timeout=60)
332
+ except (OSError, subprocess.SubprocessError):
333
+ return None
334
+ if p.returncode != 0:
335
+ return None
336
+ try:
337
+ data = json.loads(p.stdout or "[]")
338
+ except ValueError:
339
+ return None
340
+ return data if isinstance(data, list) else None
341
+
342
+
343
+ def _default_state_file():
344
+ xdg = os.environ.get("XDG_STATE_HOME", "").strip()
345
+ root = xdg if xdg else os.path.join(os.path.expanduser("~"), ".local", "state")
346
+ return os.path.join(root, "swarph", "stuck-sweep.json")
347
+
348
+
349
+ def _build_parser():
350
+ p = argparse.ArgumentParser(
351
+ prog="swarph sweep",
352
+ description="Daily stuck-work sweep: blocked rows + unreviewed PRs "
353
+ "(card #1064). DMs blockers, never closes/reassigns.")
354
+ p.add_argument("--dry-run", action="store_true",
355
+ help="print findings; send/record/write nothing")
356
+ p.add_argument("--repos", default=None,
357
+ help="comma-separated owner/repo list (default "
358
+ "$SWARPH_SWEEP_REPOS or the fleet three)")
359
+ p.add_argument("--state-file", default=None)
360
+ p.add_argument("--report-file", default=None,
361
+ help="nightly watchtower report path "
362
+ "(default $SWARPH_SWEEP_REPORT or state-adjacent log)")
363
+ p.add_argument("--json", action="store_true")
364
+ p.add_argument("--as", dest="self_name", default=None,
365
+ help="DM sender identity")
366
+ from swarph_cli.gateway_default import env_gateway
367
+ p.add_argument("--gateway", default=env_gateway())
368
+ p.add_argument("--token-file", default=None)
369
+ return p
370
+
371
+
372
+ def run_sweep_cmd(argv):
373
+ import time as _time
374
+ from swarph_cli.commands import mesh as _mesh
375
+ from swarph_cli.tokens import resolve_token
376
+ args = _build_parser().parse_args(argv)
377
+ try:
378
+ # #872 doctrine: an automated DM must name its sender. Dry-run sends
379
+ # nothing and needs no identity.
380
+ if not args.self_name and not args.dry_run:
381
+ print("swarph sweep: --as a sender is required (automated DMs "
382
+ "must name their sender; --dry-run needs no identity)",
383
+ file=sys.stderr)
384
+ return 2
385
+ resolution = resolve_token(
386
+ args.self_name, args.token_file,
387
+ identity_is_explicit=bool(args.self_name))
388
+ if resolution is None:
389
+ print("swarph sweep: no token (set MESH_GATEWAY_TOKEN, pass "
390
+ "--token-file, or --as a sender)", file=sys.stderr)
391
+ return 2
392
+ sender = args.self_name or "(dry-run)"
393
+ repos = ([r.strip() for r in args.repos.split(",") if r.strip()]
394
+ if args.repos else
395
+ [r.strip() for r in os.environ.get(
396
+ "SWARPH_SWEEP_REPOS", ",".join(_DEFAULT_REPOS)
397
+ ).split(",") if r.strip()])
398
+ state_file = args.state_file or os.environ.get(
399
+ "SWARPH_SWEEP_STATE", _default_state_file())
400
+ report_file = (args.report_file or os.environ.get("SWARPH_SWEEP_REPORT")
401
+ or os.path.splitext(state_file)[0] + ".log")
402
+ rc, findings = run_sweep(
403
+ gateway=args.gateway, token=resolution.token, sender=sender,
404
+ repos=repos, state_file=state_file, report_file=report_file,
405
+ now=_time.time(), http_get=_mesh._http_get_json,
406
+ http_post=_mesh._post_json, gh_prs=_gh_prs,
407
+ dry_run=args.dry_run)
408
+ if args.json:
409
+ print(json.dumps(findings, indent=2))
410
+ return rc
411
+ except RuntimeError as exc:
412
+ print(f"swarph sweep: {exc}", file=sys.stderr)
413
+ return 2
@@ -549,6 +549,7 @@ answers `--help`; hook-plumbing verbs are the callbacks hooks invoke, not comman
549
549
  - `swarph schedule` -- the gateway's scheduled events: create, list, get, enable, disable, delete, fire-now.
550
550
  - `swarph service` -- stand up a $0 subscription-LLM HTTP lane.
551
551
  - `swarph spawn` -- run a cell from its role name or yaml path.
552
+ - `swarph sweep` -- the card-#1064 stuck-work sweep: blocked rows and unreviewed PRs, DMs the blocker, `swarph sweep --dry-run` reads only.
552
553
  - `swarph timeline` -- deterministic temporal lookup over the timeline: around, since, range (see [Code and history](#code-and-history)).
553
554
  - `swarph version` -- per-module versions and install origin (PEP 610).
554
555
  - `swarph wake-hook-output` -- hook plumbing: the silent-wake SessionStart callback.
@@ -111,6 +111,7 @@ _VERB_HANDLERS: dict[str, str] = {
111
111
  "group": "swarph_cli.commands.group.run_group",
112
112
  "rights": "swarph_cli.commands.group.run_rights",
113
113
  "roster": "swarph_cli.commands.roster.run_roster",
114
+ "sweep": "swarph_cli.commands.sweep.run_sweep_cmd",
114
115
  "mcp-server": "swarph_cli.commands.mcp_server.run_mcp_server",
115
116
  "compress": "swarph_cli.commands.compress.run_compress",
116
117
  "cell": "swarph_cli.commands.cell.run_cell",
@@ -0,0 +1,15 @@
1
+ [Unit]
2
+ Description=Stuck-work sweep one-shot: DM blockers of >=48h-stuck rows and unreviewed PRs (card #1064)
3
+ Documentation=https://github.com/BrainSurfing-tech/swarph-cli
4
+ After=network-online.target
5
+ Wants=network-online.target
6
+
7
+ [Service]
8
+ Type=oneshot
9
+ EnvironmentFile=-/etc/default/swarph-stuck-sweep
10
+ ExecStart=/usr/local/bin/swarph sweep --as lab-ovh
11
+ StandardOutput=append:/var/log/swarph-stuck-sweep.log
12
+ StandardError=append:/var/log/swarph-stuck-sweep.log
13
+
14
+ [Install]
15
+ WantedBy=multi-user.target
@@ -0,0 +1,14 @@
1
+ [Unit]
2
+ Description=Run the stuck-work sweep: swarph sweep daily (blocked rows + unreviewed PRs, card #1064)
3
+ Documentation=https://github.com/BrainSurfing-tech/swarph-cli
4
+ Requires=swarph-stuck-sweep.service
5
+
6
+ [Timer]
7
+ OnBootSec=15min
8
+ OnCalendar=daily
9
+ AccuracySec=30min
10
+ Persistent=true
11
+ RandomizedDelaySec=15min
12
+
13
+ [Install]
14
+ WantedBy=timers.target
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.72.9
3
+ Version: 0.72.10
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
@@ -116,6 +116,7 @@ src/swarph_cli/commands/schedule.py
116
116
  src/swarph_cli/commands/security.py
117
117
  src/swarph_cli/commands/service.py
118
118
  src/swarph_cli/commands/spawn.py
119
+ src/swarph_cli/commands/sweep.py
119
120
  src/swarph_cli/commands/timeline.py
120
121
  src/swarph_cli/commands/version.py
121
122
  src/swarph_cli/commands/wake_hook_output.py
@@ -186,6 +187,8 @@ src/swarph_cli/systemd/swarph-monitor-reexec.service
186
187
  src/swarph_cli/systemd/swarph-monitor@.service
187
188
  src/swarph_cli/systemd/swarph-peer-reply-drain@.service
188
189
  src/swarph_cli/systemd/swarph-peer-reply-drain@.timer
190
+ src/swarph_cli/systemd/swarph-stuck-sweep.service
191
+ src/swarph_cli/systemd/swarph-stuck-sweep.timer
189
192
  src/swarph_cli/systemd/swarph-watchdog.default
190
193
  src/swarph_cli/systemd/swarph-watchdog.service
191
194
  src/swarph_cli/systemd/swarph-watchdog.timer
@@ -198,6 +201,8 @@ tests/test_1050_surrogate_wake.py
198
201
  tests/test_1052_opencode_deaf.py
199
202
  tests/test_1059_watchdog_unread_env.py
200
203
  tests/test_1061_roster_cli.py
204
+ tests/test_1064_stuck_sweep.py
205
+ tests/test_1065_release.py
201
206
  tests/test_124_register_merge.py
202
207
  tests/test_126_pending_from_complete.py
203
208
  tests/test_132_ensure_monitor_state_dir.py