swarph-cli 0.72.2__tar.gz → 0.72.4__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 (473) hide show
  1. {swarph_cli-0.72.2/src/swarph_cli.egg-info → swarph_cli-0.72.4}/PKG-INFO +1 -1
  2. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/pyproject.toml +1 -1
  3. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/__init__.py +1 -1
  4. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/mesh.py +40 -16
  5. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/spawn.py +7 -0
  6. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/guide/GUIDE.md +10 -0
  7. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/scripts/wake_watchdog.py +4 -4
  8. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/systemd/swarph-watchdog.service +1 -1
  9. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/systemd/swarph-watchdog.timer +1 -1
  10. {swarph_cli-0.72.2 → swarph_cli-0.72.4/src/swarph_cli.egg-info}/PKG-INFO +1 -1
  11. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli.egg-info/SOURCES.txt +1 -0
  12. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_1006_board_publisher.py +74 -0
  13. swarph_cli-0.72.4/tests/test_1010_strand_only.py +225 -0
  14. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_1013_spawn_channel.py +25 -0
  15. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_mesh_sidecar.py +18 -0
  16. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_mesh_sidecar_cursor_decoupled.py +9 -0
  17. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_mesh_sidecar_wake_survives_read.py +9 -0
  18. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_monitor_cli.py +8 -1
  19. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_monitor_state_split.py +9 -0
  20. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_tmux_wake_runstate_defer.py +7 -2
  21. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_tmux_wake_session_reanchor.py +21 -10
  22. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_tmux_wake_stale.py +8 -1
  23. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_tmux_wake_submit_verify.py +15 -4
  24. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/LICENSE +0 -0
  25. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/README.md +0 -0
  26. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/setup.cfg +0 -0
  27. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/__main__.py +0 -0
  28. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/bench/__init__.py +0 -0
  29. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/bench/backends.py +0 -0
  30. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/bench/claude_cli.py +0 -0
  31. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/bench/codex_cli.py +0 -0
  32. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/bench/pack.py +0 -0
  33. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/bench/prices.py +0 -0
  34. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/bench/providers.py +0 -0
  35. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/bench/quality.py +0 -0
  36. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/bench/refresh_prices.py +0 -0
  37. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/bench/rules.py +0 -0
  38. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/bench/runner.py +0 -0
  39. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/bench/signal_score.py +0 -0
  40. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/bench/validate.py +0 -0
  41. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/caller.py +0 -0
  42. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/capture/__init__.py +0 -0
  43. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/capture/harden.py +0 -0
  44. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/capture/lineage.py +0 -0
  45. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/capture/liveness.py +0 -0
  46. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/capture/manifest.py +0 -0
  47. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/capture/paths.py +0 -0
  48. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/capture/verify.py +0 -0
  49. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/cell.py +0 -0
  50. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/chain_token.py +0 -0
  51. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/channel.py +0 -0
  52. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/__init__.py +0 -0
  53. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/_content.py +0 -0
  54. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/_display.py +0 -0
  55. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/_gateway_client.py +0 -0
  56. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/add.py +0 -0
  57. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/bench.py +0 -0
  58. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/board.py +0 -0
  59. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/board_merge_check.py +0 -0
  60. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/brain.py +0 -0
  61. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/brain_ask.py +0 -0
  62. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/cell.py +0 -0
  63. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/cell_probe.py +0 -0
  64. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  65. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/channel.py +0 -0
  66. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/chat.py +0 -0
  67. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/codegraph.py +0 -0
  68. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/codegraph_hook.py +0 -0
  69. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/codex_hook_output.py +0 -0
  70. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/codex_waker.py +0 -0
  71. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/compress.py +0 -0
  72. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/daemon.py +0 -0
  73. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/dreaming.py +0 -0
  74. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/event.py +0 -0
  75. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/gateway.py +0 -0
  76. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/gh_route.py +0 -0
  77. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/group.py +0 -0
  78. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/guide.py +0 -0
  79. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/highlight.py +0 -0
  80. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/hook_interpreter.py +0 -0
  81. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/hook_output.py +0 -0
  82. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/hooks.py +0 -0
  83. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/import_session.py +0 -0
  84. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/init.py +0 -0
  85. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
  86. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/install_hook.py +0 -0
  87. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  88. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
  89. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
  90. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/install_wake_hook.py +0 -0
  91. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/lane.py +0 -0
  92. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/mcp_server.py +0 -0
  93. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/memory.py +0 -0
  94. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
  95. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/memory_sync.py +0 -0
  96. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/monitor.py +0 -0
  97. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/okf_links.py +0 -0
  98. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/onboard.py +0 -0
  99. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
  100. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
  101. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/protocol_handler.py +0 -0
  102. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/ratify.py +0 -0
  103. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/schedule.py +0 -0
  104. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/security.py +0 -0
  105. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/service.py +0 -0
  106. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/timeline.py +0 -0
  107. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/version.py +0 -0
  108. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/wake_hook_output.py +0 -0
  109. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/commands/watchdog.py +0 -0
  110. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/compress/__init__.py +0 -0
  111. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/compress/levers.py +0 -0
  112. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/compress/marker.py +0 -0
  113. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/compress/verify.py +0 -0
  114. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/console_safe.py +0 -0
  115. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/delivery_queue.py +0 -0
  116. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/dm_frame.py +0 -0
  117. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/dreaming/__init__.py +0 -0
  118. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/dreaming/candidates.py +0 -0
  119. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/dreaming/clone.py +0 -0
  120. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/dreaming/enrich.py +0 -0
  121. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/dreaming/index_check.py +0 -0
  122. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/dreaming/notify.py +0 -0
  123. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/dreaming/organize.py +0 -0
  124. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/dreaming/probes.py +0 -0
  125. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/dreaming/report.py +0 -0
  126. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/dreaming/run.py +0 -0
  127. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/dreaming/slm.py +0 -0
  128. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/dreaming/transcripts.py +0 -0
  129. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/dreaming/verify.py +0 -0
  130. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/gateway/__init__.py +0 -0
  131. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/gateway/feature_registry.py +0 -0
  132. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/gateway/lanes_control.py +0 -0
  133. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/gateway/schema.sql +0 -0
  134. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/gateway/server.py +0 -0
  135. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/gateway/services_control.py +0 -0
  136. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/gateway_default.py +0 -0
  137. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/gh_identity.py +0 -0
  138. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/identity.py +0 -0
  139. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/main.py +0 -0
  140. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/multiplexer.py +0 -0
  141. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/operator/__init__.py +0 -0
  142. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/operator/__main__.py +0 -0
  143. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/operator/api.py +0 -0
  144. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/operator/cli.py +0 -0
  145. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/operator/config.py +0 -0
  146. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/operator/fmt.py +0 -0
  147. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/orphan_daemons.py +0 -0
  148. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/pane_probe.py +0 -0
  149. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/parsers/__init__.py +0 -0
  150. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/parsers/claude.py +0 -0
  151. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
  152. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
  153. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
  154. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
  155. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
  156. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
  157. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
  158. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
  159. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
  160. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
  161. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
  162. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
  163. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/peer_executor.py +0 -0
  164. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/peer_executor_windows.py +0 -0
  165. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/peer_reconciliation.py +0 -0
  166. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/peer_reply_outbox.py +0 -0
  167. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/peer_service_host.py +0 -0
  168. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/peer_service_payload.py +0 -0
  169. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/peer_service_reply.py +0 -0
  170. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/peer_staging.py +0 -0
  171. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/query_filters.py +0 -0
  172. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/scripts/__init__.py +0 -0
  173. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
  174. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  175. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
  176. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
  177. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/service/__init__.py +0 -0
  178. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/service/app.py +0 -0
  179. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/service/providers.py +0 -0
  180. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/session_bridge.py +0 -0
  181. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
  182. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
  183. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/source_text.py +0 -0
  184. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/stall_alert.py +0 -0
  185. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/systemd/codex-waker.default +0 -0
  186. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
  187. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
  188. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
  189. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
  190. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
  191. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
  192. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
  193. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
  194. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
  195. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
  196. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  197. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/timeline_paths.py +0 -0
  198. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli/tokens.py +0 -0
  199. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  200. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  201. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli.egg-info/requires.txt +0 -0
  202. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/src/swarph_cli.egg-info/top_level.txt +0 -0
  203. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_1006_session_board.py +0 -0
  204. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_1017_muse_tui.py +0 -0
  205. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_124_register_merge.py +0 -0
  206. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_126_pending_from_complete.py +0 -0
  207. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_132_ensure_monitor_state_dir.py +0 -0
  208. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_138_memory_navigate.py +0 -0
  209. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_139_tool_probe.py +0 -0
  210. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_183_card_anchor.py +0 -0
  211. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_184_notify_honesty.py +0 -0
  212. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_201_win32_bash_refusal.py +0 -0
  213. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_243_onboard_per_peer_token.py +0 -0
  214. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_307_task4_ask_reply.py +0 -0
  215. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
  216. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_319_refusal_exit_codes.py +0 -0
  217. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_323_mistral_backend.py +0 -0
  218. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_332_report_organize_findings.py +0 -0
  219. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_356_query_filters.py +0 -0
  220. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_397_gh_identity_router.py +0 -0
  221. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_397_router_preflight.py +0 -0
  222. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_402_one_identity_resolver.py +0 -0
  223. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_467b_onboard_operator_credential.py +0 -0
  224. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_472_register_caller_binding.py +0 -0
  225. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_484_codex_queue_sink.py +0 -0
  226. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_523_guide.py +0 -0
  227. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_524_conftest_hermeticity.py +0 -0
  228. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_532_accept_flag.py +0 -0
  229. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_535_version_capability.py +0 -0
  230. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_538_signal_bench.py +0 -0
  231. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_546_default_gateway.py +0 -0
  232. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_547_verbs_from_registry.py +0 -0
  233. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_548_gateway_remainder.py +0 -0
  234. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_552_codegraph_gateway.py +0 -0
  235. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_555_relay_row_shape.py +0 -0
  236. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_556_relay_freshness.py +0 -0
  237. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_559_edges_truncated.py +0 -0
  238. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_564_bootstrap_first_peer.py +0 -0
  239. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
  240. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_579_register_bootstrap_message.py +0 -0
  241. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_591_cli_step_graph.py +0 -0
  242. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_644_monitor_supervision.py +0 -0
  243. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_648_install_unit.py +0 -0
  244. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_654_reexec.py +0 -0
  245. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_656_owned_slm.py +0 -0
  246. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_684_dreaming_verb.py +0 -0
  247. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_689_pkg_version_order.py +0 -0
  248. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_715_grok_wake.py +0 -0
  249. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_716_timeline_one_path.py +0 -0
  250. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_723_grok_0251.py +0 -0
  251. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_724_ps1_ascii_or_bom.py +0 -0
  252. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_725_stdio_reconfigure.py +0 -0
  253. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_726_grok_limit_screen.py +0 -0
  254. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_729_arm_check_delivery_gap.py +0 -0
  255. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_729_dm_once.py +0 -0
  256. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_729_wake_watchdog.py +0 -0
  257. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_734_dreaming_enrich_degrade.py +0 -0
  258. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_735_dm_mcp_tools.py +0 -0
  259. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_739_wake_writer.py +0 -0
  260. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_740_edit_project.py +0 -0
  261. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_762_cards_edit_priority.py +0 -0
  262. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_764_enrich_collapse.py +0 -0
  263. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_801_confirm_flow_cli.py +0 -0
  264. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_802_stage_history.py +0 -0
  265. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_825_codegraph_hook_noise.py +0 -0
  266. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_825_install_posttooluse_only.py +0 -0
  267. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_825_remaining_cap_shred_bind.py +0 -0
  268. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_829_counterfactual_column.py +0 -0
  269. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_830_hook_binding_version.py +0 -0
  270. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_84_cli_labels.py +0 -0
  271. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_866_doctrine_guide.py +0 -0
  272. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_872_identity_refuses_not_warns.py +0 -0
  273. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_874_source_text.py +0 -0
  274. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
  275. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_937_dreaming_notify.py +0 -0
  276. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_938_organize_line_limit.py +0 -0
  277. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_941_bench_backends.py +0 -0
  278. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_941_harness_342.py +0 -0
  279. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_945_same_line_pairing.py +0 -0
  280. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_955_prompt_audit.py +0 -0
  281. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_957_auth_http.py +0 -0
  282. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_959_venv_interpreter.py +0 -0
  283. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_960_channel.py +0 -0
  284. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
  285. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_961_cursor_grok_footer.py +0 -0
  286. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_961_loud_stop.py +0 -0
  287. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_963_plugin_manifest.py +0 -0
  288. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_964_channel_cell.py +0 -0
  289. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_965_mesh_wait.py +0 -0
  290. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_975_channel_hop.py +0 -0
  291. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_add_security_gate.py +0 -0
  292. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_artifact_add.py +0 -0
  293. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_artifact_hash.py +0 -0
  294. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_artifact_lib.py +0 -0
  295. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_artifact_mcp.py +0 -0
  296. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_artifact_skill.py +0 -0
  297. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_artifact_tool.py +0 -0
  298. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_artifact_uri.py +0 -0
  299. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_bench_backends.py +0 -0
  300. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_bench_claude_cli.py +0 -0
  301. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_bench_codex_cli.py +0 -0
  302. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_bench_command.py +0 -0
  303. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_bench_pack.py +0 -0
  304. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_bench_prices.py +0 -0
  305. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_bench_quality.py +0 -0
  306. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_bench_runner.py +0 -0
  307. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_bench_validate.py +0 -0
  308. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_board_card_thread.py +0 -0
  309. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_board_command.py +0 -0
  310. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_board_merge_check.py +0 -0
  311. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_board_obligation_close.py +0 -0
  312. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_brain_ask_command.py +0 -0
  313. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_brain_ask_env_diagnosis.py +0 -0
  314. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_brain_ask_identity_explicitness.py +0 -0
  315. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_brain_command.py +0 -0
  316. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_caller_meta_guard.py +0 -0
  317. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_capture_lineage.py +0 -0
  318. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_capture_liveness.py +0 -0
  319. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_capture_manifest.py +0 -0
  320. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_capture_paths.py +0 -0
  321. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_capture_security.py +0 -0
  322. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_cell_command_dispatch.py +0 -0
  323. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_cell_harden.py +0 -0
  324. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_cell_loader.py +0 -0
  325. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_cell_selfcheck.py +0 -0
  326. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_cell_verify.py +0 -0
  327. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_chain_token.py +0 -0
  328. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_channel_command.py +0 -0
  329. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_channel_poll_never_breaks_dms.py +0 -0
  330. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_channel_post.py +0 -0
  331. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_channel_wake_policy_filter.py +0 -0
  332. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_chat_command.py +0 -0
  333. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_claude_parser.py +0 -0
  334. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_claude_tmux_template.py +0 -0
  335. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_codegraph_caller_scope.py +0 -0
  336. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_codegraph_command.py +0 -0
  337. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_codegraph_hook.py +0 -0
  338. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_codegraph_mcp.py +0 -0
  339. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_codex_waker.py +0 -0
  340. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_compress_caller_convention.py +0 -0
  341. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_compress_command.py +0 -0
  342. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_compress_levers.py +0 -0
  343. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_compress_marker.py +0 -0
  344. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_compress_verify.py +0 -0
  345. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_console_encoding_e2e.py +0 -0
  346. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_content_composition.py +0 -0
  347. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_cursor_membrane.py +0 -0
  348. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_daemon_command.py +0 -0
  349. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_daemon_console_encoding.py +0 -0
  350. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_daemon_delivery.py +0 -0
  351. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_delivery_queue.py +0 -0
  352. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_display_sanitize.py +0 -0
  353. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_ensure_monitor_no_peer_default.py +0 -0
  354. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_event_emit.py +0 -0
  355. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_feature_to_uri.py +0 -0
  356. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_gateway_brain.py +0 -0
  357. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_gateway_client.py +0 -0
  358. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_gateway_command.py +0 -0
  359. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_gateway_message_idempotency.py +0 -0
  360. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_gh_route_doctor_mesh_name.py +0 -0
  361. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_group_command.py +0 -0
  362. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_guide_commands_parse.py +0 -0
  363. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_guide_selfcheck.py +0 -0
  364. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_heartbeat_check.py +0 -0
  365. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_highlight_command.py +0 -0
  366. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_hook_output.py +0 -0
  367. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_hooks_activity_marker.py +0 -0
  368. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_hooks_add.py +0 -0
  369. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_hooks_bundle.py +0 -0
  370. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_hooks_init.py +0 -0
  371. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_hooks_legacy_generation_removal.py +0 -0
  372. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_hooks_lifecycle.py +0 -0
  373. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_hooks_merge.py +0 -0
  374. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_hooks_verify.py +0 -0
  375. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_hooks_win32_bash_wrapper.py +0 -0
  376. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_hooks_win32_command_path.py +0 -0
  377. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_import_command.py +0 -0
  378. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_inbox_identity_provenance.py +0 -0
  379. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_init_command.py +0 -0
  380. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_init_provider_menu.py +0 -0
  381. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_install_codex_hooks.py +0 -0
  382. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_install_hook.py +0 -0
  383. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_install_multiplexer_command.py +0 -0
  384. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_install_postcompact_hook.py +0 -0
  385. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_install_wake_hook.py +0 -0
  386. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_lane_command.py +0 -0
  387. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_main.py +0 -0
  388. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_main_unknown_verb_guard.py +0 -0
  389. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_mcp_missing_hint.py +0 -0
  390. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_mcp_server.py +0 -0
  391. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_memory_command.py +0 -0
  392. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_memory_emit_snippet.py +0 -0
  393. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_memory_gateway.py +0 -0
  394. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_memory_sync.py +0 -0
  395. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_memory_sync_membrane.py +0 -0
  396. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_mesh_command.py +0 -0
  397. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_mesh_inbox_mark_read.py +0 -0
  398. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_mesh_inbox_sanitize.py +0 -0
  399. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_mesh_originate_thread_623.py +0 -0
  400. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_mesh_send_unknown_peer.py +0 -0
  401. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_mesh_sidecar_muse_sink.py +0 -0
  402. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_meta_edge_identity.py +0 -0
  403. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_monitor_channel_poll.py +0 -0
  404. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_monitor_daemon_collision.py +0 -0
  405. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_monitor_self_name_verification.py +0 -0
  406. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_multiplexer.py +0 -0
  407. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_muse_membrane.py +0 -0
  408. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_muse_own_cli.py +0 -0
  409. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_obligation_close_is_surfaced.py +0 -0
  410. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_okf_links.py +0 -0
  411. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_onboard_command.py +0 -0
  412. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_opencode_membrane.py +0 -0
  413. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_opencode_plugin.py +0 -0
  414. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_operator_me.py +0 -0
  415. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_orphan_daemons.py +0 -0
  416. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_packaged_artifacts_exist.py +0 -0
  417. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_packaged_skills.py +0 -0
  418. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_peer_executor.py +0 -0
  419. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_peer_executor_windows.py +0 -0
  420. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_peer_reply_drain_command.py +0 -0
  421. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_peer_reply_drain_supervisor.py +0 -0
  422. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_peer_reply_outbox.py +0 -0
  423. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_peer_reply_transport.py +0 -0
  424. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_peer_service_e2e.py +0 -0
  425. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_peer_service_host.py +0 -0
  426. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_peer_service_payload.py +0 -0
  427. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_peer_service_reply.py +0 -0
  428. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_postcompact_recall.py +0 -0
  429. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_process_liveness.py +0 -0
  430. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_protocol_handler.py +0 -0
  431. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_providers_isolation.py +0 -0
  432. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_ratify_command.py +0 -0
  433. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_readme_claims_no_gateway_default.py +0 -0
  434. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_repl_caller_convention.py +0 -0
  435. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_schedule_command.py +0 -0
  436. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_schedule_create_contract.py +0 -0
  437. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_security.py +0 -0
  438. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_service_app.py +0 -0
  439. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_service_command.py +0 -0
  440. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_service_providers.py +0 -0
  441. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_session_bridge_inject.py +0 -0
  442. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_session_bridge_probe.py +0 -0
  443. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_session_bridge_resolve.py +0 -0
  444. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_session_path_role_gate.py +0 -0
  445. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_shipped_units_match_the_cli.py +0 -0
  446. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_smoke_chat.py +0 -0
  447. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_smoke_one_shot.py +0 -0
  448. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_smoke_phase_5_5.py +0 -0
  449. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_spawn_base_hoist.py +0 -0
  450. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_spawn_command.py +0 -0
  451. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_spawn_identity_stamp.py +0 -0
  452. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_spawn_live_pin.py +0 -0
  453. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_spawn_mitosis_lineage.py +0 -0
  454. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_spawn_tmux_session.py +0 -0
  455. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_spawn_windows_relaunch.py +0 -0
  456. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_stall_alert.py +0 -0
  457. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_subprocess_decode_encoding.py +0 -0
  458. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_timeline_command.py +0 -0
  459. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_token_file_one_parser.py +0 -0
  460. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_token_precedence_explicit_first.py +0 -0
  461. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_unified_token_resolver.py +0 -0
  462. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_version_command.py +0 -0
  463. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_version_consistency.py +0 -0
  464. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_vibe_membrane.py +0 -0
  465. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_watchdog.py +0 -0
  466. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_watchdog_dm_wake.py +0 -0
  467. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  468. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  469. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_watchdog_emit_health.py +0 -0
  470. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_watchdog_install_flags.py +0 -0
  471. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_watchdog_model_rung.py +0 -0
  472. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/tests/test_watchdog_stale_peers.py +0 -0
  473. {swarph_cli-0.72.2 → swarph_cli-0.72.4}/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.2
3
+ Version: 0.72.4
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.2"
7
+ version = "0.72.4"
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.2"
19
+ __version__ = "0.72.4"
20
20
 
21
21
  __all__ = ["__version__"]
@@ -1483,6 +1483,10 @@ class TmuxSink(Sink):
1483
1483
  def deliver(self, state: "MonitorState", dms: list, up_to_id: int) -> Optional[bool]:
1484
1484
  led = state.ledger(self.name)
1485
1485
  self.failure_reason = None
1486
+ # #1010: the deferral cause, named for the monitor's DEFERRED log —
1487
+ # a hardcoded cause is a lie on every path that defers for another
1488
+ # reason (the standing-wake hold defers with a CLEAR composer).
1489
+ self.deferred_reason = None
1486
1490
  # #723: a rate-limit or usage-limit screen is not deliverable. An
1487
1491
  # empty composer beside "Retry failed: rate limit" used to inject
1488
1492
  # again, and the 1.0.44 upgrade modal (no Grok footer) used to
@@ -1601,21 +1605,36 @@ class TmuxSink(Sink):
1601
1605
  if ok is None:
1602
1606
  return None # human adopted mid-settle: defer
1603
1607
  return False # unreadable: loud failure
1604
- # Wake submitted into THIS session and still inside its
1605
- # trust window; re-injecting would only stack. This return
1606
- # is the ONLY delivery report backed by no fresh keystroke
1607
- # — every other outcome (delivered, failed, deferred)
1608
- # announces itself, and this branch's 57 lines had zero
1609
- # print() calls. Silence correlated with the lie (#611,
1610
- # cursor-win's swallowed wake): say what we are claiming
1611
- # and how old the evidence is. (#616 makes #332's
1612
- # "injection predates the ledger anchor" arm unreachable
1613
- # on this path — an undated wake is always stale — so the
1614
- # arm is removed rather than kept as readable-dead code.)
1615
- print(f"[monitor] {self.name}: delivery reported on a STANDING wake "
1616
- f"(injected {time.time() - injected_at:.0f}s ago, "
1617
- f"no keystroke this poll)")
1618
- return True
1608
+ # #1010 STRAND-ONLY (lab's ruling after the 5th FAIL,
1609
+ # science-claude #1101): this return was the ONLY
1610
+ # delivery report backed by no fresh keystroke, and it
1611
+ # STRANDED the DM batch it reported. The ledger cursor
1612
+ # advanced to the newest id, the monitor then owed
1613
+ # nothing, and this poll was the last one that would
1614
+ # ever look at the batch — measured by droplet on
1615
+ # friendly-coder: its tail was down, typing was its only
1616
+ # wake, and the standing-wake claim "delivered" every
1617
+ # DM with ZERO keystrokes while the cell idled at an
1618
+ # empty prompt, forever. Fix WITHOUT classification:
1619
+ # report the hold, not a delivery. deliver returns None
1620
+ # — the cursor does not advance, the batch stays OWED
1621
+ # and is retried on every poll — and BASE'S OWN stale
1622
+ # bound below (_WAKE_STALE_S, 600 s from the injection)
1623
+ # re-injects and types at the first idle poll past it.
1624
+ # Bounded, loud, never silently dropped; no pane
1625
+ # classifier is consulted, so every pane keeps base's
1626
+ # behaviour. The anti-stack property survives: zero
1627
+ # keystrokes while the window stands.
1628
+ print(f"[monitor] {self.name}: standing wake inside its "
1629
+ f"{_WAKE_STALE_S:.0f}s trust window (injected "
1630
+ f"{time.time() - injected_at:.0f}s ago) — DM held "
1631
+ f"owed, zero keys this poll; the stale bound "
1632
+ f"re-injects and types")
1633
+ self.deferred_reason = (
1634
+ f"standing wake inside the {_WAKE_STALE_S:.0f}s "
1635
+ "trust window — DM held owed, the stale bound "
1636
+ "re-injects and types")
1637
+ return None
1619
1638
  if composer == "busy":
1620
1639
  # Human text shares the composer (possibly merged into our
1621
1640
  # wake) — an Enter here submits THEIR line (#403's shape).
@@ -3333,8 +3352,13 @@ def _monitor_deliver(state: MonitorState) -> None:
3333
3352
  state.gateway, state.token, state.self_name, ticks, len(dms)
3334
3353
  )
3335
3354
  changed = True
3355
+ # #1010: name the deferral's real cause — a hardcoded
3356
+ # "composer holds human text" is a lie on the standing-wake
3357
+ # hold, which defers with a CLEAR composer.
3358
+ detail = getattr(sink, "deferred_reason", None) \
3359
+ or "composer holds human text"
3336
3360
  print(f"{state.log_prefix} delivery DEFERRED for {sink.name} "
3337
- f"(composer holds human text); wake stays owed, "
3361
+ f"({detail}); wake stays owed, "
3338
3362
  f"no failure counted",
3339
3363
  flush=True)
3340
3364
  continue
@@ -581,9 +581,16 @@ def _spawn_env_base(cell: Cell) -> dict[str, str]:
581
581
  # NOTE: the marketplace/validity gate lives in _validate_channel, which
582
582
  # _build_claude_argv (and the muse env builder) run. This base stays a
583
583
  # pure reflection of the resolved mode so every provider's env agrees.
584
+ # #1022: SWARPH_CHANNEL must be exported too, whatever the mode's source.
585
+ # channel_opted_in() (channel.py) gates on SWARPH_CHANNEL alone, so a
586
+ # mode that came from cell.yaml — not from the parent env — left the
587
+ # session with SWARPH_CHANNEL_CELL set and the channel deaf: the server
588
+ # never declared claude/channel (droplet 4 days, gridiron on lab).
584
589
  if mode in {"allowlisted", "dev"}:
590
+ env["SWARPH_CHANNEL"] = mode
585
591
  env["SWARPH_CHANNEL_CELL"] = cell.name
586
592
  else:
593
+ env.pop("SWARPH_CHANNEL", None)
587
594
  env.pop("SWARPH_CHANNEL_CELL", None)
588
595
  return env
589
596
 
@@ -23,6 +23,7 @@ Read the topic you need. You do not need to read this top to bottom.
23
23
  | [Check your own setup](#check-your-own-setup) | commands with their expected answers |
24
24
  | [How to](#how-to) | tasks, phrased the way you'd ask for them |
25
25
  | [Glossary](#glossary) | the words this mesh uses |
26
+ | [Architecture overview](#architecture-overview) | the five layers, the six rules, and how a change ships |
26
27
 
27
28
  ---
28
29
 
@@ -55,6 +56,15 @@ want, run the command.
55
56
  | see what a wake hook would print | `swarph wake-hook-output` |
56
57
  | search this guide | `swarph guide --search <word>` |
57
58
  | learn the standard of evidence | `swarph guide doctrine` |
59
+ | see how the stack fits together | see [Architecture overview](#architecture-overview) |
60
+
61
+ ---
62
+
63
+ ## Architecture overview
64
+
65
+ The five layers, the six rules, the delivery pipeline, shadow-to-graduate,
66
+ the security posture, and the stack-versus-off-the-shelf table are in
67
+ [docs/architecture-overview.md](../../../docs/architecture-overview.md).
58
68
 
59
69
  ---
60
70
 
@@ -1,4 +1,4 @@
1
- """Card #729. Every pass, a cell with nobody reading its inbox is an outage.
1
+ """IMMUNE (cell watchdog), card #729. Every pass, a cell with nobody reading its inbox is an outage.
2
2
 
3
3
  One DM per outage. A live watcher clears it, so the next gap can alert again.
4
4
  """
@@ -360,17 +360,17 @@ def main(argv: list[str] | None = None) -> int:
360
360
  for name in alert:
361
361
  print(f"outage {name}", flush=True)
362
362
  dm_rc = mesh_send(
363
- swarph, name, sender, "your DM wake is dead, re-arm", token_file)
363
+ swarph, name, sender, "IMMUNE (cell watchdog): your DM wake is dead, re-arm", token_file)
364
364
  dm_ok = dm_rc == 0
365
365
  card_argv = [swarph, "board", "cards", "say", "729", "--as", sender,
366
- "--to", name, "--content", f"{name}: DM wake is dead, re-arm"]
366
+ "--to", name, "--content", f"IMMUNE (cell watchdog) — {name}: DM wake is dead, re-arm"]
367
367
  if token_file:
368
368
  card_argv.extend(["--token-file", token_file])
369
369
  card = subprocess.run(card_argv, check=False)
370
370
  esc_ok = True
371
371
  if escalate and escalate != name:
372
372
  esc_rc = mesh_send(
373
- swarph, escalate, sender, f"{name}: DM wake is dead, re-arm",
373
+ swarph, escalate, sender, f"IMMUNE (cell watchdog) — {name}: DM wake is dead, re-arm",
374
374
  token_file)
375
375
  esc_ok = esc_rc == 0
376
376
  if dm_ok and card.returncode == 0 and esc_ok:
@@ -1,5 +1,5 @@
1
1
  [Unit]
2
- Description=Swarph watchdog one-shot check (stranded-session recovery)
2
+ Description=IMMUNE (cell watchdog): Swarph watchdog one-shot check (stranded-session recovery)
3
3
  Documentation=https://github.com/BrainSurfing-tech/swarph-cli
4
4
  After=network-online.target
5
5
  Wants=network-online.target
@@ -1,5 +1,5 @@
1
1
  [Unit]
2
- Description=Run swarph watchdog every 5 minutes (stranded-session recovery)
2
+ Description=Run IMMUNE (cell watchdog): swarph watchdog every 5 minutes (stranded-session recovery)
3
3
  Documentation=https://github.com/BrainSurfing-tech/swarph-cli
4
4
  Requires=swarph-watchdog.service
5
5
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.72.2
3
+ Version: 0.72.4
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
@@ -188,6 +188,7 @@ src/swarph_cli/systemd/swarph-watchdog.service
188
188
  src/swarph_cli/systemd/swarph-watchdog.timer
189
189
  tests/test_1006_board_publisher.py
190
190
  tests/test_1006_session_board.py
191
+ tests/test_1010_strand_only.py
191
192
  tests/test_1013_spawn_channel.py
192
193
  tests/test_1017_muse_tui.py
193
194
  tests/test_124_register_merge.py
@@ -346,3 +346,77 @@ def test_in_session_marker_sets_the_flag_and_strips_the_title():
346
346
  assert "[in-session]" not in marked["title"]
347
347
  assert "[commander]" not in marked["title"]
348
348
  assert plain["in_session"] is False
349
+
350
+
351
+ def _commander_obligation(oid, holder="commander", step="build", accept="PASS=x | FAIL=y", title="Do it"):
352
+ return {
353
+ "id": oid,
354
+ "holder": holder,
355
+ "card_id": 291,
356
+ "state": "open",
357
+ "step": step,
358
+ "accept": f"[commander] {title} | {accept}",
359
+ }
360
+
361
+
362
+ def test_tap_closable_commander_rows_render_yes_no():
363
+ """card #291 ruling_1157 (1): a tap-closable commander row (build or
364
+ step-less) renders as yes/no with the fixed mapping yes=pass, no=fail —
365
+ even when the row carries its own prose options."""
366
+ pub = _load()
367
+ rows = pub.commander_rows([
368
+ _commander_obligation(1, step="build"),
369
+ _commander_obligation(2, step=None),
370
+ ])
371
+ assert [r["step"] for r in rows] == ["build", None]
372
+ by_cell = {}
373
+ for row in rows:
374
+ row["title"] = "Do it"
375
+ row["options"] = [{"label": "Prose", "text": "discuss first", "rec": True}]
376
+ by_cell.setdefault(row["cell"], []).append(row)
377
+ questions = pub.build_board._questions(by_cell["commander"])
378
+ assert len(questions) == 2
379
+ for q in questions:
380
+ assert [(o["label"], o["text"], o["outcome"]) for o in q["options"]] == [
381
+ ("Yes", "yes", "pass"),
382
+ ("No", "no", "fail"),
383
+ ], "RED: tap-closable rows render yes/no, never row prose"
384
+
385
+
386
+ def test_validate_plan_review_rows_keep_display_only_options():
387
+ """card #291 ruling_1157 (1): validate/plan-review commander rows are
388
+ NOT taps — their options stay prose and every option carries
389
+ outcome None (display-only, closes nothing)."""
390
+ pub = _load()
391
+ rows = pub.commander_rows([
392
+ _commander_obligation(3, step="validate"),
393
+ _commander_obligation(4, step="plan-review"),
394
+ ])
395
+ by_cell = {}
396
+ for row in rows:
397
+ row["title"] = "Check it"
398
+ row["options"] = [{"label": "Prose", "text": "discuss first", "rec": True}]
399
+ by_cell.setdefault(row["cell"], []).append(row)
400
+ questions = pub.build_board._questions(by_cell["commander"])
401
+ assert len(questions) == 2
402
+ for q in questions:
403
+ assert all(o.get("outcome") is None for o in q["options"]), \
404
+ "RED: no prose option closes PASS by default"
405
+ assert [o["label"] for o in q["options"]] == ["Prose"]
406
+
407
+
408
+ def test_default_options_are_display_only():
409
+ """The three fallback prose options carry outcome None on every row."""
410
+ pub = _load()
411
+ rows = pub.commander_rows([_commander_obligation(5, step="build")])
412
+ row = dict(rows[0])
413
+ row["title"] = "Do it"
414
+ row.pop("options", None)
415
+ other = dict(row, cell="drop-on-meta-edge", holder="drop-on-meta-edge")
416
+ for q in pub.build_board._questions([row, other]):
417
+ if q["to_node"] == "commander":
418
+ assert [o["outcome"] for o in q["options"]] == ["pass", "fail"]
419
+ else:
420
+ assert [o["label"] for o in q["options"]] == [
421
+ "Answer on the card", "Ask for a narrower question", "Park it"]
422
+ assert all(o["outcome"] is None for o in q["options"])
@@ -0,0 +1,225 @@
1
+ """#1010 STRAND-ONLY (lab's ruling after science-claude's #1101 FAIL, the
2
+ 5th on PR 534): PR 534 ships NO pane idle/busy classifier change — every
3
+ pane keeps base 0.72.3's behaviour — and the strand is fixed in the
4
+ LEDGER layer alone.
5
+
6
+ THE STRAND (measured by droplet on friendly-coder): the tmux sink's
7
+ standing-wake path returned True with ZERO keystrokes. The monitor
8
+ advanced `last_delivered_id`, owed nothing from then on, and the DM
9
+ batch was never looked at again — every DM "delivered" in the log while
10
+ the cell idled at an empty prompt, typing its only wake, forever. The
11
+ repro that opened #1010: an ANSWERED wake, a monitor RESTART, a new DM.
12
+
13
+ THE FIX (no classification): the standing-wake hold reports the hold,
14
+ not a delivery. deliver returns None — the cursor does not advance, the
15
+ batch stays OWED and is retried on every poll — and BASE'S OWN stale
16
+ bound (_WAKE_STALE_S = 600 s from the injection) re-injects and types
17
+ at the first idle poll past it. Bounded, loud, never silently dropped.
18
+
19
+ >>> RED on e445877 (base 0.72.3): the monitor-level repro below never
20
+ typed — the first poll returned True with zero keystrokes, the cursor
21
+ advanced, and every later poll owed nothing (assert wake_calls == 1
22
+ fails with 0). The unit pins of the hold returned True there too.
23
+ """
24
+ from __future__ import annotations
25
+
26
+ import pathlib
27
+ import time
28
+
29
+ import swarph_cli.commands.mesh as mesh
30
+ import swarph_cli.commands.watchdog as watchdog
31
+
32
+
33
+ class _StubState:
34
+ def __init__(self):
35
+ self.gateway = "http://gw:8788"
36
+ self.self_name = "cursor-lin"
37
+ self.token = "tok"
38
+ self._ledgers: dict = {}
39
+
40
+ def ledger(self, name: str) -> dict:
41
+ return self._ledgers.setdefault(
42
+ name, {"last_delivered_id": 0, "last_delivery_at": 0.0,
43
+ "consecutive_failures": 0})
44
+
45
+
46
+ def _rig(monkeypatch, *, composer="clear", unread=3,
47
+ session_created=None, running=False):
48
+ """Pin every observation deliver() makes; record the keystrokes.
49
+
50
+ running defaults to False: the #1010 repro is the ANSWERED wake — the
51
+ turn already ran, the pane idles at an empty prompt. The rig pins
52
+ `_capture_pane_lines` too: the conftest guard fails any unmocked tmux
53
+ call, and deliver() reads the pane for the grok-block check before
54
+ any of this."""
55
+ calls = {"wake": 0, "enter": 0}
56
+ monkeypatch.setattr(mesh, "_capture_pane_lines", lambda t: ["row"])
57
+ monkeypatch.setattr(mesh, "_composer_state", lambda t: composer)
58
+ monkeypatch.setattr(mesh, "_tmux_session_created", lambda t: session_created)
59
+ monkeypatch.setattr(mesh, "_agent_running", lambda t: running)
60
+ monkeypatch.setattr(watchdog, "_gateway_unread_count",
61
+ lambda *a, **k: unread)
62
+
63
+ def fake_wake(target):
64
+ calls["wake"] += 1
65
+ return True
66
+
67
+ def fake_enter(target):
68
+ calls["enter"] += 1
69
+ return True
70
+
71
+ monkeypatch.setattr(mesh, "_tmux_wake", fake_wake)
72
+ monkeypatch.setattr(mesh, "_tmux_enter", fake_enter)
73
+ return calls
74
+
75
+
76
+ def _owed_state(injected_at=None):
77
+ state = _StubState()
78
+ led = state.ledger("tmux:cursor-lin")
79
+ led["wake_outstanding"] = True
80
+ if injected_at is not None:
81
+ led["last_wake_injected_at"] = injected_at
82
+ return state
83
+
84
+
85
+ # ── the hold: loud, bounded, zero keystrokes, nothing marked delivered ───
86
+
87
+ def test_standing_wake_hold_is_loud_bounded_and_keeps_the_dm_owed(
88
+ monkeypatch, capsys):
89
+ """#1010 clause 2: a DM suppressed by a standing wake is NEVER
90
+ marked delivered while untyped. The hold says what it is doing — the
91
+ trust window, the wake's age, and that the stale bound types — and
92
+ returns None, so the monitor's cursor stays put and the batch is
93
+ retried next poll. >>> RED on e445877: this returned True — delivery
94
+ reported, zero keystrokes, the DM stranded."""
95
+ now = time.time()
96
+ calls = _rig(monkeypatch, composer="clear", unread=3,
97
+ session_created=now - 7200, running=False)
98
+ state = _owed_state(injected_at=now - 60)
99
+ sink = mesh.TmuxSink("cursor-lin")
100
+
101
+ assert sink.deliver(state, [{"id": 9}], 9) is None
102
+ assert calls["wake"] == 0 and calls["enter"] == 0, "zero keys inside the window"
103
+ led = state.ledger("tmux:cursor-lin")
104
+ assert led["wake_outstanding"] is True, "the standing wake is undisturbed"
105
+ out = capsys.readouterr().out
106
+ assert "standing wake inside its 600s trust window" in out, out
107
+ assert "DM held owed" in out
108
+ assert "zero keys" in out
109
+ assert "re-injects and types" in out
110
+ # the monitor's DEFERRED line names the real cause, not human text
111
+ assert "standing wake" in (sink.deferred_reason or "")
112
+ assert "human text" not in (sink.deferred_reason or "")
113
+
114
+
115
+ def test_no_keystroke_sooner_than_base_inside_the_window(monkeypatch):
116
+ """The FAIL arm: no keystroke SOONER than base. Repeated polls inside
117
+ the trust window type nothing — the anti-stack property the
118
+ edge-trigger exists for (#312) survives the hold. Base stacked zero
119
+ keys here too; the difference is only that base also claimed
120
+ delivery."""
121
+ now = time.time()
122
+ calls = _rig(monkeypatch, composer="clear", unread=3,
123
+ session_created=now - 7200, running=False)
124
+ state = _owed_state(injected_at=now - 60)
125
+ sink = mesh.TmuxSink("cursor-lin")
126
+ for _ in range(5):
127
+ assert sink.deliver(state, [{"id": 9}], 9) is None
128
+ assert calls["wake"] == 0 and calls["enter"] == 0
129
+
130
+
131
+ def test_held_dm_is_typed_when_the_600s_window_lapses(monkeypatch):
132
+ """#1010 clause 2, the other arm: the held DM does not stay held
133
+ forever — BASE'S OWN stale bound (a wake undrained for
134
+ _WAKE_STALE_S) re-injects, and the first idle poll past the bound
135
+ carries a real keystroke. The DM is then delivered for real."""
136
+ now = time.time()
137
+ calls = _rig(monkeypatch, composer="clear", unread=3,
138
+ session_created=now - 7200, running=False)
139
+ state = _owed_state(injected_at=now - 601)
140
+ sink = mesh.TmuxSink("cursor-lin")
141
+
142
+ assert sink.deliver(state, [{"id": 9}], 9) is True
143
+ assert calls["wake"] == 1, "the stale bound re-injects — a keystroke"
144
+ led = state.ledger("tmux:cursor-lin")
145
+ assert led["last_wake_injected_at"] > now - 5, "the retry re-anchors"
146
+
147
+
148
+ # ── the original repro, at the monitor: answered wake, restart, new DM ────
149
+
150
+ class _MonitorStubState(_StubState):
151
+ """The poll-loop pass reads: sinks, the observed cursor, the ledgers,
152
+ the idle-guard clock, and the inbox log for replay."""
153
+
154
+ def __init__(self, observed_id, delivered_id, ledgers=None, inbox_log=None):
155
+ super().__init__()
156
+ if ledgers is not None:
157
+ self._ledgers = ledgers # the restart shape: one persisted ledger
158
+ self.sinks = [mesh.TmuxSink("cursor-lin")]
159
+ self.ledgers = self._ledgers
160
+ self.observed = {"last_msg_id": observed_id}
161
+ self.min_interval_s = 60
162
+ self.deliveries: dict = {}
163
+ self.ledgers_path = pathlib.Path("/dev/null")
164
+ self.log_prefix = "[test]"
165
+ self.inbox_log_path = inbox_log or pathlib.Path(
166
+ "/tmp/1010-strand-no-such-inbox.log")
167
+ self.replay_limit = 50
168
+ led = self.ledger("tmux:cursor-lin")
169
+ led["last_delivered_id"] = delivered_id
170
+ led["last_delivery_at"] = time.time() - 3600
171
+ led["wake_outstanding"] = True
172
+
173
+
174
+ def _poll(monkeypatch, state, unread):
175
+ monkeypatch.setattr(watchdog, "_gateway_unread_count",
176
+ lambda *a, **k: unread)
177
+ writes = []
178
+ monkeypatch.setattr(mesh, "_write_ledgers_atomic",
179
+ lambda p, d: writes.append(dict(d)))
180
+ mesh._monitor_deliver(state)
181
+ return writes
182
+
183
+
184
+ def test_the_original_repro_types_by_600s_even_after_a_restart(monkeypatch,
185
+ capsys):
186
+ """THE #1010 repro, end to end through the poll loop: an ANSWERED
187
+ wake (turn ran and finished; the flag stands, injected 60 s ago), a
188
+ monitor RESTART (a fresh state over the SAME ledger), a new DM.
189
+
190
+ At the head: the first poll HOLDS (zero keys, cursor unmoved, loud
191
+ line); 600 s later the stale bound re-injects — ONE keystroke — and
192
+ the ledger finally advances. Nothing strands and nothing types
193
+ sooner than base.
194
+
195
+ >>> RED on e445877: the first poll reported delivery with ZERO
196
+ keystrokes, the cursor advanced to 10, and every later poll owed
197
+ nothing — the DM was never typed (wake_calls stays 0)."""
198
+ now = time.time()
199
+ calls = _rig(monkeypatch, composer="clear", unread=3,
200
+ session_created=now - 7200, running=False)
201
+
202
+ # an answered wake, 60 s inside its trust window; the monitor owes DM 10
203
+ ledgers: dict = {}
204
+ state = _MonitorStubState(observed_id=10, delivered_id=9, ledgers=ledgers)
205
+ state.ledger("tmux:cursor-lin")["last_wake_injected_at"] = now - 60
206
+
207
+ _poll(monkeypatch, state, unread=3) # poll 1: the hold
208
+ led = ledgers["tmux:cursor-lin"]
209
+ assert led["last_delivered_id"] == 9, "nothing marked delivered untyped"
210
+ assert calls["wake"] == 0 and calls["enter"] == 0
211
+ assert "DM held owed" in capsys.readouterr().out
212
+ assert led.get("deferred_ticks") == 1, "the deferral is counted, not a failure"
213
+
214
+ # monitor restart: a fresh state over the SAME persisted ledger
215
+ state2 = _MonitorStubState(observed_id=10, delivered_id=9, ledgers=ledgers)
216
+ # 600 s pass; the same injection is now past base's stale bound
217
+ ledgers["tmux:cursor-lin"]["last_wake_injected_at"] = time.time() - 601
218
+
219
+ _poll(monkeypatch, state2, unread=3) # poll 2: past the bound
220
+ assert calls["wake"] == 1, "the stale bound re-injects and TYPES"
221
+ assert ledgers["tmux:cursor-lin"]["last_delivered_id"] == 10
222
+
223
+ _poll(monkeypatch, state2, unread=3) # poll 3: owes nothing now
224
+ assert calls["wake"] == 1, "delivered for real — no repeat keystroke"
225
+ assert "delivered to tmux:cursor-lin up to id 10" in capsys.readouterr().out
@@ -244,3 +244,28 @@ def test_parent_channel_cell_does_not_reach_a_different_cell(tmp_path, monkeypat
244
244
  assert rc == 0
245
245
  assert env_added.get("SWARPH_CHANNEL_CELL") == "science-claude"
246
246
 
247
+
248
+ def test_field_channel_exports_swarph_channel_so_serve_is_not_deaf(tmp_path, monkeypatch, capsys):
249
+ """Card #1022. A mode that came from cell.yaml must reach channel-serve:
250
+ the spawned env carries SWARPH_CHANNEL=mode (not just SWARPH_CHANNEL_CELL),
251
+ and channel_opted_in() is True under that env. On 7afd16b the field path
252
+ set only SWARPH_CHANNEL_CELL, so the server never declared claude/channel
253
+ and the cell ran deaf (droplet ~4 days, gridiron on lab).
254
+ """
255
+ from swarph_cli.channel import channel_opted_in
256
+
257
+ _registry(tmp_path, monkeypatch, with_swarph=True)
258
+ monkeypatch.delenv("SWARPH_CHANNEL", raising=False)
259
+ p = _cell_yaml(tmp_path, "claude", channel="allowlisted", name="gridiron")
260
+ rc, argv, env_added = _print(tmp_path, p, capsys)
261
+ assert rc == 0
262
+ assert "--channels" in argv and "plugin:swarph@swarph" in argv
263
+ # The env handed to claude names the mode, whatever its source was.
264
+ assert env_added.get("SWARPH_CHANNEL") == "allowlisted"
265
+ assert env_added.get("SWARPH_CHANNEL_CELL") == "gridiron"
266
+ # And the server's own gate agrees under exactly that env.
267
+ with monkeypatch.context() as ctx:
268
+ ctx.setenv("SWARPH_CHANNEL", env_added["SWARPH_CHANNEL"])
269
+ ctx.setenv("SWARPH_CHANNEL_CELL", env_added["SWARPH_CHANNEL_CELL"])
270
+ assert channel_opted_in() is True
271
+
@@ -10,6 +10,11 @@ import pytest
10
10
 
11
11
  from swarph_cli.commands import mesh
12
12
 
13
+ #: The real capture seam, taken at import before any autouse fixture pins
14
+ #: it (#1010 strand-only hermeticity): the #533 test below asserts the
15
+ #: leading capture-pane call that its own subprocess stub must serve.
16
+ _REAL_CAPTURE_PANE_LINES = mesh._capture_pane_lines
17
+
13
18
 
14
19
  @pytest.fixture(autouse=True)
15
20
  def _clean_composer(monkeypatch):
@@ -17,6 +22,15 @@ def _clean_composer(monkeypatch):
17
22
  default every composer to OBSERVED-CLEAN so the gate stays out of the
18
23
  way. The gate's own matrix lives in test_tmux_wake_submit_verify.py."""
19
24
  monkeypatch.setattr(mesh, "_composer_state", lambda t: "clear")
25
+ # #1010 strand-only hermeticity (#1083): the grok-block probe, the
26
+ # run-state read and the session-age read inside deliver() reach the
27
+ # REAL tmux binary when unpatched — a bare CI runner and a box with
28
+ # live cells must read the suite the same way. A missing pane reads
29
+ # unreadable at base; False/None keep the same falsy fall-through
30
+ # these tests pin.
31
+ monkeypatch.setattr(mesh, "_capture_pane_lines", lambda t: None)
32
+ monkeypatch.setattr(mesh, "_agent_running", lambda t: False)
33
+ monkeypatch.setattr(mesh, "_tmux_session_created", lambda t: None)
20
34
 
21
35
 
22
36
  def _state(tmp_path: Path) -> mesh.MeshSidecarState:
@@ -49,6 +63,10 @@ def test_tmux_wake_sends_literal_prompt_then_submits_and_verifies(monkeypatch):
49
63
  lambda command, **kwargs: calls.append((command, kwargs)) or Result(),
50
64
  )
51
65
  monkeypatch.setattr(mesh.time, "sleep", lambda _s: None)
66
+ # #1010 strand-only hermeticity: the autouse fixture pins the capture
67
+ # seam so no OTHER pane read reaches the real binary; this test's own
68
+ # subprocess stub serves the leading capture-pane call it asserts.
69
+ monkeypatch.setattr(mesh, "_capture_pane_lines", _REAL_CAPTURE_PANE_LINES)
52
70
  # The verifier is now a projection of the four-way composer state; the
53
71
  # autouse fixture stubs that state. Record the consultation instead of
54
72
  # expecting a raw capture-pane call in the sequence.
@@ -28,6 +28,15 @@ def _clean_composer(monkeypatch):
28
28
  default every composer to OBSERVED-CLEAN so the gate stays out of the
29
29
  way. The gate's own matrix lives in test_tmux_wake_submit_verify.py."""
30
30
  monkeypatch.setattr(mesh, "_composer_state", lambda t: "clear")
31
+ # #1010 strand-only hermeticity (#1083): the grok-block probe, the
32
+ # run-state read and the session-age read inside deliver() reach the
33
+ # REAL tmux binary when unpatched — a bare CI runner and a box with
34
+ # live cells must read the suite the same way. A missing pane reads
35
+ # unreadable at base; False/None keep the same falsy fall-through
36
+ # these tests pin.
37
+ monkeypatch.setattr(mesh, "_capture_pane_lines", lambda t: None)
38
+ monkeypatch.setattr(mesh, "_agent_running", lambda t: False)
39
+ monkeypatch.setattr(mesh, "_tmux_session_created", lambda t: None)
31
40
 
32
41
 
33
42
  def _state(tmp_path, wake_min_interval_s=0):
@@ -20,6 +20,15 @@ def _clean_composer(monkeypatch):
20
20
  default every composer to OBSERVED-CLEAN so the gate stays out of the
21
21
  way. The gate's own matrix lives in test_tmux_wake_submit_verify.py."""
22
22
  monkeypatch.setattr(mesh, "_composer_state", lambda t: "clear")
23
+ # #1010 strand-only hermeticity (#1083): the grok-block probe, the
24
+ # run-state read and the session-age read inside deliver() reach the
25
+ # REAL tmux binary when unpatched — a bare CI runner and a box with
26
+ # live cells must read the suite the same way. A missing pane reads
27
+ # unreadable at base; False/None keep the same falsy fall-through
28
+ # these tests pin.
29
+ monkeypatch.setattr(mesh, "_capture_pane_lines", lambda t: None)
30
+ monkeypatch.setattr(mesh, "_agent_running", lambda t: False)
31
+ monkeypatch.setattr(mesh, "_tmux_session_created", lambda t: None)
23
32
 
24
33
 
25
34
  def _state(tmp_path, wake_min_interval_s=0):
@@ -25,8 +25,15 @@ from swarph_cli.commands import mesh, monitor
25
25
  def _clean_composer(monkeypatch):
26
26
  """These tests pin CLI/ledger mechanics, not the politeness gate —
27
27
  default every composer to OBSERVED-CLEAN so the gate stays out of the
28
- way. The gate's own matrix lives in test_tmux_wake_submit_verify.py."""
28
+ way. The gate's own matrix lives in test_tmux_wake_submit_verify.py.
29
+ #1010 strand-only hermeticity (#1083): every pane named by this rig
30
+ is synthetic — an unpatched capture/run-state/session read reaches
31
+ the REAL tmux binary. A missing pane reads unreadable at base;
32
+ False/None keep the same falsy fall-through these tests pin."""
29
33
  monkeypatch.setattr(mesh, "_composer_state", lambda t: "clear")
34
+ monkeypatch.setattr(mesh, "_capture_pane_lines", lambda t: None)
35
+ monkeypatch.setattr(mesh, "_agent_running", lambda t: False)
36
+ monkeypatch.setattr(mesh, "_tmux_session_created", lambda t: None)
30
37
 
31
38
 
32
39
  def _env(monkeypatch):