swarph-cli 0.72.14__tar.gz → 0.72.16__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 (504) hide show
  1. {swarph_cli-0.72.14/src/swarph_cli.egg-info → swarph_cli-0.72.16}/PKG-INFO +1 -1
  2. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/pyproject.toml +1 -1
  3. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/__init__.py +1 -1
  4. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/board.py +45 -3
  5. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/triage.py +16 -3
  6. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/triage/backend.py +76 -21
  7. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/triage/owners.json +2 -1
  8. {swarph_cli-0.72.14 → swarph_cli-0.72.16/src/swarph_cli.egg-info}/PKG-INFO +1 -1
  9. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli.egg-info/SOURCES.txt +2 -0
  10. swarph_cli-0.72.16/tests/test_1073_triage_board.py +254 -0
  11. swarph_cli-0.72.16/tests/test_1075_task_cli.py +24 -0
  12. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/LICENSE +0 -0
  13. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/README.md +0 -0
  14. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/setup.cfg +0 -0
  15. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/__main__.py +0 -0
  16. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/__init__.py +0 -0
  17. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/backends.py +0 -0
  18. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/claude_cli.py +0 -0
  19. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/codex_cli.py +0 -0
  20. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/pack.py +0 -0
  21. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/prices.py +0 -0
  22. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/providers.py +0 -0
  23. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/quality.py +0 -0
  24. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/refresh_prices.py +0 -0
  25. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/rules.py +0 -0
  26. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/runner.py +0 -0
  27. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/signal_score.py +0 -0
  28. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/validate.py +0 -0
  29. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/caller.py +0 -0
  30. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/capture/__init__.py +0 -0
  31. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/capture/harden.py +0 -0
  32. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/capture/lineage.py +0 -0
  33. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/capture/liveness.py +0 -0
  34. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/capture/manifest.py +0 -0
  35. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/capture/paths.py +0 -0
  36. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/capture/verify.py +0 -0
  37. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/cell.py +0 -0
  38. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/chain_token.py +0 -0
  39. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/channel.py +0 -0
  40. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/__init__.py +0 -0
  41. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/_content.py +0 -0
  42. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/_display.py +0 -0
  43. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/_gateway_client.py +0 -0
  44. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/add.py +0 -0
  45. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/bench.py +0 -0
  46. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/board_merge_check.py +0 -0
  47. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/brain.py +0 -0
  48. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/brain_ask.py +0 -0
  49. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/cell.py +0 -0
  50. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/cell_probe.py +0 -0
  51. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  52. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/channel.py +0 -0
  53. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/chat.py +0 -0
  54. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/codegraph.py +0 -0
  55. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/codegraph_hook.py +0 -0
  56. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/codex_hook_output.py +0 -0
  57. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/codex_waker.py +0 -0
  58. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/compress.py +0 -0
  59. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/daemon.py +0 -0
  60. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/dreaming.py +0 -0
  61. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/event.py +0 -0
  62. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/gateway.py +0 -0
  63. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/gh_route.py +0 -0
  64. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/group.py +0 -0
  65. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/guide.py +0 -0
  66. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/highlight.py +0 -0
  67. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/hook_interpreter.py +0 -0
  68. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/hook_output.py +0 -0
  69. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/hooks.py +0 -0
  70. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/import_session.py +0 -0
  71. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/init.py +0 -0
  72. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
  73. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/install_hook.py +0 -0
  74. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  75. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
  76. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
  77. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/install_wake_hook.py +0 -0
  78. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/lane.py +0 -0
  79. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/mcp_server.py +0 -0
  80. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/memory.py +0 -0
  81. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
  82. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/memory_sync.py +0 -0
  83. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/mesh.py +0 -0
  84. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/monitor.py +0 -0
  85. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/okf_links.py +0 -0
  86. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/onboard.py +0 -0
  87. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
  88. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
  89. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/protocol_handler.py +0 -0
  90. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/ratify.py +0 -0
  91. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/roster.py +0 -0
  92. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/rowclear_hook.py +0 -0
  93. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/schedule.py +0 -0
  94. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/security.py +0 -0
  95. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/service.py +0 -0
  96. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/spawn.py +0 -0
  97. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/sweep.py +0 -0
  98. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/timeline.py +0 -0
  99. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/version.py +0 -0
  100. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/wake_hook_output.py +0 -0
  101. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/watchdog.py +0 -0
  102. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/compress/__init__.py +0 -0
  103. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/compress/levers.py +0 -0
  104. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/compress/marker.py +0 -0
  105. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/compress/verify.py +0 -0
  106. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/console_safe.py +0 -0
  107. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/delivery_queue.py +0 -0
  108. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dm_frame.py +0 -0
  109. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/__init__.py +0 -0
  110. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/candidates.py +0 -0
  111. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/clone.py +0 -0
  112. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/enrich.py +0 -0
  113. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/index_check.py +0 -0
  114. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/notify.py +0 -0
  115. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/organize.py +0 -0
  116. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/probes.py +0 -0
  117. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/report.py +0 -0
  118. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/run.py +0 -0
  119. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/slm.py +0 -0
  120. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/transcripts.py +0 -0
  121. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/verify.py +0 -0
  122. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/gateway/__init__.py +0 -0
  123. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/gateway/feature_registry.py +0 -0
  124. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/gateway/lanes_control.py +0 -0
  125. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/gateway/schema.sql +0 -0
  126. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/gateway/server.py +0 -0
  127. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/gateway/services_control.py +0 -0
  128. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/gateway_default.py +0 -0
  129. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/gh_identity.py +0 -0
  130. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/guide/GUIDE.md +0 -0
  131. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/identity.py +0 -0
  132. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/main.py +0 -0
  133. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/multiplexer.py +0 -0
  134. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/operator/__init__.py +0 -0
  135. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/operator/__main__.py +0 -0
  136. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/operator/api.py +0 -0
  137. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/operator/cli.py +0 -0
  138. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/operator/config.py +0 -0
  139. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/operator/fmt.py +0 -0
  140. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/orphan_daemons.py +0 -0
  141. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/pane_probe.py +0 -0
  142. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/parsers/__init__.py +0 -0
  143. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/parsers/claude.py +0 -0
  144. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
  145. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
  146. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
  147. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
  148. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
  149. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
  150. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
  151. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
  152. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
  153. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
  154. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
  155. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
  156. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/peer_executor.py +0 -0
  157. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/peer_executor_windows.py +0 -0
  158. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/peer_reconciliation.py +0 -0
  159. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/peer_reply_outbox.py +0 -0
  160. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/peer_service_host.py +0 -0
  161. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/peer_service_payload.py +0 -0
  162. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/peer_service_reply.py +0 -0
  163. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/peer_staging.py +0 -0
  164. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/query_filters.py +0 -0
  165. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/rowclear.py +0 -0
  166. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/scripts/__init__.py +0 -0
  167. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
  168. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  169. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
  170. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
  171. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
  172. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/scrub.py +0 -0
  173. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/service/__init__.py +0 -0
  174. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/service/app.py +0 -0
  175. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/service/providers.py +0 -0
  176. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/session_bridge.py +0 -0
  177. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
  178. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
  179. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/source_text.py +0 -0
  180. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/stall_alert.py +0 -0
  181. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/codex-waker.default +0 -0
  182. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
  183. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
  184. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
  185. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
  186. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
  187. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
  188. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
  189. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
  190. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
  191. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
  192. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-stuck-sweep.service +0 -0
  193. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-stuck-sweep.timer +0 -0
  194. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-triage.service +0 -0
  195. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-triage.timer +0 -0
  196. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  197. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  198. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  199. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/timeline_paths.py +0 -0
  200. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/tokens.py +0 -0
  201. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/triage/__init__.py +0 -0
  202. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/triage/job.py +0 -0
  203. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/triage/router.py +0 -0
  204. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/triage/rows.py +0 -0
  205. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/triage/sensor.py +0 -0
  206. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  207. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  208. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli.egg-info/requires.txt +0 -0
  209. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli.egg-info/top_level.txt +0 -0
  210. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1006_board_publisher.py +0 -0
  211. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1006_session_board.py +0 -0
  212. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1010_strand_only.py +0 -0
  213. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1013_spawn_channel.py +0 -0
  214. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1017_muse_tui.py +0 -0
  215. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1050_surrogate_wake.py +0 -0
  216. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1052_opencode_deaf.py +0 -0
  217. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1059_watchdog_unread_env.py +0 -0
  218. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1061_roster_cli.py +0 -0
  219. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1064_stuck_sweep.py +0 -0
  220. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1065_release.py +0 -0
  221. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1066_quiet_wakes.py +0 -0
  222. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1073_triage.py +0 -0
  223. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1073_wheel_install.py +0 -0
  224. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_124_register_merge.py +0 -0
  225. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_126_pending_from_complete.py +0 -0
  226. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_132_ensure_monitor_state_dir.py +0 -0
  227. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_138_memory_navigate.py +0 -0
  228. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_139_tool_probe.py +0 -0
  229. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_183_card_anchor.py +0 -0
  230. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_184_notify_honesty.py +0 -0
  231. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_201_win32_bash_refusal.py +0 -0
  232. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_243_onboard_per_peer_token.py +0 -0
  233. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_307_task4_ask_reply.py +0 -0
  234. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
  235. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_319_refusal_exit_codes.py +0 -0
  236. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_323_mistral_backend.py +0 -0
  237. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_332_report_organize_findings.py +0 -0
  238. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_356_query_filters.py +0 -0
  239. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_397_gh_identity_router.py +0 -0
  240. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_397_router_preflight.py +0 -0
  241. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_402_one_identity_resolver.py +0 -0
  242. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_432_rowclear.py +0 -0
  243. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_467b_onboard_operator_credential.py +0 -0
  244. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_472_register_caller_binding.py +0 -0
  245. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_484_codex_queue_sink.py +0 -0
  246. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_523_guide.py +0 -0
  247. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_524_conftest_hermeticity.py +0 -0
  248. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_532_accept_flag.py +0 -0
  249. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_535_version_capability.py +0 -0
  250. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_538_signal_bench.py +0 -0
  251. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_546_default_gateway.py +0 -0
  252. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_547_verbs_from_registry.py +0 -0
  253. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_548_gateway_remainder.py +0 -0
  254. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_552_codegraph_gateway.py +0 -0
  255. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_555_relay_row_shape.py +0 -0
  256. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_556_relay_freshness.py +0 -0
  257. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_559_edges_truncated.py +0 -0
  258. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_564_bootstrap_first_peer.py +0 -0
  259. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
  260. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_579_register_bootstrap_message.py +0 -0
  261. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_591_cli_step_graph.py +0 -0
  262. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_644_monitor_supervision.py +0 -0
  263. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_648_install_unit.py +0 -0
  264. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_654_reexec.py +0 -0
  265. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_656_owned_slm.py +0 -0
  266. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_684_dreaming_verb.py +0 -0
  267. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_689_pkg_version_order.py +0 -0
  268. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_715_grok_wake.py +0 -0
  269. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_716_timeline_one_path.py +0 -0
  270. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_723_grok_0251.py +0 -0
  271. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_724_ps1_ascii_or_bom.py +0 -0
  272. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_725_stdio_reconfigure.py +0 -0
  273. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_726_grok_limit_screen.py +0 -0
  274. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_729_arm_check_delivery_gap.py +0 -0
  275. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_729_dm_once.py +0 -0
  276. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_729_wake_watchdog.py +0 -0
  277. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_734_dreaming_enrich_degrade.py +0 -0
  278. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_735_dm_mcp_tools.py +0 -0
  279. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_739_wake_writer.py +0 -0
  280. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_740_edit_project.py +0 -0
  281. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_762_cards_edit_priority.py +0 -0
  282. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_764_enrich_collapse.py +0 -0
  283. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_801_confirm_flow_cli.py +0 -0
  284. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_802_stage_history.py +0 -0
  285. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_825_codegraph_hook_noise.py +0 -0
  286. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_825_install_posttooluse_only.py +0 -0
  287. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_825_remaining_cap_shred_bind.py +0 -0
  288. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_829_counterfactual_column.py +0 -0
  289. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_830_hook_binding_version.py +0 -0
  290. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_84_cli_labels.py +0 -0
  291. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_866_doctrine_guide.py +0 -0
  292. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_872_identity_refuses_not_warns.py +0 -0
  293. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_874_source_text.py +0 -0
  294. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
  295. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_937_dreaming_notify.py +0 -0
  296. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_938_organize_line_limit.py +0 -0
  297. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_941_bench_backends.py +0 -0
  298. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_941_harness_342.py +0 -0
  299. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_945_same_line_pairing.py +0 -0
  300. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_955_prompt_audit.py +0 -0
  301. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_957_auth_http.py +0 -0
  302. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_959_venv_interpreter.py +0 -0
  303. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_960_channel.py +0 -0
  304. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
  305. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_961_cursor_grok_footer.py +0 -0
  306. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_961_loud_stop.py +0 -0
  307. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_963_plugin_manifest.py +0 -0
  308. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_964_channel_cell.py +0 -0
  309. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_965_mesh_wait.py +0 -0
  310. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_975_channel_hop.py +0 -0
  311. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_add_security_gate.py +0 -0
  312. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_artifact_add.py +0 -0
  313. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_artifact_hash.py +0 -0
  314. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_artifact_lib.py +0 -0
  315. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_artifact_mcp.py +0 -0
  316. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_artifact_skill.py +0 -0
  317. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_artifact_tool.py +0 -0
  318. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_artifact_uri.py +0 -0
  319. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_bench_backends.py +0 -0
  320. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_bench_claude_cli.py +0 -0
  321. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_bench_codex_cli.py +0 -0
  322. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_bench_command.py +0 -0
  323. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_bench_pack.py +0 -0
  324. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_bench_prices.py +0 -0
  325. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_bench_quality.py +0 -0
  326. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_bench_runner.py +0 -0
  327. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_bench_validate.py +0 -0
  328. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_board_card_thread.py +0 -0
  329. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_board_command.py +0 -0
  330. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_board_merge_check.py +0 -0
  331. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_board_obligation_close.py +0 -0
  332. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_brain_ask_command.py +0 -0
  333. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_brain_ask_env_diagnosis.py +0 -0
  334. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_brain_ask_identity_explicitness.py +0 -0
  335. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_brain_command.py +0 -0
  336. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_caller_meta_guard.py +0 -0
  337. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_capture_lineage.py +0 -0
  338. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_capture_liveness.py +0 -0
  339. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_capture_manifest.py +0 -0
  340. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_capture_paths.py +0 -0
  341. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_capture_security.py +0 -0
  342. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_cell_command_dispatch.py +0 -0
  343. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_cell_harden.py +0 -0
  344. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_cell_loader.py +0 -0
  345. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_cell_selfcheck.py +0 -0
  346. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_cell_verify.py +0 -0
  347. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_chain_token.py +0 -0
  348. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_channel_command.py +0 -0
  349. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_channel_poll_never_breaks_dms.py +0 -0
  350. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_channel_post.py +0 -0
  351. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_channel_wake_policy_filter.py +0 -0
  352. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_chat_command.py +0 -0
  353. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_claude_parser.py +0 -0
  354. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_claude_tmux_template.py +0 -0
  355. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_codegraph_caller_scope.py +0 -0
  356. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_codegraph_command.py +0 -0
  357. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_codegraph_hook.py +0 -0
  358. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_codegraph_mcp.py +0 -0
  359. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_codex_waker.py +0 -0
  360. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_compress_caller_convention.py +0 -0
  361. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_compress_command.py +0 -0
  362. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_compress_levers.py +0 -0
  363. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_compress_marker.py +0 -0
  364. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_compress_verify.py +0 -0
  365. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_console_encoding_e2e.py +0 -0
  366. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_content_composition.py +0 -0
  367. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_copilot_membrane.py +0 -0
  368. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_cursor_membrane.py +0 -0
  369. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_daemon_command.py +0 -0
  370. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_daemon_console_encoding.py +0 -0
  371. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_daemon_delivery.py +0 -0
  372. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_delivery_queue.py +0 -0
  373. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_display_sanitize.py +0 -0
  374. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_ensure_monitor_no_peer_default.py +0 -0
  375. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_event_emit.py +0 -0
  376. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_feature_to_uri.py +0 -0
  377. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_gateway_brain.py +0 -0
  378. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_gateway_client.py +0 -0
  379. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_gateway_command.py +0 -0
  380. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_gateway_message_idempotency.py +0 -0
  381. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_gh_route_doctor_mesh_name.py +0 -0
  382. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_group_command.py +0 -0
  383. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_guide_commands_parse.py +0 -0
  384. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_guide_selfcheck.py +0 -0
  385. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_heartbeat_check.py +0 -0
  386. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_highlight_command.py +0 -0
  387. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_hook_output.py +0 -0
  388. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_hooks_activity_marker.py +0 -0
  389. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_hooks_add.py +0 -0
  390. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_hooks_bundle.py +0 -0
  391. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_hooks_init.py +0 -0
  392. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_hooks_legacy_generation_removal.py +0 -0
  393. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_hooks_lifecycle.py +0 -0
  394. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_hooks_merge.py +0 -0
  395. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_hooks_verify.py +0 -0
  396. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_hooks_win32_bash_wrapper.py +0 -0
  397. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_hooks_win32_command_path.py +0 -0
  398. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_import_command.py +0 -0
  399. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_inbox_identity_provenance.py +0 -0
  400. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_init_command.py +0 -0
  401. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_init_provider_menu.py +0 -0
  402. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_install_codex_hooks.py +0 -0
  403. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_install_hook.py +0 -0
  404. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_install_multiplexer_command.py +0 -0
  405. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_install_postcompact_hook.py +0 -0
  406. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_install_wake_hook.py +0 -0
  407. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_lane_command.py +0 -0
  408. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_main.py +0 -0
  409. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_main_unknown_verb_guard.py +0 -0
  410. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_mcp_missing_hint.py +0 -0
  411. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_mcp_server.py +0 -0
  412. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_memory_command.py +0 -0
  413. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_memory_emit_snippet.py +0 -0
  414. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_memory_gateway.py +0 -0
  415. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_memory_sync.py +0 -0
  416. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_memory_sync_membrane.py +0 -0
  417. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_mesh_command.py +0 -0
  418. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_mesh_inbox_mark_read.py +0 -0
  419. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_mesh_inbox_sanitize.py +0 -0
  420. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_mesh_originate_thread_623.py +0 -0
  421. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_mesh_send_unknown_peer.py +0 -0
  422. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_mesh_sidecar.py +0 -0
  423. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  424. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_mesh_sidecar_muse_sink.py +0 -0
  425. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  426. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_meta_edge_identity.py +0 -0
  427. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_monitor_channel_poll.py +0 -0
  428. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_monitor_cli.py +0 -0
  429. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_monitor_daemon_collision.py +0 -0
  430. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_monitor_self_name_verification.py +0 -0
  431. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_monitor_state_split.py +0 -0
  432. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_multiplexer.py +0 -0
  433. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_muse_membrane.py +0 -0
  434. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_muse_own_cli.py +0 -0
  435. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_obligation_close_is_surfaced.py +0 -0
  436. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_okf_links.py +0 -0
  437. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_onboard_command.py +0 -0
  438. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_opencode_membrane.py +0 -0
  439. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_opencode_plugin.py +0 -0
  440. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_operator_me.py +0 -0
  441. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_orphan_daemons.py +0 -0
  442. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_packaged_artifacts_exist.py +0 -0
  443. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_packaged_skills.py +0 -0
  444. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_peer_executor.py +0 -0
  445. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_peer_executor_windows.py +0 -0
  446. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_peer_reply_drain_command.py +0 -0
  447. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_peer_reply_drain_supervisor.py +0 -0
  448. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_peer_reply_outbox.py +0 -0
  449. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_peer_reply_transport.py +0 -0
  450. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_peer_service_e2e.py +0 -0
  451. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_peer_service_host.py +0 -0
  452. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_peer_service_payload.py +0 -0
  453. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_peer_service_reply.py +0 -0
  454. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_postcompact_recall.py +0 -0
  455. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_process_liveness.py +0 -0
  456. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_protocol_handler.py +0 -0
  457. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_providers_isolation.py +0 -0
  458. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_ratify_command.py +0 -0
  459. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_readme_claims_no_gateway_default.py +0 -0
  460. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_repl_caller_convention.py +0 -0
  461. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_schedule_command.py +0 -0
  462. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_schedule_create_contract.py +0 -0
  463. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_scrub.py +0 -0
  464. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_security.py +0 -0
  465. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_service_app.py +0 -0
  466. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_service_command.py +0 -0
  467. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_service_providers.py +0 -0
  468. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_session_bridge_inject.py +0 -0
  469. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_session_bridge_probe.py +0 -0
  470. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_session_bridge_resolve.py +0 -0
  471. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_session_path_role_gate.py +0 -0
  472. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_shipped_units_match_the_cli.py +0 -0
  473. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_smoke_chat.py +0 -0
  474. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_smoke_one_shot.py +0 -0
  475. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_smoke_phase_5_5.py +0 -0
  476. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_spawn_base_hoist.py +0 -0
  477. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_spawn_command.py +0 -0
  478. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_spawn_identity_stamp.py +0 -0
  479. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_spawn_live_pin.py +0 -0
  480. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_spawn_mitosis_lineage.py +0 -0
  481. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_spawn_tmux_session.py +0 -0
  482. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_spawn_windows_relaunch.py +0 -0
  483. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_stall_alert.py +0 -0
  484. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_subprocess_decode_encoding.py +0 -0
  485. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_timeline_command.py +0 -0
  486. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_tmux_wake_runstate_defer.py +0 -0
  487. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_tmux_wake_session_reanchor.py +0 -0
  488. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_tmux_wake_stale.py +0 -0
  489. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_tmux_wake_submit_verify.py +0 -0
  490. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_token_file_one_parser.py +0 -0
  491. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_token_precedence_explicit_first.py +0 -0
  492. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_unified_token_resolver.py +0 -0
  493. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_version_command.py +0 -0
  494. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_version_consistency.py +0 -0
  495. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_vibe_membrane.py +0 -0
  496. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_watchdog.py +0 -0
  497. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_watchdog_dm_wake.py +0 -0
  498. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  499. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  500. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_watchdog_emit_health.py +0 -0
  501. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_watchdog_install_flags.py +0 -0
  502. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_watchdog_model_rung.py +0 -0
  503. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_watchdog_stale_peers.py +0 -0
  504. {swarph_cli-0.72.14 → swarph_cli-0.72.16}/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.14
3
+ Version: 0.72.16
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.14"
7
+ version = "0.72.16"
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.14"
19
+ __version__ = "0.72.16"
20
20
 
21
21
  __all__ = ["__version__"]
@@ -631,7 +631,8 @@ def _step_list(values) -> list[str]:
631
631
 
632
632
 
633
633
  def _ask_payload(self_name, what, *, holder=None, step=None, needs=None, hours=None,
634
- done=None, accept=None, kind="action", timeout_hours=None) -> dict:
634
+ done=None, accept=None, kind="action", timeout_hours=None,
635
+ task=None, task_needs=None) -> dict:
635
636
  """POST /board/cards/{id}/ask body. None = not mentioned = key ABSENT (#191's
636
637
  rule; test_532 pins that a null `accept` on the wire would overwrite the
637
638
  gateway's own normalization). No holder → `requested`; `me` → the caller's
@@ -653,6 +654,10 @@ def _ask_payload(self_name, what, *, holder=None, step=None, needs=None, hours=N
653
654
  p["accept"] = accept
654
655
  if timeout_hours is not None:
655
656
  p["timeout_hours"] = timeout_hours
657
+ if task is not None:
658
+ p["task"] = task
659
+ if task_needs is not None and _step_list(task_needs):
660
+ p["task_needs"] = _step_list(task_needs)
656
661
  return p
657
662
 
658
663
 
@@ -680,6 +685,8 @@ def _ask_line(d) -> str:
680
685
  return _format_ask(d)
681
686
  n = d.get("id"); card = d.get("card_id")
682
687
  step = _s(d.get("step")) or "unstepped"
688
+ if d.get("task"):
689
+ step = f"{step} {_s(d.get('task'))}"
683
690
  holder = _s(d.get("holder")); take = _s(d.get("take_with"))
684
691
  missing = ", ".join(_s(m) for m in d.get("still_missing") or []) # the gateway's field (mesh-gateway #157)
685
692
  if state == "requested":
@@ -980,6 +987,17 @@ def _build_parser() -> argparse.ArgumentParser:
980
987
  ck.add_argument("--done", default=None, metavar="EVIDENCE",
981
988
  help="mint + take + close in ONE act (needs --holder me); the "
982
989
  "container the step names — a ref, >=40 words, or a verdict")
990
+ ck.add_argument("--task", default=None, metavar="task:<slug>",
991
+ help="task id, one token task:<slug> (letters, digits, dots, "
992
+ "underscores, hyphens). A pull-request task is "
993
+ "task:pr-<repo>-<digits>. pr:<repo>#<n> is refused. "
994
+ "Set at mint; amend cannot add it. Two live rows of one "
995
+ "step need different task ids. Omit it for the one live "
996
+ "unsplit row")
997
+ ck.add_argument("--task-needs", action="append", dest="task_needs", default=None,
998
+ metavar="task:<slug>",
999
+ help="sibling task id on this same step (repeatable). The "
1000
+ "gateway stores the edge and refuses a cycle")
983
1001
  ck.add_argument("--json", action="store_true"); _add_common(ck)
984
1002
 
985
1003
  cf = cards.add_parser(
@@ -1055,7 +1073,7 @@ def _build_parser() -> argparse.ArgumentParser:
1055
1073
  "list", help="list obligations — the READ half (#590): an obligation "
1056
1074
  "that can be minted and closed but never read makes the "
1057
1075
  "holder the only auditor")
1058
- ol.add_argument("--status", choices=["open", "closed", "fallback_fired"])
1076
+ ol.add_argument("--status", choices=["open", "closed", "fallback_fired", "withdrawn"])
1059
1077
  ol.add_argument("--holder", help="only obligations this peer owes")
1060
1078
  ol.add_argument("--card", type=int, dest="card_id", help="only this card")
1061
1079
  ol.add_argument("--overdue", action="store_true",
@@ -1092,6 +1110,15 @@ def _build_parser() -> argparse.ArgumentParser:
1092
1110
  help="the contract's signed spelling: `+step` adds, `-step` removes "
1093
1111
  "(write the latter `--needs=-step`; a bare `-step` reads as a flag)")
1094
1112
  oa.add_argument("--json", action="store_true"); _add_common(oa)
1113
+ ow = obl.add_parser(
1114
+ "withdraw",
1115
+ help="WITHDRAW an untaken row (asker, card assignee, or orchestrator). "
1116
+ "A taken row is closed or declined, not withdrawn. Status becomes "
1117
+ "withdrawn, which is neither pass nor fail")
1118
+ ow.add_argument("id", type=int, help="the obligation id")
1119
+ ow.add_argument("--reason", required=True,
1120
+ help="why the row is withdrawn; whitespace is refused")
1121
+ ow.add_argument("--json", action="store_true"); _add_common(ow)
1095
1122
  return p
1096
1123
 
1097
1124
 
@@ -1194,6 +1221,20 @@ def run_board(argv: list[str]) -> int:
1194
1221
  return 2
1195
1222
  st, d = _patch_json(f"{gw}/board/obligations/{args.id}/amend", patch, token)
1196
1223
  return _out(st, d, _obligation_act_line, aj)
1224
+ if args.command == "withdraw":
1225
+ reason = args.reason
1226
+ if not reason.strip():
1227
+ print("swarph board obligations withdraw: --reason is "
1228
+ "whitespace-only", file=sys.stderr)
1229
+ return 2
1230
+ st, d = _post_json(
1231
+ f"{gw}/board/obligations/{args.id}/withdraw",
1232
+ {"reason": reason}, token)
1233
+ return _out(st, d, lambda x: (
1234
+ f"obligation #{_s(x.get('id', args.id))} WITHDRAWN by "
1235
+ f"{_s(x.get('closed_by', self_name))}"
1236
+ + (f" task {_s(x.get('task'))}" if x.get("task") else "")
1237
+ ), aj)
1197
1238
 
1198
1239
  if args.group == "cards":
1199
1240
  if args.command == "list":
@@ -1350,7 +1391,8 @@ def run_board(argv: list[str]) -> int:
1350
1391
  body = _ask_payload(self_name, args.what, holder=holder,
1351
1392
  step=args.step, needs=args.needs, hours=args.hours,
1352
1393
  done=args.done, accept=args.accept, kind=args.kind,
1353
- timeout_hours=args.timeout_hours)
1394
+ timeout_hours=args.timeout_hours,
1395
+ task=args.task, task_needs=args.task_needs)
1354
1396
  st, d = _post_json(f"{gw}/board/cards/{args.id}/ask", body, token)
1355
1397
  return _out(st, d, _ask_line, args.json)
1356
1398
  if args.command == "confirm-flow":
@@ -212,6 +212,7 @@ def run_triage(*, gateway, token, sender, owners_map, state_file,
212
212
  sent[key] = now_iso
213
213
  return True
214
214
 
215
+ from swarph_cli.triage.backend import BackendError
215
216
  from swarph_cli.triage.rows import TriageStore
216
217
  store = TriageStore(store_backend)
217
218
  if dry_run:
@@ -219,8 +220,14 @@ def run_triage(*, gateway, token, sender, owners_map, state_file,
219
220
  print(f"DRY {record['source_organ']} {record['type']} "
220
221
  f"sharp={record['sharp']} item={record['item_id']}")
221
222
  return 0, {"dry_run_records": len(records)}
222
- summary = _job.route_and_store(store, owners_map, records,
223
- dm_sender=dm_sender)
223
+ try:
224
+ summary = _job.route_and_store(store, owners_map, records,
225
+ dm_sender=dm_sender)
226
+ except BackendError as exc:
227
+ # A failed create or append names itself here and exits non-zero
228
+ # — never the bare 'accept line not added' assertion (#1446).
229
+ print(f"swarph triage: board write failed: {exc}", file=sys.stderr)
230
+ return 1, {"board_error": str(exc)}
224
231
  _save_state(state_file, sent)
225
232
  return 0, summary
226
233
 
@@ -262,6 +269,9 @@ def _build_parser():
262
269
  p.add_argument("--owners-file", default=None,
263
270
  help="local owner-map override (PROVEN calibration "
264
271
  "writes here; deep-merges over shipped owners.json)")
272
+ p.add_argument("--project-id", type=int, default=None,
273
+ help="board project for triage-row cards (default: "
274
+ "project_id from the owner map, 9)")
265
275
  p.add_argument("--json", action="store_true")
266
276
  p.add_argument("--as", dest="self_name", default=None,
267
277
  help="DM sender identity")
@@ -298,9 +308,12 @@ def run_triage_cmd(argv):
298
308
  return 2
299
309
  state_file = args.state_file or os.environ.get(
300
310
  "SWARPH_SWEEP_STATE", _sweep._default_state_file())
311
+ project_id = (args.project_id if args.project_id is not None
312
+ else owners_map.get("project_id", 9))
301
313
  backend = _backend.BoardBackend(
302
314
  args.gateway, resolution.token, sender,
303
- _mesh._http_get_json, _mesh._post_json)
315
+ _mesh._http_get_json, _mesh._post_json,
316
+ project_id=project_id)
304
317
  rc, summary = run_triage(
305
318
  gateway=args.gateway, token=resolution.token, sender=sender,
306
319
  owners_map=owners_map, state_file=state_file,
@@ -5,20 +5,36 @@ protocol; production rows live on board cards titled "<owner> triage".
5
5
  from __future__ import annotations
6
6
 
7
7
 
8
+ class BackendError(RuntimeError):
9
+ """A named board failure: what was attempted, the HTTP status, and
10
+ the gateway's own detail. Raised, never a row with id None."""
11
+
12
+
8
13
  class MemoryBackend:
9
- """In-memory double for tests: rows are dicts, posts are dicts."""
14
+ """In-memory double for tests: rows are dicts, posts are dicts.
15
+
16
+ Strict where the gateway is strict (card #1073, #1446): a create
17
+ without a project_id is refused, and a post to a thread that does
18
+ not exist is refused — the old permissive double is what let the
19
+ 0.72.14 live failure pass every test.
20
+ """
10
21
 
11
- def __init__(self):
22
+ def __init__(self, project_id=9):
12
23
  self.rows: dict[str, dict] = {}
13
24
  self.posts: dict[str, list[dict]] = {}
14
25
  self.next_id = 1
26
+ self.project_id = project_id
15
27
 
16
28
  def find_open_row(self, title):
17
29
  return self.rows.get(title)
18
30
 
19
31
  def create_row(self, title, owner):
32
+ if self.project_id is None:
33
+ raise BackendError(
34
+ f"create '{title}': project_id is required — the gateway "
35
+ f"refuses a card create without one (BoardCardCreate)")
20
36
  row = {"id": self.next_id, "title": title, "owner": owner,
21
- "status": "open"}
37
+ "project_id": self.project_id, "status": "open"}
22
38
  self.next_id += 1
23
39
  self.rows[title] = row
24
40
  self.posts[title] = []
@@ -28,6 +44,10 @@ class MemoryBackend:
28
44
  return list(self.posts.get(row["title"], []))
29
45
 
30
46
  def append(self, row, body, kind="item"):
47
+ if row["title"] not in self.posts:
48
+ raise BackendError(
49
+ f"post to '{row['title']}': no such thread — the row was "
50
+ f"never created")
31
51
  post = {"id": self.next_id, "body": body, "kind": kind}
32
52
  self.next_id += 1
33
53
  self.posts[row["title"]].append(post)
@@ -49,12 +69,26 @@ class BoardBackend:
49
69
  gateway errors by returning None/[] (unevaluable, never invented).
50
70
  """
51
71
 
52
- def __init__(self, gateway, token, sender, http_get, http_post):
72
+ def __init__(self, gateway, token, sender, http_get, http_post,
73
+ project_id=9):
53
74
  self.gateway = gateway.rstrip("/")
54
75
  self.token = token
55
76
  self.sender = sender
56
77
  self._http_get = http_get
57
78
  self._http_post = http_post
79
+ self.project_id = project_id
80
+
81
+ @staticmethod
82
+ def _detail(body) -> str:
83
+ if isinstance(body, dict):
84
+ detail = body.get("detail")
85
+ if isinstance(detail, str) and detail.strip():
86
+ return detail.strip()
87
+ return "<no detail>"
88
+
89
+ def _refused(self, what: str, status, body) -> BackendError:
90
+ return BackendError(
91
+ f"{what}: gateway refused {status}: {self._detail(body)}")
58
92
 
59
93
  def find_open_row(self, title):
60
94
  try:
@@ -76,31 +110,50 @@ class BoardBackend:
76
110
  return None
77
111
 
78
112
  def create_row(self, title, owner):
113
+ # BoardCardCreate REQUIRES project_id (card #1073, #1446: the
114
+ # 0.72.14 live run POSTed title+body only, was refused, and the
115
+ # old code carried the refusal forward as a row with id None).
116
+ # The owner is the card assignee, so the row lands in a queue.
117
+ payload = {"actor": self.sender, "project_id": self.project_id,
118
+ "title": title,
119
+ "body": f"Daily triage row for {owner} (card #1073). "
120
+ "Items are appended by the triage job; "
121
+ "owners work them off the row.",
122
+ "assignee": owner}
79
123
  try:
80
124
  status, body = self._http_post(
81
- f"{self.gateway}/board/cards",
82
- {"title": title,
83
- "body": f"Daily triage row for {owner} (card #1073). "
84
- "Items are appended by the triage job; "
85
- "owners work them off the row."},
86
- self.token)
87
- except Exception:
88
- return {"id": None, "title": title, "status": "unknown"}
125
+ f"{self.gateway}/board/cards", payload, self.token)
126
+ except Exception as exc:
127
+ raise BackendError(f"create '{title}': {exc}") from exc
128
+ if status is None or not 200 <= status < 300:
129
+ raise self._refused(f"create '{title}'", status, body)
89
130
  card = body if isinstance(body, dict) else {}
131
+ if not isinstance(card.get("id"), int):
132
+ raise BackendError(
133
+ f"create '{title}': gateway replied {status} with no card "
134
+ f"id: {self._detail(body)}")
90
135
  return {"id": card.get("id"), "title": title, "owner": owner,
91
136
  "status": "open"}
92
137
 
93
138
  def _thread_uuid(self, row):
94
139
  if not row.get("id"):
95
- return None
140
+ raise BackendError(
141
+ f"post to '{row.get('title')}': the row has no board id — "
142
+ f"its create failed and was not reported")
96
143
  try:
97
144
  status, body = self._http_get(
98
145
  f"{self.gateway}/board/cards/{row['id']}", self.token)
99
- except Exception:
100
- return None
146
+ except Exception as exc:
147
+ raise BackendError(
148
+ f"thread of '{row.get('title')}': {exc}") from exc
101
149
  if status != 200 or not isinstance(body, dict):
102
- return None
103
- return body.get("thread_uuid")
150
+ raise self._refused(f"thread of '{row.get('title')}'", status,
151
+ body)
152
+ thread_uuid = body.get("thread_uuid")
153
+ if not thread_uuid:
154
+ raise BackendError(
155
+ f"thread of '{row.get('title')}': card has no thread")
156
+ return thread_uuid
104
157
 
105
158
  def list_posts(self, row):
106
159
  if not row.get("id"):
@@ -121,8 +174,6 @@ class BoardBackend:
121
174
 
122
175
  def append(self, row, body, kind="item"):
123
176
  thread_uuid = self._thread_uuid(row)
124
- if thread_uuid is None:
125
- return {"id": None, "body": body, "kind": kind}
126
177
  try:
127
178
  status, resp = self._http_post(
128
179
  f"{self.gateway}/messages",
@@ -131,7 +182,11 @@ class BoardBackend:
131
182
  "kind": "answer" if kind == "security" else "fyi",
132
183
  "content": body, "thread_id": thread_uuid},
133
184
  self.token)
134
- except Exception:
135
- return {"id": None, "body": body, "kind": kind}
185
+ except Exception as exc:
186
+ raise BackendError(
187
+ f"append to '{row.get('title')}': {exc}") from exc
188
+ if status is None or not 200 <= status < 300:
189
+ raise self._refused(f"append to '{row.get('title')}'", status,
190
+ resp)
136
191
  msg = resp if isinstance(resp, dict) else {}
137
192
  return {"id": msg.get("id"), "body": body, "kind": kind}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 1,
3
- "comment": "Organ triage owner map + calibration thresholds (card #1073, rulings #1413). DATA, not code: the router reads this file and contains no owner or threshold literals. Thresholds are written by a PROVEN calibration; until an organ has one, every model result for it is split. Owners keyed here route to '<owner> triage' rows; an organ with no entry routes to lab-ovh and is counted.",
3
+ "comment": "Organ triage owner map + calibration thresholds (card #1073, rulings #1413). DATA, not code: the router reads this file and contains no owner or threshold literals. Thresholds are written by a PROVEN calibration; until an organ has one, every model result for it is split. Owners keyed here route to '<owner> triage' rows; an organ with no entry routes to lab-ovh and is counted. project_id is the board project triage-row cards are created under (BoardCardCreate requires it; #1446).",
4
4
  "owners": {
5
5
  "stuck-work": {
6
6
  "owner": null,
@@ -19,6 +19,7 @@
19
19
  "note": "pattern: detector calibration -> science-claude"
20
20
  }
21
21
  },
22
+ "project_id": 9,
22
23
  "thresholds": {},
23
24
  "label_sets": {
24
25
  "stuck-work": ["real-stall", "deliberate-wait", "needed-row-closed-race"],
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.72.14
3
+ Version: 0.72.16
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
@@ -216,7 +216,9 @@ tests/test_1064_stuck_sweep.py
216
216
  tests/test_1065_release.py
217
217
  tests/test_1066_quiet_wakes.py
218
218
  tests/test_1073_triage.py
219
+ tests/test_1073_triage_board.py
219
220
  tests/test_1073_wheel_install.py
221
+ tests/test_1075_task_cli.py
220
222
  tests/test_124_register_merge.py
221
223
  tests/test_126_pending_from_complete.py
222
224
  tests/test_132_ensure_monitor_state_dir.py
@@ -0,0 +1,254 @@
1
+ """card #1073 rework (#1446): the router's first live run died on the
2
+ board it never wrote to in tests.
3
+
4
+ 0.72.14 POSTed a card create with title+body only; the gateway refused
5
+ it (BoardCardCreate requires project_id); the old code carried the
6
+ refusal forward as a row with id None and died on the bare 'accept
7
+ line not added' assertion. These tests pin the fix: the create sends
8
+ project_id + assignee, every non-2xx names its status and detail, the
9
+ run exits non-zero saying so, and the double rejects what the gateway
10
+ rejects (a create without project_id, a post to a missing thread).
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import pytest
15
+
16
+ from swarph_cli.commands import triage as _triage_cmd
17
+ from swarph_cli.triage import backend as _backend
18
+ from swarph_cli.triage import router as _router
19
+
20
+ NOW = 1_790_000_000.0
21
+
22
+
23
+ def _iso(age_s):
24
+ from datetime import datetime, timezone
25
+ return datetime.fromtimestamp(NOW - age_s, timezone.utc).isoformat()
26
+
27
+
28
+ class _FakeHttp:
29
+ """Records bodies; scripted (status, body) per URL suffix.
30
+
31
+ Enforces caller-binding like the gateway (card #1073, #1451):
32
+ POST /board/cards requires actor == the sender, POST /messages
33
+ requires from_node == the sender; anything else is refused 403
34
+ with the gateway's shape. Enforcement runs before the scripted
35
+ responses, so a mutant dropping the identity fails every test.
36
+ """
37
+
38
+ def __init__(self, sender="lab-ovh"):
39
+ self.posts = []
40
+ self.scripts = {}
41
+ self.sender = sender
42
+ self.card_threads = {}
43
+ self.thread_messages = {}
44
+ self.next_id = 501
45
+
46
+ def on_post(self, suffix, status, body):
47
+ self.scripts[("POST", suffix)] = (status, body)
48
+
49
+ def on_get(self, suffix, status, body):
50
+ self.scripts[("GET", suffix)] = (status, body)
51
+
52
+ def _binding_refusal(self, field, claimed):
53
+ return (403, {"detail":
54
+ f"caller-binding: authenticated peer {self.sender!r} "
55
+ f"may not act as {field}={claimed!r}"})
56
+
57
+ def post(self, url, payload, token):
58
+ self.posts.append((url, dict(payload)))
59
+ if url.endswith("/board/cards"):
60
+ actor = payload.get("actor")
61
+ if actor != self.sender:
62
+ return self._binding_refusal("actor", actor)
63
+ if url.endswith("/messages"):
64
+ sender = payload.get("from_node")
65
+ if sender != self.sender:
66
+ return self._binding_refusal("from_node", sender)
67
+ for (method, suffix), response in self.scripts.items():
68
+ if method == "POST" and url.endswith(suffix):
69
+ status, body = response
70
+ if url.endswith("/messages") and status is not None and (
71
+ 200 <= status < 300):
72
+ # Like the gateway, a thread-bound post lands in the
73
+ # card thread a later GET .../thread reads back.
74
+ self.thread_messages.setdefault(
75
+ payload.get("thread_id"), []).append(
76
+ {"id": self.next_id,
77
+ "content": payload.get("content"),
78
+ "kind": payload.get("kind")})
79
+ self.next_id += 1
80
+ return response
81
+ raise AssertionError(f"unscripted POST {url}")
82
+
83
+ def get(self, url, token):
84
+ import re
85
+ thread = re.search(r"/board/cards/(\d+)/thread$", url)
86
+ if thread:
87
+ uuid = self.card_threads.get(thread.group(1))
88
+ return 200, {"card_id": int(thread.group(1)),
89
+ "messages": list(
90
+ self.thread_messages.get(uuid, []))}
91
+ for (method, suffix), response in self.scripts.items():
92
+ if method == "GET" and url.endswith(suffix):
93
+ card = re.search(r"/board/cards/(\d+)$", url)
94
+ if card and isinstance(response[1], dict):
95
+ uuid = response[1].get("thread_uuid")
96
+ if uuid:
97
+ self.card_threads[card.group(1)] = uuid
98
+ return response
99
+ raise AssertionError(f"unscripted GET {url}")
100
+
101
+
102
+ def _board(project_id=9):
103
+ http = _FakeHttp()
104
+ return _backend.BoardBackend("http://gw:9", "tok", "lab-ovh",
105
+ http.get, http.post,
106
+ project_id=project_id), http
107
+
108
+
109
+ def test_create_sends_project_id_and_assignee():
110
+ backend, http = _board(project_id=9)
111
+ http.on_post("/board/cards", 200, {"id": 77})
112
+ row = backend.create_row("lab-ovh triage", "lab-ovh")
113
+ assert row["id"] == 77
114
+ _url, payload = http.posts[-1]
115
+ assert payload["project_id"] == 9
116
+ assert payload["assignee"] == "lab-ovh"
117
+ assert payload["title"] == "lab-ovh triage"
118
+ assert payload["actor"] == "lab-ovh"
119
+
120
+
121
+ def test_actor_none_is_refused_like_the_live_run():
122
+ """The 0.72.15 shape: no actor identity on the create is a 403
123
+ naming caller-binding, not a silent row."""
124
+ backend, http = _board()
125
+ backend.sender = None
126
+ with pytest.raises(_backend.BackendError) as exc:
127
+ backend.create_row("lab-ovh triage", "lab-ovh")
128
+ assert "403" in str(exc.value) and "caller-binding" in str(exc.value)
129
+
130
+
131
+ def test_append_without_sender_is_refused():
132
+ backend, http = _board()
133
+ http.on_get("/board/cards/77", 200, {"id": 77,
134
+ "thread_uuid": "thread-1"})
135
+ backend.sender = None
136
+ row = {"id": 77, "title": "lab-ovh triage", "owner": "lab-ovh",
137
+ "status": "open"}
138
+ with pytest.raises(_backend.BackendError) as exc:
139
+ backend.append(row, "ACCEPT x y")
140
+ assert "403" in str(exc.value) and "from_node" in str(exc.value)
141
+
142
+
143
+ def test_create_422_names_status_and_detail():
144
+ backend, http = _board()
145
+ http.on_post("/board/cards", 422, {"detail": "project_id: field required"})
146
+ with pytest.raises(_backend.BackendError) as exc:
147
+ backend.create_row("lab-ovh triage", "lab-ovh")
148
+ assert "422" in str(exc.value)
149
+ assert "project_id: field required" in str(exc.value)
150
+
151
+
152
+ def test_append_422_names_status_and_detail():
153
+ backend, http = _board()
154
+ http.on_get("/board/cards/77", 200, {"id": 77,
155
+ "thread_uuid": "thread-1"})
156
+ http.on_post("/messages", 422, {"detail": "unknown thread"})
157
+ row = {"id": 77, "title": "lab-ovh triage", "owner": "lab-ovh",
158
+ "status": "open"}
159
+ with pytest.raises(_backend.BackendError) as exc:
160
+ backend.append(row, "ACCEPT x y")
161
+ assert "422" in str(exc.value) and "unknown thread" in str(exc.value)
162
+
163
+
164
+ def test_failed_create_is_never_a_row_with_id_none():
165
+ backend, http = _board()
166
+ http.on_post("/board/cards", 500, {"detail": "boom"})
167
+ with pytest.raises(_backend.BackendError):
168
+ backend.create_row("lab-ovh triage", "lab-ovh")
169
+
170
+
171
+ def test_memory_double_rejects_create_without_project_id():
172
+ backend = _backend.MemoryBackend(project_id=None)
173
+ with pytest.raises(_backend.BackendError) as exc:
174
+ backend.create_row("lab-ovh triage", "lab-ovh")
175
+ assert "project_id" in str(exc.value)
176
+
177
+
178
+ def test_memory_double_rejects_post_to_missing_thread():
179
+ backend = _backend.MemoryBackend()
180
+ with pytest.raises(_backend.BackendError) as exc:
181
+ backend.append({"id": 999, "title": "ghost triage",
182
+ "owner": "lab-ovh", "status": "open"}, "ACCEPT x y")
183
+ assert "no such thread" in str(exc.value)
184
+
185
+
186
+ def _board_fixture():
187
+ old = _iso(10 * 86400)
188
+ waiter = {"id": 1, "status": "open", "taken_at": old, "step": "build",
189
+ "card_id": 7, "holder": "waiter"}
190
+ dep = {"id": 2, "status": "open", "holder": "lab-ovh",
191
+ "holder_known": True, "created_at": old, "card_id": 8}
192
+ return [waiter], {1: waiter, 2: dep}, {"lab-ovh"}
193
+
194
+
195
+ def _live_http():
196
+ http = _FakeHttp()
197
+ http.on_get("/board/cards/7/graph", 200, {"steps": [
198
+ {"step": "build", "needs": [
199
+ {"step": "dep", "row_id": 2, "satisfied": False}]}]})
200
+ http.on_get("/board/cards/8", 200, {"id": 8, "due_at": None})
201
+ return http
202
+
203
+
204
+ def test_run_names_the_refusal_and_exits_nonzero(capsys, tmp_path):
205
+ """The 0.72.14 shape end to end: the create is refused, the run
206
+ says so (status + detail) and exits 1 — no bare assertion."""
207
+ backend, http = _board()
208
+ http.on_post("/board/cards", 422,
209
+ {"detail": "project_id: field required"})
210
+ live = _live_http()
211
+ rows, by_id, peers = _board_fixture()
212
+ rc, summary = _triage_cmd.run_triage(
213
+ gateway="http://gw:9", token="tok", sender="lab-ovh",
214
+ owners_map=_router.load_owners(_triage_cmd._OWNERS_DEFAULT),
215
+ state_file=str(tmp_path / "state.json"), store_backend=backend,
216
+ peers_fetcher=None, board_fetcher=lambda: (rows, by_id, peers),
217
+ http_get=live.get, http_post=http.post, now=NOW)
218
+ err = capsys.readouterr().err
219
+ assert rc == 1
220
+ assert "422" in err and "project_id: field required" in err
221
+ assert "accept line not added" not in err
222
+ assert "board_error" in summary
223
+
224
+
225
+ def test_run_dm_carries_sender_identity(tmp_path):
226
+ """Third write path (card #1073, #1451): the one-a-day DM POSTs
227
+ /messages with from_node == the sender. Caller-binding enforcement
228
+ is active on the fake, so a mutant dropping the identity fails
229
+ here (append raises first) or starves dm_sent (DM refused)."""
230
+ http = _FakeHttp()
231
+ http.on_get("/board/cards", 200, {"cards": []})
232
+ http.on_get("/board/cards/7/graph", 200, {"steps": [
233
+ {"step": "build", "needs": [
234
+ {"step": "dep", "row_id": 2, "satisfied": False}]}]})
235
+ http.on_get("/board/cards/8", 200, {"id": 8, "due_at": None})
236
+ http.on_get("/board/cards/77", 200, {"id": 77,
237
+ "thread_uuid": "thread-1"})
238
+ http.on_post("/board/cards", 200, {"id": 77})
239
+ http.on_post("/messages", 200, {"id": 501})
240
+ backend = _backend.BoardBackend(
241
+ "http://gw:9", "tok", "lab-ovh", http.get, http.post,
242
+ project_id=9)
243
+ rows, by_id, peers = _board_fixture()
244
+ rc, summary = _triage_cmd.run_triage(
245
+ gateway="http://gw:9", token="tok", sender="lab-ovh",
246
+ owners_map=_router.load_owners(_triage_cmd._OWNERS_DEFAULT),
247
+ state_file=str(tmp_path / "state.json"), store_backend=backend,
248
+ peers_fetcher=None, board_fetcher=lambda: (rows, by_id, peers),
249
+ http_get=http.get, http_post=http.post, now=NOW)
250
+ assert rc == 0
251
+ assert summary["dm_sent"] == 1
252
+ message_posts = [p for u, p in http.posts if u.endswith("/messages")]
253
+ assert message_posts, "no /messages write happened at all"
254
+ assert all(p.get("from_node") == "lab-ovh" for p in message_posts)
@@ -0,0 +1,24 @@
1
+ """card #1075 — the ask carries a task id, and withdraw is its own act."""
2
+ from swarph_cli.commands import board
3
+
4
+
5
+ def test_ask_payload_sends_task_and_omits_it_when_absent():
6
+ p = board._ask_payload(
7
+ "cursor-lin", "wire it", holder="me", step="build",
8
+ task="task:pr-lab-orchestrator-160",
9
+ task_needs=["task:pr-lab-orchestrator-159"])
10
+ assert p["task"] == "task:pr-lab-orchestrator-160"
11
+ assert p["task_needs"] == ["task:pr-lab-orchestrator-159"]
12
+ assert p["holder"] == "cursor-lin"
13
+ bare = board._ask_payload("cursor-lin", "wire it", step="build")
14
+ assert "task" not in bare and "task_needs" not in bare
15
+
16
+
17
+ def test_withdraw_is_a_command():
18
+ parser = board._build_parser()
19
+ args = parser.parse_args(
20
+ ["obligations", "withdraw", "1422", "--reason", "asker pulls it"])
21
+ assert args.group == "obligations"
22
+ assert args.command == "withdraw"
23
+ assert args.id == 1422
24
+ assert args.reason == "asker pulls it"