swarph-cli 0.55.0__tar.gz → 0.56.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (392) hide show
  1. {swarph_cli-0.55.0/src/swarph_cli.egg-info → swarph_cli-0.56.0}/PKG-INFO +4 -2
  2. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/README.md +3 -1
  3. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/pyproject.toml +1 -1
  4. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/__init__.py +1 -1
  5. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/board.py +34 -8
  6. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/channel.py +2 -1
  7. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/highlight.py +174 -19
  8. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/mesh.py +34 -8
  9. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/timeline.py +8 -4
  10. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/gateway/server.py +114 -9
  11. swarph_cli-0.56.0/src/swarph_cli/timeline_paths.py +63 -0
  12. {swarph_cli-0.55.0 → swarph_cli-0.56.0/src/swarph_cli.egg-info}/PKG-INFO +4 -2
  13. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli.egg-info/SOURCES.txt +3 -0
  14. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_591_cli_step_graph.py +24 -1
  15. swarph_cli-0.56.0/tests/test_716_timeline_one_path.py +79 -0
  16. swarph_cli-0.56.0/tests/test_762_cards_edit_priority.py +54 -0
  17. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_channel_wake_policy_filter.py +45 -0
  18. swarph_cli-0.56.0/tests/test_highlight_command.py +480 -0
  19. swarph_cli-0.55.0/tests/test_highlight_command.py +0 -201
  20. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/LICENSE +0 -0
  21. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/setup.cfg +0 -0
  22. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/__main__.py +0 -0
  23. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/bench/__init__.py +0 -0
  24. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/bench/backends.py +0 -0
  25. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/bench/pack.py +0 -0
  26. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/bench/prices.py +0 -0
  27. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/bench/quality.py +0 -0
  28. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/bench/refresh_prices.py +0 -0
  29. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/bench/runner.py +0 -0
  30. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/bench/validate.py +0 -0
  31. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/caller.py +0 -0
  32. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/capture/__init__.py +0 -0
  33. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/capture/harden.py +0 -0
  34. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/capture/lineage.py +0 -0
  35. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/capture/liveness.py +0 -0
  36. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/capture/manifest.py +0 -0
  37. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/capture/paths.py +0 -0
  38. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/capture/verify.py +0 -0
  39. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/cell.py +0 -0
  40. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/chain_token.py +0 -0
  41. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/__init__.py +0 -0
  42. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/_content.py +0 -0
  43. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/_display.py +0 -0
  44. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/_gateway_client.py +0 -0
  45. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/add.py +0 -0
  46. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/bench.py +0 -0
  47. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/board_merge_check.py +0 -0
  48. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/brain.py +0 -0
  49. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/brain_ask.py +0 -0
  50. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/cell.py +0 -0
  51. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/cell_probe.py +0 -0
  52. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  53. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/chat.py +0 -0
  54. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/codegraph.py +0 -0
  55. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/codegraph_hook.py +0 -0
  56. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/codex_hook_output.py +0 -0
  57. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/codex_waker.py +0 -0
  58. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/compress.py +0 -0
  59. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/daemon.py +0 -0
  60. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/dreaming.py +0 -0
  61. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/event.py +0 -0
  62. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/gateway.py +0 -0
  63. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/gh_route.py +0 -0
  64. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/group.py +0 -0
  65. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/guide.py +0 -0
  66. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/hook_output.py +0 -0
  67. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/hooks.py +0 -0
  68. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/import_session.py +0 -0
  69. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/init.py +0 -0
  70. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
  71. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/install_hook.py +0 -0
  72. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  73. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
  74. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/install_wake_hook.py +0 -0
  75. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/lane.py +0 -0
  76. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/mcp_server.py +0 -0
  77. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/memory.py +0 -0
  78. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
  79. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/memory_sync.py +0 -0
  80. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/monitor.py +0 -0
  81. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/okf_links.py +0 -0
  82. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/onboard.py +0 -0
  83. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
  84. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
  85. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/protocol_handler.py +0 -0
  86. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/ratify.py +0 -0
  87. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/schedule.py +0 -0
  88. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/security.py +0 -0
  89. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/service.py +0 -0
  90. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/spawn.py +0 -0
  91. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/version.py +0 -0
  92. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/wake_hook_output.py +0 -0
  93. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/commands/watchdog.py +0 -0
  94. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/compress/__init__.py +0 -0
  95. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/compress/levers.py +0 -0
  96. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/compress/marker.py +0 -0
  97. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/compress/verify.py +0 -0
  98. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/console_safe.py +0 -0
  99. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/delivery_queue.py +0 -0
  100. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/dreaming/__init__.py +0 -0
  101. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/dreaming/candidates.py +0 -0
  102. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/dreaming/clone.py +0 -0
  103. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/dreaming/enrich.py +0 -0
  104. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/dreaming/index_check.py +0 -0
  105. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/dreaming/organize.py +0 -0
  106. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/dreaming/probes.py +0 -0
  107. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/dreaming/report.py +0 -0
  108. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/dreaming/run.py +0 -0
  109. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/dreaming/transcripts.py +0 -0
  110. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/dreaming/verify.py +0 -0
  111. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/gateway/__init__.py +0 -0
  112. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/gateway/feature_registry.py +0 -0
  113. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/gateway/lanes_control.py +0 -0
  114. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/gateway/schema.sql +0 -0
  115. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/gateway/services_control.py +0 -0
  116. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/gateway_default.py +0 -0
  117. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/gh_identity.py +0 -0
  118. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/guide/GUIDE.md +0 -0
  119. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/main.py +0 -0
  120. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/multiplexer.py +0 -0
  121. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/orphan_daemons.py +0 -0
  122. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/pane_probe.py +0 -0
  123. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/parsers/__init__.py +0 -0
  124. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/parsers/claude.py +0 -0
  125. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
  126. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
  127. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
  128. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
  129. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
  130. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
  131. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
  132. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
  133. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
  134. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
  135. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
  136. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/peer_executor.py +0 -0
  137. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/peer_executor_windows.py +0 -0
  138. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/peer_reconciliation.py +0 -0
  139. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/peer_reply_outbox.py +0 -0
  140. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/peer_service_host.py +0 -0
  141. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/peer_service_payload.py +0 -0
  142. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/peer_service_reply.py +0 -0
  143. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/peer_staging.py +0 -0
  144. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/query_filters.py +0 -0
  145. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/scripts/__init__.py +0 -0
  146. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
  147. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  148. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
  149. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
  150. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/service/__init__.py +0 -0
  151. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/service/app.py +0 -0
  152. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/service/providers.py +0 -0
  153. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/session_bridge.py +0 -0
  154. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
  155. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
  156. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/stall_alert.py +0 -0
  157. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/systemd/codex-waker.default +0 -0
  158. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
  159. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
  160. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
  161. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
  162. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
  163. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
  164. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
  165. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
  166. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
  167. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
  168. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  169. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  170. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  171. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli/tokens.py +0 -0
  172. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  173. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  174. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli.egg-info/requires.txt +0 -0
  175. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/src/swarph_cli.egg-info/top_level.txt +0 -0
  176. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_124_register_merge.py +0 -0
  177. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_132_ensure_monitor_state_dir.py +0 -0
  178. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_139_tool_probe.py +0 -0
  179. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_201_win32_bash_refusal.py +0 -0
  180. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_243_onboard_per_peer_token.py +0 -0
  181. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_307_task4_ask_reply.py +0 -0
  182. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
  183. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_319_refusal_exit_codes.py +0 -0
  184. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_323_mistral_backend.py +0 -0
  185. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_356_query_filters.py +0 -0
  186. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_397_gh_identity_router.py +0 -0
  187. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_397_router_preflight.py +0 -0
  188. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_467b_onboard_operator_credential.py +0 -0
  189. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_523_guide.py +0 -0
  190. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_524_conftest_hermeticity.py +0 -0
  191. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_532_accept_flag.py +0 -0
  192. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_535_version_capability.py +0 -0
  193. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_546_default_gateway.py +0 -0
  194. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_548_gateway_remainder.py +0 -0
  195. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_552_codegraph_gateway.py +0 -0
  196. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_555_relay_row_shape.py +0 -0
  197. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_556_relay_freshness.py +0 -0
  198. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_564_bootstrap_first_peer.py +0 -0
  199. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
  200. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_579_register_bootstrap_message.py +0 -0
  201. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_644_monitor_supervision.py +0 -0
  202. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_648_install_unit.py +0 -0
  203. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_654_reexec.py +0 -0
  204. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_684_dreaming_verb.py +0 -0
  205. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_689_pkg_version_order.py +0 -0
  206. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_724_ps1_ascii_or_bom.py +0 -0
  207. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_725_stdio_reconfigure.py +0 -0
  208. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_734_dreaming_enrich_degrade.py +0 -0
  209. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_740_edit_project.py +0 -0
  210. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_84_cli_labels.py +0 -0
  211. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_add_security_gate.py +0 -0
  212. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_artifact_add.py +0 -0
  213. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_artifact_hash.py +0 -0
  214. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_artifact_lib.py +0 -0
  215. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_artifact_mcp.py +0 -0
  216. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_artifact_skill.py +0 -0
  217. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_artifact_tool.py +0 -0
  218. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_artifact_uri.py +0 -0
  219. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_bench_backends.py +0 -0
  220. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_bench_command.py +0 -0
  221. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_bench_pack.py +0 -0
  222. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_bench_prices.py +0 -0
  223. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_bench_quality.py +0 -0
  224. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_bench_runner.py +0 -0
  225. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_bench_validate.py +0 -0
  226. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_board_card_thread.py +0 -0
  227. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_board_command.py +0 -0
  228. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_board_merge_check.py +0 -0
  229. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_board_obligation_close.py +0 -0
  230. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_brain_ask_command.py +0 -0
  231. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_brain_ask_env_diagnosis.py +0 -0
  232. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_brain_ask_identity_explicitness.py +0 -0
  233. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_brain_command.py +0 -0
  234. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_caller_meta_guard.py +0 -0
  235. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_capture_lineage.py +0 -0
  236. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_capture_liveness.py +0 -0
  237. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_capture_manifest.py +0 -0
  238. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_capture_paths.py +0 -0
  239. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_capture_security.py +0 -0
  240. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_cell_command_dispatch.py +0 -0
  241. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_cell_harden.py +0 -0
  242. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_cell_loader.py +0 -0
  243. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_cell_selfcheck.py +0 -0
  244. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_cell_verify.py +0 -0
  245. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_chain_token.py +0 -0
  246. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_channel_command.py +0 -0
  247. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_channel_poll_never_breaks_dms.py +0 -0
  248. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_channel_post.py +0 -0
  249. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_chat_command.py +0 -0
  250. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_claude_parser.py +0 -0
  251. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_claude_tmux_template.py +0 -0
  252. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_codegraph_caller_scope.py +0 -0
  253. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_codegraph_command.py +0 -0
  254. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_codegraph_hook.py +0 -0
  255. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_codegraph_mcp.py +0 -0
  256. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_codex_waker.py +0 -0
  257. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_compress_caller_convention.py +0 -0
  258. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_compress_command.py +0 -0
  259. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_compress_levers.py +0 -0
  260. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_compress_marker.py +0 -0
  261. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_compress_verify.py +0 -0
  262. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_console_encoding_e2e.py +0 -0
  263. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_content_composition.py +0 -0
  264. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_cursor_membrane.py +0 -0
  265. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_daemon_command.py +0 -0
  266. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_daemon_console_encoding.py +0 -0
  267. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_daemon_delivery.py +0 -0
  268. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_delivery_queue.py +0 -0
  269. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_display_sanitize.py +0 -0
  270. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_ensure_monitor_no_peer_default.py +0 -0
  271. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_event_emit.py +0 -0
  272. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_feature_to_uri.py +0 -0
  273. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_gateway_brain.py +0 -0
  274. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_gateway_client.py +0 -0
  275. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_gateway_command.py +0 -0
  276. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_gateway_message_idempotency.py +0 -0
  277. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_gh_route_doctor_mesh_name.py +0 -0
  278. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_group_command.py +0 -0
  279. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_guide_commands_parse.py +0 -0
  280. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_guide_selfcheck.py +0 -0
  281. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_heartbeat_check.py +0 -0
  282. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_hook_output.py +0 -0
  283. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_hooks_activity_marker.py +0 -0
  284. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_hooks_add.py +0 -0
  285. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_hooks_bundle.py +0 -0
  286. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_hooks_init.py +0 -0
  287. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_hooks_legacy_generation_removal.py +0 -0
  288. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_hooks_lifecycle.py +0 -0
  289. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_hooks_merge.py +0 -0
  290. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_hooks_verify.py +0 -0
  291. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_hooks_win32_bash_wrapper.py +0 -0
  292. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_hooks_win32_command_path.py +0 -0
  293. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_import_command.py +0 -0
  294. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_inbox_identity_provenance.py +0 -0
  295. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_init_command.py +0 -0
  296. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_init_provider_menu.py +0 -0
  297. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_install_codex_hooks.py +0 -0
  298. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_install_hook.py +0 -0
  299. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_install_multiplexer_command.py +0 -0
  300. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_install_postcompact_hook.py +0 -0
  301. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_install_wake_hook.py +0 -0
  302. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_lane_command.py +0 -0
  303. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_main.py +0 -0
  304. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_main_unknown_verb_guard.py +0 -0
  305. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_mcp_missing_hint.py +0 -0
  306. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_mcp_server.py +0 -0
  307. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_memory_command.py +0 -0
  308. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_memory_gateway.py +0 -0
  309. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_memory_sync.py +0 -0
  310. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_memory_sync_membrane.py +0 -0
  311. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_mesh_command.py +0 -0
  312. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_mesh_inbox_mark_read.py +0 -0
  313. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_mesh_inbox_sanitize.py +0 -0
  314. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_mesh_send_unknown_peer.py +0 -0
  315. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_mesh_sidecar.py +0 -0
  316. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  317. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  318. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_meta_edge_identity.py +0 -0
  319. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_monitor_channel_poll.py +0 -0
  320. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_monitor_cli.py +0 -0
  321. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_monitor_daemon_collision.py +0 -0
  322. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_monitor_self_name_verification.py +0 -0
  323. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_monitor_state_split.py +0 -0
  324. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_multiplexer.py +0 -0
  325. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_muse_membrane.py +0 -0
  326. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_muse_own_cli.py +0 -0
  327. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_obligation_close_is_surfaced.py +0 -0
  328. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_okf_links.py +0 -0
  329. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_onboard_command.py +0 -0
  330. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_orphan_daemons.py +0 -0
  331. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_packaged_artifacts_exist.py +0 -0
  332. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_packaged_skills.py +0 -0
  333. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_peer_executor.py +0 -0
  334. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_peer_executor_windows.py +0 -0
  335. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_peer_reply_drain_command.py +0 -0
  336. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_peer_reply_drain_supervisor.py +0 -0
  337. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_peer_reply_outbox.py +0 -0
  338. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_peer_reply_transport.py +0 -0
  339. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_peer_service_e2e.py +0 -0
  340. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_peer_service_host.py +0 -0
  341. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_peer_service_payload.py +0 -0
  342. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_peer_service_reply.py +0 -0
  343. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_postcompact_recall.py +0 -0
  344. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_process_liveness.py +0 -0
  345. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_protocol_handler.py +0 -0
  346. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_providers_isolation.py +0 -0
  347. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_ratify_command.py +0 -0
  348. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_readme_claims_no_gateway_default.py +0 -0
  349. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_repl_caller_convention.py +0 -0
  350. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_schedule_command.py +0 -0
  351. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_schedule_create_contract.py +0 -0
  352. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_security.py +0 -0
  353. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_service_app.py +0 -0
  354. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_service_command.py +0 -0
  355. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_service_providers.py +0 -0
  356. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_session_bridge_inject.py +0 -0
  357. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_session_bridge_probe.py +0 -0
  358. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_session_bridge_resolve.py +0 -0
  359. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_session_path_role_gate.py +0 -0
  360. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_shipped_units_match_the_cli.py +0 -0
  361. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_smoke_chat.py +0 -0
  362. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_smoke_one_shot.py +0 -0
  363. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_smoke_phase_5_5.py +0 -0
  364. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_spawn_base_hoist.py +0 -0
  365. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_spawn_command.py +0 -0
  366. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_spawn_identity_stamp.py +0 -0
  367. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_spawn_live_pin.py +0 -0
  368. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_spawn_mitosis_lineage.py +0 -0
  369. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_spawn_tmux_session.py +0 -0
  370. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_spawn_windows_relaunch.py +0 -0
  371. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_stall_alert.py +0 -0
  372. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_subprocess_decode_encoding.py +0 -0
  373. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_timeline_command.py +0 -0
  374. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_tmux_wake_runstate_defer.py +0 -0
  375. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_tmux_wake_session_reanchor.py +0 -0
  376. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_tmux_wake_stale.py +0 -0
  377. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_tmux_wake_submit_verify.py +0 -0
  378. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_token_file_one_parser.py +0 -0
  379. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_token_precedence_explicit_first.py +0 -0
  380. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_unified_token_resolver.py +0 -0
  381. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_version_command.py +0 -0
  382. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_version_consistency.py +0 -0
  383. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_vibe_membrane.py +0 -0
  384. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_watchdog.py +0 -0
  385. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_watchdog_dm_wake.py +0 -0
  386. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  387. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  388. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_watchdog_emit_health.py +0 -0
  389. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_watchdog_install_flags.py +0 -0
  390. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_watchdog_model_rung.py +0 -0
  391. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_watchdog_stale_peers.py +0 -0
  392. {swarph_cli-0.55.0 → swarph_cli-0.56.0}/tests/test_windows_waker_installer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.55.0
3
+ Version: 0.56.0
4
4
  Summary: The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`.
5
5
  Author: Pierre Samson, Claude Opus
6
6
  License: MIT
@@ -180,6 +180,7 @@ swarph board projects add <slug> --title "…" [--goal "…"]
180
180
  swarph board cards list [--project <id|slug>] [--stage <s>] [--assignee <who>]
181
181
  swarph board cards show <id>
182
182
  swarph board cards add --project <id|slug> --title "…" [--body "…"] [--ai2] [--priority N]
183
+ swarph board cards edit <id> [--title "…"] [--body "…"] [--project <id|slug>] [--priority N]
183
184
  swarph board cards move <id> <stage> # advance the card (proposed→idea→spec→plan→build→test→done)
184
185
  swarph board cards link <id> <key> <value> # add/update a link (merges — never clobbers existing links)
185
186
  swarph board cards assign <id> <who>
@@ -256,7 +257,7 @@ Config is via env, mirroring `swarph mesh`'s token model: `GBRAIN_MCP_URL` or `S
256
257
 
257
258
  ```
258
259
  swarph memory get <slug> # read one page by exact slug
259
- swarph memory list [--type T] [--tag T] # filter pages (deterministic — use --type; --tag currently inert)
260
+ swarph memory list [--type T] [--tag T] # filter pages (deterministic; --tag is the reliable scope — a zero usually means no page carries that tag)
260
261
  swarph memory links <slug> # a concept's forward [[wiki-links]]
261
262
  ```
262
263
 
@@ -375,6 +376,7 @@ The gateway's **automation control plane** — channels (pub/sub), scheduled eve
375
376
  # channels — converge work into pub/sub rooms
376
377
  $ swarph channel create research --kind topic --description "market-structure notes"
377
378
  $ swarph channel join research --wake-policy mentions_only
379
+ $ swarph channel join alerts --wake-policy severity_only # wake only on posts the gateway marks severe: a leading [ALERT]/[CRITICAL]/[ERROR]/[FATAL] or 🚨, or an explicit severity tag (needs mesh-gateway #173+; 0.56.0)
378
380
  $ swarph channel list
379
381
  $ swarph channel post releases --content "📦 pkg X.Y.Z shipped — <notes>" # sets channel, omits to_node
380
382
  $ swarph channel read releases --limit 10 # recent posts (or --json)
@@ -132,6 +132,7 @@ swarph board projects add <slug> --title "…" [--goal "…"]
132
132
  swarph board cards list [--project <id|slug>] [--stage <s>] [--assignee <who>]
133
133
  swarph board cards show <id>
134
134
  swarph board cards add --project <id|slug> --title "…" [--body "…"] [--ai2] [--priority N]
135
+ swarph board cards edit <id> [--title "…"] [--body "…"] [--project <id|slug>] [--priority N]
135
136
  swarph board cards move <id> <stage> # advance the card (proposed→idea→spec→plan→build→test→done)
136
137
  swarph board cards link <id> <key> <value> # add/update a link (merges — never clobbers existing links)
137
138
  swarph board cards assign <id> <who>
@@ -208,7 +209,7 @@ Config is via env, mirroring `swarph mesh`'s token model: `GBRAIN_MCP_URL` or `S
208
209
 
209
210
  ```
210
211
  swarph memory get <slug> # read one page by exact slug
211
- swarph memory list [--type T] [--tag T] # filter pages (deterministic — use --type; --tag currently inert)
212
+ swarph memory list [--type T] [--tag T] # filter pages (deterministic; --tag is the reliable scope — a zero usually means no page carries that tag)
212
213
  swarph memory links <slug> # a concept's forward [[wiki-links]]
213
214
  ```
214
215
 
@@ -327,6 +328,7 @@ The gateway's **automation control plane** — channels (pub/sub), scheduled eve
327
328
  # channels — converge work into pub/sub rooms
328
329
  $ swarph channel create research --kind topic --description "market-structure notes"
329
330
  $ swarph channel join research --wake-policy mentions_only
331
+ $ swarph channel join alerts --wake-policy severity_only # wake only on posts the gateway marks severe: a leading [ALERT]/[CRITICAL]/[ERROR]/[FATAL] or 🚨, or an explicit severity tag (needs mesh-gateway #173+; 0.56.0)
330
332
  $ swarph channel list
331
333
  $ swarph channel post releases --content "📦 pkg X.Y.Z shipped — <notes>" # sets channel, omits to_node
332
334
  $ swarph channel read releases --limit 10 # recent posts (or --json)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "swarph-cli"
7
- version = "0.55.0"
7
+ version = "0.56.0"
8
8
  description = "The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -16,6 +16,6 @@ The architecture splits CLI from substrate so:
16
16
 
17
17
  from __future__ import annotations
18
18
 
19
- __version__ = "0.55.0"
19
+ __version__ = "0.56.0"
20
20
 
21
21
  __all__ = ["__version__"]
@@ -138,7 +138,8 @@ def _format_obligations(data: dict) -> str:
138
138
  return "\n".join(lines)
139
139
 
140
140
 
141
- def _card_edit_payload(actor, title, body, *, due_at=_SENTINEL, project_id=None) -> dict:
141
+ def _card_edit_payload(actor, title, body, *, due_at=_SENTINEL, project_id=None,
142
+ priority=None) -> dict:
142
143
  """#191: the edit patch — title and/or body, the two fields that made a
143
144
  card's text write-once until the gateway grew them (BoardCardPatch).
144
145
 
@@ -150,6 +151,13 @@ def _card_edit_payload(actor, title, body, *, due_at=_SENTINEL, project_id=None)
150
151
 
151
152
  ``due_at=_SENTINEL`` means the caller did not pass ``--due``; an explicit
152
153
  empty string clears the due date (#145).
154
+
155
+ ``priority`` needs no sentinel: there is no "clear" for it (the column is
156
+ NOT NULL DEFAULT 0), so None unambiguously means "not mentioned". THE RANGE
157
+ IS NOT CHECKED HERE ON PURPOSE — `_validate_priority` on the gateway is the
158
+ single definition, applied at every write path, and #256b is the card about
159
+ what happens when that range lives on one of two paths while a comment
160
+ claims otherwise. A second copy here could drift; the 400 is the answer.
153
161
  """
154
162
  patch = {"actor": actor}
155
163
  if title is not None:
@@ -160,8 +168,11 @@ def _card_edit_payload(actor, title, body, *, due_at=_SENTINEL, project_id=None)
160
168
  patch["due_at"] = _normalize_due_at(due_at) if due_at else None
161
169
  if project_id is not None:
162
170
  patch["project_id"] = int(project_id) # #740: re-home; the gateway gates it (orchestrator + both owners)
171
+ if priority is not None:
172
+ patch["priority"] = int(priority)
163
173
  if len(patch) == 1:
164
- raise ValueError("nothing to edit — pass --title, --body, --due and/or --project")
174
+ raise ValueError(
175
+ "nothing to edit — pass --title, --body, --due, --project and/or --priority")
165
176
  return patch
166
177
 
167
178
 
@@ -598,9 +609,15 @@ def _format_graph(g) -> str:
598
609
  f"{_s(n.get('step'))} #{n.get('row_id')} "
599
610
  f"{'ok' if n.get('satisfied') else _s(n.get('state')) or '-'}")
600
611
  for n in s.get("needs") or []) or "-"
612
+ # #785: delivery (ref|text|review) is what distinguishes the steps.
613
+ # Gateway already emits it; dropping it from the default render taught
614
+ # readers the wrong rule (validate takes prose; build needs a URL) until
615
+ # a close refusal. Always print the key — absent → `-` — so a missing
616
+ # field cannot look like "this step has no container".
601
617
  line = (f" {_s(s.get('step'))} [{'M' if s.get('mandatory') else 'opt'}] "
602
618
  f"{_s(s.get('state'))} holder={_s(s.get('holder')) or '-'} "
603
- f"due={_s(s.get('due')) or '-'} needs={needs}")
619
+ f"due={_s(s.get('due')) or '-'} "
620
+ f"delivery={_s(s.get('delivery')) or '-'} needs={needs}")
604
621
  if s.get("eligible") is not None:
605
622
  line += f" eligible={', '.join(_s(e) for e in s['eligible']) or '(nobody)'}"
606
623
  lines.append(line)
@@ -793,8 +810,9 @@ def _build_parser() -> argparse.ArgumentParser:
793
810
  "container the step names — a ref, >=40 words, or a verdict")
794
811
  ck.add_argument("--json", action="store_true"); _add_common(ck)
795
812
 
796
- cg = cards.add_parser("graph", help="read the card's step graph (#591): every menu "
797
- "step, its state, holder, due, edges, eligible set")
813
+ cg = cards.add_parser("graph", help="read the card's step graph (#591/#785): every menu "
814
+ "step, its state, holder, due, delivery container, "
815
+ "edges, eligible set")
798
816
  cg.add_argument("id", type=int)
799
817
  cg.add_argument("--json", action="store_true"); _add_common(cg)
800
818
 
@@ -803,8 +821,9 @@ def _build_parser() -> argparse.ArgumentParser:
803
821
  cr.add_argument("--json", action="store_true"); _add_common(cr)
804
822
 
805
823
  ce = cards.add_parser(
806
- "edit", help="edit a card's title and/or body (#191: a correction "
807
- "belongs ON the card, not only in its thread)")
824
+ "edit", help="edit a card's title, body, due date, project and/or "
825
+ "priority (#191: a correction belongs ON the card, not "
826
+ "only in its thread)")
808
827
  ce.add_argument("id", type=int)
809
828
  add_content_args(ce, "--title", required=False,
810
829
  noun="card title", noun_plural="titles")
@@ -814,6 +833,12 @@ def _build_parser() -> argparse.ArgumentParser:
814
833
  "the gateway refuses anyone else). Keeps the card's id, thread and links.")
815
834
  ce.add_argument("--due", default=_SENTINEL, metavar="DATE",
816
835
  help="set due date (YYYY-MM-DD or ISO); pass empty string to clear")
836
+ ce.add_argument("--priority", type=int, default=None, metavar="N",
837
+ help="re-prioritise the card. `add` has taken --priority since "
838
+ "#256 and `edit` did not, so a card filed at the wrong "
839
+ "priority could not be repriced from the CLI — the gateway "
840
+ "has accepted it on PATCH the whole time. Range is the "
841
+ "gateway's to enforce (0..13); it answers 400.")
817
842
  ce.add_argument("--json", action="store_true"); _add_common(ce)
818
843
 
819
844
  obl = top.add_parser(
@@ -1068,7 +1093,8 @@ def run_board(argv: list[str]) -> int:
1068
1093
  return 1
1069
1094
  try:
1070
1095
  patch = _card_edit_payload(
1071
- self_name, title_text, body_text, due_at=args.due, project_id=project_id)
1096
+ self_name, title_text, body_text, due_at=args.due,
1097
+ project_id=project_id, priority=args.priority)
1072
1098
  except ValueError as exc:
1073
1099
  print(f"swarph board cards edit: {exc}", file=sys.stderr)
1074
1100
  return 2
@@ -44,7 +44,8 @@ def _build_parser() -> argparse.ArgumentParser:
44
44
  join.add_argument(
45
45
  "--wake-policy",
46
46
  default=None,
47
- choices=["mentions_only", "here_and_mentions", "all", "muted"],
47
+ choices=["mentions_only", "here_and_mentions", "all", "muted",
48
+ "severity_only"],
48
49
  )
49
50
  add_common_args(join)
50
51
 
@@ -10,8 +10,9 @@ Usage:
10
10
  swarph highlight "<one-line highlight>" [memory-pointer]
11
11
  [--cell NAME] [--timeline-dir DIR] [--when ISO8601] [--no-push]
12
12
 
13
- Timeline dir: ``--timeline-dir`` > ``SWARPH_TIMELINE_DIR`` > ``~/.swarph/timeline``
14
- (auto-created + ``git init``'d + given a ``merge=union`` .gitattributes if absent).
13
+ Timeline dir: ``--timeline-dir`` > ``SWARPH_TIMELINE_DIR`` / ``SWARPH_TIMELINE``
14
+ > ``~/swarph-timeline`` (same default ``swarph timeline`` reads; #716).
15
+ Auto-created + ``git init``'d + given a ``merge=union`` .gitattributes if absent.
15
16
  Cell identity (#657 / house order #332): ``--cell`` > ``SWARPH_SELF`` >
16
17
  ``SWARPH_CELL`` > git user.name > hostname. SELF outranks CELL — psmux leaks
17
18
  ``SWARPH_CELL`` (#538), so CELL-first posts under another cell's name.
@@ -22,6 +23,7 @@ Push: only if an ``origin`` remote exists and ``--no-push`` is not set; otherwis
22
23
  from __future__ import annotations
23
24
 
24
25
  import argparse
26
+ import http.client
25
27
  import os
26
28
  import socket
27
29
  import subprocess
@@ -30,6 +32,7 @@ from datetime import datetime, timezone
30
32
  from pathlib import Path
31
33
 
32
34
  from swarph_cli.commands.mesh import _post_json, _resolve_token
35
+ from swarph_cli.timeline_paths import timeline_dir
33
36
 
34
37
  _PUSH_RETRIES = 8
35
38
 
@@ -84,25 +87,117 @@ def _credential_error(cell: str, source: str, exc: RuntimeError) -> str:
84
87
  return f"swarph highlight: {exc}"
85
88
 
86
89
 
90
+ def _normalise_repo(repo: Path) -> Path:
91
+ return Path(os.path.normpath(os.path.expanduser(str(repo))))
92
+
93
+
94
+ def _existing_ancestor(repo: Path) -> Path:
95
+ """Walk up to the nearest existing path. ``normpath`` first so
96
+ ``x/../swarph-timeline`` (x missing) still names the clone (#65)."""
97
+ repo = _normalise_repo(repo)
98
+ while not repo.exists() and repo != repo.parent:
99
+ repo = repo.parent
100
+ return repo
101
+
102
+
103
+ def _git_gateway_clone_flag(repo: Path) -> bool:
104
+ """``.git``-resident declaration. Survives clean/stash/reset; cannot be committed.
105
+
106
+ ``git config --local swarph.gatewayClone 1`` (ops, once, on the gateway host).
107
+ A worktree marker dies to ``git clean -fdx`` and propagates on ``git add -A``.
108
+ Ask git on the nearest existing ancestor — a nonexistent subpath of the
109
+ flagged clone is still the clone (#65).
110
+ """
111
+ repo = _existing_ancestor(repo)
112
+ if not _is_git_repo(repo):
113
+ return False
114
+ r = _git(repo, "config", "--local", "--type=bool", "--get", "swarph.gatewayClone")
115
+ return r.returncode == 0 and (r.stdout or "").strip().lower() == "true"
116
+
117
+
118
+ def _is_gateway_clone(repo: Path) -> bool:
119
+ """True when ``repo`` is the live gateway working tree (PR #392 / #64 / #65).
120
+
121
+ The property belongs to the directory, not the caller's environ.
122
+ Honour env match (including subpaths) when present, OR
123
+ ``swarph.gatewayClone`` in the repo's local git config. Walk up to an
124
+ existing ancestor before either check so ``<clone>/newsub`` cannot
125
+ mkdir-and-commit into the gateway history.
126
+ """
127
+ if _git_gateway_clone_flag(repo):
128
+ return True
129
+ raw = (os.environ.get("GATEWAY_TIMELINE_DIR") or "").strip()
130
+ if not raw:
131
+ return False
132
+ try:
133
+ flagged = Path(os.path.normpath(os.path.expanduser(raw))).resolve()
134
+ intended = _normalise_repo(repo).resolve()
135
+ cand = _existing_ancestor(repo).resolve()
136
+ except OSError:
137
+ return False
138
+ # Equal or descendant of the flagged clone. Compare the intended
139
+ # path too — walking up a not-yet-existing flagged dir would
140
+ # otherwise miss `cand == flagged` (#63). Do NOT ask
141
+ # `flagged.is_relative_to(cand)`: cand is the parent of a new
142
+ # sibling and that over-refuses (#73).
143
+ if intended == flagged or cand == flagged:
144
+ return True
145
+ for p in (intended, cand):
146
+ try:
147
+ if p.is_relative_to(flagged):
148
+ return True
149
+ except ValueError:
150
+ if flagged in p.parents:
151
+ return True
152
+ return False
153
+
154
+
87
155
  def _log_via_gateway(gateway: str, cell: str, highlight: str,
88
156
  memory: str, when: str, token_file: str | None,
89
- *, cell_source: str = "unknown") -> int:
157
+ *, cell_source: str = "unknown") -> int | None:
90
158
  """POST the highlight to the gateway `/highlights` — the gateway holds the git
91
159
  push credential, so the cell needs only its mesh peer token (no GitHub PAT).
92
- Fail-loud: a non-200 or connection error returns 1 (never a silent git double-write)."""
160
+
161
+ Returns 0 on success. Returns 1 (no local write) on credential failure,
162
+ any HTTP status ≠ 200 (401/422/502 are refusals, not "unreachable"), or
163
+ a post-send exception (timeout, reset-after-read, bad status line,
164
+ incomplete body, JSON/Unicode decode of a 200) — the client died after
165
+ the request may have been committed; that is AMBIGUOUS, not a fallback.
166
+ Returns None only when status == 0 (no HTTP response: refused/DNS/reset
167
+ *before* send) so the caller may write locally and announce LOCAL
168
+ FALLBACK (#716 / PR #392).
169
+
170
+ The wrap is HERE, not in ``_post_json`` (17 callers). Catching OSError
171
+ there and returning status 0 would turn a committed-but-unseen timeout
172
+ into a duplicate local line. A reset belongs under status 0 only when
173
+ it beats the send (URLError); post-send resets escape ``_post_json``.
174
+ """
93
175
  url = gateway.rstrip("/") + "/highlights"
94
- body: dict = {"highlight": highlight, "cell": cell}
176
+ body: dict = {"highlight": highlight, "cell": cell, "when": when}
95
177
  if memory:
96
178
  body["memory"] = memory
97
- if when:
98
- body["when"] = when
99
179
  try:
100
180
  token = _resolve_token(cell, token_file)
101
181
  except RuntimeError as exc:
102
182
  print(_credential_error(cell, cell_source, exc), file=sys.stderr)
103
183
  return 1
104
- status, resp = _post_json(url, body, token)
184
+ try:
185
+ status, resp = _post_json(url, body, token)
186
+ except http.client.InvalidURL as exc:
187
+ print(f"swarph highlight: bad gateway URL ({exc})", file=sys.stderr)
188
+ return 1
189
+ except (OSError, http.client.HTTPException, ValueError):
190
+ print(
191
+ "swarph highlight: AMBIGUOUS: the gateway may have committed — "
192
+ f"check swarph timeline since {when} before retrying",
193
+ file=sys.stderr,
194
+ )
195
+ return 1
105
196
  if status == 200:
197
+ if not isinstance(resp, dict):
198
+ print("swarph highlight: gateway POST failed (HTTP 200): "
199
+ "non-object body", file=sys.stderr)
200
+ return 1
106
201
  ts = resp.get("ts", "")
107
202
  print(f"logged -> TIMELINE.md @ {ts} (via gateway)"
108
203
  + (f" -> {memory}" if memory else ""))
@@ -113,6 +208,8 @@ def _log_via_gateway(gateway: str, cell: str, highlight: str,
113
208
  detail = resp.get("detail") if isinstance(resp, dict) else resp
114
209
  where = f"HTTP {status}" if status else "connection failed"
115
210
  print(f"swarph highlight: gateway POST failed ({where}): {detail}", file=sys.stderr)
211
+ if status == 0:
212
+ return None # no HTTP response — local fallback is the closed-port case
116
213
  return 1
117
214
 
118
215
 
@@ -139,8 +236,8 @@ def _git(repo: Path, *args: str, check: bool = False):
139
236
 
140
237
 
141
238
  def _resolve_dir(arg) -> Path:
142
- raw = arg or os.environ.get("SWARPH_TIMELINE_DIR") or "~/.swarph/timeline"
143
- return Path(os.path.expanduser(raw))
239
+ """Same default ``swarph timeline`` reads. Call-time — see timeline_paths."""
240
+ return timeline_dir(dir_arg=arg)
144
241
 
145
242
 
146
243
  def _is_git_repo(repo: Path) -> bool:
@@ -195,6 +292,34 @@ def _has_remote(repo: Path) -> bool:
195
292
  return _git(repo, "remote", "get-url", "origin").returncode == 0
196
293
 
197
294
 
295
+ def _revert_append(repo: Path, line: str) -> None:
296
+ """Undo our failed add/commit without deleting another writer's bytes (#65).
297
+
298
+ If the worktree already equals HEAD, our line was swept into their commit
299
+ — leave it. Else strip our line from the tail only when HEAD does not
300
+ already end with it. Then ``git reset -q -- TIMELINE.md``. Not
301
+ checkout/restore: those are rc 128 under index.lock (the add-failure case).
302
+ """
303
+ if _git(repo, "diff", "--quiet", "HEAD", "--", "TIMELINE.md").returncode == 0:
304
+ return
305
+ our = line if line.endswith("\n") else line + "\n"
306
+ tl = repo / "TIMELINE.md"
307
+ try:
308
+ body = tl.read_text(encoding="utf-8")
309
+ except OSError:
310
+ _git(repo, "reset", "-q", "--", "TIMELINE.md")
311
+ return
312
+ if body.endswith(our):
313
+ head = _git(repo, "show", "HEAD:TIMELINE.md")
314
+ head_txt = head.stdout if head.returncode == 0 else ""
315
+ if not head_txt.endswith(our):
316
+ try:
317
+ tl.write_text(body[: -len(our)], encoding="utf-8")
318
+ except OSError:
319
+ pass
320
+ _git(repo, "reset", "-q", "--", "TIMELINE.md")
321
+
322
+
198
323
  def _current_branch(repo: Path) -> str:
199
324
  r = _git(repo, "rev-parse", "--abbrev-ref", "HEAD")
200
325
  b = r.stdout.strip()
@@ -212,7 +337,8 @@ def run_highlight(argv: list) -> int:
212
337
  help="cell identity (else SWARPH_SELF / SWARPH_CELL / "
213
338
  "git user / hostname)")
214
339
  p.add_argument("--timeline-dir", default=None,
215
- help="timeline repo (else SWARPH_TIMELINE_DIR / ~/.swarph/timeline)")
340
+ help="timeline repo (else SWARPH_TIMELINE_DIR / "
341
+ "SWARPH_TIMELINE / ~/swarph-timeline)")
216
342
  p.add_argument("--when", default=None,
217
343
  help="ISO8601 event time for a backfilled highlight; default now")
218
344
  p.add_argument("--no-push", action="store_true",
@@ -230,19 +356,31 @@ def run_highlight(argv: list) -> int:
230
356
  cell, cell_source = _resolve_cell(args.cell, repo)
231
357
  highlight = _collapse(args.highlight)
232
358
  memory = _collapse(args.memory)
233
- when = _collapse(args.when) if args.when else ""
359
+ # Always stamp `when` so a gateway commit and a local fallback are
360
+ # byte-identical (merge=union collapses the duplicate). The gateway
361
+ # accepts the client's minute verbatim.
362
+ when = _collapse(args.when) if args.when else _now_ts()
234
363
 
235
364
  # Peer-token path (default when a gateway is configured): the gateway holds the
236
365
  # git credential, so a cell logs with only its mesh peer token — no GitHub PAT.
237
366
  # `--local` forces the legacy git path; with no gateway configured it's the git
238
367
  # path too, so existing solo/offline timelines are unaffected.
239
368
  gateway = "" if args.local else _resolve_gateway(args.gateway)
369
+ fallback = False
240
370
  if gateway:
241
- return _log_via_gateway(gateway, cell, highlight, memory, when,
242
- args.token_file, cell_source=cell_source)
371
+ gw_rc = _log_via_gateway(gateway, cell, highlight, memory, when,
372
+ args.token_file, cell_source=cell_source)
373
+ if gw_rc is not None:
374
+ return gw_rc
375
+ fallback = True
376
+
377
+ if fallback and _is_gateway_clone(repo):
378
+ print("swarph highlight: gateway unreachable; not writing into the "
379
+ "gateway's clone; retry or use --timeline-dir", file=sys.stderr)
380
+ return 1
243
381
 
244
382
  _ensure_timeline(repo, cell)
245
- ts = when or _now_ts()
383
+ ts = when
246
384
  branch = _current_branch(repo)
247
385
  pushing = (not args.no_push) and _has_remote(repo)
248
386
 
@@ -254,15 +392,32 @@ def run_highlight(argv: list) -> int:
254
392
  "conflict) — NOT logged", file=sys.stderr)
255
393
  return 1
256
394
 
257
- with (repo / "TIMELINE.md").open("a", encoding="utf-8") as f:
258
- f.write(_format_line(ts, cell, highlight, memory) + "\n")
259
- _git(repo, "add", "TIMELINE.md")
395
+ tl = repo / "TIMELINE.md"
396
+ entry = _format_line(ts, cell, highlight, memory) + "\n"
397
+ with tl.open("a", encoding="utf-8") as f:
398
+ f.write(entry)
399
+ add = _git(repo, "add", "TIMELINE.md")
400
+ if add.returncode != 0:
401
+ _revert_append(repo, entry)
402
+ print(f"swarph highlight: git add failed: {add.stderr.strip()}", file=sys.stderr)
403
+ return 1
260
404
  commit = _git(repo, "commit", "-m", f"highlight({cell}): {highlight[:60]}")
261
405
  if commit.returncode != 0:
406
+ _revert_append(repo, entry)
262
407
  print(f"swarph highlight: commit failed: {commit.stderr.strip()}", file=sys.stderr)
263
408
  return 1
264
409
 
265
- done = f"logged -> TIMELINE.md @ {ts}" + (f" -> {memory}" if memory else "")
410
+ if fallback:
411
+ done = f"logged -> {repo} (LOCAL FALLBACK — gateway unreachable)."
412
+ from swarph_cli.timeline_paths import timeline_file
413
+ wrote = repo / "TIMELINE.md"
414
+ if wrote.resolve() != timeline_file().resolve():
415
+ done += "\n`swarph timeline` will NOT show this until it is drained."
416
+ if not _has_remote(repo):
417
+ done += ("\nno origin remote — this commit stays on this filesystem "
418
+ "until it is drained.")
419
+ else:
420
+ done = f"logged -> TIMELINE.md @ {ts}" + (f" -> {memory}" if memory else "")
266
421
  if not pushing:
267
422
  print(done)
268
423
  return 0
@@ -22,6 +22,7 @@ import argparse
22
22
  import difflib
23
23
  import json
24
24
  import os
25
+ import re
25
26
  import stat
26
27
  import subprocess
27
28
  import sys
@@ -2023,16 +2024,27 @@ def _wake_policy_admits(policy, msg: dict, self_name: str) -> bool:
2023
2024
 
2024
2025
  `muted` is handled by the caller (it skips the fetch entirely). Here:
2025
2026
  · mentions_only -> only posts that name this cell
2027
+ · severity_only -> only a row whose stored `priority` is `high` (#777 / #80)
2026
2028
  · all / anything else / None -> admit
2027
2029
 
2028
- >>> FAIL OPEN, DELIBERATELY. <<< An unknown policy value, or a gateway that
2029
- does not send one at all, admits the post. Failing CLOSED would drop channel
2030
- posts silently — which is card #125's ORIGINAL DEFECT, not a safe default:
2031
- seventeen cells set a policy, nothing honoured it, and nobody could tell
2032
- because the absence looked exactly like "no posts". A filter that errs toward
2033
- delivering is recoverable by the reader; one that errs toward silence is not.
2034
- The inert case is announced by the caller so it cannot pass for enforcement.
2030
+ Severity is ONE stored value, written by the gateway `_fan_out` and
2031
+ echoed by GET /messages. This reader does not re-infer from content
2032
+ or from a request-only `severity` tag — those two producers disagreed
2033
+ by construction on a tag-only-severe post.
2034
+
2035
+ >>> FAIL OPEN, DELIBERATELY (unknown policy). <<< An unknown policy
2036
+ value, or a gateway that does not send one at all, admits the post.
2037
+ Failing CLOSED would drop channel posts silently — which is card
2038
+ #125's ORIGINAL DEFECT, not a safe default. `severity_only` is a
2039
+ named filter: missing/`normal` priority is not high, so it does not
2040
+ admit. The two inert cases are announced by the caller
2041
+ (`_poll_channel_subscriptions_inner`) so they cannot pass for
2042
+ enforcement: wake_policy absent from GET /channels, and
2043
+ severity_only against a GET /messages that sends no `priority` key
2044
+ (gateway predates #173).
2035
2045
  """
2046
+ if policy == "severity_only":
2047
+ return (msg.get("priority") or "").strip().lower() == "high"
2036
2048
  if policy != "mentions_only":
2037
2049
  return True
2038
2050
  raw = msg.get("mentions")
@@ -2101,6 +2113,7 @@ def _poll_channel_subscriptions_inner(state: MonitorState) -> None:
2101
2113
  f"channel filtering INERT, surfacing all posts (gateway predates #125 C1)",
2102
2114
  file=sys.stderr, flush=True)
2103
2115
 
2116
+ priority_inert_announced = False
2104
2117
  for channel in subscribed:
2105
2118
  policy = policies.get(channel)
2106
2119
  if policy == "muted":
@@ -2111,8 +2124,21 @@ def _poll_channel_subscriptions_inner(state: MonitorState) -> None:
2111
2124
  cstatus, cbody = _http_get_json(curl, state.token)
2112
2125
  if cstatus != 200:
2113
2126
  continue
2127
+ fetched = cbody.get("messages", [])
2128
+ # Mixed-version: a live gateway predating #173 accepts no
2129
+ # severity_only join, but a rollback / partial deploy can leave
2130
+ # a holder against a GET that sends no `priority` key. Then
2131
+ # every post is silently dropped. Announce once per poll so
2132
+ # that silence cannot pass for enforcement (#777 / #80).
2133
+ if (policy == "severity_only" and fetched
2134
+ and not any("priority" in m for m in fetched)
2135
+ and not priority_inert_announced):
2136
+ print(f"{state.log_prefix} priority absent from GET /messages — "
2137
+ f"severity_only filter INERT (gateway predates #173)",
2138
+ file=sys.stderr, flush=True)
2139
+ priority_inert_announced = True
2114
2140
  new_posts = [
2115
- m for m in cbody.get("messages", [])
2141
+ m for m in fetched
2116
2142
  if int(m.get("id", 0)) > last_id and m.get("from_node") != state.self_name
2117
2143
  and int(m.get("id", 0)) not in existing_ids
2118
2144
  and _wake_policy_admits(policy, m, state.self_name)
@@ -18,22 +18,22 @@ from __future__ import annotations
18
18
  import argparse
19
19
  import datetime as dt
20
20
  import json
21
- import os
22
21
  import re
23
22
  import sys
24
23
  from collections import namedtuple
25
24
 
26
25
  from swarph_cli.commands.okf_links import parse_okf_links
26
+ from swarph_cli.timeline_paths import timeline_file
27
27
 
28
28
  Entry = namedtuple("Entry", "ts cell text links")
29
29
 
30
- _DEFAULT_TIMELINE = os.path.expanduser("~/swarph-timeline/TIMELINE.md")
31
30
  # - <ISO-ts> · **<cell>** · <rest>
32
31
  _LINE = re.compile(r"^- (?P<ts>\S+)\s+·\s+\*\*(?P<cell>[^*]+)\*\*\s+·\s+(?P<rest>.*)$")
33
32
 
34
33
 
35
34
  def _timeline_path() -> str:
36
- return os.environ.get("SWARPH_TIMELINE", _DEFAULT_TIMELINE)
35
+ """Same default ``swarph highlight`` writes. Call-time — see timeline_paths."""
36
+ return str(timeline_file())
37
37
 
38
38
 
39
39
  # EVERY FORM THE SHARED TIMELINE ACTUALLY CONTAINS, not only the one we write today.
@@ -134,8 +134,12 @@ def _fmt_human(e: Entry) -> str:
134
134
  def run_timeline(argv: list) -> int:
135
135
  p = argparse.ArgumentParser(
136
136
  prog="swarph timeline",
137
+ formatter_class=argparse.RawDescriptionHelpFormatter,
137
138
  description="Deterministic temporal lookup over the git-backed swarph timeline "
138
- "(range/around/since). $0, no model, no network.")
139
+ "(range/around/since). $0, no model, no network.\n"
140
+ "Default file: ~/swarph-timeline/TIMELINE.md "
141
+ "(SWARPH_TIMELINE / SWARPH_TIMELINE_DIR; same default "
142
+ "swarph highlight writes; #716).")
139
143
  sub = p.add_subparsers(dest="subcommand")
140
144
  pr = sub.add_parser("range", help="entries between two dates (inclusive)")
141
145
  pr.add_argument("start"); pr.add_argument("end")