swarph-cli 0.41.1__tar.gz → 0.41.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. {swarph_cli-0.41.1/src/swarph_cli.egg-info → swarph_cli-0.41.2}/PKG-INFO +1 -1
  2. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/pyproject.toml +1 -1
  3. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/__init__.py +1 -1
  4. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/board_merge_check.py +75 -1
  5. {swarph_cli-0.41.1 → swarph_cli-0.41.2/src/swarph_cli.egg-info}/PKG-INFO +1 -1
  6. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_board_merge_check.py +129 -1
  7. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_brain_ask_command.py +1 -1
  8. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_watchdog.py +1 -1
  9. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/LICENSE +0 -0
  10. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/README.md +0 -0
  11. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/setup.cfg +0 -0
  12. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/bench/__init__.py +0 -0
  13. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/bench/backends.py +0 -0
  14. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/bench/pack.py +0 -0
  15. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/bench/prices.py +0 -0
  16. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/bench/quality.py +0 -0
  17. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/bench/refresh_prices.py +0 -0
  18. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/bench/runner.py +0 -0
  19. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/bench/validate.py +0 -0
  20. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/caller.py +0 -0
  21. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/capture/__init__.py +0 -0
  22. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/capture/harden.py +0 -0
  23. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/capture/lineage.py +0 -0
  24. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/capture/liveness.py +0 -0
  25. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/capture/manifest.py +0 -0
  26. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/capture/paths.py +0 -0
  27. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/capture/verify.py +0 -0
  28. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/cell.py +0 -0
  29. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/chain_token.py +0 -0
  30. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/__init__.py +0 -0
  31. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/_display.py +0 -0
  32. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/_gateway_client.py +0 -0
  33. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/add.py +0 -0
  34. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/bench.py +0 -0
  35. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/board.py +0 -0
  36. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/brain.py +0 -0
  37. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/brain_ask.py +0 -0
  38. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/cell.py +0 -0
  39. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  40. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/channel.py +0 -0
  41. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/chat.py +0 -0
  42. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/codegraph.py +0 -0
  43. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/codegraph_hook.py +0 -0
  44. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/compress.py +0 -0
  45. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/daemon.py +0 -0
  46. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/event.py +0 -0
  47. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/gateway.py +0 -0
  48. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/group.py +0 -0
  49. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/highlight.py +0 -0
  50. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/hook_output.py +0 -0
  51. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/hooks.py +0 -0
  52. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/import_session.py +0 -0
  53. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/init.py +0 -0
  54. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/install_hook.py +0 -0
  55. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  56. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/lane.py +0 -0
  57. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/mcp_server.py +0 -0
  58. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/memory.py +0 -0
  59. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/memory_sync.py +0 -0
  60. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/mesh.py +0 -0
  61. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/monitor.py +0 -0
  62. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/okf_links.py +0 -0
  63. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/onboard.py +0 -0
  64. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/protocol_handler.py +0 -0
  65. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/ratify.py +0 -0
  66. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/schedule.py +0 -0
  67. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/security.py +0 -0
  68. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/service.py +0 -0
  69. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/spawn.py +0 -0
  70. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/timeline.py +0 -0
  71. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/commands/watchdog.py +0 -0
  72. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/compress/__init__.py +0 -0
  73. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/compress/levers.py +0 -0
  74. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/compress/marker.py +0 -0
  75. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/compress/verify.py +0 -0
  76. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/console_safe.py +0 -0
  77. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/delivery_queue.py +0 -0
  78. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/gateway/__init__.py +0 -0
  79. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/gateway/feature_registry.py +0 -0
  80. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/gateway/lanes_control.py +0 -0
  81. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/gateway/schema.sql +0 -0
  82. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/gateway/server.py +0 -0
  83. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/gateway/services_control.py +0 -0
  84. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/main.py +0 -0
  85. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/multiplexer.py +0 -0
  86. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/parsers/__init__.py +0 -0
  87. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/parsers/claude.py +0 -0
  88. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  89. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/service/__init__.py +0 -0
  90. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/service/app.py +0 -0
  91. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/service/providers.py +0 -0
  92. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/session_bridge.py +0 -0
  93. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/stall_alert.py +0 -0
  94. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  95. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  96. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  97. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli.egg-info/SOURCES.txt +0 -0
  98. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  99. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  100. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli.egg-info/requires.txt +0 -0
  101. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/src/swarph_cli.egg-info/top_level.txt +0 -0
  102. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_add_security_gate.py +0 -0
  103. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_artifact_add.py +0 -0
  104. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_artifact_hash.py +0 -0
  105. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_artifact_lib.py +0 -0
  106. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_artifact_mcp.py +0 -0
  107. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_artifact_skill.py +0 -0
  108. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_artifact_tool.py +0 -0
  109. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_artifact_uri.py +0 -0
  110. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_bench_backends.py +0 -0
  111. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_bench_command.py +0 -0
  112. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_bench_pack.py +0 -0
  113. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_bench_prices.py +0 -0
  114. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_bench_quality.py +0 -0
  115. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_bench_runner.py +0 -0
  116. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_bench_validate.py +0 -0
  117. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_board_command.py +0 -0
  118. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_brain_ask_env_diagnosis.py +0 -0
  119. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_brain_command.py +0 -0
  120. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_caller_meta_guard.py +0 -0
  121. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_capture_lineage.py +0 -0
  122. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_capture_liveness.py +0 -0
  123. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_capture_manifest.py +0 -0
  124. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_capture_paths.py +0 -0
  125. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_capture_security.py +0 -0
  126. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_cell_command_dispatch.py +0 -0
  127. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_cell_harden.py +0 -0
  128. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_cell_loader.py +0 -0
  129. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_cell_selfcheck.py +0 -0
  130. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_cell_verify.py +0 -0
  131. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_chain_token.py +0 -0
  132. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_channel_command.py +0 -0
  133. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_channel_post.py +0 -0
  134. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_chat_command.py +0 -0
  135. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_claude_parser.py +0 -0
  136. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_claude_tmux_template.py +0 -0
  137. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_codegraph_caller_scope.py +0 -0
  138. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_codegraph_command.py +0 -0
  139. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_codegraph_hook.py +0 -0
  140. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_codegraph_mcp.py +0 -0
  141. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_compress_caller_convention.py +0 -0
  142. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_compress_command.py +0 -0
  143. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_compress_levers.py +0 -0
  144. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_compress_marker.py +0 -0
  145. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_compress_verify.py +0 -0
  146. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_console_encoding_e2e.py +0 -0
  147. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_daemon_command.py +0 -0
  148. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_daemon_console_encoding.py +0 -0
  149. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_daemon_delivery.py +0 -0
  150. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_delivery_queue.py +0 -0
  151. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_display_sanitize.py +0 -0
  152. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_event_emit.py +0 -0
  153. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_feature_to_uri.py +0 -0
  154. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_gateway_brain.py +0 -0
  155. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_gateway_client.py +0 -0
  156. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_gateway_command.py +0 -0
  157. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_group_command.py +0 -0
  158. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_highlight_command.py +0 -0
  159. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_hook_output.py +0 -0
  160. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_hooks_activity_marker.py +0 -0
  161. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_hooks_add.py +0 -0
  162. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_hooks_bundle.py +0 -0
  163. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_hooks_init.py +0 -0
  164. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_hooks_lifecycle.py +0 -0
  165. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_hooks_merge.py +0 -0
  166. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_import_command.py +0 -0
  167. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_init_command.py +0 -0
  168. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_install_hook.py +0 -0
  169. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_install_multiplexer_command.py +0 -0
  170. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_lane_command.py +0 -0
  171. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_main.py +0 -0
  172. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_main_unknown_verb_guard.py +0 -0
  173. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_mcp_server.py +0 -0
  174. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_memory_command.py +0 -0
  175. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_memory_gateway.py +0 -0
  176. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_memory_sync.py +0 -0
  177. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_memory_sync_membrane.py +0 -0
  178. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_mesh_command.py +0 -0
  179. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_mesh_inbox_mark_read.py +0 -0
  180. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_mesh_inbox_sanitize.py +0 -0
  181. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_mesh_sidecar.py +0 -0
  182. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  183. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  184. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_meta_edge_identity.py +0 -0
  185. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_monitor_cli.py +0 -0
  186. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_monitor_daemon_collision.py +0 -0
  187. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_monitor_self_name_verification.py +0 -0
  188. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_monitor_state_split.py +0 -0
  189. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_multiplexer.py +0 -0
  190. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_okf_links.py +0 -0
  191. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_onboard_command.py +0 -0
  192. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_packaged_artifacts_exist.py +0 -0
  193. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_process_liveness.py +0 -0
  194. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_protocol_handler.py +0 -0
  195. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_providers_isolation.py +0 -0
  196. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_ratify_command.py +0 -0
  197. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_repl_caller_convention.py +0 -0
  198. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_schedule_command.py +0 -0
  199. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_schedule_create_contract.py +0 -0
  200. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_security.py +0 -0
  201. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_service_app.py +0 -0
  202. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_service_command.py +0 -0
  203. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_service_providers.py +0 -0
  204. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_session_bridge_inject.py +0 -0
  205. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_session_bridge_probe.py +0 -0
  206. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_session_bridge_resolve.py +0 -0
  207. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_session_path_role_gate.py +0 -0
  208. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_shipped_units_match_the_cli.py +0 -0
  209. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_smoke_chat.py +0 -0
  210. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_smoke_one_shot.py +0 -0
  211. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_smoke_phase_5_5.py +0 -0
  212. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_spawn_base_hoist.py +0 -0
  213. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_spawn_command.py +0 -0
  214. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_spawn_live_pin.py +0 -0
  215. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_spawn_mitosis_lineage.py +0 -0
  216. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_spawn_tmux_session.py +0 -0
  217. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_spawn_windows_relaunch.py +0 -0
  218. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_stall_alert.py +0 -0
  219. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_timeline_command.py +0 -0
  220. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_token_file_one_parser.py +0 -0
  221. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_version_consistency.py +0 -0
  222. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_watchdog_dm_wake.py +0 -0
  223. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  224. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  225. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_watchdog_emit_health.py +0 -0
  226. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_watchdog_install_flags.py +0 -0
  227. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_watchdog_model_rung.py +0 -0
  228. {swarph_cli-0.41.1 → swarph_cli-0.41.2}/tests/test_watchdog_stale_peers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.41.1
3
+ Version: 0.41.2
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`, 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.41.1"
7
+ version = "0.41.2"
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`, 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.41.1"
19
+ __version__ = "0.41.2"
20
20
 
21
21
  __all__ = ["__version__"]
@@ -126,11 +126,51 @@ def decide(card: dict, pr_state: dict) -> Decision:
126
126
  "" if verdict else "missing or unparseable — expected '<cell> APPROVED <sha>'")
127
127
 
128
128
  author = card.get("created_by")
129
+ assignee = card.get("assignee")
129
130
  if verdict:
130
131
  # Author identity comes from the CARD. Git says the same account for every
131
132
  # cell, so it cannot answer "was this reviewed by someone else".
132
133
  add("reviewer is not the author", verdict["cell"] != author,
133
134
  f"reviewer={verdict['cell']} author={author}")
135
+ # >>> LEG 2 (#144): READ THE FROZEN, BOUND self_authored FLAG — NOT A
136
+ # GATE-TIME COMPARISON OF AN ASSERTED NAME. <<<
137
+ #
138
+ # The first version of this leg compared verdict["cell"] against
139
+ # card["assignee"]. drop-on-meta-edge showed it was DOUBLY WEAK, verified
140
+ # against the merged commit:
141
+ #
142
+ # (i) ASSERTED — verdict["cell"] is parsed out of the peer_verdict VALUE,
143
+ # which the writer controls. The assignee simply writes
144
+ # "not-me APPROVED <sha>": cell(not-me) != assignee(me) PASSES, and the
145
+ # created_by leg passes too. The self-review leg was defeated by
146
+ # asserting a non-self name, so it meant nothing until the value's cell
147
+ # was forced to equal the bound writer.
148
+ #
149
+ # (ii) GATE-TIME — it read card["assignee"] AT MERGE. Self-verdict, then the
150
+ # work reassigns (the normal flow: 9% of live cards already have
151
+ # created_by != assignee), and the gate sees cell != assignee(new) so a
152
+ # STALE SELF-APPROVAL PASSES. Frozen-vs-mutable, on the fix itself.
153
+ #
154
+ # `self_authored` closes both in one read. The gateway computes it
155
+ # SERVER-SIDE from row["assignee"] == the BOUND writer and FREEZES it in
156
+ # link_stamps at write time: BOUND (so no asserted name can dodge it — no
157
+ # dependency on leg (a)) and FROZEN (so reassignment cannot launder it).
158
+ #
159
+ # ABSENCE IS NOT INNOCENCE. A verdict written before the flag existed has no
160
+ # stamp, and treating that as "not self-authored" would make this leg
161
+ # vacuous for exactly the backlog it must cover. ok=None => COULD NOT
162
+ # EVALUATE => REFUSE, which is this file's existing doctrine for the
163
+ # unreachable-GitHub case and the reason it has a tri-state at all.
164
+ _vstamp = (card.get("link_stamps") or {}).get("peer_verdict")
165
+ if _vstamp is None:
166
+ add("verdict is not self-authored", None,
167
+ "no link_stamp for peer_verdict — cannot tell who wrote it "
168
+ "(pre-#199 verdict, or a producer that dropped link_stamps); "
169
+ "COULD NOT EVALUATE, never a pass")
170
+ else:
171
+ add("verdict is not self-authored", not _vstamp.get("self_authored"),
172
+ f"self_authored={_vstamp.get('self_authored')!r} by={_vstamp.get('by')!r} "
173
+ f"— the person being reviewed cannot supply the review")
134
174
  add("verdict is APPROVED", verdict["verdict"] == "APPROVED",
135
175
  f"verdict={verdict['verdict']}")
136
176
  head = pr_state.get("head_sha") or ""
@@ -220,7 +260,41 @@ def fetch_pr_state(repo: str, number: str) -> dict:
220
260
 
221
261
 
222
262
  def format_decision(d: Decision) -> str:
223
- lines = [f"card #{d.card_id}: {d.verdict}"]
263
+ """Render a decision, and >>> DISTINGUISH *FAILED* FROM *COULD NOT EVALUATE* IN
264
+ THE SUMMARY, NOT ONLY PER-LINE. <<<
265
+
266
+ Condition 1 of the date-holder's ruling on #144 (droplet, 2026-08-01):
267
+
268
+ "A gate that graduates and then refuses everything is INDISTINGUISHABLE
269
+ from a gate that never graduated — from the outside, and from the board.
270
+ The abstention rate must be REPORTED, not silent."
271
+
272
+ The register exists because "a recording phase that never graduates is a
273
+ control that does not exist, and it LOOKS LIKE PROGRESS ON A BOARD."
274
+ Abstention is that same failure wearing the opposite mask: near-total
275
+ abstention is acceptable as a TRANSIENT and is a dead control as a steady
276
+ state, and the only thing that tells them apart is whether anyone can SEE it
277
+ without going to look.
278
+
279
+ SCOPE, stated so it is not mistaken for more: this tool evaluates ONE card per
280
+ run (--card-json). It cannot compute a RATE. What it can do — and now does — is
281
+ make every run self-describing, naming which checks abstained and why, so the
282
+ caller that iterates cards can aggregate without parsing prose.
283
+ """
284
+ ok = sum(1 for c in d.checks if c.ok is True)
285
+ failed = [c for c in d.checks if c.ok is False]
286
+ blind = [c for c in d.checks if c.ok is None]
287
+ head = f"card #{d.card_id}: {d.verdict}"
288
+ if failed or blind:
289
+ head += (f" — {ok} ok · {len(failed)} FAILED · "
290
+ f"{len(blind)} COULD-NOT-EVALUATE")
291
+ if blind:
292
+ # Name them. A count alone cannot tell "abstained on self-authorship
293
+ # because the stamp predates the flag" from "abstained because GitHub
294
+ # was unreachable", and those decay differently: one shrinks to zero as
295
+ # verdicts are rewritten, the other is an outage.
296
+ head += f" [blind: {', '.join(c.name for c in blind)}]"
297
+ lines = [head]
224
298
  for c in d.checks:
225
299
  mark = "ok " if c.ok is True else ("FAIL" if c.ok is False else "????")
226
300
  lines.append(f" [{mark}] {c.name}" + (f" — {c.detail}" if c.detail else ""))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.41.1
3
+ Version: 0.41.2
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`, session `import`, and a stranded-session `watchdog`.
5
5
  Author: Pierre Samson, Claude Opus
6
6
  License: MIT
@@ -28,7 +28,15 @@ SHA = "abc1234def5678"
28
28
  def _card(**over):
29
29
  card = {"id": 152, "stage": "build", "move_ready": True, "created_by": "lab-ovh",
30
30
  "links": {"pr": "https://github.com/o/r/pull/152",
31
- "peer_verdict": f"droplet APPROVED {SHA}"}}
31
+ "peer_verdict": f"droplet APPROVED {SHA}"},
32
+ # #144 leg 2: the DEFAULT fixture is a NORMAL card — a bound
33
+ # third-party verdict. Without a stamp the self-authorship leg reports
34
+ # COULD-NOT-EVALUATE (never a pass), which is correct behaviour and
35
+ # would otherwise make every unrelated test refuse for the wrong
36
+ # reason. Tests that care about the flag override it explicitly.
37
+ "link_stamps": {"peer_verdict": {"by": "droplet", "self_authored": False,
38
+ "caller_bound": True,
39
+ "at": "2026-08-01T00:00:00Z"}}}
32
40
  card.update(over)
33
41
  return card
34
42
 
@@ -219,3 +227,123 @@ def test_cli_reports_dry_run(tmp_path, capsys):
219
227
  out = capsys.readouterr().out
220
228
  assert "DRY RUN" in out
221
229
  assert rc == 2, "offline means COULD NOT EVALUATE (2), never a measured refusal (1)"
230
+
231
+
232
+ # ── #144 leg 2: read the FROZEN, BOUND self_authored flag ────────────────────
233
+ # The first version compared verdict["cell"] against card["assignee"].
234
+ # drop-on-meta-edge showed it was doubly weak: (i) ASSERTED — the writer controls
235
+ # the cell string, so an assignee writing "not-me APPROVED <sha>" passed it; and
236
+ # (ii) GATE-TIME — it read the assignee at merge, so a self-verdict laundered by a
237
+ # later reassignment passed. Both close by reading the server-computed flag.
238
+
239
+ def _stamped(self_authored, by="droplet", **over):
240
+ """A card whose peer_verdict carries a link_stamp."""
241
+ return _card(link_stamps={"peer_verdict": {"by": by, "self_authored": self_authored,
242
+ "caller_bound": True, "at": "2026-08-01T00:00:00Z"}},
243
+ **over)
244
+
245
+
246
+ def test_a_SELF_AUTHORED_verdict_is_refused():
247
+ """>>> THE HOLE. <<< `is_exec_assignee` permits the assignee to write
248
+ peer_verdict on their own card, so the person being reviewed supplies the
249
+ review — no forgery, no privilege escalation. 20 of 213 live cards (9%) have
250
+ created_by != assignee, so the created_by leg passes on the normal split."""
251
+ d = mc.decide(_stamped(True, created_by="lab-ovh"), _pr())
252
+ assert not _fail(d, "reviewer is not the author"), (
253
+ "the created_by leg fired — this would pass for the wrong reason")
254
+ assert _fail(d, "verdict is not self-authored")
255
+ assert d.verdict != "WOULD_MERGE"
256
+
257
+
258
+ def test_an_ASSERTED_non_self_name_CANNOT_dodge_it():
259
+ """WEAKNESS (i) OF THE FIRST VERSION, pinned. The assignee writes a verdict
260
+ naming someone else. The old leg compared that ASSERTED string and passed;
261
+ the flag is computed server-side from the BOUND writer, so the string is
262
+ irrelevant."""
263
+ card = _stamped(True, created_by="lab-ovh",
264
+ links={"pr": "https://github.com/o/r/pull/152",
265
+ "peer_verdict": f"not-me APPROVED {SHA}"})
266
+ d = mc.decide(card, _pr())
267
+ assert _fail(d, "verdict is not self-authored"), (
268
+ "an asserted non-self name defeated the self-review leg")
269
+
270
+
271
+ def test_a_LATER_REASSIGNMENT_cannot_launder_a_self_verdict():
272
+ """WEAKNESS (ii), pinned. droplet self-verdicts, then the work is reassigned.
273
+ A gate-time comparison sees cell != assignee(new) and passes; the FROZEN flag
274
+ still says the verdict was self-authored when written."""
275
+ card = _stamped(True, created_by="lab-ovh", assignee="gridiron") # reassigned away
276
+ d = mc.decide(card, _pr())
277
+ assert _fail(d, "verdict is not self-authored")
278
+
279
+
280
+ def test_a_genuine_third_party_review_still_passes():
281
+ """NON-VACUITY. If the leg fired on real reviews it would be a blocker, not a
282
+ gate — and third-party review is the entire feature."""
283
+ card = _stamped(False, by="grok-researcher", created_by="lab-ovh",
284
+ links={"pr": "https://github.com/o/r/pull/152",
285
+ "peer_verdict": f"grok-researcher APPROVED {SHA}"})
286
+ d = mc.decide(card, _pr())
287
+ assert not _fail(d, "verdict is not self-authored")
288
+ assert d.verdict == "WOULD_MERGE", [(c.name, c.detail) for c in d.blockers]
289
+
290
+
291
+ def test_a_MISSING_stamp_is_COULD_NOT_EVALUATE_never_a_pass():
292
+ """>>> ABSENCE IS NOT INNOCENCE. <<< A verdict written before the flag existed
293
+ has no stamp. Treating that as "not self-authored" would make this leg vacuous
294
+ for exactly the backlog it must cover — the empty-subject defect, in the fix
295
+ for a self-review defect. ok=None is this file's existing doctrine."""
296
+ for absent in ({}, None, {"some_other_key": {"by": "x"}}):
297
+ d = mc.decide(_card(created_by="lab-ovh", link_stamps=absent), _pr())
298
+ assert _fail(d, "verdict is not self-authored"), (
299
+ f"a verdict with NO peer_verdict stamp passed (link_stamps={absent!r}) "
300
+ "— absence read as innocence")
301
+ check = [c for c in d.blockers if c.name == "verdict is not self-authored"][0]
302
+ assert check.ok is None, "must be COULD-NOT-EVALUATE, not a hard False"
303
+
304
+
305
+ def test_the_summary_DISTINGUISHES_failed_from_could_not_evaluate():
306
+ """>>> CONDITION 1 OF THE DATE-HOLDER'S RULING. <<< (droplet, #144.)
307
+
308
+ "A gate that graduates and then refuses everything is INDISTINGUISHABLE from a
309
+ gate that never graduated — from the outside, and from the board."
310
+
311
+ A REFUSE that fails a check and a REFUSE that could not see its subject are
312
+ different facts with different decay: the first is the gate working, the
313
+ second shrinks to zero as verdicts are rewritten — or doesn't, which is the
314
+ dead-control signal. They must not print the same.
315
+ """
316
+ d = mc.decide(_card(created_by="lab-ovh", link_stamps={}), _pr())
317
+ out = mc.format_decision(d)
318
+ assert "COULD-NOT-EVALUATE" in out
319
+ assert "blind:" in out
320
+ assert "verdict is not self-authored" in out.split("\n")[0], (
321
+ "the abstaining check must be NAMED on the summary line, not only "
322
+ "buried in the per-check list — a count cannot tell a stamp-less backlog "
323
+ "from a GitHub outage")
324
+
325
+
326
+ def test_a_clean_pass_does_not_print_an_abstention_summary():
327
+ """NON-VACUITY. If the summary always printed the tri-state it would be noise
328
+ on every healthy run, and noise is how a gate stops being read."""
329
+ out = mc.format_decision(mc.decide(_card(), _pr()))
330
+ assert "COULD-NOT-EVALUATE" not in out
331
+ assert out.split("\n")[0] == "card #152: WOULD_MERGE"
332
+
333
+
334
+ def test_a_READY_card_with_NO_peer_verdict_REFUSES():
335
+ """>>> drop-on-meta-edge's pin, and it guards a live emptiness. <<< MEASURED
336
+ 2026-08-01: 13 cards are in build/test and ZERO carry a peer_verdict link. So
337
+ at arming EVERY ready card hits "peer verdict recorded on the card" = False →
338
+ REFUSE → human gate. The gate is safe-BY-EMPTINESS today.
339
+
340
+ That is the same empty-subject shape found all night (group_grants, the verdict
341
+ store) except here it fails SAFE — refuse, not pass — which is the one
342
+ direction that makes an empty subject acceptable rather than a defect.
343
+
344
+ Pinned so the day someone "helpfully" defaults a missing verdict to pass, it
345
+ fails loudly instead of silently auto-merging an unreviewed card.
346
+ """
347
+ d = mc.decide(_card(links={"pr": "https://github.com/o/r/pull/152"}), _pr())
348
+ assert _fail(d, "peer verdict recorded on the card")
349
+ assert d.verdict != "WOULD_MERGE"
@@ -17,7 +17,7 @@ from swarph_cli.commands import brain_ask as ba
17
17
 
18
18
  def test_version_is_0_33_0():
19
19
  import swarph_cli
20
- assert swarph_cli.__version__ == "0.41.1"
20
+ assert swarph_cli.__version__ == "0.41.2"
21
21
 
22
22
 
23
23
  # --- request / response plumbing -------------------------------------------
@@ -1043,4 +1043,4 @@ def test_parser_defaults_process_name_claude_liveness_cmd_none():
1043
1043
 
1044
1044
  def test_version_is_0_33_0():
1045
1045
  import swarph_cli
1046
- assert swarph_cli.__version__ == "0.41.1"
1046
+ assert swarph_cli.__version__ == "0.41.2"
File without changes
File without changes
File without changes