swarph-cli 0.72.7__tar.gz → 0.72.9__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 (481) hide show
  1. {swarph_cli-0.72.7/src/swarph_cli.egg-info → swarph_cli-0.72.9}/PKG-INFO +1 -1
  2. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/pyproject.toml +1 -1
  3. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/__init__.py +1 -1
  4. swarph_cli-0.72.9/src/swarph_cli/commands/roster.py +237 -0
  5. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/watchdog.py +113 -9
  6. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/guide/GUIDE.md +1 -0
  7. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/main.py +1 -0
  8. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/rowclear.py +65 -4
  9. {swarph_cli-0.72.7 → swarph_cli-0.72.9/src/swarph_cli.egg-info}/PKG-INFO +1 -1
  10. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli.egg-info/SOURCES.txt +3 -0
  11. swarph_cli-0.72.9/tests/test_1059_watchdog_unread_env.py +179 -0
  12. swarph_cli-0.72.9/tests/test_1061_roster_cli.py +137 -0
  13. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_432_rowclear.py +165 -0
  14. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_mesh_sidecar.py +40 -0
  15. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_watchdog.py +2 -0
  16. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/LICENSE +0 -0
  17. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/README.md +0 -0
  18. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/setup.cfg +0 -0
  19. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/__main__.py +0 -0
  20. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/__init__.py +0 -0
  21. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/backends.py +0 -0
  22. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/claude_cli.py +0 -0
  23. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/codex_cli.py +0 -0
  24. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/pack.py +0 -0
  25. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/prices.py +0 -0
  26. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/providers.py +0 -0
  27. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/quality.py +0 -0
  28. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/refresh_prices.py +0 -0
  29. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/rules.py +0 -0
  30. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/runner.py +0 -0
  31. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/signal_score.py +0 -0
  32. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/validate.py +0 -0
  33. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/caller.py +0 -0
  34. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/capture/__init__.py +0 -0
  35. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/capture/harden.py +0 -0
  36. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/capture/lineage.py +0 -0
  37. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/capture/liveness.py +0 -0
  38. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/capture/manifest.py +0 -0
  39. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/capture/paths.py +0 -0
  40. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/capture/verify.py +0 -0
  41. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/cell.py +0 -0
  42. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/chain_token.py +0 -0
  43. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/channel.py +0 -0
  44. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/__init__.py +0 -0
  45. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/_content.py +0 -0
  46. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/_display.py +0 -0
  47. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/_gateway_client.py +0 -0
  48. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/add.py +0 -0
  49. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/bench.py +0 -0
  50. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/board.py +0 -0
  51. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/board_merge_check.py +0 -0
  52. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/brain.py +0 -0
  53. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/brain_ask.py +0 -0
  54. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/cell.py +0 -0
  55. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/cell_probe.py +0 -0
  56. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  57. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/channel.py +0 -0
  58. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/chat.py +0 -0
  59. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/codegraph.py +0 -0
  60. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/codegraph_hook.py +0 -0
  61. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/codex_hook_output.py +0 -0
  62. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/codex_waker.py +0 -0
  63. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/compress.py +0 -0
  64. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/daemon.py +0 -0
  65. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/dreaming.py +0 -0
  66. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/event.py +0 -0
  67. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/gateway.py +0 -0
  68. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/gh_route.py +0 -0
  69. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/group.py +0 -0
  70. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/guide.py +0 -0
  71. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/highlight.py +0 -0
  72. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/hook_interpreter.py +0 -0
  73. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/hook_output.py +0 -0
  74. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/hooks.py +0 -0
  75. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/import_session.py +0 -0
  76. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/init.py +0 -0
  77. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
  78. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/install_hook.py +0 -0
  79. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  80. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
  81. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
  82. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/install_wake_hook.py +0 -0
  83. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/lane.py +0 -0
  84. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/mcp_server.py +0 -0
  85. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/memory.py +0 -0
  86. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
  87. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/memory_sync.py +0 -0
  88. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/mesh.py +0 -0
  89. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/monitor.py +0 -0
  90. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/okf_links.py +0 -0
  91. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/onboard.py +0 -0
  92. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
  93. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
  94. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/protocol_handler.py +0 -0
  95. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/ratify.py +0 -0
  96. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/rowclear_hook.py +0 -0
  97. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/schedule.py +0 -0
  98. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/security.py +0 -0
  99. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/service.py +0 -0
  100. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/spawn.py +0 -0
  101. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/timeline.py +0 -0
  102. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/version.py +0 -0
  103. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/wake_hook_output.py +0 -0
  104. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/compress/__init__.py +0 -0
  105. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/compress/levers.py +0 -0
  106. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/compress/marker.py +0 -0
  107. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/compress/verify.py +0 -0
  108. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/console_safe.py +0 -0
  109. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/delivery_queue.py +0 -0
  110. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dm_frame.py +0 -0
  111. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/__init__.py +0 -0
  112. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/candidates.py +0 -0
  113. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/clone.py +0 -0
  114. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/enrich.py +0 -0
  115. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/index_check.py +0 -0
  116. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/notify.py +0 -0
  117. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/organize.py +0 -0
  118. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/probes.py +0 -0
  119. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/report.py +0 -0
  120. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/run.py +0 -0
  121. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/slm.py +0 -0
  122. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/transcripts.py +0 -0
  123. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/verify.py +0 -0
  124. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/gateway/__init__.py +0 -0
  125. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/gateway/feature_registry.py +0 -0
  126. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/gateway/lanes_control.py +0 -0
  127. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/gateway/schema.sql +0 -0
  128. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/gateway/server.py +0 -0
  129. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/gateway/services_control.py +0 -0
  130. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/gateway_default.py +0 -0
  131. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/gh_identity.py +0 -0
  132. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/identity.py +0 -0
  133. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/multiplexer.py +0 -0
  134. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/operator/__init__.py +0 -0
  135. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/operator/__main__.py +0 -0
  136. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/operator/api.py +0 -0
  137. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/operator/cli.py +0 -0
  138. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/operator/config.py +0 -0
  139. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/operator/fmt.py +0 -0
  140. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/orphan_daemons.py +0 -0
  141. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/pane_probe.py +0 -0
  142. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/parsers/__init__.py +0 -0
  143. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/parsers/claude.py +0 -0
  144. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
  145. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
  146. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
  147. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
  148. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
  149. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
  150. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
  151. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
  152. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
  153. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
  154. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
  155. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
  156. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/peer_executor.py +0 -0
  157. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/peer_executor_windows.py +0 -0
  158. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/peer_reconciliation.py +0 -0
  159. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/peer_reply_outbox.py +0 -0
  160. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/peer_service_host.py +0 -0
  161. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/peer_service_payload.py +0 -0
  162. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/peer_service_reply.py +0 -0
  163. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/peer_staging.py +0 -0
  164. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/query_filters.py +0 -0
  165. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/scripts/__init__.py +0 -0
  166. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
  167. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  168. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
  169. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
  170. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
  171. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/service/__init__.py +0 -0
  172. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/service/app.py +0 -0
  173. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/service/providers.py +0 -0
  174. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/session_bridge.py +0 -0
  175. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
  176. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
  177. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/source_text.py +0 -0
  178. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/stall_alert.py +0 -0
  179. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/codex-waker.default +0 -0
  180. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
  181. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
  182. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
  183. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
  184. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
  185. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
  186. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
  187. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
  188. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
  189. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
  190. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  191. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  192. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  193. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/timeline_paths.py +0 -0
  194. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/tokens.py +0 -0
  195. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  196. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  197. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli.egg-info/requires.txt +0 -0
  198. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli.egg-info/top_level.txt +0 -0
  199. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_1006_board_publisher.py +0 -0
  200. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_1006_session_board.py +0 -0
  201. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_1010_strand_only.py +0 -0
  202. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_1013_spawn_channel.py +0 -0
  203. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_1017_muse_tui.py +0 -0
  204. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_1050_surrogate_wake.py +0 -0
  205. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_1052_opencode_deaf.py +0 -0
  206. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_124_register_merge.py +0 -0
  207. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_126_pending_from_complete.py +0 -0
  208. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_132_ensure_monitor_state_dir.py +0 -0
  209. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_138_memory_navigate.py +0 -0
  210. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_139_tool_probe.py +0 -0
  211. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_183_card_anchor.py +0 -0
  212. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_184_notify_honesty.py +0 -0
  213. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_201_win32_bash_refusal.py +0 -0
  214. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_243_onboard_per_peer_token.py +0 -0
  215. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_307_task4_ask_reply.py +0 -0
  216. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
  217. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_319_refusal_exit_codes.py +0 -0
  218. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_323_mistral_backend.py +0 -0
  219. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_332_report_organize_findings.py +0 -0
  220. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_356_query_filters.py +0 -0
  221. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_397_gh_identity_router.py +0 -0
  222. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_397_router_preflight.py +0 -0
  223. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_402_one_identity_resolver.py +0 -0
  224. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_467b_onboard_operator_credential.py +0 -0
  225. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_472_register_caller_binding.py +0 -0
  226. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_484_codex_queue_sink.py +0 -0
  227. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_523_guide.py +0 -0
  228. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_524_conftest_hermeticity.py +0 -0
  229. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_532_accept_flag.py +0 -0
  230. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_535_version_capability.py +0 -0
  231. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_538_signal_bench.py +0 -0
  232. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_546_default_gateway.py +0 -0
  233. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_547_verbs_from_registry.py +0 -0
  234. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_548_gateway_remainder.py +0 -0
  235. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_552_codegraph_gateway.py +0 -0
  236. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_555_relay_row_shape.py +0 -0
  237. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_556_relay_freshness.py +0 -0
  238. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_559_edges_truncated.py +0 -0
  239. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_564_bootstrap_first_peer.py +0 -0
  240. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
  241. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_579_register_bootstrap_message.py +0 -0
  242. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_591_cli_step_graph.py +0 -0
  243. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_644_monitor_supervision.py +0 -0
  244. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_648_install_unit.py +0 -0
  245. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_654_reexec.py +0 -0
  246. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_656_owned_slm.py +0 -0
  247. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_684_dreaming_verb.py +0 -0
  248. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_689_pkg_version_order.py +0 -0
  249. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_715_grok_wake.py +0 -0
  250. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_716_timeline_one_path.py +0 -0
  251. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_723_grok_0251.py +0 -0
  252. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_724_ps1_ascii_or_bom.py +0 -0
  253. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_725_stdio_reconfigure.py +0 -0
  254. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_726_grok_limit_screen.py +0 -0
  255. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_729_arm_check_delivery_gap.py +0 -0
  256. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_729_dm_once.py +0 -0
  257. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_729_wake_watchdog.py +0 -0
  258. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_734_dreaming_enrich_degrade.py +0 -0
  259. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_735_dm_mcp_tools.py +0 -0
  260. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_739_wake_writer.py +0 -0
  261. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_740_edit_project.py +0 -0
  262. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_762_cards_edit_priority.py +0 -0
  263. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_764_enrich_collapse.py +0 -0
  264. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_801_confirm_flow_cli.py +0 -0
  265. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_802_stage_history.py +0 -0
  266. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_825_codegraph_hook_noise.py +0 -0
  267. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_825_install_posttooluse_only.py +0 -0
  268. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_825_remaining_cap_shred_bind.py +0 -0
  269. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_829_counterfactual_column.py +0 -0
  270. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_830_hook_binding_version.py +0 -0
  271. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_84_cli_labels.py +0 -0
  272. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_866_doctrine_guide.py +0 -0
  273. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_872_identity_refuses_not_warns.py +0 -0
  274. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_874_source_text.py +0 -0
  275. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
  276. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_937_dreaming_notify.py +0 -0
  277. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_938_organize_line_limit.py +0 -0
  278. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_941_bench_backends.py +0 -0
  279. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_941_harness_342.py +0 -0
  280. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_945_same_line_pairing.py +0 -0
  281. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_955_prompt_audit.py +0 -0
  282. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_957_auth_http.py +0 -0
  283. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_959_venv_interpreter.py +0 -0
  284. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_960_channel.py +0 -0
  285. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
  286. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_961_cursor_grok_footer.py +0 -0
  287. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_961_loud_stop.py +0 -0
  288. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_963_plugin_manifest.py +0 -0
  289. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_964_channel_cell.py +0 -0
  290. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_965_mesh_wait.py +0 -0
  291. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_975_channel_hop.py +0 -0
  292. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_add_security_gate.py +0 -0
  293. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_artifact_add.py +0 -0
  294. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_artifact_hash.py +0 -0
  295. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_artifact_lib.py +0 -0
  296. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_artifact_mcp.py +0 -0
  297. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_artifact_skill.py +0 -0
  298. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_artifact_tool.py +0 -0
  299. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_artifact_uri.py +0 -0
  300. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_bench_backends.py +0 -0
  301. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_bench_claude_cli.py +0 -0
  302. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_bench_codex_cli.py +0 -0
  303. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_bench_command.py +0 -0
  304. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_bench_pack.py +0 -0
  305. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_bench_prices.py +0 -0
  306. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_bench_quality.py +0 -0
  307. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_bench_runner.py +0 -0
  308. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_bench_validate.py +0 -0
  309. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_board_card_thread.py +0 -0
  310. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_board_command.py +0 -0
  311. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_board_merge_check.py +0 -0
  312. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_board_obligation_close.py +0 -0
  313. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_brain_ask_command.py +0 -0
  314. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_brain_ask_env_diagnosis.py +0 -0
  315. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_brain_ask_identity_explicitness.py +0 -0
  316. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_brain_command.py +0 -0
  317. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_caller_meta_guard.py +0 -0
  318. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_capture_lineage.py +0 -0
  319. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_capture_liveness.py +0 -0
  320. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_capture_manifest.py +0 -0
  321. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_capture_paths.py +0 -0
  322. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_capture_security.py +0 -0
  323. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_cell_command_dispatch.py +0 -0
  324. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_cell_harden.py +0 -0
  325. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_cell_loader.py +0 -0
  326. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_cell_selfcheck.py +0 -0
  327. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_cell_verify.py +0 -0
  328. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_chain_token.py +0 -0
  329. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_channel_command.py +0 -0
  330. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_channel_poll_never_breaks_dms.py +0 -0
  331. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_channel_post.py +0 -0
  332. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_channel_wake_policy_filter.py +0 -0
  333. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_chat_command.py +0 -0
  334. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_claude_parser.py +0 -0
  335. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_claude_tmux_template.py +0 -0
  336. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_codegraph_caller_scope.py +0 -0
  337. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_codegraph_command.py +0 -0
  338. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_codegraph_hook.py +0 -0
  339. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_codegraph_mcp.py +0 -0
  340. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_codex_waker.py +0 -0
  341. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_compress_caller_convention.py +0 -0
  342. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_compress_command.py +0 -0
  343. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_compress_levers.py +0 -0
  344. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_compress_marker.py +0 -0
  345. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_compress_verify.py +0 -0
  346. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_console_encoding_e2e.py +0 -0
  347. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_content_composition.py +0 -0
  348. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_cursor_membrane.py +0 -0
  349. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_daemon_command.py +0 -0
  350. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_daemon_console_encoding.py +0 -0
  351. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_daemon_delivery.py +0 -0
  352. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_delivery_queue.py +0 -0
  353. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_display_sanitize.py +0 -0
  354. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_ensure_monitor_no_peer_default.py +0 -0
  355. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_event_emit.py +0 -0
  356. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_feature_to_uri.py +0 -0
  357. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_gateway_brain.py +0 -0
  358. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_gateway_client.py +0 -0
  359. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_gateway_command.py +0 -0
  360. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_gateway_message_idempotency.py +0 -0
  361. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_gh_route_doctor_mesh_name.py +0 -0
  362. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_group_command.py +0 -0
  363. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_guide_commands_parse.py +0 -0
  364. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_guide_selfcheck.py +0 -0
  365. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_heartbeat_check.py +0 -0
  366. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_highlight_command.py +0 -0
  367. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_hook_output.py +0 -0
  368. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_hooks_activity_marker.py +0 -0
  369. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_hooks_add.py +0 -0
  370. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_hooks_bundle.py +0 -0
  371. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_hooks_init.py +0 -0
  372. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_hooks_legacy_generation_removal.py +0 -0
  373. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_hooks_lifecycle.py +0 -0
  374. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_hooks_merge.py +0 -0
  375. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_hooks_verify.py +0 -0
  376. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_hooks_win32_bash_wrapper.py +0 -0
  377. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_hooks_win32_command_path.py +0 -0
  378. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_import_command.py +0 -0
  379. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_inbox_identity_provenance.py +0 -0
  380. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_init_command.py +0 -0
  381. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_init_provider_menu.py +0 -0
  382. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_install_codex_hooks.py +0 -0
  383. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_install_hook.py +0 -0
  384. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_install_multiplexer_command.py +0 -0
  385. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_install_postcompact_hook.py +0 -0
  386. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_install_wake_hook.py +0 -0
  387. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_lane_command.py +0 -0
  388. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_main.py +0 -0
  389. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_main_unknown_verb_guard.py +0 -0
  390. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_mcp_missing_hint.py +0 -0
  391. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_mcp_server.py +0 -0
  392. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_memory_command.py +0 -0
  393. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_memory_emit_snippet.py +0 -0
  394. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_memory_gateway.py +0 -0
  395. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_memory_sync.py +0 -0
  396. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_memory_sync_membrane.py +0 -0
  397. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_mesh_command.py +0 -0
  398. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_mesh_inbox_mark_read.py +0 -0
  399. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_mesh_inbox_sanitize.py +0 -0
  400. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_mesh_originate_thread_623.py +0 -0
  401. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_mesh_send_unknown_peer.py +0 -0
  402. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  403. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_mesh_sidecar_muse_sink.py +0 -0
  404. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  405. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_meta_edge_identity.py +0 -0
  406. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_monitor_channel_poll.py +0 -0
  407. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_monitor_cli.py +0 -0
  408. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_monitor_daemon_collision.py +0 -0
  409. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_monitor_self_name_verification.py +0 -0
  410. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_monitor_state_split.py +0 -0
  411. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_multiplexer.py +0 -0
  412. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_muse_membrane.py +0 -0
  413. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_muse_own_cli.py +0 -0
  414. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_obligation_close_is_surfaced.py +0 -0
  415. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_okf_links.py +0 -0
  416. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_onboard_command.py +0 -0
  417. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_opencode_membrane.py +0 -0
  418. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_opencode_plugin.py +0 -0
  419. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_operator_me.py +0 -0
  420. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_orphan_daemons.py +0 -0
  421. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_packaged_artifacts_exist.py +0 -0
  422. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_packaged_skills.py +0 -0
  423. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_peer_executor.py +0 -0
  424. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_peer_executor_windows.py +0 -0
  425. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_peer_reply_drain_command.py +0 -0
  426. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_peer_reply_drain_supervisor.py +0 -0
  427. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_peer_reply_outbox.py +0 -0
  428. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_peer_reply_transport.py +0 -0
  429. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_peer_service_e2e.py +0 -0
  430. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_peer_service_host.py +0 -0
  431. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_peer_service_payload.py +0 -0
  432. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_peer_service_reply.py +0 -0
  433. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_postcompact_recall.py +0 -0
  434. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_process_liveness.py +0 -0
  435. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_protocol_handler.py +0 -0
  436. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_providers_isolation.py +0 -0
  437. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_ratify_command.py +0 -0
  438. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_readme_claims_no_gateway_default.py +0 -0
  439. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_repl_caller_convention.py +0 -0
  440. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_schedule_command.py +0 -0
  441. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_schedule_create_contract.py +0 -0
  442. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_security.py +0 -0
  443. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_service_app.py +0 -0
  444. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_service_command.py +0 -0
  445. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_service_providers.py +0 -0
  446. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_session_bridge_inject.py +0 -0
  447. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_session_bridge_probe.py +0 -0
  448. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_session_bridge_resolve.py +0 -0
  449. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_session_path_role_gate.py +0 -0
  450. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_shipped_units_match_the_cli.py +0 -0
  451. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_smoke_chat.py +0 -0
  452. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_smoke_one_shot.py +0 -0
  453. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_smoke_phase_5_5.py +0 -0
  454. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_spawn_base_hoist.py +0 -0
  455. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_spawn_command.py +0 -0
  456. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_spawn_identity_stamp.py +0 -0
  457. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_spawn_live_pin.py +0 -0
  458. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_spawn_mitosis_lineage.py +0 -0
  459. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_spawn_tmux_session.py +0 -0
  460. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_spawn_windows_relaunch.py +0 -0
  461. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_stall_alert.py +0 -0
  462. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_subprocess_decode_encoding.py +0 -0
  463. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_timeline_command.py +0 -0
  464. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_tmux_wake_runstate_defer.py +0 -0
  465. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_tmux_wake_session_reanchor.py +0 -0
  466. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_tmux_wake_stale.py +0 -0
  467. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_tmux_wake_submit_verify.py +0 -0
  468. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_token_file_one_parser.py +0 -0
  469. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_token_precedence_explicit_first.py +0 -0
  470. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_unified_token_resolver.py +0 -0
  471. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_version_command.py +0 -0
  472. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_version_consistency.py +0 -0
  473. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_vibe_membrane.py +0 -0
  474. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_watchdog_dm_wake.py +0 -0
  475. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  476. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  477. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_watchdog_emit_health.py +0 -0
  478. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_watchdog_install_flags.py +0 -0
  479. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_watchdog_model_rung.py +0 -0
  480. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_watchdog_stale_peers.py +0 -0
  481. {swarph_cli-0.72.7 → swarph_cli-0.72.9}/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.7
3
+ Version: 0.72.9
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.7"
7
+ version = "0.72.9"
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.7"
19
+ __version__ = "0.72.9"
20
20
 
21
21
  __all__ = ["__version__"]
@@ -0,0 +1,237 @@
1
+ """``swarph roster`` — read/write the gateway's operator-maintained capacity table.
2
+
3
+ card #1061 (ROSTER AS DATA): the roster replaces tribal knowledge about who
4
+ builds, who reviews, on which model/effort, under which quota, and who is out
5
+ until when. Reads hit GET /roster (any authenticated caller); writes hit
6
+ PUT /roster/{peer} (operators only — the gateway 403s the rest).
7
+
8
+ swarph roster [--peer PEER] [--json] list the table (or one row)
9
+ swarph roster set PEER [--model M] [--effort E] [--roles r1,r2]
10
+ [--quota-pool P] [--quota-used PCT --quota-as-of TS] [--resets DATE]
11
+ [--until TS | --clear-until] [--note TEXT]
12
+
13
+ A quota VALUE without its as_of coordinate is a rumour: --quota-used /
14
+ --resets without --quota-as-of are refused client-side (exit 2) before the
15
+ gateway's own 400. The pool NAME needs no coordinate (identifier, not a
16
+ measurement). --clear-until is the grok-returns flow (explicit null clears).
17
+ """
18
+ from __future__ import annotations
19
+
20
+ import argparse
21
+ import json
22
+ import sys
23
+ import urllib.error
24
+ import urllib.request
25
+
26
+ # card #1061 role vocabulary — mirrors the gateway's _ROSTER_ROLES. A role
27
+ # the gateway does not know is refused here (exit 2) before its 400; when the
28
+ # vocabulary grows, BOTH pins change together (gateway tests + test_1061 below).
29
+ ROSTER_ROLES = ("build", "validate", "plan-review", "orchestrate")
30
+
31
+
32
+ def _http_get_json(url, token, *, timeout=10.0):
33
+ from swarph_cli.commands.mesh import _http_get_json as _get
34
+ return _get(url, token, timeout=timeout)
35
+
36
+
37
+ def _http_put_json(url, body, token, *, timeout=10.0):
38
+ from swarph_cli.commands.mesh import _require_absolute_gateway_url
39
+ _require_absolute_gateway_url(url)
40
+ data = json.dumps(body).encode("utf-8")
41
+ req = urllib.request.Request(
42
+ url, data=data, method="PUT",
43
+ headers={"Authorization": f"Bearer {token}",
44
+ "Content-Type": "application/json"})
45
+ try:
46
+ with urllib.request.urlopen(req, timeout=timeout) as resp:
47
+ return resp.status, json.loads(resp.read().decode("utf-8") or "{}")
48
+ except urllib.error.HTTPError as exc:
49
+ try:
50
+ err_body = json.loads(exc.read().decode("utf-8") or "{}")
51
+ except Exception:
52
+ err_body = {"detail": str(exc)}
53
+ return exc.code, err_body
54
+ except urllib.error.URLError as exc:
55
+ return 0, {"detail": str(exc)}
56
+
57
+
58
+ def _resolve(args):
59
+ from swarph_cli.tokens import resolve_token
60
+ resolution = resolve_token(args.self_name, args.token_file,
61
+ identity_is_explicit=bool(args.self_name))
62
+ if resolution is None:
63
+ print("swarph roster: no token (set MESH_GATEWAY_TOKEN, pass "
64
+ "--token-file, or --as a peer with a stored token)",
65
+ file=sys.stderr)
66
+ return None
67
+ return resolution.token
68
+
69
+
70
+ def _quota_compact(r):
71
+ if r.get("quota_used_pct") is None and not r.get("quota_pool"):
72
+ return "-"
73
+ parts = []
74
+ if r.get("quota_pool"):
75
+ parts.append(str(r["quota_pool"]))
76
+ if r.get("quota_used_pct") is not None:
77
+ parts.append(f"{r['quota_used_pct']}%")
78
+ if r.get("quota_as_of"):
79
+ parts.append(f"@{r['quota_as_of']}")
80
+ if r.get("quota_resets_at"):
81
+ parts.append(f"(resets {r['quota_resets_at']})")
82
+ return " ".join(parts)
83
+
84
+
85
+ def _line(r):
86
+ roles = ",".join(r["roles"]) if r.get("roles") else "-"
87
+ bits = [r["peer"],
88
+ f"model={r.get('model') or '-'}",
89
+ f"effort={r.get('effort') or '-'}",
90
+ f"roles={roles}",
91
+ f"quota={_quota_compact(r)}"]
92
+ if r.get("unavailable_until"):
93
+ bits.append(f"out-until={r['unavailable_until']}")
94
+ bits.append(f"updated_by={r.get('updated_by') or '?'}")
95
+ return " ".join(bits)
96
+
97
+
98
+ def _run_list(args):
99
+ token = _resolve(args)
100
+ if token is None:
101
+ return 2
102
+ gateway = args.gateway.rstrip("/")
103
+ status, payload = _http_get_json(f"{gateway}/roster", token)
104
+ if status < 200 or status >= 300:
105
+ detail = payload.get("detail", "<gateway error>") if isinstance(payload, dict) else "<gateway error>"
106
+ print(f"swarph roster: gateway {status}: {detail}", file=sys.stderr)
107
+ return 1
108
+ rows = payload.get("roster") if isinstance(payload, dict) else None
109
+ if not isinstance(rows, list):
110
+ print("swarph roster: unexpected /roster shape (no roster list)",
111
+ file=sys.stderr)
112
+ return 1
113
+ if args.peer:
114
+ rows = [r for r in rows if isinstance(r, dict) and r.get("peer") == args.peer]
115
+ if not rows:
116
+ print(f"swarph roster: no row for {args.peer!r}", file=sys.stderr)
117
+ return 1
118
+ if args.json:
119
+ print(json.dumps(rows, indent=2))
120
+ else:
121
+ for r in rows:
122
+ print(_line(r) if isinstance(r, dict) else r)
123
+ return 0
124
+
125
+
126
+ def _run_set(args):
127
+ token = _resolve(args)
128
+ if token is None:
129
+ return 2
130
+ if ((args.quota_used is not None or args.resets is not None)
131
+ and args.quota_as_of is None):
132
+ print("swarph roster set: --quota-used / --resets need --quota-as-of "
133
+ "(a quota value without its coordinate is a rumour)",
134
+ file=sys.stderr)
135
+ return 2
136
+ body = {}
137
+ if args.model is not None:
138
+ body["model"] = args.model
139
+ if args.effort is not None:
140
+ body["effort"] = args.effort
141
+ if args.roles is not None:
142
+ roles = [x.strip() for x in args.roles.split(",") if x.strip()]
143
+ bad = [x for x in roles if x not in ROSTER_ROLES]
144
+ if bad:
145
+ print(f"swarph roster set: unknown role(s) {bad} "
146
+ f"(vocabulary: {', '.join(ROSTER_ROLES)})", file=sys.stderr)
147
+ return 2
148
+ body["roles"] = roles
149
+ if args.quota_pool is not None:
150
+ body["quota_pool"] = args.quota_pool
151
+ if args.quota_used is not None:
152
+ body["quota_used_pct"] = args.quota_used
153
+ if args.resets is not None:
154
+ body["quota_resets_at"] = args.resets
155
+ if args.quota_as_of is not None:
156
+ body["quota_as_of"] = args.quota_as_of
157
+ if args.until is not None:
158
+ body["unavailable_until"] = args.until
159
+ if args.clear_until:
160
+ body["unavailable_until"] = None
161
+ if args.note is not None:
162
+ body["note"] = args.note
163
+ if not body:
164
+ print("swarph roster set: nothing to set (pass a field flag)",
165
+ file=sys.stderr)
166
+ return 2
167
+ gateway = args.gateway.rstrip("/")
168
+ status, payload = _http_put_json(
169
+ f"{gateway}/roster/{args.peer}", body, token)
170
+ if status < 200 or status >= 300:
171
+ detail = payload.get("detail", "<gateway error>") if isinstance(payload, dict) else "<gateway error>"
172
+ print(f"swarph roster set: gateway {status}: {detail}", file=sys.stderr)
173
+ return 1
174
+ if args.json:
175
+ print(json.dumps(payload, indent=2))
176
+ else:
177
+ print(f"updated {args.peer} (updated_by={payload.get('updated_by', '?')})")
178
+ return 0
179
+
180
+
181
+ def _add_global(p):
182
+ p.add_argument("--as", dest="self_name", default=None,
183
+ help="peer identity for token resolution")
184
+ from swarph_cli.gateway_default import env_gateway
185
+ p.add_argument("--gateway", default=env_gateway(),
186
+ help="mesh-gateway base URL")
187
+ p.add_argument("--token-file", default=None,
188
+ help="explicit Bearer [REDACTED] file")
189
+
190
+
191
+ def _build_parser():
192
+ p = argparse.ArgumentParser(
193
+ prog="swarph roster",
194
+ description="Read/write the gateway capacity roster (card #1061).")
195
+ _add_global(p)
196
+ p.add_argument("--peer", default=None,
197
+ help="show only this peer (list mode)")
198
+ p.add_argument("--json", action="store_true",
199
+ help="print the raw gateway payload")
200
+ sub = p.add_subparsers(dest="command")
201
+ s = sub.add_parser("set", help="upsert one roster row (operators only)")
202
+ _add_global(s)
203
+ s.add_argument("peer", help="peer the row belongs to")
204
+ s.add_argument("--json", action="store_true",
205
+ help="print the raw gateway payload")
206
+ s.add_argument("--model", default=None)
207
+ s.add_argument("--effort", default=None)
208
+ s.add_argument("--roles", default=None,
209
+ help="comma-separated subset of " + ",".join(ROSTER_ROLES))
210
+ s.add_argument("--quota-pool", default=None)
211
+ s.add_argument("--quota-used", type=float, default=None,
212
+ help="quota_used_pct (needs --quota-as-of)")
213
+ s.add_argument("--quota-as-of", default=None)
214
+ s.add_argument("--resets", default=None,
215
+ help="quota_resets_at date (needs --quota-as-of)")
216
+ group = s.add_mutually_exclusive_group()
217
+ group.add_argument("--until", default=None,
218
+ help="unavailable_until timestamp")
219
+ group.add_argument("--clear-until", action="store_true",
220
+ help="clear unavailable_until (peer is back)")
221
+ s.add_argument("--note", default=None)
222
+ return p
223
+
224
+
225
+ def run_roster(argv):
226
+ parser = _build_parser()
227
+ args = parser.parse_args(argv)
228
+ try:
229
+ if args.command == "set":
230
+ return _run_set(args)
231
+ if args.command is None:
232
+ return _run_list(args)
233
+ parser.error(f"unknown command: {args.command}")
234
+ except RuntimeError as exc:
235
+ print(f"swarph roster: {exc}", file=sys.stderr)
236
+ return 2
237
+ return 2
@@ -468,6 +468,14 @@ def _gateway_configured(gateway: str) -> bool:
468
468
  """
469
469
  return bool((gateway or "").strip())
470
470
 
471
+ def _resolve_unread_token(token: Optional[str]) -> Optional[str]:
472
+ """Token precedence for the unread check: explicit arg (cron setups
473
+ pass env straight through), else the watchdog identity's token file
474
+ (card #1059 — template units have no env). The VALUE is never logged.
475
+ """
476
+ return token or _read_service_token()
477
+
478
+
471
479
  def _gateway_unread_count(gateway: str, peer: str, token: Optional[str]) -> Optional[int]:
472
480
  """Query gateway for unread DM count addressed to peer.
473
481
 
@@ -483,15 +491,15 @@ def _gateway_unread_count(gateway: str, peer: str, token: Optional[str]) -> Opti
483
491
  the code it was documenting — found 2026-08-12 when the contradiction
484
492
  caused a real reader to conclude a gateway/token outage makes A1 MORE
485
493
  eager, when it makes A1 permanently inert for that cell instead.
494
+
495
+ Prefers the count-only endpoint (card #1059: no bodies cross the wire);
496
+ a 404 (gateway predates the endpoint) falls back to the list query so a
497
+ mixed fleet never goes blind from the client side alone.
486
498
  """
487
499
  if not _gateway_configured(gateway):
488
500
  return None
489
- query = urllib.parse.urlencode({"to_node": peer, "unread_only": "true", "limit": 1})
490
- url = f"{gateway.rstrip('/')}/messages?{query}"
491
- req = urllib.request.Request(url)
492
- if token:
493
- req.add_header("Authorization", f"Bearer {token}")
494
- else:
501
+ token = _resolve_unread_token(token)
502
+ if not token:
495
503
  # A missing token and a network outage both surface as None to the
496
504
  # caller (correctly — the decision matrix treats them the same way),
497
505
  # but they are different FACTS an operator would want to know apart.
@@ -503,10 +511,50 @@ def _gateway_unread_count(gateway: str, peer: str, token: Optional[str]) -> Opti
503
511
  f"unread-count check may 401 if the gateway requires auth; "
504
512
  f"A1 stays inert until one is set",
505
513
  file=sys.stderr)
514
+ count = _gateway_unread_via_count(gateway, peer, token)
515
+ if count is not None:
516
+ return count
517
+ return _gateway_unread_via_list(gateway, peer, token)
518
+
519
+
520
+ def _gateway_request(gateway: str, path: str, query: dict,
521
+ token: Optional[str]):
522
+ """GET path+query with an optional Bearer token. Returns the decoded
523
+ JSON body, or (None, status) on transport failure — the status lets
524
+ callers tell a missing route (404: fall back) from a dead end."""
525
+ url = f"{gateway.rstrip('/')}{path}?{urllib.parse.urlencode(query)}"
526
+ req = urllib.request.Request(url)
527
+ if token:
528
+ req.add_header("Authorization", f"Bearer {token}")
506
529
  try:
507
530
  with urllib.request.urlopen(req, timeout=5) as resp:
508
- data = json.loads(resp.read().decode("utf-8"))
509
- except (urllib.error.URLError, urllib.error.HTTPError, OSError, json.JSONDecodeError):
531
+ return json.loads(resp.read().decode("utf-8")), resp.status
532
+ except urllib.error.HTTPError as exc:
533
+ return None, exc.code
534
+ except (urllib.error.URLError, OSError, json.JSONDecodeError):
535
+ return None, None
536
+
537
+
538
+ def _gateway_unread_via_count(gateway: str, peer: str,
539
+ token: Optional[str]) -> Optional[int]:
540
+ """Count-only read: {"to_node", "n"}. No bodies cross the wire, so a
541
+ least-privilege watchdog identity suffices (card #1059)."""
542
+ data, _status = _gateway_request(
543
+ gateway, "/messages/unread-count", {"to_node": peer}, token)
544
+ if isinstance(data, dict) and isinstance(data.get("n"), int):
545
+ return data["n"]
546
+ return None
547
+
548
+
549
+ def _gateway_unread_via_list(gateway: str, peer: str,
550
+ token: Optional[str]) -> Optional[int]:
551
+ """Pre-count-endpoint fallback: parse the list query. Kept so a mixed
552
+ fleet (new client, old gateway) degrades to body reads, never to
553
+ blindness — the 404 is the only trigger, never a 403/401."""
554
+ data, status = _gateway_request(
555
+ gateway, "/messages",
556
+ {"to_node": peer, "unread_only": "true", "limit": 1}, token)
557
+ if status == 404:
510
558
  return None
511
559
  # Gateway shape varies — handle both list and {"messages": [...]} forms
512
560
  if isinstance(data, list):
@@ -1874,6 +1922,37 @@ def _execstart_flags(args: argparse.Namespace) -> str:
1874
1922
  return (" " + " ".join(parts)) if parts else ""
1875
1923
 
1876
1924
 
1925
+ def _watchdog_service_token_path() -> Path:
1926
+ """Where the watchdog identity's token lives. Overridable for tests
1927
+ and unusual homes; never a unit file, never argv (card #1059)."""
1928
+ return Path(os.environ.get(
1929
+ "SWARPH_WATCHDOG_TOKEN_FILE",
1930
+ "~/.config/swarph/watchdog-service.token")).expanduser()
1931
+
1932
+
1933
+ def _read_service_token() -> Optional[str]:
1934
+ """The watchdog identity's token from its file, or None (absent /
1935
+ unreadable / blank). The VALUE never hits logs — callers log only
1936
+ whether one was found."""
1937
+ try:
1938
+ tok = _watchdog_service_token_path().read_text(
1939
+ encoding="utf-8").strip()
1940
+ except OSError:
1941
+ return None
1942
+ return tok or None
1943
+
1944
+
1945
+ def _write_service_token_file(path: Path, token: str) -> None:
1946
+ """Write the token file 0600 (parents created). Refuses multiline
1947
+ values — an env file line cannot hold them, and silently writing
1948
+ half a credential is worse than failing."""
1949
+ if "\n" in token:
1950
+ raise ValueError("token contains a newline")
1951
+ path.parent.mkdir(parents=True, exist_ok=True)
1952
+ path.write_text(token.strip() + "\n", encoding="utf-8")
1953
+ os.chmod(path, 0o600)
1954
+
1955
+
1877
1956
  def run_install_service(args: argparse.Namespace) -> int:
1878
1957
  """Install systemd timer + service for periodic watchdog --check.
1879
1958
 
@@ -1950,6 +2029,25 @@ def run_install_service(args: argparse.Namespace) -> int:
1950
2029
  1,
1951
2030
  )
1952
2031
 
2032
+ # Card #1059/ruling_1332: template units run without the installer's
2033
+ # env, so the unread check reads None and A1 stays inert (exit 7,
2034
+ # opencode + cursor-lin measured live). The watchdog identity's
2035
+ # token lives in a 0600 token FILE (path via SWARPH_WATCHDOG_TOKEN_FILE,
2036
+ # default ~/.config/swarph/watchdog-service.token) — never in a unit,
2037
+ # never in argv, never in the EnvironmentFile default. The installer
2038
+ # writes it from its own env; rotation is re-running the install.
2039
+ install_token = os.environ.get("MESH_GATEWAY_TOKEN")
2040
+ token_path = _watchdog_service_token_path()
2041
+ if install_token is not None and "\n" in install_token:
2042
+ print("ERROR: MESH_GATEWAY_TOKEN contains a newline — refusing "
2043
+ "to write the token file.", file=sys.stderr)
2044
+ return 4
2045
+ if not install_token:
2046
+ print("WARNING: MESH_GATEWAY_TOKEN not in installer env — "
2047
+ "template runs will exit 7 (noop_unread_unknown) until the "
2048
+ f"token file exists at {token_path}; re-run --install-service "
2049
+ "with it set.", file=sys.stderr)
2050
+
1953
2051
  targets = [
1954
2052
  (_SYSTEMD_UNIT_DIR / service_name, service_content),
1955
2053
  (_SYSTEMD_UNIT_DIR / timer_name, timer_content),
@@ -1970,6 +2068,9 @@ def run_install_service(args: argparse.Namespace) -> int:
1970
2068
  for path, content in targets:
1971
2069
  print(f"\n# would write {path}:", file=sys.stderr)
1972
2070
  print(content, file=sys.stderr)
2071
+ if install_token:
2072
+ print(f"\n# would write {token_path} (0600, value not shown):",
2073
+ file=sys.stderr)
1973
2074
  if legacy_note:
1974
2075
  print(f"\n{legacy_note}", file=sys.stderr)
1975
2076
  print(
@@ -1992,7 +2093,10 @@ def run_install_service(args: argparse.Namespace) -> int:
1992
2093
  for path, content in targets:
1993
2094
  path.write_text(content, encoding="utf-8")
1994
2095
  print(f"wrote {path}", file=sys.stderr)
1995
- except (OSError, PermissionError) as exc:
2096
+ if install_token:
2097
+ _write_service_token_file(token_path, install_token)
2098
+ print(f"wrote {token_path} (0600)", file=sys.stderr)
2099
+ except (OSError, PermissionError, ValueError) as exc:
1996
2100
  print(f"ERROR: failed to write unit files: {exc}", file=sys.stderr)
1997
2101
  return 5
1998
2102
 
@@ -543,6 +543,7 @@ answers `--help`; hook-plumbing verbs are the callbacks hooks invoke, not comman
543
543
  - `swarph protocol-handler` -- register `swarph://` as an OS URL-scheme handler.
544
544
  - `swarph ratify` -- witness flip that admits an onboarded peer; takes the peer name and `--reason`.
545
545
  - `swarph rights` -- RBAC rights over the gateway (companion of `swarph group`).
546
+ - `swarph roster` -- the gateway capacity table (card #1061): who builds, who reviews, quota, who is out until when. `swarph roster --peer <peer>`, `swarph roster set <peer> --roles build` (operators only).
546
547
  - `swarph rowclear-hook` -- SessionStart hook (card #432): when `source` is `clear`, write the new session id into the role's `<role>.session-id` pin so the next spawn resumes the cleared session.
547
548
  - `swarph scan` -- statically scan an artifact for dangerous patterns before publish.
548
549
  - `swarph schedule` -- the gateway's scheduled events: create, list, get, enable, disable, delete, fire-now.
@@ -110,6 +110,7 @@ _VERB_HANDLERS: dict[str, str] = {
110
110
  "board": "swarph_cli.commands.board.run_board",
111
111
  "group": "swarph_cli.commands.group.run_group",
112
112
  "rights": "swarph_cli.commands.group.run_rights",
113
+ "roster": "swarph_cli.commands.roster.run_roster",
113
114
  "mcp-server": "swarph_cli.commands.mcp_server.run_mcp_server",
114
115
  "compress": "swarph_cli.commands.compress.run_compress",
115
116
  "cell": "swarph_cli.commands.cell.run_cell",
@@ -6,6 +6,10 @@ when the cell holds no live row, the pane reads idle, and no DM is owed.
6
6
  type nothing), or ``live``. v1 opt-in is drop-on-meta-edge only: a cell
7
7
  that owns a project keeps its context (design_1007 / optin_owners).
8
8
 
9
+ v1.1 (#1362): a taken row whose step needs are UNMET is waiting, not
10
+ working — it does not count as live. Needs state comes from the card's
11
+ step graph; anything unreadable about it fails closed (no clear).
12
+
9
13
  A ``/clear`` mints a new Claude session id. The SessionStart hook with
10
14
  ``source=clear`` writes that id into the role's ``<role>.session-id``
11
15
  pin, so the next spawn resumes the cleared session instead of the long
@@ -95,11 +99,62 @@ def _dm_owed(state) -> bool:
95
99
  return False
96
100
 
97
101
 
102
+ def _step_needs_met(state, row: dict, http_get: Callable,
103
+ graphs: dict) -> Optional[bool]:
104
+ """Is this open row's work actionable (True), waiting (False), or
105
+ unevaluable (None)?
106
+
107
+ A row with no step has no needs to unmeet (ruling_1236: still live).
108
+ Otherwise the card's step graph (GET /board/cards/{id}/graph) names the
109
+ step's needs with a satisfied flag — every need satisfied means the
110
+ holder can act; one unsatisfied need means the cell is between rows.
111
+ Anything unreadable — no card id, a failed graph fetch, a missing step
112
+ entry, a needs entry without a boolean satisfied flag — is None: an
113
+ unevaluable row must not look like an empty cell (fail closed, #1362).
114
+ One graph fetch per card per poll; the same http_get the board read uses.
115
+ """
116
+ step = row.get("step")
117
+ if not step:
118
+ return True
119
+ card_id = row.get("card_id")
120
+ if not isinstance(card_id, int) or isinstance(card_id, bool):
121
+ return None
122
+ if card_id not in graphs:
123
+ url = f"{state.gateway}/board/cards/{card_id}/graph"
124
+ try:
125
+ status, body = http_get(url, state.token)
126
+ except Exception:
127
+ return None
128
+ if status != 200 or not isinstance(body, dict):
129
+ return None
130
+ steps = body.get("steps")
131
+ if not isinstance(steps, list):
132
+ return None
133
+ graphs[card_id] = steps
134
+ for entry in graphs[card_id]:
135
+ if isinstance(entry, dict) and entry.get("step") == step:
136
+ needs = entry.get("needs")
137
+ if not isinstance(needs, list):
138
+ return None
139
+ for need in needs:
140
+ if not isinstance(need, dict) or not isinstance(
141
+ need.get("satisfied"), bool):
142
+ return None
143
+ if not need["satisfied"]:
144
+ return False
145
+ return True
146
+ return None
147
+
148
+
98
149
  def _live_row_count(state, http_get: Callable) -> Optional[int]:
99
- """How many unfinished rows this cell holds, or None if unread.
150
+ """How many ACTIONABLE rows this cell holds, or None if unread.
100
151
 
101
152
  None fails closed: an unreadable board must not look like an empty
102
153
  one, or a live row we failed to see gets a ``/clear``.
154
+
155
+ v1.1 (#1362): an open row whose step needs are unmet is waiting, not
156
+ working — it does not count. An open row with no step, or whose step
157
+ needs nothing, is still live (ruling_1236 unchanged).
103
158
  """
104
159
  url = f"{state.gateway}/board/obligations?holder={state.self_name}"
105
160
  try:
@@ -111,15 +166,21 @@ def _live_row_count(state, http_get: Callable) -> Optional[int]:
111
166
  rows = body.get("obligations")
112
167
  if not isinstance(rows, list):
113
168
  return None
169
+ graphs: dict = {}
114
170
  live = 0
115
171
  for row in rows:
116
172
  if not isinstance(row, dict):
117
173
  return None
118
174
  status = str(row.get("status") or "")
119
- if status == _LIVE:
120
- live += 1
121
- elif status not in _FINISHED:
175
+ if status in _FINISHED:
176
+ continue
177
+ if status != _LIVE:
122
178
  return None
179
+ met = _step_needs_met(state, row, http_get, graphs)
180
+ if met is None:
181
+ return None
182
+ if met:
183
+ live += 1
123
184
  return live
124
185
 
125
186
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.72.7
3
+ Version: 0.72.9
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
@@ -110,6 +110,7 @@ src/swarph_cli/commands/peer_reply_drain.py
110
110
  src/swarph_cli/commands/postcompact_hook_output.py
111
111
  src/swarph_cli/commands/protocol_handler.py
112
112
  src/swarph_cli/commands/ratify.py
113
+ src/swarph_cli/commands/roster.py
113
114
  src/swarph_cli/commands/rowclear_hook.py
114
115
  src/swarph_cli/commands/schedule.py
115
116
  src/swarph_cli/commands/security.py
@@ -195,6 +196,8 @@ tests/test_1013_spawn_channel.py
195
196
  tests/test_1017_muse_tui.py
196
197
  tests/test_1050_surrogate_wake.py
197
198
  tests/test_1052_opencode_deaf.py
199
+ tests/test_1059_watchdog_unread_env.py
200
+ tests/test_1061_roster_cli.py
198
201
  tests/test_124_register_merge.py
199
202
  tests/test_126_pending_from_complete.py
200
203
  tests/test_132_ensure_monitor_state_dir.py