swarph-cli 0.45.2__tar.gz → 0.47.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. {swarph_cli-0.45.2/src/swarph_cli.egg-info → swarph_cli-0.47.0}/PKG-INFO +2 -1
  2. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/README.md +1 -0
  3. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/pyproject.toml +3 -2
  4. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/__init__.py +1 -1
  5. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/board.py +52 -3
  6. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/brain_ask.py +7 -3
  7. swarph_cli-0.47.0/src/swarph_cli/commands/gateway.py +278 -0
  8. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/gh_route.py +2 -0
  9. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/init.py +7 -1
  10. swarph_cli-0.47.0/src/swarph_cli/commands/install_postcompact_hook.py +403 -0
  11. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/install_wake_hook.py +57 -6
  12. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/memory_emit_hook.py +15 -5
  13. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/mesh.py +246 -16
  14. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/monitor.py +1 -0
  15. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/onboard.py +155 -6
  16. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/spawn.py +113 -176
  17. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/wake_hook_output.py +3 -1
  18. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/gateway/schema.sql +3 -1
  19. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/guide/GUIDE.md +3 -2
  20. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/main.py +7 -1
  21. swarph_cli-0.47.0/src/swarph_cli/payloads/postcompact/_shim.cmd +4 -0
  22. swarph_cli-0.47.0/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +9 -0
  23. swarph_cli-0.47.0/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +19 -0
  24. swarph_cli-0.47.0/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +22 -0
  25. swarph_cli-0.47.0/src/swarph_cli/payloads/postcompact/precompact-flag.sh +10 -0
  26. swarph_cli-0.47.0/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +23 -0
  27. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/systemd/codex-waker.default +4 -1
  28. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/systemd/peer-reply-drain.default +4 -1
  29. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/tokens.py +22 -0
  30. {swarph_cli-0.45.2 → swarph_cli-0.47.0/src/swarph_cli.egg-info}/PKG-INFO +2 -1
  31. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli.egg-info/SOURCES.txt +13 -0
  32. swarph_cli-0.47.0/tests/test_124_register_merge.py +112 -0
  33. swarph_cli-0.47.0/tests/test_319_refusal_exit_codes.py +147 -0
  34. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_467b_onboard_operator_credential.py +42 -0
  35. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_532_accept_flag.py +6 -0
  36. swarph_cli-0.47.0/tests/test_535_version_capability.py +113 -0
  37. swarph_cli-0.47.0/tests/test_548_gateway_remainder.py +82 -0
  38. swarph_cli-0.47.0/tests/test_board_obligation_close.py +77 -0
  39. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_brain_ask_command.py +5 -2
  40. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_cursor_membrane.py +43 -2
  41. swarph_cli-0.47.0/tests/test_gateway_command.py +226 -0
  42. swarph_cli-0.47.0/tests/test_install_postcompact_hook.py +341 -0
  43. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_install_wake_hook.py +56 -2
  44. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_mesh_command.py +5 -1
  45. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_monitor_state_split.py +57 -0
  46. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_onboard_command.py +192 -0
  47. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_postcompact_recall.py +66 -0
  48. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_spawn_base_hoist.py +39 -0
  49. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_spawn_command.py +3 -1
  50. swarph_cli-0.45.2/src/swarph_cli/commands/gateway.py +0 -116
  51. swarph_cli-0.45.2/tests/test_gateway_command.py +0 -92
  52. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/LICENSE +0 -0
  53. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/setup.cfg +0 -0
  54. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/__main__.py +0 -0
  55. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/bench/__init__.py +0 -0
  56. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/bench/backends.py +0 -0
  57. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/bench/pack.py +0 -0
  58. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/bench/prices.py +0 -0
  59. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/bench/quality.py +0 -0
  60. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/bench/refresh_prices.py +0 -0
  61. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/bench/runner.py +0 -0
  62. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/bench/validate.py +0 -0
  63. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/caller.py +0 -0
  64. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/capture/__init__.py +0 -0
  65. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/capture/harden.py +0 -0
  66. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/capture/lineage.py +0 -0
  67. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/capture/liveness.py +0 -0
  68. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/capture/manifest.py +0 -0
  69. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/capture/paths.py +0 -0
  70. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/capture/verify.py +0 -0
  71. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/cell.py +0 -0
  72. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/chain_token.py +0 -0
  73. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/__init__.py +0 -0
  74. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/_content.py +0 -0
  75. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/_display.py +0 -0
  76. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/_gateway_client.py +0 -0
  77. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/add.py +0 -0
  78. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/bench.py +0 -0
  79. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/board_merge_check.py +0 -0
  80. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/brain.py +0 -0
  81. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/cell.py +0 -0
  82. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  83. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/channel.py +0 -0
  84. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/chat.py +0 -0
  85. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/codegraph.py +0 -0
  86. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/codegraph_hook.py +0 -0
  87. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/codex_hook_output.py +0 -0
  88. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/codex_waker.py +0 -0
  89. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/compress.py +0 -0
  90. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/daemon.py +0 -0
  91. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/event.py +0 -0
  92. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/group.py +0 -0
  93. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/guide.py +0 -0
  94. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/highlight.py +0 -0
  95. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/hook_output.py +0 -0
  96. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/hooks.py +0 -0
  97. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/import_session.py +0 -0
  98. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
  99. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/install_hook.py +0 -0
  100. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  101. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/lane.py +0 -0
  102. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/mcp_server.py +0 -0
  103. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/memory.py +0 -0
  104. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/memory_sync.py +0 -0
  105. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/okf_links.py +0 -0
  106. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
  107. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
  108. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/protocol_handler.py +0 -0
  109. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/ratify.py +0 -0
  110. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/schedule.py +0 -0
  111. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/security.py +0 -0
  112. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/service.py +0 -0
  113. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/timeline.py +0 -0
  114. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/commands/watchdog.py +0 -0
  115. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/compress/__init__.py +0 -0
  116. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/compress/levers.py +0 -0
  117. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/compress/marker.py +0 -0
  118. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/compress/verify.py +0 -0
  119. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/console_safe.py +0 -0
  120. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/delivery_queue.py +0 -0
  121. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/gateway/__init__.py +0 -0
  122. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/gateway/feature_registry.py +0 -0
  123. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/gateway/lanes_control.py +0 -0
  124. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/gateway/server.py +0 -0
  125. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/gateway/services_control.py +0 -0
  126. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/gh_identity.py +0 -0
  127. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/multiplexer.py +0 -0
  128. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/parsers/__init__.py +0 -0
  129. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/parsers/claude.py +0 -0
  130. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/peer_executor.py +0 -0
  131. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/peer_executor_windows.py +0 -0
  132. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/peer_reconciliation.py +0 -0
  133. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/peer_reply_outbox.py +0 -0
  134. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/peer_service_host.py +0 -0
  135. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/peer_service_payload.py +0 -0
  136. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/peer_service_reply.py +0 -0
  137. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/peer_staging.py +0 -0
  138. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/scripts/__init__.py +0 -0
  139. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
  140. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  141. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
  142. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/service/__init__.py +0 -0
  143. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/service/app.py +0 -0
  144. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/service/providers.py +0 -0
  145. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/session_bridge.py +0 -0
  146. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/stall_alert.py +0 -0
  147. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
  148. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
  149. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
  150. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
  151. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
  152. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
  153. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  154. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  155. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  156. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  157. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  158. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli.egg-info/requires.txt +0 -0
  159. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/src/swarph_cli.egg-info/top_level.txt +0 -0
  160. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_201_win32_bash_refusal.py +0 -0
  161. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_243_onboard_per_peer_token.py +0 -0
  162. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_307_task4_ask_reply.py +0 -0
  163. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_323_mistral_backend.py +0 -0
  164. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_397_gh_identity_router.py +0 -0
  165. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_397_router_preflight.py +0 -0
  166. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_523_guide.py +0 -0
  167. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_524_conftest_hermeticity.py +0 -0
  168. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_546_default_gateway.py +0 -0
  169. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_552_codegraph_gateway.py +0 -0
  170. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_555_relay_row_shape.py +0 -0
  171. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_556_relay_freshness.py +0 -0
  172. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_84_cli_labels.py +0 -0
  173. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_add_security_gate.py +0 -0
  174. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_artifact_add.py +0 -0
  175. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_artifact_hash.py +0 -0
  176. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_artifact_lib.py +0 -0
  177. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_artifact_mcp.py +0 -0
  178. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_artifact_skill.py +0 -0
  179. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_artifact_tool.py +0 -0
  180. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_artifact_uri.py +0 -0
  181. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_bench_backends.py +0 -0
  182. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_bench_command.py +0 -0
  183. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_bench_pack.py +0 -0
  184. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_bench_prices.py +0 -0
  185. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_bench_quality.py +0 -0
  186. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_bench_runner.py +0 -0
  187. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_bench_validate.py +0 -0
  188. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_board_card_thread.py +0 -0
  189. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_board_command.py +0 -0
  190. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_board_merge_check.py +0 -0
  191. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_brain_ask_env_diagnosis.py +0 -0
  192. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_brain_ask_identity_explicitness.py +0 -0
  193. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_brain_command.py +0 -0
  194. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_caller_meta_guard.py +0 -0
  195. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_capture_lineage.py +0 -0
  196. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_capture_liveness.py +0 -0
  197. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_capture_manifest.py +0 -0
  198. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_capture_paths.py +0 -0
  199. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_capture_security.py +0 -0
  200. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_cell_command_dispatch.py +0 -0
  201. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_cell_harden.py +0 -0
  202. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_cell_loader.py +0 -0
  203. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_cell_selfcheck.py +0 -0
  204. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_cell_verify.py +0 -0
  205. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_chain_token.py +0 -0
  206. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_channel_command.py +0 -0
  207. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_channel_poll_never_breaks_dms.py +0 -0
  208. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_channel_post.py +0 -0
  209. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_channel_wake_policy_filter.py +0 -0
  210. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_chat_command.py +0 -0
  211. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_claude_parser.py +0 -0
  212. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_claude_tmux_template.py +0 -0
  213. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_codegraph_caller_scope.py +0 -0
  214. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_codegraph_command.py +0 -0
  215. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_codegraph_hook.py +0 -0
  216. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_codegraph_mcp.py +0 -0
  217. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_codex_waker.py +0 -0
  218. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_compress_caller_convention.py +0 -0
  219. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_compress_command.py +0 -0
  220. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_compress_levers.py +0 -0
  221. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_compress_marker.py +0 -0
  222. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_compress_verify.py +0 -0
  223. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_console_encoding_e2e.py +0 -0
  224. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_content_composition.py +0 -0
  225. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_daemon_command.py +0 -0
  226. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_daemon_console_encoding.py +0 -0
  227. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_daemon_delivery.py +0 -0
  228. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_delivery_queue.py +0 -0
  229. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_display_sanitize.py +0 -0
  230. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_ensure_monitor_no_peer_default.py +0 -0
  231. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_event_emit.py +0 -0
  232. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_feature_to_uri.py +0 -0
  233. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_gateway_brain.py +0 -0
  234. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_gateway_client.py +0 -0
  235. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_gateway_message_idempotency.py +0 -0
  236. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_gh_route_doctor_mesh_name.py +0 -0
  237. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_group_command.py +0 -0
  238. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_heartbeat_check.py +0 -0
  239. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_highlight_command.py +0 -0
  240. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_hook_output.py +0 -0
  241. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_hooks_activity_marker.py +0 -0
  242. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_hooks_add.py +0 -0
  243. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_hooks_bundle.py +0 -0
  244. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_hooks_init.py +0 -0
  245. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_hooks_legacy_generation_removal.py +0 -0
  246. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_hooks_lifecycle.py +0 -0
  247. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_hooks_merge.py +0 -0
  248. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_hooks_verify.py +0 -0
  249. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_hooks_win32_bash_wrapper.py +0 -0
  250. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_hooks_win32_command_path.py +0 -0
  251. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_import_command.py +0 -0
  252. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_inbox_identity_provenance.py +0 -0
  253. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_init_command.py +0 -0
  254. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_init_provider_menu.py +0 -0
  255. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_install_codex_hooks.py +0 -0
  256. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_install_hook.py +0 -0
  257. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_install_multiplexer_command.py +0 -0
  258. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_lane_command.py +0 -0
  259. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_main.py +0 -0
  260. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_main_unknown_verb_guard.py +0 -0
  261. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_mcp_server.py +0 -0
  262. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_memory_command.py +0 -0
  263. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_memory_gateway.py +0 -0
  264. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_memory_sync.py +0 -0
  265. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_memory_sync_membrane.py +0 -0
  266. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_mesh_inbox_mark_read.py +0 -0
  267. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_mesh_inbox_sanitize.py +0 -0
  268. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_mesh_send_unknown_peer.py +0 -0
  269. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_mesh_sidecar.py +0 -0
  270. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  271. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  272. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_meta_edge_identity.py +0 -0
  273. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_monitor_channel_poll.py +0 -0
  274. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_monitor_cli.py +0 -0
  275. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_monitor_daemon_collision.py +0 -0
  276. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_monitor_self_name_verification.py +0 -0
  277. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_multiplexer.py +0 -0
  278. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_muse_membrane.py +0 -0
  279. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_muse_own_cli.py +0 -0
  280. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_obligation_close_is_surfaced.py +0 -0
  281. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_okf_links.py +0 -0
  282. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_packaged_artifacts_exist.py +0 -0
  283. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_peer_executor.py +0 -0
  284. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_peer_executor_windows.py +0 -0
  285. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_peer_reply_drain_command.py +0 -0
  286. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_peer_reply_drain_supervisor.py +0 -0
  287. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_peer_reply_outbox.py +0 -0
  288. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_peer_reply_transport.py +0 -0
  289. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_peer_service_e2e.py +0 -0
  290. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_peer_service_host.py +0 -0
  291. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_peer_service_payload.py +0 -0
  292. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_peer_service_reply.py +0 -0
  293. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_process_liveness.py +0 -0
  294. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_protocol_handler.py +0 -0
  295. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_providers_isolation.py +0 -0
  296. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_ratify_command.py +0 -0
  297. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_repl_caller_convention.py +0 -0
  298. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_schedule_command.py +0 -0
  299. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_schedule_create_contract.py +0 -0
  300. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_security.py +0 -0
  301. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_service_app.py +0 -0
  302. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_service_command.py +0 -0
  303. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_service_providers.py +0 -0
  304. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_session_bridge_inject.py +0 -0
  305. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_session_bridge_probe.py +0 -0
  306. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_session_bridge_resolve.py +0 -0
  307. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_session_path_role_gate.py +0 -0
  308. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_shipped_units_match_the_cli.py +0 -0
  309. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_smoke_chat.py +0 -0
  310. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_smoke_one_shot.py +0 -0
  311. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_smoke_phase_5_5.py +0 -0
  312. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_spawn_identity_stamp.py +0 -0
  313. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_spawn_live_pin.py +0 -0
  314. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_spawn_mitosis_lineage.py +0 -0
  315. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_spawn_tmux_session.py +0 -0
  316. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_spawn_windows_relaunch.py +0 -0
  317. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_stall_alert.py +0 -0
  318. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_subprocess_decode_encoding.py +0 -0
  319. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_timeline_command.py +0 -0
  320. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_token_file_one_parser.py +0 -0
  321. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_token_precedence_explicit_first.py +0 -0
  322. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_unified_token_resolver.py +0 -0
  323. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_version_consistency.py +0 -0
  324. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_vibe_membrane.py +0 -0
  325. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_watchdog.py +0 -0
  326. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_watchdog_dm_wake.py +0 -0
  327. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  328. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  329. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_watchdog_emit_health.py +0 -0
  330. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_watchdog_install_flags.py +0 -0
  331. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_watchdog_model_rung.py +0 -0
  332. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_watchdog_stale_peers.py +0 -0
  333. {swarph_cli-0.45.2 → swarph_cli-0.47.0}/tests/test_windows_waker_installer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.45.2
3
+ Version: 0.47.0
4
4
  Summary: The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`.
5
5
  Author: Pierre Samson, Claude Opus
6
6
  License: MIT
@@ -186,6 +186,7 @@ swarph board cards thread <id> [--limit N] # the card's conversation (a card
186
186
  swarph board cards say <id> --content "…" [--to <peer>] [--kind fyi|question|answer|status|unblock]
187
187
  swarph board cards ask <id> <holder> "<what>" [--timeout-hours N] # MINT an obligation:
188
188
  # name who owes what as a ROW, not a sentence (#307)
189
+ swarph board obligations close <id> --outcome <pass|fail|cannot_evaluate> --evidence "what you observed"
189
190
  ```
190
191
 
191
192
  `--project` accepts a numeric id **or** a slug (resolved via `projects list`). Note: card creation always starts at `proposed` (the gateway has no stage-on-create) — use `cards move` to advance.
@@ -138,6 +138,7 @@ swarph board cards thread <id> [--limit N] # the card's conversation (a card
138
138
  swarph board cards say <id> --content "…" [--to <peer>] [--kind fyi|question|answer|status|unblock]
139
139
  swarph board cards ask <id> <holder> "<what>" [--timeout-hours N] # MINT an obligation:
140
140
  # name who owes what as a ROW, not a sentence (#307)
141
+ swarph board obligations close <id> --outcome <pass|fail|cannot_evaluate> --evidence "what you observed"
141
142
  ```
142
143
 
143
144
  `--project` accepts a numeric id **or** a slug (resolved via `projects list`). Note: card creation always starts at `proposed` (the gateway has no stage-on-create) — use `cards move` to advance.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "swarph-cli"
7
- version = "0.45.2"
7
+ version = "0.47.0"
8
8
  description = "The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -120,7 +120,8 @@ where = ["src"]
120
120
  # importlib.resources, so an undeclared GUIDE.md passes every test in the
121
121
  # tree and fails on every installed wheel -- the 0.39.3 bug above, again.
122
122
  swarph_cli = ["systemd/*.service", "systemd/*.timer", "systemd/*.default",
123
- "scripts/*.sh", "scripts/*.ps1", "guide/*.md"]
123
+ "scripts/*.sh", "scripts/*.ps1", "guide/*.md",
124
+ "payloads/postcompact/*.sh", "payloads/postcompact/*.cmd"]
124
125
  # v0.15: ship the bundled mesh-gateway schema so `swarph gateway serve`
125
126
  # can apply it at startup via MESH_SCHEMA_PATH (defaults next to server.py).
126
127
  "swarph_cli.gateway" = ["*.sql"]
@@ -16,6 +16,6 @@ The architecture splits CLI from substrate so:
16
16
 
17
17
  from __future__ import annotations
18
18
 
19
- __version__ = "0.45.2"
19
+ __version__ = "0.47.0"
20
20
 
21
21
  __all__ = ["__version__"]
@@ -284,9 +284,10 @@ def _format_ask(d) -> str:
284
284
  return (f"obligation #{d.get('id')} on card #{d.get('card_id')}: "
285
285
  f"{d.get('holder')} owes it, status={d.get('status')}, {deadline}\n"
286
286
  f" {falsifier}\n"
287
- f" thread {d.get('thread_uuid')} — closes on the holder's "
288
- f"`mesh reply` IN that thread; a plain `mesh send` cannot close it "
289
- f"(#509 — the VERB is the mechanism, the thread is where it lands)")
287
+ f" thread {d.get('thread_uuid')} — work lands IN that thread; CLOSE "
288
+ f"it with `swarph board obligations close {d.get('id')}` plus an "
289
+ f"outcome and evidence; a plain `mesh send` cannot close it "
290
+ f"(#509/#562 — the VERB is the mechanism, the thread is where it lands)")
290
291
 
291
292
 
292
293
  def _format_thread(data) -> str:
@@ -377,6 +378,25 @@ def _build_parser() -> argparse.ArgumentParser:
377
378
  cr = cards.add_parser("ready", help="flag a card ready-to-advance (move_ready) for the orchestrator")
378
379
  cr.add_argument("id", type=int); cr.add_argument("--clear", action="store_true", help="unset move_ready")
379
380
  cr.add_argument("--json", action="store_true"); _add_common(cr)
381
+
382
+ obl = top.add_parser(
383
+ "obligations",
384
+ help="card obligations — the close act (#562)").add_subparsers(
385
+ dest="command", required=True)
386
+ oc = obl.add_parser(
387
+ "close",
388
+ help="CLOSE an obligation: the DISTINCT ACT that records the check's "
389
+ "outcome plus the evidence observed. A thread reply does NOT "
390
+ "close an obligation (#562) — this does")
391
+ oc.add_argument("id", type=int, help="the obligation id")
392
+ oc.add_argument("--outcome", required=True,
393
+ choices=("pass", "fail", "cannot_evaluate"),
394
+ help="the accept check's OUTCOME")
395
+ oc.add_argument("--evidence", required=True,
396
+ help="what you OBSERVED running the check — whitespace is "
397
+ "refused: whitespace evidence is the vibe-close this "
398
+ "endpoint exists to kill")
399
+ oc.add_argument("--json", action="store_true"); _add_common(oc)
380
400
  return p
381
401
 
382
402
 
@@ -425,6 +445,35 @@ def run_board(argv: list[str]) -> int:
425
445
  st, d = _post_json(f"{gw}/board/projects", _project_add_payload(self_name, args.slug, args.title, goal=args.goal), token)
426
446
  return _out(st, d, lambda x: f"created project #{x.get('id')} ({x.get('slug')})", aj)
427
447
 
448
+ if args.group == "obligations":
449
+ if args.command == "close":
450
+ # #562's CLI half: the explicit close act. The gateway guards the
451
+ # identity (holder, creator, or board orchestrator) and refuses a
452
+ # second close; the CLI's job is to refuse what it can see —
453
+ # whitespace evidence — and to propagate the gateway's refusals
454
+ # with their detail intact.
455
+ evidence = args.evidence
456
+ if not evidence.strip():
457
+ print("swarph board obligations close: --evidence is "
458
+ "whitespace-only — the close act records what you "
459
+ "OBSERVED, and nothing was observed", file=sys.stderr)
460
+ return 2
461
+ st, d = _post_json(
462
+ f"{gw}/board/obligations/{args.id}/close",
463
+ {"outcome": args.outcome, "evidence": evidence}, token)
464
+ if st < 200 or st >= 300:
465
+ detail = d.get("detail", d) if isinstance(d, dict) else d
466
+ print(f"swarph board obligations close: gateway {st}: {_s(detail)}",
467
+ file=sys.stderr)
468
+ return 1
469
+ if aj:
470
+ print(json.dumps(d, indent=2))
471
+ else:
472
+ print(f"obligation #{_s(d.get('id', args.id))} CLOSED by "
473
+ f"{_s(d.get('closed_by', self_name))} — outcome: "
474
+ f"{_s(d.get('close_outcome', args.outcome))}")
475
+ return 0
476
+
428
477
  if args.group == "cards":
429
478
  if args.command == "list":
430
479
  pid, err = _resolve_project(gw, token, args.project)
@@ -10,7 +10,8 @@ verb so any cell can search the swarm's shared memory the same way. Two modes:
10
10
  Stdlib-only. Config from the environment, mirroring ``swarph mesh``'s token model:
11
11
 
12
12
  GBRAIN_MCP_URL gbrain MCP endpoint; falls back to SWARPH_BRAIN_MCP, else
13
- / SWARPH_BRAIN_MCP http://127.0.0.1:8792/mcp
13
+ / SWARPH_BRAIN_MCP http://100.107.222.72:8792/mcp (tailnet IP — gbrain
14
+ binds no loopback; measured 2026-08-23, card #548)
14
15
  GBRAIN_TOKEN read token; falls back to SWARPH_BRAIN_TOKEN, then to the
15
16
  / SWARPH_BRAIN_TOKEN mesh per-peer token (~/.config/swarph/<self>.peer_token).
16
17
  / peer-token file Once gbrain accepts mesh peer tokens, the peer token IS
@@ -34,12 +35,15 @@ from swarph_cli import tokens
34
35
  from pathlib import Path
35
36
  from typing import Optional
36
37
 
37
- _DEFAULT_GBRAIN = "http://127.0.0.1:8792/mcp"
38
+ # TAILNET IP, NOT loopback (card #548): measured 2026-08-23, gbrain binds
39
+ # 100.107.222.72:8792 ONLY — 127.0.0.1:8792 refuses on the gateway box itself.
40
+ # A loopback default is deaf everywhere, including on the box running gbrain.
41
+ _DEFAULT_GBRAIN = "http://100.107.222.72:8792/mcp"
38
42
  _DEFAULT_TOPK = 6
39
43
 
40
44
 
41
45
  def _resolve_endpoint() -> str:
42
- """Endpoint precedence: GBRAIN_MCP_URL > SWARPH_BRAIN_MCP > localhost default.
46
+ """Endpoint precedence: GBRAIN_MCP_URL > SWARPH_BRAIN_MCP > tailnet default.
43
47
 
44
48
  The SWARPH_BRAIN_MCP fallback keeps the verb config-compatible with the
45
49
  standalone ``swarph-brain-ask`` script (which reads SWARPH_BRAIN_*), so one
@@ -0,0 +1,278 @@
1
+ """``swarph gateway`` — run the bundled mesh-gateway server.
2
+
3
+ The mesh-gateway is the coordination/DM server behind the swarph mesh:
4
+ peer registry, DM inbox/outbox, feature aggregation + allowlist/caps,
5
+ and lane/service control. It used to live as a standalone deployment;
6
+ ``swarph gateway serve`` bundles it as a first-class verb so any host
7
+ can stand up a gateway the same way it runs the client verbs.
8
+
9
+ The FastAPI/uvicorn server stack is an OPTIONAL extra so the core
10
+ client paths (one-shot / chat / mesh / brain-ask) stay dependency-light::
11
+
12
+ pip install "swarph-cli[gateway]"
13
+
14
+ Token model (mirrors ``swarph mesh``'s ``MESH_GATEWAY_TOKEN`` bearer):
15
+
16
+ * ``--token`` sets ``MESH_GATEWAY_TOKEN`` for the served process.
17
+ * Otherwise an existing ``MESH_GATEWAY_TOKEN`` in the environment is used.
18
+ * If neither is present a fresh token is minted (``secrets.token_urlsafe``)
19
+ and printed once so the operator can hand it to the mesh cells.
20
+
21
+ ``bootstrap-ratify`` (#565-B) is the ratify ladder's first rung: on a fresh
22
+ gateway zero peers are ratified, so no witness exists and ``swarph ratify``
23
+ can never complete. This verb flips ONE peer ratified via a local DB write —
24
+ an explicit, audited act by the human commander on the gateway box, not a
25
+ migration accident. It self-destroys: it refuses when any ratified peer
26
+ already exists.
27
+
28
+ Bare ``swarph gateway`` prints help.
29
+ """
30
+
31
+ from __future__ import annotations
32
+
33
+ import argparse
34
+ import getpass
35
+ import os
36
+ import secrets
37
+ import socket
38
+ import sqlite3
39
+ import sys
40
+ from datetime import datetime, timezone
41
+
42
+
43
+ def _build_parser() -> argparse.ArgumentParser:
44
+ p = argparse.ArgumentParser(
45
+ prog="swarph gateway",
46
+ description=(
47
+ "Run the bundled mesh-gateway server (peer registry + DM "
48
+ "inbox/outbox + feature aggregation + lane/service control)."
49
+ ),
50
+ )
51
+ sub = p.add_subparsers(dest="command")
52
+
53
+ serve = sub.add_parser("serve", help="run the mesh-gateway HTTP server")
54
+ serve.add_argument(
55
+ "--host",
56
+ default="127.0.0.1",
57
+ help="bind address (default 127.0.0.1; use a tailnet IP to expose to peers)",
58
+ )
59
+ serve.add_argument(
60
+ "--port",
61
+ type=int,
62
+ default=8788,
63
+ help="bind port (default 8788)",
64
+ )
65
+ serve.add_argument(
66
+ "--token",
67
+ default=None,
68
+ help="bearer token for the gateway (sets MESH_GATEWAY_TOKEN); "
69
+ "minted + printed once if omitted and not already in the env",
70
+ )
71
+ serve.add_argument(
72
+ "--db",
73
+ default=None,
74
+ help="path to the gateway SQLite DB (sets MESH_DB_PATH)",
75
+ )
76
+
77
+ boot = sub.add_parser(
78
+ "bootstrap-ratify",
79
+ help="ratify the FIRST peer on a fresh gateway (human commander, "
80
+ "local DB write, audited)",
81
+ )
82
+ boot.add_argument("peer", help="peer name to bootstrap-ratify")
83
+ boot.add_argument(
84
+ "--db",
85
+ default=None,
86
+ help="path to the gateway SQLite DB (default: $MESH_DB_PATH or "
87
+ "~/.swarph/mesh.db)",
88
+ )
89
+ boot.add_argument(
90
+ "--reason",
91
+ default=None,
92
+ help="extra audit text appended to the bootstrap reason",
93
+ )
94
+ return p
95
+
96
+
97
+ def _serve(args: argparse.Namespace) -> int:
98
+ # Dependency gate — the server stack is an optional extra. Probe the
99
+ # imports before touching uvicorn so a missing extra prints a clean
100
+ # install hint instead of a traceback.
101
+ try:
102
+ import fastapi # noqa: F401
103
+ import uvicorn # noqa: F401
104
+ except ImportError:
105
+ sys.stderr.write(
106
+ 'swarph gateway needs the optional server deps: '
107
+ 'pip install "swarph-cli[gateway]"\n'
108
+ )
109
+ return 2
110
+
111
+ if args.db:
112
+ os.environ["MESH_DB_PATH"] = args.db
113
+
114
+ token = args.token or os.environ.get("MESH_GATEWAY_TOKEN")
115
+ if not token:
116
+ token = secrets.token_urlsafe(48)
117
+ print(
118
+ "swarph gateway: minted a new MESH_GATEWAY_TOKEN "
119
+ "(give this to your mesh cells):\n"
120
+ f" {token}",
121
+ file=sys.stderr,
122
+ )
123
+ os.environ["MESH_GATEWAY_TOKEN"] = token
124
+
125
+ uvicorn.run(
126
+ "swarph_cli.gateway.server:app",
127
+ host=args.host,
128
+ port=args.port,
129
+ log_level="info",
130
+ )
131
+ return 0
132
+
133
+
134
+ def _bootstrap_ratify(args: argparse.Namespace) -> int:
135
+ """#565-B: the ratify ladder's first rung, as an audited local write.
136
+
137
+ On a fresh gateway, `swarph ratify` can never complete — the witness must
138
+ itself be ratified, and zero peers are. The live mesh only works because
139
+ a one-time Phase 5.5 migration grandfathered its cohort; every deployment
140
+ since hangs off that accident. This verb makes the bootstrap explicit:
141
+
142
+ * LOCAL DB write — operator-with-filesystem-access is already god; this
143
+ makes the act explicit and audited instead of a SQL escape hatch.
144
+ * HUMAN-only — an interactive tty and a typed 'yes'. One prompt, no
145
+ ceremony: the gate must not cut into the onboarding flow it serves.
146
+ * SELF-DESTROYING — refuses when ANY ratified peer exists, so it can
147
+ never become a second ratification path. Recovery stays possible:
148
+ whenever zero ratified peers exist, the gate passes again.
149
+ * HAND-IN-HAND with onboarding — the target must already be registered
150
+ (onboard first), and the output names the next step.
151
+ """
152
+ db_path = (
153
+ args.db
154
+ or os.environ.get("MESH_DB_PATH")
155
+ or os.path.expanduser("~/.swarph/mesh.db")
156
+ )
157
+ if not os.path.exists(db_path):
158
+ sys.stderr.write(
159
+ f"swarph gateway bootstrap-ratify: no gateway DB at {db_path}\n"
160
+ " Run this on the gateway box, or pass --db / set MESH_DB_PATH.\n"
161
+ )
162
+ return 2
163
+
164
+ # Match server.py _conn(): autocommit with explicit transaction control
165
+ # (we BEGIN IMMEDIATE ourselves) and FK enforcement ON — the audit insert
166
+ # into peer_ratifications references claude_peers, and without the pragma
167
+ # a dangling reference would write silently.
168
+ conn = sqlite3.connect(db_path, isolation_level=None)
169
+ conn.execute("PRAGMA foreign_keys = ON")
170
+ conn.row_factory = sqlite3.Row
171
+ try:
172
+ row = conn.execute(
173
+ "SELECT ratified FROM claude_peers WHERE name = ?", (args.peer,)
174
+ ).fetchone()
175
+ if row is None:
176
+ sys.stderr.write(
177
+ f"swarph gateway bootstrap-ratify: {args.peer!r} is not "
178
+ "registered —\n onboard it first:\n"
179
+ f" swarph onboard {args.peer}\n"
180
+ )
181
+ return 2
182
+ if row["ratified"]:
183
+ print(f"{args.peer!r} is already ratified — nothing to do.")
184
+ return 0
185
+ existing = conn.execute(
186
+ "SELECT name FROM claude_peers WHERE ratified = 1 LIMIT 1"
187
+ ).fetchone()
188
+ if existing:
189
+ sys.stderr.write(
190
+ f"swarph gateway bootstrap-ratify: refusing — "
191
+ f"{existing['name']!r} is already ratified.\n"
192
+ " The ladder has its first rung; bootstrap exists ONLY for a "
193
+ "fresh gateway.\n"
194
+ f" Ratify {args.peer!r} through the normal witness path "
195
+ f"(a ratified witness runs:\n"
196
+ f" swarph ratify {args.peer} --reason \"<short text>\")\n"
197
+ )
198
+ return 2
199
+
200
+ if not sys.stdin.isatty():
201
+ sys.stderr.write(
202
+ "swarph gateway bootstrap-ratify: refusing — this is the "
203
+ "human commander's act.\n"
204
+ " Run it interactively on the gateway box (no pipes, no "
205
+ "scripts, no cells).\n"
206
+ )
207
+ return 2
208
+
209
+ operator = f"{getpass.getuser()}@{socket.gethostname()}"
210
+ print(f"Bootstrap-ratify {args.peer!r} on {db_path}:")
211
+ print(" - zero ratified peers exist (fresh gateway)")
212
+ print(" - flips ratified=1 and writes an audited peer_ratifications "
213
+ "row (binding_regime='bootstrap')")
214
+ print(f" - performed by {operator}")
215
+ if input("Type 'yes' to confirm: ").strip().lower() != "yes":
216
+ print("aborted — nothing written.")
217
+ return 1
218
+
219
+ now = datetime.now(timezone.utc).isoformat()
220
+ reason = f"bootstrap by {operator} (#565)"
221
+ if args.reason:
222
+ reason += f": {args.reason}"
223
+ conn.execute("BEGIN IMMEDIATE")
224
+ try:
225
+ row = conn.execute(
226
+ "SELECT ratified FROM claude_peers WHERE name = ?", (args.peer,)
227
+ ).fetchone()
228
+ existing = conn.execute(
229
+ "SELECT name FROM claude_peers WHERE ratified = 1 LIMIT 1"
230
+ ).fetchone()
231
+ if row is None or row["ratified"] or existing:
232
+ conn.execute("ROLLBACK")
233
+ sys.stderr.write(
234
+ "swarph gateway bootstrap-ratify: gateway state changed while "
235
+ "you confirmed; refusing to create a second first rung.\n"
236
+ )
237
+ return 2
238
+ conn.execute(
239
+ "UPDATE claude_peers SET ratified=1, ratified_at=?, "
240
+ "ratified_by=?, ratification_reason=? WHERE name=?",
241
+ (now, "bootstrap", reason, args.peer),
242
+ )
243
+ conn.execute(
244
+ "INSERT INTO peer_ratifications "
245
+ "(peer, ratified_by, ratified_at, reason, witness_dm_id, "
246
+ "binding_regime) VALUES (?, ?, ?, ?, NULL, 'bootstrap')",
247
+ (args.peer, "bootstrap", now, reason),
248
+ )
249
+ conn.execute("COMMIT")
250
+ except Exception:
251
+ conn.execute("ROLLBACK")
252
+ raise
253
+ finally:
254
+ conn.close()
255
+
256
+ print(f"{args.peer!r} ratified (bootstrap). The ladder has its first "
257
+ "rung — hand in hand with onboarding:")
258
+ print(f" - {args.peer} can now WITNESS ratifications for the cells "
259
+ "that follow")
260
+ print(f" - next cell: `swarph onboard <cell>` on its box, complete the "
261
+ f"handshake, then a witness runs:")
262
+ print(f" swarph ratify <cell> --reason \"<short text>\"")
263
+ return 0
264
+
265
+
266
+ def run_gateway(argv: list) -> int:
267
+ """Entry point invoked by ``swarph_cli.main`` verb dispatch."""
268
+ parser = _build_parser()
269
+ args = parser.parse_args(argv)
270
+
271
+ if args.command == "serve":
272
+ return _serve(args)
273
+ if args.command == "bootstrap-ratify":
274
+ return _bootstrap_ratify(args)
275
+
276
+ # bare `swarph gateway` (no subcommand) — print help.
277
+ parser.print_help()
278
+ return 0
@@ -51,6 +51,8 @@ def _allow_unchanged() -> int:
51
51
 
52
52
  def run_hook(stdin_text: Optional[str] = None) -> int:
53
53
  raw = stdin_text if stdin_text is not None else sys.stdin.read()
54
+ # PS 5.1 native pipes prefix a (double) UTF-8 BOM; strip before parse.
55
+ raw = (raw or "").lstrip("\ufeff")
54
56
  try:
55
57
  payload = json.loads(raw or "{}")
56
58
  except Exception:
@@ -265,7 +265,13 @@ def run_init(argv: list[str]) -> int:
265
265
  # its own context); an operator running `swarph onboard` first mints the
266
266
  # token into a process that discards it, and the cell's later self-register
267
267
  # returns token_status=existing with nothing.
268
- print(f"next: from the CELL'S OWN context: `swarph mesh register --as {name}` "
268
+ # Scoped as CONDITIONAL (#319 d): a standalone outsider has no mesh and
269
+ # cannot ratify — the mesh path is named for the user who IS joining one,
270
+ # not presented as the next thing everyone must do. The outsider's next
271
+ # step is the `ready:` line above (swarph spawn), which works with zero
272
+ # mesh.
273
+ print(f"next: if you're joining a mesh — from the CELL'S OWN context: "
274
+ f"`swarph mesh register --as {name}` "
269
275
  f"(mints + stores its once-only per-peer token, mode 600); then a witness "
270
276
  f"runs `swarph ratify {name}`. Status at any point: "
271
277
  f"`swarph onboard --check {name}`.")