swarph-cli 0.72.43__tar.gz → 0.72.45__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 (576) hide show
  1. {swarph_cli-0.72.43/src/swarph_cli.egg-info → swarph_cli-0.72.45}/PKG-INFO +1 -1
  2. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/pyproject.toml +1 -1
  3. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/__init__.py +1 -1
  4. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/event.py +51 -4
  5. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/mcp_server.py +9 -3
  6. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/mesh.py +8 -1
  7. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/triage.py +12 -6
  8. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/redact.py +6 -0
  9. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/scrub.py +7 -0
  10. {swarph_cli-0.72.43 → swarph_cli-0.72.45/src/swarph_cli.egg-info}/PKG-INFO +1 -1
  11. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli.egg-info/SOURCES.txt +2 -0
  12. swarph_cli-0.72.45/tests/test_1472_redact_boundaries.py +218 -0
  13. swarph_cli-0.72.45/tests/test_1478_triage_dm.py +99 -0
  14. swarph_cli-0.72.45/tests/test_event_emit.py +166 -0
  15. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_mcp_server.py +39 -0
  16. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_scrub.py +6 -0
  17. swarph_cli-0.72.43/tests/test_event_emit.py +0 -36
  18. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/LICENSE +0 -0
  19. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/README.md +0 -0
  20. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/setup.cfg +0 -0
  21. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/__main__.py +0 -0
  22. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/bench/__init__.py +0 -0
  23. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/bench/backends.py +0 -0
  24. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/bench/claude_cli.py +0 -0
  25. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/bench/codex_cli.py +0 -0
  26. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/bench/pack.py +0 -0
  27. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/bench/prices.py +0 -0
  28. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/bench/providers.py +0 -0
  29. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/bench/quality.py +0 -0
  30. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/bench/refresh_prices.py +0 -0
  31. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/bench/rules.py +0 -0
  32. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/bench/runner.py +0 -0
  33. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/bench/signal_score.py +0 -0
  34. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/bench/validate.py +0 -0
  35. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/caller.py +0 -0
  36. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/capture/__init__.py +0 -0
  37. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/capture/harden.py +0 -0
  38. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/capture/lineage.py +0 -0
  39. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/capture/liveness.py +0 -0
  40. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/capture/manifest.py +0 -0
  41. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/capture/paths.py +0 -0
  42. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/capture/verify.py +0 -0
  43. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/cell.py +0 -0
  44. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/chain_token.py +0 -0
  45. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/channel.py +0 -0
  46. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/__init__.py +0 -0
  47. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/_content.py +0 -0
  48. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/_display.py +0 -0
  49. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/_gateway_client.py +0 -0
  50. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/add.py +0 -0
  51. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/bench.py +0 -0
  52. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/board.py +0 -0
  53. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/board_cost.py +0 -0
  54. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/board_merge_check.py +0 -0
  55. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/board_usage.py +0 -0
  56. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/brain.py +0 -0
  57. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/brain_ask.py +0 -0
  58. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/cell.py +0 -0
  59. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/cell_probe.py +0 -0
  60. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  61. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/channel.py +0 -0
  62. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/chat.py +0 -0
  63. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/codegraph.py +0 -0
  64. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/codegraph_hook.py +0 -0
  65. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/codex_hook_output.py +0 -0
  66. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/codex_waker.py +0 -0
  67. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/commander.py +0 -0
  68. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/committee.py +0 -0
  69. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/compress.py +0 -0
  70. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/daemon.py +0 -0
  71. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/dev_extract.py +0 -0
  72. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/dreaming.py +0 -0
  73. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/gateway.py +0 -0
  74. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/gh_route.py +0 -0
  75. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/group.py +0 -0
  76. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/guide.py +0 -0
  77. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/highlight.py +0 -0
  78. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/hook_interpreter.py +0 -0
  79. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/hook_output.py +0 -0
  80. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/hooks.py +0 -0
  81. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/import_session.py +0 -0
  82. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/init.py +0 -0
  83. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
  84. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/install_hook.py +0 -0
  85. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  86. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
  87. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
  88. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/install_wake_hook.py +0 -0
  89. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/lane.py +0 -0
  90. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/memory.py +0 -0
  91. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
  92. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/memory_sync.py +0 -0
  93. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/monitor.py +0 -0
  94. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/okf_links.py +0 -0
  95. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/onboard.py +0 -0
  96. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
  97. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
  98. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/protocol_handler.py +0 -0
  99. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/ratify.py +0 -0
  100. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/roster.py +0 -0
  101. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/rowclear_hook.py +0 -0
  102. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/schedule.py +0 -0
  103. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/security.py +0 -0
  104. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/service.py +0 -0
  105. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/skunkworks.py +0 -0
  106. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/spawn.py +0 -0
  107. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/sweep.py +0 -0
  108. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/timeline.py +0 -0
  109. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/version.py +0 -0
  110. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/wake_hook_output.py +0 -0
  111. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commands/watchdog.py +0 -0
  112. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/commit_trailers.py +0 -0
  113. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/compress/__init__.py +0 -0
  114. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/compress/levers.py +0 -0
  115. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/compress/marker.py +0 -0
  116. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/compress/verify.py +0 -0
  117. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/console_safe.py +0 -0
  118. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/delivery_queue.py +0 -0
  119. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/dm_frame.py +0 -0
  120. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/__init__.py +0 -0
  121. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/candidates.py +0 -0
  122. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/clone.py +0 -0
  123. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/enrich.py +0 -0
  124. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/index_check.py +0 -0
  125. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/notify.py +0 -0
  126. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/organize.py +0 -0
  127. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/probes.py +0 -0
  128. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/report.py +0 -0
  129. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/run.py +0 -0
  130. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/slm.py +0 -0
  131. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/transcripts.py +0 -0
  132. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/verify.py +0 -0
  133. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/duplicate_session.py +0 -0
  134. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/gateway/__init__.py +0 -0
  135. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/gateway/feature_registry.py +0 -0
  136. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/gateway/lanes_control.py +0 -0
  137. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/gateway/schema.sql +0 -0
  138. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/gateway/server.py +0 -0
  139. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/gateway/services_control.py +0 -0
  140. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/gateway_default.py +0 -0
  141. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/gh_identity.py +0 -0
  142. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/guide/GUIDE.md +0 -0
  143. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/identity.py +0 -0
  144. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/launchd/swarph-codex-outbox.plist +0 -0
  145. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/launchd/swarph-codex-outbox.timer.plist +0 -0
  146. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/launchd/swarph-monitor.plist +0 -0
  147. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/launchd/swarph-watchdog.plist +0 -0
  148. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/launchd/swarph-watchdog.timer.plist +0 -0
  149. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/launchd_render.py +0 -0
  150. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/main.py +0 -0
  151. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/multiplexer.py +0 -0
  152. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/operator/__init__.py +0 -0
  153. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/operator/__main__.py +0 -0
  154. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/operator/api.py +0 -0
  155. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/operator/cli.py +0 -0
  156. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/operator/config.py +0 -0
  157. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/operator/fmt.py +0 -0
  158. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/orphan_daemons.py +0 -0
  159. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/pane_probe.py +0 -0
  160. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/parsers/__init__.py +0 -0
  161. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/parsers/claude.py +0 -0
  162. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
  163. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
  164. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
  165. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
  166. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
  167. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
  168. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
  169. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
  170. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
  171. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
  172. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
  173. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
  174. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/peer_executor.py +0 -0
  175. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/peer_executor_windows.py +0 -0
  176. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/peer_reconciliation.py +0 -0
  177. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/peer_reply_outbox.py +0 -0
  178. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/peer_service_host.py +0 -0
  179. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/peer_service_payload.py +0 -0
  180. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/peer_service_reply.py +0 -0
  181. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/peer_staging.py +0 -0
  182. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/query_filters.py +0 -0
  183. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/quota_sync.py +0 -0
  184. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/rowclear.py +0 -0
  185. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/scripts/__init__.py +0 -0
  186. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
  187. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  188. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
  189. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
  190. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
  191. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/service/__init__.py +0 -0
  192. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/service/app.py +0 -0
  193. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/service/providers.py +0 -0
  194. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/session_bridge.py +0 -0
  195. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
  196. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
  197. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/source_text.py +0 -0
  198. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/stall_alert.py +0 -0
  199. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/systemd/codex-waker.default +0 -0
  200. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
  201. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
  202. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
  203. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
  204. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
  205. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
  206. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
  207. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
  208. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
  209. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
  210. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-stuck-sweep.service +0 -0
  211. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-stuck-sweep.timer +0 -0
  212. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-triage.service +0 -0
  213. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-triage.timer +0 -0
  214. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  215. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  216. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  217. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/the_ten.py +0 -0
  218. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/timeline_paths.py +0 -0
  219. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/tokens.py +0 -0
  220. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/triage/__init__.py +0 -0
  221. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/triage/backend.py +0 -0
  222. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/triage/job.py +0 -0
  223. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/triage/owners.json +0 -0
  224. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/triage/router.py +0 -0
  225. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/triage/rows.py +0 -0
  226. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli/triage/sensor.py +0 -0
  227. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  228. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  229. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli.egg-info/requires.txt +0 -0
  230. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/src/swarph_cli.egg-info/top_level.txt +0 -0
  231. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1006_board_publisher.py +0 -0
  232. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1006_session_board.py +0 -0
  233. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1010_strand_only.py +0 -0
  234. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1013_spawn_channel.py +0 -0
  235. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1017_muse_tui.py +0 -0
  236. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1034_copy_mode.py +0 -0
  237. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1050_surrogate_wake.py +0 -0
  238. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1052_opencode_deaf.py +0 -0
  239. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1059_watchdog_unread_env.py +0 -0
  240. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1061_roster_cli.py +0 -0
  241. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1064_stuck_sweep.py +0 -0
  242. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1065_release.py +0 -0
  243. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1066_quiet_wakes.py +0 -0
  244. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1068_structured_accept_cli.py +0 -0
  245. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1071_quota_sync.py +0 -0
  246. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1073_triage.py +0 -0
  247. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1073_triage_board.py +0 -0
  248. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1073_wheel_install.py +0 -0
  249. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1075_task_cli.py +0 -0
  250. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1083_openai_decisions.py +0 -0
  251. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1144_cell_yaml_github.py +0 -0
  252. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1197_commander_taps.py +0 -0
  253. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1203_swarph_me_refusal.py +0 -0
  254. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1206_offer_question.py +0 -0
  255. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1214_egress.py +0 -0
  256. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1219_egress_probe.py +0 -0
  257. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1221_quota_windows.py +0 -0
  258. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1222_maybe_start_cap.py +0 -0
  259. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1228_quota_reach.py +0 -0
  260. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1243_the_ten.py +0 -0
  261. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1248_ship_price_table.py +0 -0
  262. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_124_register_merge.py +0 -0
  263. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1250_price_cache.py +0 -0
  264. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1256_commander_wake.py +0 -0
  265. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1261_mcp_install.py +0 -0
  266. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1262_wake_followups.py +0 -0
  267. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1265_wake_note.py +0 -0
  268. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1269_triage_label.py +0 -0
  269. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_126_pending_from_complete.py +0 -0
  270. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1277_close_usage.py +0 -0
  271. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1278_dreaming_reach.py +0 -0
  272. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1286_dm_unread_cursor.py +0 -0
  273. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1297_commander.py +0 -0
  274. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1300_authorization_check.py +0 -0
  275. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1301_restart_skew.py +0 -0
  276. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1320_commit_trailers.py +0 -0
  277. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1321_layered_redaction.py +0 -0
  278. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_132_ensure_monitor_state_dir.py +0 -0
  279. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1340_router_rollout.py +0 -0
  280. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1346_shell_string_gh.py +0 -0
  281. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1347_shell_depth_cap.py +0 -0
  282. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1353_obligation_release_cli.py +0 -0
  283. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1385_duplicate_session_argv.py +0 -0
  284. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_138_memory_navigate.py +0 -0
  285. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1396_dev_extract.py +0 -0
  286. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_139_tool_probe.py +0 -0
  287. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1405_skunkworks_basic.py +0 -0
  288. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1405_skunkworks_cli.py +0 -0
  289. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1427_membrane_workflow.py +0 -0
  290. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1436_dev_extract_freshness.py +0 -0
  291. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1452_rework1_engine.py +0 -0
  292. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1452_rework2_engine.py +0 -0
  293. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1452_rework3_engine.py +0 -0
  294. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1452_standing_wake_expiry.py +0 -0
  295. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_1470_gh_router_fail_closed.py +0 -0
  296. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_183_card_anchor.py +0 -0
  297. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_184_notify_honesty.py +0 -0
  298. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_201_win32_bash_refusal.py +0 -0
  299. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_243_onboard_per_peer_token.py +0 -0
  300. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_307_task4_ask_reply.py +0 -0
  301. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
  302. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_3130_sender.py +0 -0
  303. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_3150_identity_router_install.py +0 -0
  304. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_319_refusal_exit_codes.py +0 -0
  305. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_323_mistral_backend.py +0 -0
  306. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_332_report_organize_findings.py +0 -0
  307. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_356_query_filters.py +0 -0
  308. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_397_gh_identity_router.py +0 -0
  309. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_397_router_preflight.py +0 -0
  310. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_402_one_identity_resolver.py +0 -0
  311. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_432_rowclear.py +0 -0
  312. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_467b_onboard_operator_credential.py +0 -0
  313. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_472_register_caller_binding.py +0 -0
  314. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_484_codex_queue_sink.py +0 -0
  315. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_513_duplicate_session.py +0 -0
  316. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_523_guide.py +0 -0
  317. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_524_conftest_hermeticity.py +0 -0
  318. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_532_accept_flag.py +0 -0
  319. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_535_version_capability.py +0 -0
  320. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_538_signal_bench.py +0 -0
  321. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_546_default_gateway.py +0 -0
  322. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_547_verbs_from_registry.py +0 -0
  323. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_548_gateway_remainder.py +0 -0
  324. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_552_codegraph_gateway.py +0 -0
  325. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_555_relay_row_shape.py +0 -0
  326. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_556_relay_freshness.py +0 -0
  327. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_559_edges_truncated.py +0 -0
  328. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_564_bootstrap_first_peer.py +0 -0
  329. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
  330. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_579_register_bootstrap_message.py +0 -0
  331. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_591_cli_step_graph.py +0 -0
  332. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_644_monitor_supervision.py +0 -0
  333. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_648_install_unit.py +0 -0
  334. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_654_reexec.py +0 -0
  335. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_656_owned_slm.py +0 -0
  336. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_684_dreaming_verb.py +0 -0
  337. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_689_pkg_version_order.py +0 -0
  338. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_715_grok_wake.py +0 -0
  339. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_716_timeline_one_path.py +0 -0
  340. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_723_grok_0251.py +0 -0
  341. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_724_ps1_ascii_or_bom.py +0 -0
  342. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_725_stdio_reconfigure.py +0 -0
  343. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_726_grok_limit_screen.py +0 -0
  344. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_729_arm_check_delivery_gap.py +0 -0
  345. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_729_dm_once.py +0 -0
  346. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_729_wake_watchdog.py +0 -0
  347. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_734_dreaming_enrich_degrade.py +0 -0
  348. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_735_dm_mcp_tools.py +0 -0
  349. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_739_wake_writer.py +0 -0
  350. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_740_edit_project.py +0 -0
  351. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_762_cards_edit_priority.py +0 -0
  352. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_764_enrich_collapse.py +0 -0
  353. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_801_confirm_flow_cli.py +0 -0
  354. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_802_stage_history.py +0 -0
  355. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_825_codegraph_hook_noise.py +0 -0
  356. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_825_install_posttooluse_only.py +0 -0
  357. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_825_remaining_cap_shred_bind.py +0 -0
  358. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_829_counterfactual_column.py +0 -0
  359. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_830_hook_binding_version.py +0 -0
  360. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_84_cli_labels.py +0 -0
  361. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_866_doctrine_guide.py +0 -0
  362. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_872_identity_refuses_not_warns.py +0 -0
  363. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_874_source_text.py +0 -0
  364. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
  365. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_931_board_search.py +0 -0
  366. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_935_deadline_clock.py +0 -0
  367. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_937_dreaming_notify.py +0 -0
  368. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_938_organize_line_limit.py +0 -0
  369. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_941_bench_backends.py +0 -0
  370. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_941_harness_342.py +0 -0
  371. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_945_same_line_pairing.py +0 -0
  372. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_955_prompt_audit.py +0 -0
  373. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_957_auth_http.py +0 -0
  374. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_959_venv_interpreter.py +0 -0
  375. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_960_channel.py +0 -0
  376. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
  377. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_961_cursor_grok_footer.py +0 -0
  378. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_961_loud_stop.py +0 -0
  379. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_963_plugin_manifest.py +0 -0
  380. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_964_channel_cell.py +0 -0
  381. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_965_mesh_wait.py +0 -0
  382. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_975_channel_hop.py +0 -0
  383. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_add_security_gate.py +0 -0
  384. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_artifact_add.py +0 -0
  385. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_artifact_hash.py +0 -0
  386. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_artifact_lib.py +0 -0
  387. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_artifact_mcp.py +0 -0
  388. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_artifact_skill.py +0 -0
  389. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_artifact_tool.py +0 -0
  390. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_artifact_uri.py +0 -0
  391. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_bench_backends.py +0 -0
  392. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_bench_claude_cli.py +0 -0
  393. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_bench_codex_cli.py +0 -0
  394. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_bench_command.py +0 -0
  395. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_bench_pack.py +0 -0
  396. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_bench_prices.py +0 -0
  397. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_bench_quality.py +0 -0
  398. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_bench_runner.py +0 -0
  399. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_bench_validate.py +0 -0
  400. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_board_card_thread.py +0 -0
  401. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_board_command.py +0 -0
  402. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_board_merge_check.py +0 -0
  403. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_board_obligation_close.py +0 -0
  404. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_brain_ask_command.py +0 -0
  405. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_brain_ask_env_diagnosis.py +0 -0
  406. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_brain_ask_identity_explicitness.py +0 -0
  407. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_brain_command.py +0 -0
  408. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_caller_meta_guard.py +0 -0
  409. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_capture_lineage.py +0 -0
  410. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_capture_liveness.py +0 -0
  411. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_capture_manifest.py +0 -0
  412. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_capture_paths.py +0 -0
  413. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_capture_security.py +0 -0
  414. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_cell_command_dispatch.py +0 -0
  415. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_cell_harden.py +0 -0
  416. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_cell_loader.py +0 -0
  417. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_cell_selfcheck.py +0 -0
  418. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_cell_verify.py +0 -0
  419. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_chain_token.py +0 -0
  420. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_channel_command.py +0 -0
  421. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_channel_poll_never_breaks_dms.py +0 -0
  422. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_channel_post.py +0 -0
  423. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_channel_wake_policy_filter.py +0 -0
  424. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_chat_command.py +0 -0
  425. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_claude_parser.py +0 -0
  426. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_claude_tmux_template.py +0 -0
  427. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_codegraph_caller_scope.py +0 -0
  428. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_codegraph_command.py +0 -0
  429. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_codegraph_hook.py +0 -0
  430. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_codegraph_mcp.py +0 -0
  431. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_codex_waker.py +0 -0
  432. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_committee_command.py +0 -0
  433. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_compress_caller_convention.py +0 -0
  434. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_compress_command.py +0 -0
  435. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_compress_levers.py +0 -0
  436. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_compress_marker.py +0 -0
  437. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_compress_verify.py +0 -0
  438. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_console_encoding_e2e.py +0 -0
  439. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_content_composition.py +0 -0
  440. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_copilot_membrane.py +0 -0
  441. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_copilot_pane_reader.py +0 -0
  442. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_cursor_membrane.py +0 -0
  443. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_daemon_command.py +0 -0
  444. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_daemon_console_encoding.py +0 -0
  445. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_daemon_delivery.py +0 -0
  446. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_delivery_queue.py +0 -0
  447. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_display_sanitize.py +0 -0
  448. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_ensure_monitor_no_peer_default.py +0 -0
  449. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_feature_to_uri.py +0 -0
  450. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_gateway_brain.py +0 -0
  451. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_gateway_client.py +0 -0
  452. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_gateway_command.py +0 -0
  453. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_gateway_message_idempotency.py +0 -0
  454. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_gh_route_doctor_mesh_name.py +0 -0
  455. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_group_command.py +0 -0
  456. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_guide_commands_parse.py +0 -0
  457. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_guide_selfcheck.py +0 -0
  458. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_heartbeat_check.py +0 -0
  459. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_highlight_command.py +0 -0
  460. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_hook_output.py +0 -0
  461. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_hooks_activity_marker.py +0 -0
  462. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_hooks_add.py +0 -0
  463. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_hooks_bundle.py +0 -0
  464. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_hooks_init.py +0 -0
  465. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_hooks_legacy_generation_removal.py +0 -0
  466. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_hooks_lifecycle.py +0 -0
  467. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_hooks_merge.py +0 -0
  468. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_hooks_verify.py +0 -0
  469. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_hooks_win32_bash_wrapper.py +0 -0
  470. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_hooks_win32_command_path.py +0 -0
  471. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_import_command.py +0 -0
  472. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_inbox_identity_provenance.py +0 -0
  473. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_init_command.py +0 -0
  474. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_init_provider_menu.py +0 -0
  475. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_install_codex_hooks.py +0 -0
  476. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_install_hook.py +0 -0
  477. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_install_multiplexer_command.py +0 -0
  478. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_install_postcompact_hook.py +0 -0
  479. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_install_wake_hook.py +0 -0
  480. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_lane_command.py +0 -0
  481. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_launchd_units.py +0 -0
  482. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_main.py +0 -0
  483. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_main_unknown_verb_guard.py +0 -0
  484. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_mcp_missing_hint.py +0 -0
  485. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_memory_command.py +0 -0
  486. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_memory_emit_snippet.py +0 -0
  487. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_memory_gateway.py +0 -0
  488. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_memory_sync.py +0 -0
  489. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_memory_sync_membrane.py +0 -0
  490. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_mesh_command.py +0 -0
  491. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_mesh_inbox_mark_read.py +0 -0
  492. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_mesh_inbox_sanitize.py +0 -0
  493. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_mesh_originate_thread_623.py +0 -0
  494. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_mesh_send_unknown_peer.py +0 -0
  495. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_mesh_sidecar.py +0 -0
  496. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  497. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_mesh_sidecar_muse_sink.py +0 -0
  498. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  499. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_meta_edge_identity.py +0 -0
  500. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_monitor_channel_poll.py +0 -0
  501. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_monitor_cli.py +0 -0
  502. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_monitor_daemon_collision.py +0 -0
  503. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_monitor_self_name_verification.py +0 -0
  504. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_monitor_state_split.py +0 -0
  505. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_multiplexer.py +0 -0
  506. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_muse_membrane.py +0 -0
  507. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_muse_own_cli.py +0 -0
  508. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_obligation_close_is_surfaced.py +0 -0
  509. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_okf_links.py +0 -0
  510. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_onboard_command.py +0 -0
  511. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_opencode_membrane.py +0 -0
  512. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_opencode_plugin.py +0 -0
  513. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_operator_me.py +0 -0
  514. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_orphan_daemons.py +0 -0
  515. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_packaged_artifacts_exist.py +0 -0
  516. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_packaged_skills.py +0 -0
  517. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_peer_executor.py +0 -0
  518. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_peer_executor_windows.py +0 -0
  519. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_peer_reply_drain_command.py +0 -0
  520. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_peer_reply_drain_supervisor.py +0 -0
  521. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_peer_reply_outbox.py +0 -0
  522. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_peer_reply_transport.py +0 -0
  523. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_peer_service_e2e.py +0 -0
  524. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_peer_service_host.py +0 -0
  525. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_peer_service_payload.py +0 -0
  526. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_peer_service_reply.py +0 -0
  527. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_postcompact_recall.py +0 -0
  528. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_process_liveness.py +0 -0
  529. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_protocol_handler.py +0 -0
  530. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_providers_isolation.py +0 -0
  531. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_ratify_command.py +0 -0
  532. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_readme_claims_no_gateway_default.py +0 -0
  533. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_repl_caller_convention.py +0 -0
  534. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_schedule_command.py +0 -0
  535. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_schedule_create_contract.py +0 -0
  536. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_security.py +0 -0
  537. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_service_app.py +0 -0
  538. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_service_command.py +0 -0
  539. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_service_providers.py +0 -0
  540. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_session_bridge_inject.py +0 -0
  541. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_session_bridge_probe.py +0 -0
  542. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_session_bridge_resolve.py +0 -0
  543. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_session_path_role_gate.py +0 -0
  544. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_shipped_units_match_the_cli.py +0 -0
  545. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_smoke_chat.py +0 -0
  546. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_smoke_one_shot.py +0 -0
  547. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_smoke_phase_5_5.py +0 -0
  548. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_spawn_base_hoist.py +0 -0
  549. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_spawn_command.py +0 -0
  550. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_spawn_identity_stamp.py +0 -0
  551. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_spawn_live_pin.py +0 -0
  552. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_spawn_mitosis_lineage.py +0 -0
  553. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_spawn_tmux_session.py +0 -0
  554. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_spawn_windows_relaunch.py +0 -0
  555. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_stall_alert.py +0 -0
  556. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_subprocess_decode_encoding.py +0 -0
  557. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_timeline_command.py +0 -0
  558. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_tmux_wake_runstate_defer.py +0 -0
  559. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_tmux_wake_session_reanchor.py +0 -0
  560. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_tmux_wake_stale.py +0 -0
  561. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_tmux_wake_submit_verify.py +0 -0
  562. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_token_file_one_parser.py +0 -0
  563. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_token_precedence_explicit_first.py +0 -0
  564. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_unified_token_resolver.py +0 -0
  565. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_version_command.py +0 -0
  566. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_version_consistency.py +0 -0
  567. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_vibe_membrane.py +0 -0
  568. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_watchdog.py +0 -0
  569. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_watchdog_dm_wake.py +0 -0
  570. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  571. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  572. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_watchdog_emit_health.py +0 -0
  573. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_watchdog_install_flags.py +0 -0
  574. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_watchdog_model_rung.py +0 -0
  575. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/tests/test_watchdog_stale_peers.py +0 -0
  576. {swarph_cli-0.72.43 → swarph_cli-0.72.45}/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.43
3
+ Version: 0.72.45
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.43"
7
+ version = "0.72.45"
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.43"
19
+ __version__ = "0.72.45"
20
20
 
21
21
  __all__ = ["__version__"]
@@ -20,7 +20,7 @@ from ._gateway_client import (
20
20
  resolve_self_name,
21
21
  resolve_token,
22
22
  )
23
- from swarph_cli.redact import redact_text
23
+ from swarph_cli.redact import encode_outbound_json, redact_text
24
24
 
25
25
 
26
26
  def _build_parser() -> argparse.ArgumentParser:
@@ -47,6 +47,34 @@ def _build_parser() -> argparse.ArgumentParser:
47
47
  return p
48
48
 
49
49
 
50
+ class ChainTokenAttachError(RuntimeError):
51
+ """The redacted event bytes are not JSON, so the chain token is not sent."""
52
+
53
+
54
+ def _attach_chain_token(data: bytes, chain_token: str) -> bytes:
55
+ """Add the CLI chain token onto an already-redacted event body.
56
+
57
+ Same shape as ``mcp_server._search``: redact first, then set the one
58
+ field that must reach the wire. A pasted chain_token line in the payload
59
+ was already scrubbed and is not this value.
60
+
61
+ Redaction can rewrite a quote inside the content string and leave bytes
62
+ that are not JSON. That refusal is by name (``JSONDecodeError`` or
63
+ ``UnicodeDecodeError``). The token is not copied into the error, and the
64
+ caller sends nothing.
65
+ """
66
+ try:
67
+ body_obj = json.loads(data.decode("utf-8"))
68
+ inner = json.loads(body_obj["content"])
69
+ except (UnicodeDecodeError, json.JSONDecodeError) as exc:
70
+ raise ChainTokenAttachError(
71
+ f"_attach_chain_token refused: {type(exc).__name__}"
72
+ ) from None
73
+ inner["swarph_event"]["chain_token"] = chain_token
74
+ body_obj["content"] = json.dumps(inner, separators=(",", ":"), sort_keys=True)
75
+ return json.dumps(body_obj).encode("utf-8")
76
+
77
+
50
78
  def _post_channel(
51
79
  *,
52
80
  channel: str,
@@ -76,8 +104,6 @@ def _post_channel(
76
104
  base = (gateway or env_gateway()).rstrip("/")
77
105
  payload = redact_text(payload, caller="event.emit") or ""
78
106
  ev: dict = {"event": event, "payload": payload}
79
- if chain_token is not None: # carry an explicit "" through; only omit when unset
80
- ev["chain_token"] = chain_token
81
107
  # Sentinel-wrapped so P1's guard can UNAMBIGUOUSLY identify an event post: a
82
108
  # normal channel message won't carry the top-level `swarph_event` key, so the
83
109
  # guard keys off `content.swarph_event` — never a false-positive on a plain
@@ -91,7 +117,28 @@ def _post_channel(
91
117
  "kind": "fyi",
92
118
  "content": json.dumps(content, separators=(",", ":"), sort_keys=True),
93
119
  }
94
- status, resp = post_json(f"{base}/messages", body, token)
120
+ # Redact with no chain token in the body. Attach the CLI value after, so
121
+ # only that value skips redaction. An explicit "" is carried; omit only
122
+ # when the flag was unset. Card #1472.
123
+ if chain_token is None:
124
+ status, resp = post_json(f"{base}/messages", body, token)
125
+ else:
126
+ # Attach after redaction. If those bytes are not JSON, name the
127
+ # refusal and return before post_json: nothing is sent, no traceback.
128
+ try:
129
+ encoded = _attach_chain_token(
130
+ encode_outbound_json(body, caller="event.emit"),
131
+ chain_token,
132
+ )
133
+ except ChainTokenAttachError as exc:
134
+ print(f"swarph event: {exc}", file=sys.stderr)
135
+ return 2
136
+ status, resp = post_json(
137
+ f"{base}/messages",
138
+ body,
139
+ token,
140
+ encoded=encoded,
141
+ )
95
142
  if 200 <= status < 300:
96
143
  print(f"emitted event={event} channel={channel} id={resp.get('id')}")
97
144
  return 0
@@ -89,10 +89,16 @@ def _search(query: str, *, url: str, token: str | None = None) -> list[dict]:
89
89
  transport exception.
90
90
  """
91
91
  endpoint = url.rstrip("/") + "/api/ask"
92
- payload: dict = {"message": query}
92
+ # Redact the query only. The auth token is the credential this call
93
+ # presents to the search face. Putting it in the dict that
94
+ # encode_outbound_json walks replaces a credential-shaped value
95
+ # (secrets.token_urlsafe(32), the gateway mint) with
96
+ # [REDACTED:HIGH_ENTROPY] before the request is sent. Card #1472.
97
+ data = encode_outbound_json({"message": query}, caller="mcp_server._search")
93
98
  if token:
94
- payload["token"] = token
95
- data = encode_outbound_json(payload, caller="mcp_server._search")
99
+ body_obj = json.loads(data.decode("utf-8"))
100
+ body_obj["token"] = token
101
+ data = json.dumps(body_obj).encode("utf-8")
96
102
  req = urllib.request.Request(
97
103
  endpoint,
98
104
  data=data,
@@ -444,9 +444,16 @@ def _post_json(
444
444
  token: str,
445
445
  *,
446
446
  timeout: float = 10.0,
447
+ encoded: bytes | None = None,
447
448
  ) -> tuple[int, dict]:
448
449
  _require_absolute_gateway_url(url)
449
- data = encode_outbound_json(body, caller="mesh._post_json")
450
+ # event.emit redacts with no chain token, then passes the bytes with the
451
+ # CLI --chain-token already attached. Encoding those bytes again would
452
+ # strip that one value. Every other caller leaves encoded unset.
453
+ if encoded is None:
454
+ data = encode_outbound_json(body, caller="mesh._post_json")
455
+ else:
456
+ data = encoded
450
457
  req = urllib.request.Request(
451
458
  url,
452
459
  data=data,
@@ -62,8 +62,9 @@ def _stuck_records(rows, by_id, peers, http_get, gateway, token, now,
62
62
  if waiter.get("status") != "open" or not waiter.get("taken_at"):
63
63
  continue
64
64
  step = waiter.get("step")
65
- if not step:
66
- continue
65
+ # A waiter with no step still matches a graph entry whose step is
66
+ # the same empty value. The DM prints a dash, not None (#1478).
67
+ waiter_step = step if step else "-"
67
68
  card_id = waiter.get("card_id")
68
69
  if not isinstance(card_id, int) or isinstance(card_id, bool):
69
70
  continue
@@ -91,6 +92,8 @@ def _stuck_records(rows, by_id, peers, http_get, gateway, token, now,
91
92
  base = {
92
93
  "source_organ": _sensor.ORGAN_STUCK,
93
94
  "waiter_id": waiter.get("id"),
95
+ "waiter": waiter.get("holder"),
96
+ "waiter_step": waiter_step,
94
97
  "card_id": card_id,
95
98
  "need": need.get("step"),
96
99
  "need_id": rid,
@@ -109,6 +112,7 @@ def _stuck_records(rows, by_id, peers, http_get, gateway, token, now,
109
112
  holder = dep.get("holder")
110
113
  blocker = (holder if holder and dep.get("holder_known", True)
111
114
  else None)
115
+ age_days = None
112
116
  if dep.get("status") == "closed":
113
117
  classified = _sensor.map_stuck_finding(
114
118
  {"blocker": blocker, "id": waiter.get("id"),
@@ -118,6 +122,7 @@ def _stuck_records(rows, by_id, peers, http_get, gateway, token, now,
118
122
  changed = _sweep._row_last_change(dep)
119
123
  if changed is None or now - changed < _sweep._STUCK_H * 3600:
120
124
  continue
125
+ age_days = _sweep._age_days(now, changed)
121
126
  need_card_id = dep.get("card_id")
122
127
  due_at = (_need_card_due_at(http_get, gateway, token,
123
128
  need_card_id, card_cache)
@@ -135,7 +140,7 @@ def _stuck_records(rows, by_id, peers, http_get, gateway, token, now,
135
140
  evidence=_sensor.stuck_evidence(waiter.get("id"), rid),
136
141
  item_id=(f"stuck:{waiter.get('id')}:{rid}:"
137
142
  f"{need.get('step')}"),
138
- security=False)
143
+ security=False, age_days=age_days)
139
144
  records.append(record)
140
145
  return records
141
146
 
@@ -175,16 +180,17 @@ def run_triage(*, gateway, token, sender, owners_map, state_file,
175
180
  sent = _load_state(state_file)
176
181
 
177
182
  def dm_sender(record, owner):
183
+ raw_step = record.get("waiter_step")
178
184
  finding = {
179
185
  "kind": "row",
180
186
  "waiter_id": record.get("waiter_id"),
181
- "waiter": None,
182
- "waiter_step": None,
187
+ "waiter": record.get("waiter"),
188
+ "waiter_step": raw_step if raw_step else "-",
183
189
  "card_id": record.get("card_id"),
184
190
  "need": record.get("need"),
185
191
  "need_id": record.get("need_id"),
186
192
  "blocker": record.get("owner_hint"),
187
- "age_days": None,
193
+ "age_days": record.get("age_days"),
188
194
  }
189
195
  key = _sweep._finding_key(finding)
190
196
  try:
@@ -132,6 +132,11 @@ _ANTHROPIC_RE = re.compile(r"\bsk-ant-[A-Za-z0-9\-_]{8,}\b")
132
132
  _OPENAI_RE = re.compile(r"\bsk-(?!ant-)(?:proj-|svcacct-)?[A-Za-z0-9\-_]{16,}\b")
133
133
  _XAI_RE = re.compile(r"\bxai-[A-Za-z0-9\-_]{16,}\b")
134
134
  _GCP_RE = re.compile(r"\bAIza[0-9A-Za-z\-_]{35}\b")
135
+ _GCP_FINAL_RE = re.compile(
136
+ r"(?:\bAIza[0-9A-Za-z\-_]{34}(?:-|[0-9A-Za-z_](?![0-9A-Za-z_]))"
137
+ r"|(?<=[0-9A-Za-z_])AIza[0-9A-Za-z\-_]{34}[0-9A-Za-z_](?![0-9A-Za-z_])"
138
+ r"|(?<=\w)AIza[0-9A-Za-z\-_]{34}-)"
139
+ )
135
140
  _SLACK_RE = re.compile(r"\bxox[baprs]-[A-Za-z0-9\-]{8,}\b")
136
141
  _AWS_KEY_RE = re.compile(r"\b(?:AKIA|ASIA)[0-9A-Z]{16}\b")
137
142
  _AWS_SECRET_RE = re.compile(
@@ -332,6 +337,7 @@ def redact_text(
332
337
  # Layer 6: Shannon Entropy Scoring
333
338
  out = _SECRET_ASSIGN_RE.sub(_replace_secret_assignment, out)
334
339
  out = _replace_standalone_tokens(out)
340
+ out = _GCP_FINAL_RE.sub("[REDACTED:GCP_API_KEY]", out)
335
341
 
336
342
  return out
337
343
 
@@ -39,6 +39,13 @@ _SECRET_PATTERNS = [
39
39
  # password/token/secret assignments.
40
40
  (re.compile(r"(?i)\b(password|passwd|pwd|api[_-]?key|auth[_-]?token|secret)\b\s*[:=]\s*\S+"),
41
41
  r"\1=[REDACTED]"),
42
+ (re.compile(r"\bsk-(?!ant-)(?:proj-|svcacct-)?[A-Za-z0-9\-_]{16,}\b"),
43
+ "[REDACTED-API-KEY]"),
44
+ (re.compile(
45
+ r"(?:\bAIza[0-9A-Za-z\-_]{34}(?:-|[0-9A-Za-z_](?![0-9A-Za-z_]))"
46
+ r"|(?<=[0-9A-Za-z_])AIza[0-9A-Za-z\-_]{34}[0-9A-Za-z_](?![0-9A-Za-z_])"
47
+ r"|(?<=\w)AIza[0-9A-Za-z\-_]{34}-)"),
48
+ "[REDACTED-API-KEY]"),
42
49
  ]
43
50
 
44
51
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.72.43
3
+ Version: 0.72.45
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
@@ -285,6 +285,8 @@ tests/test_1452_rework2_engine.py
285
285
  tests/test_1452_rework3_engine.py
286
286
  tests/test_1452_standing_wake_expiry.py
287
287
  tests/test_1470_gh_router_fail_closed.py
288
+ tests/test_1472_redact_boundaries.py
289
+ tests/test_1478_triage_dm.py
288
290
  tests/test_183_card_anchor.py
289
291
  tests/test_184_notify_honesty.py
290
292
  tests/test_201_win32_bash_refusal.py
@@ -0,0 +1,218 @@
1
+ """Card #1472: pasted chain_token text stays scrubbed, and a trailing-dash
2
+ AIza key is scrubbed too.
3
+
4
+ Every secret is built at runtime. No realistic key literal.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import json
9
+
10
+ from swarph_cli.redact import encode_outbound_json, redact_text
11
+ from swarph_cli.scrub import scrub_text
12
+
13
+
14
+ def _sk_proj() -> str:
15
+ return "sk-proj-" + ("n7" * 12)
16
+
17
+
18
+ def _ghp() -> str:
19
+ return "ghp_" + ("Ab" * 16)
20
+
21
+
22
+ def _aiza_trailing_dash() -> str:
23
+ # AIza + 35 chars, last character '-'. No digits, so the entropy
24
+ # layer does not also eat it: only the AIza rule can.
25
+ secret = "AI" + "za" + ("m" * 34) + "-"
26
+ assert len(secret) == 39
27
+ assert secret.endswith("-")
28
+ return secret
29
+
30
+
31
+ def test_dm_body_pasted_chain_token_line_is_fully_redacted():
32
+ """Drop's measured leak: keys sit inside a pasted "chain_token": "..." value.
33
+
34
+ encode_outbound_json is what mesh._post_json sends. Both keys must be
35
+ gone from those bytes. A shield that puts the quoted span back fails this.
36
+ """
37
+ sk = _sk_proj()
38
+ ghp = _ghp()
39
+ content = (
40
+ 'pasted log "chain_token": "\n'
41
+ f"export OPENAI_API_KEY={sk}\n"
42
+ f"GITHUB={ghp}\n"
43
+ '" end'
44
+ )
45
+ followed = f"chain_token: note\n{sk}\n{ghp}"
46
+ for text in (content, followed):
47
+ body = {
48
+ "from_node": "friendly-coder",
49
+ "to_node": "lab-ovh",
50
+ "kind": "answer",
51
+ "content": text,
52
+ }
53
+ raw = encode_outbound_json(body, caller="t").decode("utf-8")
54
+ assert sk not in raw
55
+ assert ghp not in raw
56
+ assert "REDACTED" in raw
57
+
58
+
59
+ def test_redact_text_redacts_chain_token_json_holding_an_sk_proj_key():
60
+ sk = _sk_proj()
61
+ blob = '{"chain_token": "' + sk + '"}'
62
+ out = redact_text(blob, caller="t")
63
+ assert sk not in out
64
+ assert "REDACTED" in out
65
+
66
+
67
+ def test_chain_placeholder_literal_is_left_alone():
68
+ literal = "SWARPH.CHAIN.0.END"
69
+ assert redact_text(f"see {literal} here", caller="t") == f"see {literal} here"
70
+ sk = _sk_proj()
71
+ text = literal + '\n{"chain_token": "' + sk + '"}'
72
+ out = redact_text(text, caller="t")
73
+ assert out.startswith(literal)
74
+ assert sk not in out
75
+ assert json.loads(out.split("\n", 1)[1])["chain_token"] != sk
76
+
77
+
78
+ def test_aiza_key_then_hyphen_suffix_is_redacted():
79
+ """A '-' after the 35-character body is a boundary, not a longer tail.
80
+
81
+ key-x and key-- must be scrubbed. Putting '-' back into the lookahead
82
+ (?![0-9A-Za-z\\-_]) hides both, because the next character is '-'.
83
+ """
84
+ secret = "AI" + "za" + ("m" * 35)
85
+ assert len(secret) == 39
86
+ assert not secret.endswith("-")
87
+ for suffix in ("-x", "--"):
88
+ text = secret + suffix
89
+ scrubbed = scrub_text(text)
90
+ assert scrubbed is not None
91
+ assert secret not in scrubbed
92
+ assert "REDACTED" in scrubbed
93
+ redacted = redact_text(text, caller="t")
94
+ assert secret not in redacted
95
+ assert "REDACTED" in redacted
96
+
97
+
98
+ def _assert_scrub_and_redact(text: str, *secrets: str) -> None:
99
+ scrubbed = scrub_text(text)
100
+ assert scrubbed is not None
101
+ redacted = redact_text(text, caller="t")
102
+ for secret in secrets:
103
+ assert secret not in scrubbed
104
+ assert secret not in redacted
105
+ assert "REDACTED" in scrubbed
106
+ assert "REDACTED" in redacted
107
+
108
+
109
+ def test_aiza_dash_key_followed_by_word_char_is_redacted():
110
+ """A 35th character of '-' then x, _, or 'abc def' (drop #4429).
111
+
112
+ {35}(?![0-9A-Za-z_]) refuses that follower, so the key stays visible.
113
+ Both scrub_text and redact_text have to take the dash.
114
+ """
115
+ secret = _aiza_trailing_dash()
116
+ for suffix in ("x", "_", "abc def"):
117
+ _assert_scrub_and_redact(secret + suffix, secret)
118
+
119
+
120
+ def test_dash_ending_aiza_then_api_key_hides_key_and_secret():
121
+ """:<dash-ending key>api_key=<secret> leaves neither (drop #4437).
122
+
123
+ The match stops on the key. A greedy tail would swallow api_key, and
124
+ the secret would stay where the keyword-value rule should have put
125
+ [REDACTED:DSN_CREDENTIAL].
126
+ """
127
+ key = _aiza_trailing_dash()
128
+ secret = "plain-" + "secret-value"
129
+ text = f":{key}api_key={secret}"
130
+ scrubbed = scrub_text(text)
131
+ assert scrubbed is not None
132
+ assert key not in scrubbed
133
+ assert secret not in scrubbed
134
+ assert "api_key=[REDACTED]" in scrubbed
135
+ redacted = redact_text(text, caller="t")
136
+ assert key not in redacted
137
+ assert secret not in redacted
138
+ assert "api_key=[REDACTED:DSN_CREDENTIAL]" in redacted
139
+
140
+
141
+ def test_dash_ending_aiza_glued_through_chain_token_redacts_both_keys():
142
+ """token=<dash-ending key>--chain_token<full key> leaves neither key.
143
+
144
+ A match that stops on the first key leaves the second glued to the
145
+ word character at the end of chain_token. Values are built here, with
146
+ no digits, so the entropy layer cannot hide a miss.
147
+ """
148
+ dash_key = _aiza_trailing_dash()
149
+ full_key = "AI" + "za" + ("n" * 35)
150
+ assert len(full_key) == 39
151
+ assert "-" not in full_key
152
+ text = f"token={dash_key}--chain_token{full_key}"
153
+ _assert_scrub_and_redact(text, dash_key, full_key)
154
+
155
+
156
+ def test_drop_4452_chain_and_proj_shapes_leave_no_key():
157
+ """Drop #4452's two shapes, on scrub_text and redact_text.
158
+
159
+ chain_token<dash key>x<full key>{ leaves neither key. A proj key glued
160
+ to a dash-ending key and then api_key=<another dash key> leaves none of
161
+ the three. The keyword value below is a low-entropy secret: scrub_text
162
+ must hide it. redact_text's OpenAI rule is main's, and it still runs
163
+ before the keyword rule, so that low-entropy value can remain there.
164
+ """
165
+ dash = _aiza_trailing_dash()
166
+ full = "AI" + "za" + ("n" * 35)
167
+ _assert_scrub_and_redact(f"chain_token{dash}x{full}{{", dash, full)
168
+
169
+ proj = "sk-proj-" + ("n7" * 12)
170
+ trail = "AI" + "za" + ("p" * 34) + "-"
171
+ assert len(trail) == 39 and trail.endswith("-")
172
+ _assert_scrub_and_redact(f"{proj}{dash}api_key={trail}", proj, dash, trail)
173
+
174
+ secret = "plain-" + "secret-value"
175
+ text = f"{proj}{dash}api_key={secret}"
176
+ scrubbed = scrub_text(text)
177
+ assert scrubbed is not None
178
+ assert proj not in scrubbed
179
+ assert dash not in scrubbed
180
+ assert secret not in scrubbed
181
+ redacted = redact_text(text, caller="t")
182
+ assert proj not in redacted
183
+ assert dash not in redacted
184
+
185
+
186
+ def test_dash_ending_key_glued_after_word_char_is_redacted():
187
+ """A dash-ending key glued after x, _, another key, or é is redacted.
188
+
189
+ No digits, so the entropy layer cannot hide a miss. Both scrub_text
190
+ and redact_text have to take the key.
191
+ """
192
+ dash = _aiza_trailing_dash()
193
+ other = "AI" + "za" + ("q" * 35)
194
+ assert len(other) == 39
195
+ for prefix in ("x", "_", other, "é"):
196
+ _assert_scrub_and_redact(prefix + dash, dash)
197
+
198
+
199
+ def test_aiza_key_ending_in_dash_is_redacted_before_space_quote_or_punctuation():
200
+ secret = _aiza_trailing_dash()
201
+ samples = [
202
+ f"prefix {secret} suffix",
203
+ f'"{secret}"',
204
+ f"'{secret}'",
205
+ secret,
206
+ f"{secret}.",
207
+ f"{secret},",
208
+ f"({secret})",
209
+ f"{secret}\nnext",
210
+ ]
211
+ for text in samples:
212
+ scrubbed = scrub_text(text)
213
+ assert scrubbed is not None
214
+ assert secret not in scrubbed
215
+ assert "REDACTED" in scrubbed
216
+ redacted = redact_text(text, caller="t")
217
+ assert secret not in redacted
218
+ assert "REDACTED" in redacted
@@ -0,0 +1,99 @@
1
+ """card #1478 — stuck-row DMs name the waiter, the step, and the age.
2
+
3
+ run_triage.dm_sender used to hand sweep._dm_content a finding whose
4
+ waiter, waiter_step, and age_days were None, so a blocker read
5
+ "row #N (None, None, card #C) ... unchanged Noned". Rows in this file
6
+ are built in the test, not loaded from a fixture.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ from datetime import datetime, timezone
11
+
12
+ from swarph_cli.commands import triage as triage_cmd
13
+ from swarph_cli.triage import backend as backend
14
+ from swarph_cli.triage import router as router
15
+
16
+ NOW = 1_790_000_000.0
17
+ HOLDER = "holder-rowan"
18
+ BLOCKER = "blocker-peer"
19
+ STEP = "validate"
20
+
21
+
22
+ def _iso(age_s):
23
+ return datetime.fromtimestamp(NOW - age_s, timezone.utc).isoformat()
24
+
25
+
26
+ def _http(graph_steps):
27
+ posts = []
28
+
29
+ def http_get(url, token):
30
+ if url.endswith("/board/cards/7/graph"):
31
+ return 200, {"steps": graph_steps}
32
+ if url.endswith("/board/cards/8"):
33
+ return 200, {"id": 8, "due_at": None}
34
+ raise AssertionError(f"unexpected GET {url}")
35
+
36
+ def http_post(url, body, token):
37
+ posts.append((url, body))
38
+ return 200, {"id": 1, **body}
39
+
40
+ return http_get, http_post, posts
41
+
42
+
43
+ def _run(tmp_path, waiter, dep, graph_steps):
44
+ http_get, http_post, posts = _http(graph_steps)
45
+ rows = [waiter, dep]
46
+ rc, summary = triage_cmd.run_triage(
47
+ gateway="http://gw:9", token="tok", sender="triage",
48
+ owners_map=router.load_owners(triage_cmd._OWNERS_DEFAULT),
49
+ state_file=str(tmp_path / "state.json"),
50
+ store_backend=backend.MemoryBackend(),
51
+ peers_fetcher=None,
52
+ board_fetcher=lambda: (rows, {r["id"]: r for r in rows}, {BLOCKER}),
53
+ http_get=http_get, http_post=http_post, now=NOW)
54
+ assert rc == 0 and summary["dm_sent"] == 1
55
+ assert len(posts) == 1 and posts[0][0].endswith("/messages")
56
+ return posts[0][1]["content"]
57
+
58
+
59
+ def _waiter(step):
60
+ row = {
61
+ "id": 8, "status": "open", "holder": HOLDER, "card_id": 7,
62
+ "taken_at": _iso(86400), "created_at": _iso(10 * 86400),
63
+ "holder_known": True,
64
+ }
65
+ if step is not None:
66
+ row["step"] = step
67
+ return row
68
+
69
+
70
+ def _dep():
71
+ """Open need whose last change (_row_last_change) is taken_at, 3 days ago."""
72
+ return {
73
+ "id": 9, "status": "open", "holder": BLOCKER, "card_id": 8,
74
+ "holder_known": True,
75
+ "taken_at": _iso(3 * 86400),
76
+ "created_at": _iso(10 * 86400),
77
+ }
78
+
79
+
80
+ def test_stuck_dm_names_holder_step_and_age(tmp_path):
81
+ """Known waiter, dependency last changed 3 days ago: holder, step, 3d, no None."""
82
+ content = _run(
83
+ tmp_path, _waiter(STEP), _dep(),
84
+ [{"step": STEP, "needs": [
85
+ {"step": "build", "row_id": 9, "satisfied": False}]}])
86
+ assert HOLDER in content
87
+ assert STEP in content
88
+ assert "3d" in content
89
+ assert "None" not in content
90
+
91
+
92
+ def test_waiter_with_no_step_prints_dash_not_none(tmp_path):
93
+ """A waiter with no step prints a dash in the DM, not the string None."""
94
+ content = _run(
95
+ tmp_path, _waiter(None), _dep(),
96
+ [{"step": None, "needs": [
97
+ {"step": "build", "row_id": 9, "satisfied": False}]}])
98
+ assert f"({HOLDER}, -, card #7)" in content
99
+ assert "None" not in content