swarph-cli 0.72.28__tar.gz → 0.72.30__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 (536) hide show
  1. {swarph_cli-0.72.28/src/swarph_cli.egg-info → swarph_cli-0.72.30}/PKG-INFO +2 -2
  2. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/README.md +1 -1
  3. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/pyproject.toml +4 -1
  4. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/__init__.py +1 -1
  5. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/claude_cli.py +5 -2
  6. swarph_cli-0.72.30/src/swarph_cli/bench/prices.py +284 -0
  7. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/refresh_prices.py +40 -7
  8. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/board.py +84 -12
  9. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/hook_output.py +45 -37
  10. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/lane.py +16 -1
  11. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/mesh.py +83 -3
  12. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/roster.py +11 -5
  13. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/schedule.py +12 -7
  14. swarph_cli-0.72.30/src/swarph_cli/duplicate_session.py +284 -0
  15. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/scripts/wake_watchdog.py +251 -26
  16. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/stall_alert.py +35 -63
  17. swarph_cli-0.72.30/src/swarph_cli/the_ten.py +223 -0
  18. {swarph_cli-0.72.28 → swarph_cli-0.72.30/src/swarph_cli.egg-info}/PKG-INFO +2 -2
  19. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli.egg-info/SOURCES.txt +12 -0
  20. swarph_cli-0.72.30/tests/test_1034_copy_mode.py +61 -0
  21. swarph_cli-0.72.30/tests/test_1222_maybe_start_cap.py +126 -0
  22. swarph_cli-0.72.30/tests/test_1228_quota_reach.py +53 -0
  23. swarph_cli-0.72.30/tests/test_1243_the_ten.py +274 -0
  24. swarph_cli-0.72.30/tests/test_1248_ship_price_table.py +157 -0
  25. swarph_cli-0.72.30/tests/test_1250_price_cache.py +274 -0
  26. swarph_cli-0.72.30/tests/test_1256_commander_wake.py +172 -0
  27. swarph_cli-0.72.30/tests/test_513_duplicate_session.py +215 -0
  28. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_729_wake_watchdog.py +45 -17
  29. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_739_wake_writer.py +27 -1
  30. swarph_cli-0.72.30/tests/test_931_board_search.py +88 -0
  31. swarph_cli-0.72.30/tests/test_935_deadline_clock.py +55 -0
  32. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_960_channel.py +24 -5
  33. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_bench_claude_cli.py +15 -4
  34. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_bench_command.py +4 -1
  35. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_bench_prices.py +6 -3
  36. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_copilot_pane_reader.py +5 -1
  37. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_hook_output.py +3 -0
  38. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_lane_command.py +80 -3
  39. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_schedule_command.py +35 -12
  40. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_stall_alert.py +101 -62
  41. swarph_cli-0.72.28/src/swarph_cli/bench/prices.py +0 -103
  42. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/LICENSE +0 -0
  43. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/setup.cfg +0 -0
  44. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/__main__.py +0 -0
  45. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/__init__.py +0 -0
  46. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/backends.py +0 -0
  47. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/codex_cli.py +0 -0
  48. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/pack.py +0 -0
  49. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/providers.py +0 -0
  50. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/quality.py +0 -0
  51. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/rules.py +0 -0
  52. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/runner.py +0 -0
  53. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/signal_score.py +0 -0
  54. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/validate.py +0 -0
  55. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/caller.py +0 -0
  56. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/capture/__init__.py +0 -0
  57. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/capture/harden.py +0 -0
  58. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/capture/lineage.py +0 -0
  59. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/capture/liveness.py +0 -0
  60. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/capture/manifest.py +0 -0
  61. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/capture/paths.py +0 -0
  62. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/capture/verify.py +0 -0
  63. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/cell.py +0 -0
  64. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/chain_token.py +0 -0
  65. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/channel.py +0 -0
  66. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/__init__.py +0 -0
  67. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/_content.py +0 -0
  68. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/_display.py +0 -0
  69. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/_gateway_client.py +0 -0
  70. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/add.py +0 -0
  71. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/bench.py +0 -0
  72. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/board_merge_check.py +0 -0
  73. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/brain.py +0 -0
  74. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/brain_ask.py +0 -0
  75. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/cell.py +0 -0
  76. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/cell_probe.py +0 -0
  77. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  78. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/channel.py +0 -0
  79. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/chat.py +0 -0
  80. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/codegraph.py +0 -0
  81. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/codegraph_hook.py +0 -0
  82. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/codex_hook_output.py +0 -0
  83. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/codex_waker.py +0 -0
  84. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/compress.py +0 -0
  85. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/daemon.py +0 -0
  86. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/dreaming.py +0 -0
  87. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/event.py +0 -0
  88. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/gateway.py +0 -0
  89. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/gh_route.py +0 -0
  90. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/group.py +0 -0
  91. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/guide.py +0 -0
  92. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/highlight.py +0 -0
  93. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/hook_interpreter.py +0 -0
  94. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/hooks.py +0 -0
  95. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/import_session.py +0 -0
  96. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/init.py +0 -0
  97. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
  98. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/install_hook.py +0 -0
  99. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  100. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
  101. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
  102. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/install_wake_hook.py +0 -0
  103. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/mcp_server.py +0 -0
  104. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/memory.py +0 -0
  105. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
  106. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/memory_sync.py +0 -0
  107. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/monitor.py +0 -0
  108. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/okf_links.py +0 -0
  109. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/onboard.py +0 -0
  110. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
  111. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
  112. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/protocol_handler.py +0 -0
  113. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/ratify.py +0 -0
  114. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/rowclear_hook.py +0 -0
  115. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/security.py +0 -0
  116. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/service.py +0 -0
  117. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/spawn.py +0 -0
  118. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/sweep.py +0 -0
  119. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/timeline.py +0 -0
  120. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/triage.py +0 -0
  121. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/version.py +0 -0
  122. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/wake_hook_output.py +0 -0
  123. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/watchdog.py +0 -0
  124. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/compress/__init__.py +0 -0
  125. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/compress/levers.py +0 -0
  126. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/compress/marker.py +0 -0
  127. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/compress/verify.py +0 -0
  128. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/console_safe.py +0 -0
  129. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/delivery_queue.py +0 -0
  130. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dm_frame.py +0 -0
  131. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/__init__.py +0 -0
  132. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/candidates.py +0 -0
  133. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/clone.py +0 -0
  134. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/enrich.py +0 -0
  135. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/index_check.py +0 -0
  136. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/notify.py +0 -0
  137. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/organize.py +0 -0
  138. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/probes.py +0 -0
  139. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/report.py +0 -0
  140. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/run.py +0 -0
  141. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/slm.py +0 -0
  142. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/transcripts.py +0 -0
  143. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/verify.py +0 -0
  144. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/gateway/__init__.py +0 -0
  145. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/gateway/feature_registry.py +0 -0
  146. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/gateway/lanes_control.py +0 -0
  147. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/gateway/schema.sql +0 -0
  148. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/gateway/server.py +0 -0
  149. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/gateway/services_control.py +0 -0
  150. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/gateway_default.py +0 -0
  151. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/gh_identity.py +0 -0
  152. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/guide/GUIDE.md +0 -0
  153. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/identity.py +0 -0
  154. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/launchd/swarph-codex-outbox.plist +0 -0
  155. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/launchd/swarph-codex-outbox.timer.plist +0 -0
  156. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/launchd/swarph-monitor.plist +0 -0
  157. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/launchd/swarph-watchdog.plist +0 -0
  158. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/launchd/swarph-watchdog.timer.plist +0 -0
  159. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/launchd_render.py +0 -0
  160. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/main.py +0 -0
  161. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/multiplexer.py +0 -0
  162. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/operator/__init__.py +0 -0
  163. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/operator/__main__.py +0 -0
  164. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/operator/api.py +0 -0
  165. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/operator/cli.py +0 -0
  166. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/operator/config.py +0 -0
  167. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/operator/fmt.py +0 -0
  168. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/orphan_daemons.py +0 -0
  169. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/pane_probe.py +0 -0
  170. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/parsers/__init__.py +0 -0
  171. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/parsers/claude.py +0 -0
  172. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
  173. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
  174. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
  175. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
  176. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
  177. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
  178. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
  179. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
  180. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
  181. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
  182. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
  183. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
  184. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/peer_executor.py +0 -0
  185. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/peer_executor_windows.py +0 -0
  186. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/peer_reconciliation.py +0 -0
  187. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/peer_reply_outbox.py +0 -0
  188. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/peer_service_host.py +0 -0
  189. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/peer_service_payload.py +0 -0
  190. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/peer_service_reply.py +0 -0
  191. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/peer_staging.py +0 -0
  192. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/query_filters.py +0 -0
  193. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/quota_sync.py +0 -0
  194. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/rowclear.py +0 -0
  195. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/scripts/__init__.py +0 -0
  196. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
  197. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  198. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
  199. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
  200. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/scrub.py +0 -0
  201. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/service/__init__.py +0 -0
  202. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/service/app.py +0 -0
  203. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/service/providers.py +0 -0
  204. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/session_bridge.py +0 -0
  205. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
  206. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
  207. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/source_text.py +0 -0
  208. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/codex-waker.default +0 -0
  209. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
  210. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
  211. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
  212. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
  213. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
  214. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
  215. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
  216. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
  217. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
  218. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
  219. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-stuck-sweep.service +0 -0
  220. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-stuck-sweep.timer +0 -0
  221. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-triage.service +0 -0
  222. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-triage.timer +0 -0
  223. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  224. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  225. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  226. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/timeline_paths.py +0 -0
  227. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/tokens.py +0 -0
  228. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/triage/__init__.py +0 -0
  229. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/triage/backend.py +0 -0
  230. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/triage/job.py +0 -0
  231. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/triage/owners.json +0 -0
  232. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/triage/router.py +0 -0
  233. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/triage/rows.py +0 -0
  234. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/triage/sensor.py +0 -0
  235. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  236. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  237. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli.egg-info/requires.txt +0 -0
  238. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli.egg-info/top_level.txt +0 -0
  239. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1006_board_publisher.py +0 -0
  240. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1006_session_board.py +0 -0
  241. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1010_strand_only.py +0 -0
  242. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1013_spawn_channel.py +0 -0
  243. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1017_muse_tui.py +0 -0
  244. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1050_surrogate_wake.py +0 -0
  245. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1052_opencode_deaf.py +0 -0
  246. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1059_watchdog_unread_env.py +0 -0
  247. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1061_roster_cli.py +0 -0
  248. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1064_stuck_sweep.py +0 -0
  249. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1065_release.py +0 -0
  250. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1066_quiet_wakes.py +0 -0
  251. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1068_structured_accept_cli.py +0 -0
  252. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1071_quota_sync.py +0 -0
  253. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1073_triage.py +0 -0
  254. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1073_triage_board.py +0 -0
  255. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1073_wheel_install.py +0 -0
  256. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1075_task_cli.py +0 -0
  257. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1083_openai_decisions.py +0 -0
  258. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1144_cell_yaml_github.py +0 -0
  259. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1197_commander_taps.py +0 -0
  260. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1203_swarph_me_refusal.py +0 -0
  261. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1206_offer_question.py +0 -0
  262. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1214_egress.py +0 -0
  263. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1219_egress_probe.py +0 -0
  264. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1221_quota_windows.py +0 -0
  265. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_124_register_merge.py +0 -0
  266. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_126_pending_from_complete.py +0 -0
  267. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_132_ensure_monitor_state_dir.py +0 -0
  268. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_138_memory_navigate.py +0 -0
  269. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_139_tool_probe.py +0 -0
  270. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_183_card_anchor.py +0 -0
  271. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_184_notify_honesty.py +0 -0
  272. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_201_win32_bash_refusal.py +0 -0
  273. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_243_onboard_per_peer_token.py +0 -0
  274. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_307_task4_ask_reply.py +0 -0
  275. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
  276. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_319_refusal_exit_codes.py +0 -0
  277. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_323_mistral_backend.py +0 -0
  278. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_332_report_organize_findings.py +0 -0
  279. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_356_query_filters.py +0 -0
  280. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_397_gh_identity_router.py +0 -0
  281. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_397_router_preflight.py +0 -0
  282. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_402_one_identity_resolver.py +0 -0
  283. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_432_rowclear.py +0 -0
  284. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_467b_onboard_operator_credential.py +0 -0
  285. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_472_register_caller_binding.py +0 -0
  286. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_484_codex_queue_sink.py +0 -0
  287. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_523_guide.py +0 -0
  288. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_524_conftest_hermeticity.py +0 -0
  289. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_532_accept_flag.py +0 -0
  290. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_535_version_capability.py +0 -0
  291. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_538_signal_bench.py +0 -0
  292. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_546_default_gateway.py +0 -0
  293. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_547_verbs_from_registry.py +0 -0
  294. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_548_gateway_remainder.py +0 -0
  295. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_552_codegraph_gateway.py +0 -0
  296. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_555_relay_row_shape.py +0 -0
  297. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_556_relay_freshness.py +0 -0
  298. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_559_edges_truncated.py +0 -0
  299. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_564_bootstrap_first_peer.py +0 -0
  300. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
  301. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_579_register_bootstrap_message.py +0 -0
  302. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_591_cli_step_graph.py +0 -0
  303. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_644_monitor_supervision.py +0 -0
  304. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_648_install_unit.py +0 -0
  305. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_654_reexec.py +0 -0
  306. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_656_owned_slm.py +0 -0
  307. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_684_dreaming_verb.py +0 -0
  308. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_689_pkg_version_order.py +0 -0
  309. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_715_grok_wake.py +0 -0
  310. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_716_timeline_one_path.py +0 -0
  311. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_723_grok_0251.py +0 -0
  312. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_724_ps1_ascii_or_bom.py +0 -0
  313. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_725_stdio_reconfigure.py +0 -0
  314. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_726_grok_limit_screen.py +0 -0
  315. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_729_arm_check_delivery_gap.py +0 -0
  316. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_729_dm_once.py +0 -0
  317. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_734_dreaming_enrich_degrade.py +0 -0
  318. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_735_dm_mcp_tools.py +0 -0
  319. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_740_edit_project.py +0 -0
  320. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_762_cards_edit_priority.py +0 -0
  321. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_764_enrich_collapse.py +0 -0
  322. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_801_confirm_flow_cli.py +0 -0
  323. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_802_stage_history.py +0 -0
  324. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_825_codegraph_hook_noise.py +0 -0
  325. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_825_install_posttooluse_only.py +0 -0
  326. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_825_remaining_cap_shred_bind.py +0 -0
  327. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_829_counterfactual_column.py +0 -0
  328. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_830_hook_binding_version.py +0 -0
  329. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_84_cli_labels.py +0 -0
  330. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_866_doctrine_guide.py +0 -0
  331. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_872_identity_refuses_not_warns.py +0 -0
  332. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_874_source_text.py +0 -0
  333. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
  334. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_937_dreaming_notify.py +0 -0
  335. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_938_organize_line_limit.py +0 -0
  336. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_941_bench_backends.py +0 -0
  337. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_941_harness_342.py +0 -0
  338. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_945_same_line_pairing.py +0 -0
  339. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_955_prompt_audit.py +0 -0
  340. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_957_auth_http.py +0 -0
  341. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_959_venv_interpreter.py +0 -0
  342. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
  343. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_961_cursor_grok_footer.py +0 -0
  344. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_961_loud_stop.py +0 -0
  345. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_963_plugin_manifest.py +0 -0
  346. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_964_channel_cell.py +0 -0
  347. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_965_mesh_wait.py +0 -0
  348. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_975_channel_hop.py +0 -0
  349. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_add_security_gate.py +0 -0
  350. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_artifact_add.py +0 -0
  351. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_artifact_hash.py +0 -0
  352. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_artifact_lib.py +0 -0
  353. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_artifact_mcp.py +0 -0
  354. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_artifact_skill.py +0 -0
  355. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_artifact_tool.py +0 -0
  356. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_artifact_uri.py +0 -0
  357. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_bench_backends.py +0 -0
  358. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_bench_codex_cli.py +0 -0
  359. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_bench_pack.py +0 -0
  360. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_bench_quality.py +0 -0
  361. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_bench_runner.py +0 -0
  362. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_bench_validate.py +0 -0
  363. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_board_card_thread.py +0 -0
  364. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_board_command.py +0 -0
  365. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_board_merge_check.py +0 -0
  366. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_board_obligation_close.py +0 -0
  367. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_brain_ask_command.py +0 -0
  368. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_brain_ask_env_diagnosis.py +0 -0
  369. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_brain_ask_identity_explicitness.py +0 -0
  370. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_brain_command.py +0 -0
  371. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_caller_meta_guard.py +0 -0
  372. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_capture_lineage.py +0 -0
  373. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_capture_liveness.py +0 -0
  374. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_capture_manifest.py +0 -0
  375. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_capture_paths.py +0 -0
  376. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_capture_security.py +0 -0
  377. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_cell_command_dispatch.py +0 -0
  378. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_cell_harden.py +0 -0
  379. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_cell_loader.py +0 -0
  380. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_cell_selfcheck.py +0 -0
  381. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_cell_verify.py +0 -0
  382. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_chain_token.py +0 -0
  383. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_channel_command.py +0 -0
  384. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_channel_poll_never_breaks_dms.py +0 -0
  385. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_channel_post.py +0 -0
  386. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_channel_wake_policy_filter.py +0 -0
  387. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_chat_command.py +0 -0
  388. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_claude_parser.py +0 -0
  389. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_claude_tmux_template.py +0 -0
  390. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_codegraph_caller_scope.py +0 -0
  391. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_codegraph_command.py +0 -0
  392. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_codegraph_hook.py +0 -0
  393. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_codegraph_mcp.py +0 -0
  394. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_codex_waker.py +0 -0
  395. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_compress_caller_convention.py +0 -0
  396. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_compress_command.py +0 -0
  397. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_compress_levers.py +0 -0
  398. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_compress_marker.py +0 -0
  399. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_compress_verify.py +0 -0
  400. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_console_encoding_e2e.py +0 -0
  401. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_content_composition.py +0 -0
  402. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_copilot_membrane.py +0 -0
  403. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_cursor_membrane.py +0 -0
  404. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_daemon_command.py +0 -0
  405. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_daemon_console_encoding.py +0 -0
  406. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_daemon_delivery.py +0 -0
  407. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_delivery_queue.py +0 -0
  408. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_display_sanitize.py +0 -0
  409. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_ensure_monitor_no_peer_default.py +0 -0
  410. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_event_emit.py +0 -0
  411. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_feature_to_uri.py +0 -0
  412. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_gateway_brain.py +0 -0
  413. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_gateway_client.py +0 -0
  414. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_gateway_command.py +0 -0
  415. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_gateway_message_idempotency.py +0 -0
  416. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_gh_route_doctor_mesh_name.py +0 -0
  417. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_group_command.py +0 -0
  418. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_guide_commands_parse.py +0 -0
  419. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_guide_selfcheck.py +0 -0
  420. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_heartbeat_check.py +0 -0
  421. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_highlight_command.py +0 -0
  422. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_hooks_activity_marker.py +0 -0
  423. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_hooks_add.py +0 -0
  424. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_hooks_bundle.py +0 -0
  425. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_hooks_init.py +0 -0
  426. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_hooks_legacy_generation_removal.py +0 -0
  427. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_hooks_lifecycle.py +0 -0
  428. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_hooks_merge.py +0 -0
  429. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_hooks_verify.py +0 -0
  430. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_hooks_win32_bash_wrapper.py +0 -0
  431. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_hooks_win32_command_path.py +0 -0
  432. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_import_command.py +0 -0
  433. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_inbox_identity_provenance.py +0 -0
  434. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_init_command.py +0 -0
  435. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_init_provider_menu.py +0 -0
  436. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_install_codex_hooks.py +0 -0
  437. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_install_hook.py +0 -0
  438. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_install_multiplexer_command.py +0 -0
  439. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_install_postcompact_hook.py +0 -0
  440. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_install_wake_hook.py +0 -0
  441. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_launchd_units.py +0 -0
  442. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_main.py +0 -0
  443. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_main_unknown_verb_guard.py +0 -0
  444. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_mcp_missing_hint.py +0 -0
  445. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_mcp_server.py +0 -0
  446. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_memory_command.py +0 -0
  447. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_memory_emit_snippet.py +0 -0
  448. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_memory_gateway.py +0 -0
  449. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_memory_sync.py +0 -0
  450. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_memory_sync_membrane.py +0 -0
  451. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_mesh_command.py +0 -0
  452. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_mesh_inbox_mark_read.py +0 -0
  453. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_mesh_inbox_sanitize.py +0 -0
  454. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_mesh_originate_thread_623.py +0 -0
  455. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_mesh_send_unknown_peer.py +0 -0
  456. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_mesh_sidecar.py +0 -0
  457. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  458. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_mesh_sidecar_muse_sink.py +0 -0
  459. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  460. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_meta_edge_identity.py +0 -0
  461. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_monitor_channel_poll.py +0 -0
  462. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_monitor_cli.py +0 -0
  463. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_monitor_daemon_collision.py +0 -0
  464. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_monitor_self_name_verification.py +0 -0
  465. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_monitor_state_split.py +0 -0
  466. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_multiplexer.py +0 -0
  467. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_muse_membrane.py +0 -0
  468. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_muse_own_cli.py +0 -0
  469. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_obligation_close_is_surfaced.py +0 -0
  470. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_okf_links.py +0 -0
  471. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_onboard_command.py +0 -0
  472. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_opencode_membrane.py +0 -0
  473. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_opencode_plugin.py +0 -0
  474. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_operator_me.py +0 -0
  475. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_orphan_daemons.py +0 -0
  476. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_packaged_artifacts_exist.py +0 -0
  477. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_packaged_skills.py +0 -0
  478. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_peer_executor.py +0 -0
  479. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_peer_executor_windows.py +0 -0
  480. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_peer_reply_drain_command.py +0 -0
  481. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_peer_reply_drain_supervisor.py +0 -0
  482. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_peer_reply_outbox.py +0 -0
  483. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_peer_reply_transport.py +0 -0
  484. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_peer_service_e2e.py +0 -0
  485. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_peer_service_host.py +0 -0
  486. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_peer_service_payload.py +0 -0
  487. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_peer_service_reply.py +0 -0
  488. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_postcompact_recall.py +0 -0
  489. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_process_liveness.py +0 -0
  490. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_protocol_handler.py +0 -0
  491. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_providers_isolation.py +0 -0
  492. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_ratify_command.py +0 -0
  493. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_readme_claims_no_gateway_default.py +0 -0
  494. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_repl_caller_convention.py +0 -0
  495. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_schedule_create_contract.py +0 -0
  496. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_scrub.py +0 -0
  497. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_security.py +0 -0
  498. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_service_app.py +0 -0
  499. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_service_command.py +0 -0
  500. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_service_providers.py +0 -0
  501. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_session_bridge_inject.py +0 -0
  502. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_session_bridge_probe.py +0 -0
  503. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_session_bridge_resolve.py +0 -0
  504. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_session_path_role_gate.py +0 -0
  505. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_shipped_units_match_the_cli.py +0 -0
  506. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_smoke_chat.py +0 -0
  507. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_smoke_one_shot.py +0 -0
  508. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_smoke_phase_5_5.py +0 -0
  509. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_spawn_base_hoist.py +0 -0
  510. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_spawn_command.py +0 -0
  511. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_spawn_identity_stamp.py +0 -0
  512. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_spawn_live_pin.py +0 -0
  513. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_spawn_mitosis_lineage.py +0 -0
  514. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_spawn_tmux_session.py +0 -0
  515. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_spawn_windows_relaunch.py +0 -0
  516. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_subprocess_decode_encoding.py +0 -0
  517. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_timeline_command.py +0 -0
  518. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_tmux_wake_runstate_defer.py +0 -0
  519. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_tmux_wake_session_reanchor.py +0 -0
  520. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_tmux_wake_stale.py +0 -0
  521. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_tmux_wake_submit_verify.py +0 -0
  522. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_token_file_one_parser.py +0 -0
  523. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_token_precedence_explicit_first.py +0 -0
  524. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_unified_token_resolver.py +0 -0
  525. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_version_command.py +0 -0
  526. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_version_consistency.py +0 -0
  527. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_vibe_membrane.py +0 -0
  528. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_watchdog.py +0 -0
  529. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_watchdog_dm_wake.py +0 -0
  530. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  531. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  532. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_watchdog_emit_health.py +0 -0
  533. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_watchdog_install_flags.py +0 -0
  534. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_watchdog_model_rung.py +0 -0
  535. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_watchdog_stale_peers.py +0 -0
  536. {swarph_cli-0.72.28 → swarph_cli-0.72.30}/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.28
3
+ Version: 0.72.30
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
@@ -179,7 +179,7 @@ The mesh **board** — the projects + cards kanban the mesh coordinates on — a
179
179
  ```
180
180
  swarph board projects list
181
181
  swarph board projects add <slug> --title "…" [--goal "…"]
182
- swarph board cards list [--project <id|slug>] [--stage <s>] [--assignee <who>]
182
+ swarph board cards list [--project <id|slug>] [--stage <s>] [--assignee <who>] [--search <text>] # --search: title OR body, says how many cards it searched
183
183
  swarph board cards show <id>
184
184
  swarph board cards add --project <id|slug> --title "…" [--body "…"] [--ai2] [--priority N]
185
185
  swarph board cards edit <id> [--title "…"] [--body "…"] [--project <id|slug>] [--priority N]
@@ -130,7 +130,7 @@ The mesh **board** — the projects + cards kanban the mesh coordinates on — a
130
130
  ```
131
131
  swarph board projects list
132
132
  swarph board projects add <slug> --title "…" [--goal "…"]
133
- swarph board cards list [--project <id|slug>] [--stage <s>] [--assignee <who>]
133
+ swarph board cards list [--project <id|slug>] [--stage <s>] [--assignee <who>] [--search <text>] # --search: title OR body, says how many cards it searched
134
134
  swarph board cards show <id>
135
135
  swarph board cards add --project <id|slug> --title "…" [--body "…"] [--ai2] [--priority N]
136
136
  swarph board cards edit <id> [--title "…"] [--body "…"] [--project <id|slug>] [--priority N]
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "swarph-cli"
7
- version = "0.72.28"
7
+ version = "0.72.30"
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" }
@@ -173,3 +173,6 @@ testpaths = ["tests"]
173
173
  # nearly identical. That agreement is a polluted baseline, not isolation.
174
174
  pythonpath = ["src"]
175
175
  addopts = "-v --tb=short"
176
+ markers = [
177
+ "live_tmux: drives a real tmux server/pane; opt-in, and the test must state its reason (card #1034)",
178
+ ]
@@ -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.28"
19
+ __version__ = "0.72.30"
20
20
 
21
21
  __all__ = ["__version__"]
@@ -23,9 +23,12 @@ class CliResult:
23
23
 
24
24
 
25
25
  def build_argv(model_id: str, system: str) -> list:
26
+ # Card #1090: one token each. --mcp-config is variadic, so a following
27
+ # JSON token is consumed only while the token after that is a flag.
26
28
  return ["claude", "-p", "--model", model_id, "--system-prompt", system, "--max-turns", "1",
27
- "--output-format", "stream-json", "--verbose", "--strict-mcp-config",
28
- "--mcp-config", '{"mcpServers":{}}', "--tools", "", "--setting-sources", ""]
29
+ "--output-format", "stream-json", "--verbose",
30
+ "--tools=", "--strict-mcp-config",
31
+ '--mcp-config={"mcpServers":{}}', "--setting-sources="]
29
32
 
30
33
 
31
34
  def clean_env(env: dict) -> dict:
@@ -0,0 +1,284 @@
1
+ """The auto-refreshed LLM LIST-price table (spec §4) — LIFTED from the
2
+ reference lab's already-tested ``workers/llm_prices.py`` (adapted imports/
3
+ cache path only; lookup semantics, fallback table, and substring-order bug
4
+ guard are unchanged). Do NOT reinvent this — see
5
+ ``docs/2026-07-24-model-showdown-findings.md`` shipped-alongside note: it
6
+ already caught a real ~5x under-count on gemini-2.5-flash-lite.
7
+
8
+ The LIST-price BASE is auto-refreshed from the community-maintained LiteLLM
9
+ repo (:mod:`swarph_cli.bench.refresh_prices` -> a local JSON cache; TODO wire
10
+ a weekly cron the way the reference lab does), so new models are never
11
+ silently mis-priced by a stale hardcoded bucket.
12
+
13
+ Public API:
14
+ lookup(model) -> (input_$/Mtok, output_$/Mtok) # never raises
15
+ cost_usd(model, tokens_in, tokens_thought, tokens_out) -> $
16
+ """
17
+ from __future__ import annotations
18
+
19
+ import datetime
20
+ import json
21
+ import logging
22
+ import os
23
+ import tempfile
24
+
25
+ log = logging.getLogger("swarph.bench.prices")
26
+
27
+ # card #1250. A cache older than this is refreshed on load(). Exactly this
28
+ # age is still fresh: "older than", not "at least".
29
+ PRICE_CACHE_TTL_S = 7 * 24 * 60 * 60
30
+ # Both load()-triggered fetches. refresh_prices.main() keeps its own 30s.
31
+ LOAD_FETCH_TIMEOUT_S = 5
32
+
33
+
34
+ def _default_cache_path() -> str:
35
+ """User cache, never the installed package.
36
+
37
+ card #1248: a table written next to this file lands in site-packages and
38
+ goes stale in the wheel. The cache lives under XDG_CACHE_HOME (or
39
+ ~/.cache). SWARPH_BENCH_PRICE_CACHE overrides it.
40
+ """
41
+ override = os.environ.get("SWARPH_BENCH_PRICE_CACHE")
42
+ if override:
43
+ return override
44
+ base = os.environ.get("XDG_CACHE_HOME") or os.path.join(
45
+ os.path.expanduser("~"), ".cache")
46
+ return os.path.join(base, "swarph", "llm_prices.json")
47
+
48
+
49
+ _CACHE_PATH = _default_cache_path()
50
+ _MAP: dict | None = None
51
+ # lookup() warns once per model per process when a loaded table has no row.
52
+ _FALLBACK_WARNED: set[str] = set()
53
+
54
+ # Reconciled fallbacks ($/Mtok input, output) — used ONLY when a model isn't in
55
+ # the refreshed cache. The cache is the real source; these are the safety net.
56
+ _FALLBACK = {
57
+ "pro": (1.25, 10.0),
58
+ "lite": (0.019, 0.075),
59
+ "flash": (0.30, 2.50),
60
+ }
61
+
62
+
63
+ def _now() -> datetime.datetime:
64
+ return datetime.datetime.now(datetime.timezone.utc)
65
+
66
+
67
+ def _read_payload() -> dict | None:
68
+ try:
69
+ with open(_CACHE_PATH) as f:
70
+ data = json.load(f) or {}
71
+ return data if isinstance(data, dict) else None
72
+ except Exception:
73
+ return None
74
+
75
+
76
+ def _prices_of(payload: dict | None) -> dict:
77
+ if not payload:
78
+ return {}
79
+ prices = payload.get("prices")
80
+ return prices if isinstance(prices, dict) else {}
81
+
82
+
83
+ def _generated_at(payload: dict | None) -> datetime.datetime | None:
84
+ """UTC timestamp, or None when the field is missing or unparseable."""
85
+ if not payload:
86
+ return None
87
+ raw = payload.get("generated_at")
88
+ if not isinstance(raw, str) or not raw.strip():
89
+ return None
90
+ try:
91
+ parsed = datetime.datetime.fromisoformat(raw.strip())
92
+ except ValueError:
93
+ return None
94
+ if parsed.tzinfo is None:
95
+ parsed = parsed.replace(tzinfo=datetime.timezone.utc)
96
+ return parsed
97
+
98
+
99
+ def _age_days_label(generated: datetime.datetime | None) -> str:
100
+ if generated is None:
101
+ return "unknown"
102
+ return str(max(0, int((_now() - generated).total_seconds() // 86400)))
103
+
104
+
105
+ def _read_cache() -> dict:
106
+ return _prices_of(_read_payload())
107
+
108
+
109
+ def _write_cache(prices: dict) -> None:
110
+ """Atomic write of the user cache. Never writes inside this package."""
111
+ dest = os.path.abspath(_CACHE_PATH)
112
+ pkg = os.path.dirname(os.path.abspath(__file__))
113
+ if dest == pkg or dest.startswith(pkg + os.sep):
114
+ log.warning(
115
+ "refusing to write the price table into the package tree (%s)", dest)
116
+ return
117
+ payload = {
118
+ "generated_at": _now().isoformat(),
119
+ "source": "BerriAI/litellm model_prices_and_context_window.json",
120
+ "count": len(prices),
121
+ "prices": prices,
122
+ }
123
+ try:
124
+ os.makedirs(os.path.dirname(dest), exist_ok=True)
125
+ fd, tmp = tempfile.mkstemp(dir=os.path.dirname(dest), suffix=".tmp")
126
+ with os.fdopen(fd, "w") as f:
127
+ json.dump(payload, f)
128
+ os.replace(tmp, dest)
129
+ except OSError as e:
130
+ log.warning("llm price cache not written (%s)", e)
131
+
132
+
133
+ def _fetch_into_cache() -> dict:
134
+ """Fill the user cache from the price source. Never writes inside
135
+ this package (that path is site-packages once the wheel is installed).
136
+
137
+ This is the empty-cache path. A stale cache goes through
138
+ ``refresh_prices.guard`` in ``load()`` before it is written.
139
+ """
140
+ try:
141
+ from .refresh_prices import fetch
142
+ fresh = fetch(timeout=LOAD_FETCH_TIMEOUT_S)
143
+ except Exception as e:
144
+ log.warning("llm price fetch failed (%s)", e)
145
+ return {}
146
+ if not fresh:
147
+ return {}
148
+ _write_cache(fresh)
149
+ return fresh
150
+
151
+
152
+ def _keep_stale(prior: dict, generated: datetime.datetime | None) -> dict:
153
+ log.warning(
154
+ "llm price refresh failed — keeping the stale table, %s days old",
155
+ _age_days_label(generated),
156
+ )
157
+ return prior
158
+
159
+
160
+ def load(force: bool = False) -> dict:
161
+ """The cached ``{normalized_model: {"in": $/Mtok, "out": $/Mtok}}`` map.
162
+ Cached in-process; ``force=True`` re-reads. Never raises.
163
+
164
+ A missing cache is fetched once into the user cache. If that also fails,
165
+ the empty table is a WARNING, not a DEBUG line: every later lookup is
166
+ then a built-in fallback (card #1248).
167
+
168
+ A cache older than ``PRICE_CACHE_TTL_S``, or one whose ``generated_at``
169
+ is missing or unparseable, is refreshed through ``refresh_prices.guard``.
170
+ A failed refresh keeps the stale table (card #1250).
171
+ """
172
+ global _MAP
173
+ if _MAP is not None and not force:
174
+ return _MAP
175
+ payload = _read_payload()
176
+ prior = _prices_of(payload)
177
+ generated = _generated_at(payload)
178
+ if prior and generated is not None:
179
+ age = (_now() - generated).total_seconds()
180
+ if age <= PRICE_CACHE_TTL_S:
181
+ _MAP = prior
182
+ return _MAP
183
+ if prior:
184
+ table = _refresh_stale(prior, generated)
185
+ else:
186
+ table = _fetch_into_cache()
187
+ if not table:
188
+ log.warning(
189
+ "llm price table unavailable — pricing from the built-in "
190
+ "fallbacks (pro/lite/flash). No cache at %s and the price "
191
+ "source could not be fetched.",
192
+ _CACHE_PATH,
193
+ )
194
+ _MAP = {}
195
+ return _MAP
196
+ _MAP = table
197
+ return _MAP
198
+
199
+
200
+ def _refresh_stale(prior: dict, generated: datetime.datetime | None) -> dict:
201
+ """Refetch a stale table. The guard runs only on this path, and the
202
+ guarded-count WARNING is logged only here — a first fill has no prior
203
+ to guard."""
204
+ try:
205
+ from .refresh_prices import fetch, guard
206
+ fresh = fetch(timeout=LOAD_FETCH_TIMEOUT_S)
207
+ except Exception as e:
208
+ log.warning("llm price fetch failed (%s)", e)
209
+ return _keep_stale(prior, generated)
210
+ if not fresh:
211
+ return _keep_stale(prior, generated)
212
+ merged, flagged = guard(fresh, prior)
213
+ log.warning(
214
+ "llm price refresh guarded %d change(s) against the stale table",
215
+ len(flagged),
216
+ )
217
+ _write_cache(merged)
218
+ return merged
219
+
220
+
221
+ def _norm(model: str) -> str:
222
+ """Strip provider prefix (gemini/, vertex_ai/, ...) + lowercase."""
223
+ return (model or "").split("/")[-1].strip().lower()
224
+
225
+
226
+ def lookup(model: str) -> tuple[float, float]:
227
+ """(input_$/Mtok, output_$/Mtok) for ``model``. Exact cache hit ->
228
+ substring fallback (lite before flash so 'flash-lite' resolves as lite —
229
+ order-sensitive, don't reorder). Never raises."""
230
+ m = _norm(model)
231
+ prices = load()
232
+ p = prices.get(m)
233
+ if p and "in" in p and "out" in p:
234
+ return (float(p["in"]), float(p["out"]))
235
+ # substring fallback — ORDER MATTERS: lite is a substring of flash-lite
236
+ # and must win over flash.
237
+ if "lite" in m:
238
+ chosen, label = _FALLBACK["lite"], "lite"
239
+ elif "pro" in m:
240
+ chosen, label = _FALLBACK["pro"], "pro"
241
+ else:
242
+ chosen, label = _FALLBACK["flash"], "flash"
243
+ # A loaded table that lacks this model is the #1248 defect class for a
244
+ # model upstream does not list yet. Once per model per process. An empty
245
+ # table already warned in load(); don't warn again on every lookup.
246
+ if prices and m not in _FALLBACK_WARNED:
247
+ _FALLBACK_WARNED.add(m)
248
+ log.warning(
249
+ "llm price for %s is not in the loaded table — using the %s "
250
+ "fallback %s",
251
+ m, label, chosen,
252
+ )
253
+ return chosen
254
+
255
+
256
+ def is_known(model_id: str) -> bool:
257
+ """True if ``model_id`` has an exact hit in the refreshed cache (as
258
+ opposed to resolving via the substring fallback)."""
259
+ p = load().get(_norm(model_id))
260
+ return bool(p and "in" in p and "out" in p)
261
+
262
+
263
+ def cost_usd(model_id: str, tokens_in: int, tokens_thought: int, tokens_out: int) -> float:
264
+ """Metered $ for one call: in-tokens at the input price, (thought + out)
265
+ tokens at the output price (mirrors the reference ``metered_usd()``)."""
266
+ price_in, price_out = lookup(model_id)
267
+ thought = tokens_thought or 0
268
+ return (tokens_in * price_in + (thought + tokens_out) * price_out) / 1_000_000
269
+
270
+
271
+ def all_cached(force: bool = False) -> dict[str, tuple[float, float]]:
272
+ """Every ``{model_id: (in, out)}`` currently in the refreshed cache — the
273
+ full LiteLLM-sourced price list, not just the models ``bench`` happens to
274
+ price. Exposed so the cache is a reusable mesh asset (e.g. a
275
+ cost-governor/ledger can read the same cache via ``swarph bench prices``)
276
+ rather than plumbing private to the runner."""
277
+ return {m: (float(v["in"]), float(v["out"])) for m, v in load(force=force).items()
278
+ if isinstance(v, dict) and "in" in v and "out" in v}
279
+
280
+
281
+ def fallback_buckets() -> dict[str, tuple[float, float]]:
282
+ """The 3 built-in substring-fallback buckets (pro/lite/flash), for
283
+ display when the cache is empty/stale."""
284
+ return dict(_FALLBACK)
@@ -19,6 +19,10 @@ not wired to any scheduler in this PR, just the ported fetch+guard logic.
19
19
 
20
20
  Network call — never exercised in the offline test suite (mocked or skipped).
21
21
  Read-only wrt the rest of swarph-cli: only ever writes the price cache.
22
+
23
+ ``SWARPH_BENCH_PRICE_URL`` overrides the source URL. Only the ``https``,
24
+ ``http`` and ``file`` schemes are accepted (card #1250). ``load()`` passes a
25
+ 5s timeout; this CLI keeps 30s.
22
26
  """
23
27
  from __future__ import annotations
24
28
 
@@ -26,22 +30,45 @@ import json
26
30
  import os
27
31
  import sys
28
32
  import tempfile
33
+ import urllib.parse
29
34
  import urllib.request
30
35
  from datetime import datetime, timezone
31
36
 
32
- from .prices import _CACHE_PATH as CACHE_PATH
37
+ def _cache_path() -> str:
38
+ """Read at call time so a monkeypatch of prices._CACHE_PATH is honored,
39
+ and so a refresh never keeps an import-time path inside the package."""
40
+ from .prices import _CACHE_PATH
41
+ return _CACHE_PATH
33
42
 
34
43
  LITELLM_URL = ("https://raw.githubusercontent.com/BerriAI/litellm/main/"
35
44
  "model_prices_and_context_window.json")
36
45
  GUARD_RATIO = 3.0 # reject a new price >3x or <1/3x the prior cached value
46
+ # The CLI's own fetch. load() passes prices.LOAD_FETCH_TIMEOUT_S instead.
47
+ CLI_FETCH_TIMEOUT_S = 30
48
+ _ALLOWED_SOURCE_SCHEMES = frozenset({"https", "http", "file"})
49
+
50
+
51
+ def price_source_url() -> str:
52
+ """The JSON URL ``fetch`` reads.
53
+
54
+ ``SWARPH_BENCH_PRICE_URL`` overrides ``LITELLM_URL``. Schemes other than
55
+ https, http and file are refused.
56
+ """
57
+ raw = (os.environ.get("SWARPH_BENCH_PRICE_URL") or LITELLM_URL).strip()
58
+ scheme = urllib.parse.urlparse(raw).scheme.lower()
59
+ if scheme not in _ALLOWED_SOURCE_SCHEMES:
60
+ raise ValueError(
61
+ f"SWARPH_BENCH_PRICE_URL scheme {scheme!r} is not allowed "
62
+ "(https, http, file)")
63
+ return raw
37
64
 
38
65
 
39
66
  def _norm(key: str) -> str:
40
67
  return key.split("/")[-1].strip().lower()
41
68
 
42
69
 
43
- def fetch() -> dict:
44
- raw = urllib.request.urlopen(LITELLM_URL, timeout=30).read()
70
+ def fetch(timeout: float = CLI_FETCH_TIMEOUT_S) -> dict:
71
+ raw = urllib.request.urlopen(price_source_url(), timeout=timeout).read()
45
72
  d = json.loads(raw)
46
73
  out: dict[str, dict] = {}
47
74
  for key, v in d.items():
@@ -59,7 +86,7 @@ def fetch() -> dict:
59
86
 
60
87
  def load_prior() -> dict:
61
88
  try:
62
- with open(CACHE_PATH) as f:
89
+ with open(_cache_path()) as f:
63
90
  return (json.load(f) or {}).get("prices", {}) or {}
64
91
  except Exception:
65
92
  return {}
@@ -98,11 +125,17 @@ def main() -> int:
98
125
  payload = {"generated_at": datetime.now(timezone.utc).isoformat(),
99
126
  "source": "BerriAI/litellm model_prices_and_context_window.json",
100
127
  "count": len(merged), "prices": merged}
101
- os.makedirs(os.path.dirname(CACHE_PATH), exist_ok=True)
102
- fd, tmp = tempfile.mkstemp(dir=os.path.dirname(CACHE_PATH), suffix=".tmp")
128
+ dest = _cache_path()
129
+ pkg = os.path.dirname(os.path.abspath(os.path.join(os.path.dirname(__file__), "prices.py")))
130
+ if os.path.abspath(dest).startswith(pkg + os.sep):
131
+ print(f"refresh_prices: refusing to write into the package tree ({dest})",
132
+ file=sys.stderr)
133
+ return 1
134
+ os.makedirs(os.path.dirname(dest), exist_ok=True)
135
+ fd, tmp = tempfile.mkstemp(dir=os.path.dirname(dest), suffix=".tmp")
103
136
  with os.fdopen(fd, "w") as f:
104
137
  json.dump(payload, f, indent=2, sort_keys=True)
105
- os.replace(tmp, CACHE_PATH)
138
+ os.replace(tmp, dest)
106
139
  print(f"refresh_prices: {len(merged)} models cached "
107
140
  f"({len(fresh)} fetched, {len(merged) - len(fresh)} prior-only kept). "
108
141
  f"{len(flagged)} guarded.")
@@ -71,9 +71,11 @@ def _patch_json(url: str, body: dict, token: str, *, timeout: float = 10.0) -> t
71
71
  # ── pure builders (unit-tested) ───────────────────────────────────────────────
72
72
 
73
73
  def _cards_list_url(gateway: str, *, project=None, stage=None, assignee=None,
74
- label=None) -> str:
74
+ label=None, search=None) -> str:
75
75
  q = {k: v for k, v in (("project", project), ("stage", stage),
76
76
  ("assignee", assignee), ("label", label)) if v}
77
+ if search is not None: # blank still goes: the gateway refuses it, we do not drop it
78
+ q["q"] = search
77
79
  base = f"{gateway.rstrip('/')}/board/cards"
78
80
  return f"{base}?{urllib.parse.urlencode(q)}" if q else base
79
81
 
@@ -353,6 +355,10 @@ def _format_due_head(data) -> list[str]:
353
355
  def _format_cards(data) -> str:
354
356
  head = _format_due_head(data)
355
357
  rows = _cards_of(data)
358
+ if isinstance(data, dict) and "searched" in data:
359
+ # #931: a zero must say how many cards it looked at.
360
+ head = [f"searched {data['searched']} cards: {len(rows)} match"] + (
361
+ [""] + head if head else [])
356
362
  if not rows:
357
363
  body = ["(no cards)"]
358
364
  else:
@@ -604,14 +610,49 @@ def _thread_line(d) -> str:
604
610
  f"(#509/#562 — the VERB is the mechanism, the thread is where it lands)")
605
611
 
606
612
 
613
+ def _hours_token(n) -> str:
614
+ if isinstance(n, float) and n.is_integer():
615
+ n = int(n)
616
+ return str(n)
617
+
618
+
619
+ def _clock_line(d) -> str | None:
620
+ """The due-date clock (#935). It starts when the row is taken, not now.
621
+
622
+ `clock_hours` is the menu step's hours, or the `--hours` override.
623
+ `timeout_at` is a different clock — when the row reads OVERDUE — and
624
+ is not this line. Absent fields mean an older gateway; the caller
625
+ keeps its previous sentence rather than inventing a clock.
626
+ """
627
+ hours = d.get("clock_hours")
628
+ if hours is None:
629
+ return None
630
+ source = d.get("clock_source") or "menu"
631
+ menu = d.get("menu_hours")
632
+ if (source == "override" and isinstance(menu, (int, float))
633
+ and not isinstance(menu, bool) and menu
634
+ and isinstance(hours, (int, float)) and not isinstance(hours, bool)):
635
+ if hours % menu == 0:
636
+ detail = f"override, {int(hours // menu)}x menu {_hours_token(menu)}h"
637
+ else:
638
+ detail = f"override, menu {_hours_token(menu)}h"
639
+ else:
640
+ detail = str(source)
641
+ return f"clock: {_hours_token(hours)}h ({detail}) — STARTS ON TAKE, not now"
642
+
643
+
607
644
  def _format_ask(d) -> str:
608
- """One line naming the obligation, its holder, and when it goes red.
645
+ """Name the obligation, who owes it, and the deadline clock.
609
646
 
610
647
  >>> NAMING THE DEADLINE OR ITS ABSENCE IS THE POINT, NOT DECORATION. <<< #145's
611
648
  lesson, one layer over: "0 overdue" must not be able to mean "nobody set dates".
612
- An obligation with no timeout never goes red on its own, and the operator has to
613
- be told that AT THE MOMENT THEY CREATE IT — afterwards it looks identical to one
614
- that simply is not late yet.
649
+ The deadline is the due-date clock (`clock_hours`): the menu step's hours,
650
+ or the `--hours` override, and it STARTS ON TAKE, not at mint (#935).
651
+ `timeout_at` is the other clock — when the row READS OVERDUE — and is
652
+ printed under that name, never as the deadline. A response that carries
653
+ neither clock says NO TIMEOUT, because an obligation with no timeout never
654
+ goes red on its own and the operator has to be told that AT THE MOMENT
655
+ THEY CREATE IT.
615
656
 
616
657
  #532 applies the same rule to the falsifier: an obligation with no accept
617
658
  check reads RED in the sweep, and one whose check has no FAIL branch is
@@ -619,9 +660,17 @@ def _format_ask(d) -> str:
619
660
  rows look identical to sound ones.
620
661
  """
621
662
  when = d.get("timeout_at")
622
- deadline = f"overdue after {when}" if when else _NO_TIMEOUT
623
- return (f"obligation #{d.get('id')} on card #{d.get('card_id')}: "
624
- f"{d.get('holder')} owes it, status={d.get('status')}, {deadline}\n"
663
+ clock = _clock_line(d)
664
+ head = (f"obligation #{d.get('id')} on card #{d.get('card_id')}: "
665
+ f"{d.get('holder')} owes it, status={d.get('status')}")
666
+ if clock:
667
+ extra = f"\n {clock}"
668
+ extra += f"\n overdue after {when}" if when else f"\n {_NO_TIMEOUT}"
669
+ else:
670
+ deadline = f"overdue after {when}" if when else _NO_TIMEOUT
671
+ head = f"{head}, {deadline}"
672
+ extra = ""
673
+ return (f"{head}{extra}\n"
625
674
  f" {_accept_line(d.get('accept'))}\n"
626
675
  f" {_thread_line(d)}")
627
676
 
@@ -763,6 +812,13 @@ def _ask_line(d) -> str:
763
812
  if missing:
764
813
  line += f" · still missing: {missing}"
765
814
  if not str(state).startswith("closed"):
815
+ clock = _clock_line(d)
816
+ if clock:
817
+ # #935: the due-date clock, then the overdue clock under its own name.
818
+ # A stepped mint used to say neither, so --timeout-hours was read as the due date.
819
+ line += "\n " + clock
820
+ if d.get("timeout_at"):
821
+ line += "\n overdue after " + _s(d["timeout_at"])
766
822
  line += "\n " + _accept_line(d.get("accept"))
767
823
  if d.get("step") is None:
768
824
  # unstepped: no menu clock, no container — the pre-#591 rules hold
@@ -974,6 +1030,9 @@ def _build_parser() -> argparse.ArgumentParser:
974
1030
  cl = cards.add_parser("list", help="list cards")
975
1031
  cl.add_argument("--project"); cl.add_argument("--stage"); cl.add_argument("--assignee")
976
1032
  cl.add_argument("--label", help="only cards carrying this label (exact match)")
1033
+ cl.add_argument("--search", metavar="TEXT",
1034
+ help="only cards whose title OR body contains TEXT (case-insensitive, "
1035
+ "matched by the gateway); prints how many cards were searched")
977
1036
  cl.add_argument("--json", action="store_true"); _add_common(cl)
978
1037
  cs = cards.add_parser("show", help="show one card"); cs.add_argument("id", type=int)
979
1038
  cs.add_argument("--json", action="store_true"); _add_common(cs)
@@ -1034,7 +1093,8 @@ def _build_parser() -> argparse.ArgumentParser:
1034
1093
  ck.add_argument("what", help="what is owed, in one line")
1035
1094
  ck.add_argument("--timeout-hours", type=int, default=None,
1036
1095
  help="hours until this obligation reads OVERDUE (default: none, "
1037
- "which means it never goes red on its own)")
1096
+ "which means it never goes red on its own). This is not "
1097
+ "the due-date clock; that is --hours, and it starts on take")
1038
1098
  ck.add_argument("--accept", default=None, metavar='"PASS = ... | FAIL = ..."',
1039
1099
  help="the falsifiable check that closes this obligation "
1040
1100
  "(#532). Omitting it mints an obligation that reads RED "
@@ -1057,7 +1117,9 @@ def _build_parser() -> argparse.ArgumentParser:
1057
1117
  help="explicit edges by step name (added to the menu's); repeatable, "
1058
1118
  "comma lists and the contract's `+step` spelling all accepted")
1059
1119
  ck.add_argument("--hours", type=int, default=None,
1060
- help="clock override, up to 4x the menu value")
1120
+ help="the due-date clock in hours. Starts on take, not now. "
1121
+ "Default is the menu step's hours. Up to 4x that value. "
1122
+ "Not the overdue timeout (--timeout-hours)")
1061
1123
  ck.add_argument("--holder", dest="holder_flag", default=None, metavar="PEER|me",
1062
1124
  help="who owes it: a peer (offered), `me` (open: a bound self-ask "
1063
1125
  "is a take), or omit (requested: the eligible set is DM'd)")
@@ -1186,7 +1248,9 @@ def _build_parser() -> argparse.ArgumentParser:
1186
1248
  oa.add_argument("--accept-json", default=None, metavar="'{...}'",
1187
1249
  help="replace the check with a structured one (#1068); "
1188
1250
  "mutually exclusive with --accept")
1189
- oa.add_argument("--hours", type=int, default=None, help="clock override, up to 4x the menu value")
1251
+ oa.add_argument("--hours", type=int, default=None,
1252
+ help="the due-date clock, starts on take, up to 4x the menu value. "
1253
+ "Not the overdue timeout")
1190
1254
  oa.add_argument("--needs-add", action="append", dest="needs_add", default=None,
1191
1255
  metavar="STEP", help="add an edge (repeatable)")
1192
1256
  oa.add_argument("--needs-remove", action="append", dest="needs_remove", default=None,
@@ -1338,7 +1402,15 @@ def run_board(argv: list[str]) -> int:
1338
1402
  return 1
1339
1403
  st, d = _http_get_json(_cards_list_url(gw, project=pid, stage=args.stage,
1340
1404
  assignee=args.assignee,
1341
- label=getattr(args, "label", None)), token)
1405
+ label=getattr(args, "label", None),
1406
+ search=args.search), token)
1407
+ if (args.search is not None and st and 200 <= st < 300
1408
+ and not (isinstance(d, dict) and "searched" in d)):
1409
+ # A gateway older than #931 ignores q and returns EVERY card.
1410
+ print("swarph board: the gateway ignored --search (its reply has no "
1411
+ "`searched` count), so this list is NOT filtered; refusing to "
1412
+ "show it as a search result", file=sys.stderr)
1413
+ return 1
1342
1414
  return _out(st, d, _format_cards, aj)
1343
1415
  if args.command == "show":
1344
1416
  st, d = _http_get_json(f"{gw}/board/cards/{args.id}", token)