swarph-cli 0.62.0__tar.gz → 0.64.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 (414) hide show
  1. {swarph_cli-0.62.0/src/swarph_cli.egg-info → swarph_cli-0.64.0}/PKG-INFO +6 -2
  2. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/README.md +5 -1
  3. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/pyproject.toml +1 -1
  4. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/__init__.py +1 -1
  5. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/board.py +59 -12
  6. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/codegraph.py +63 -8
  7. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/codegraph_hook.py +24 -1
  8. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/dreaming.py +8 -0
  9. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/guide.py +55 -2
  10. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/hooks.py +59 -34
  11. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/mcp_server.py +9 -2
  12. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/mesh.py +24 -7
  13. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/monitor.py +4 -2
  14. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/onboard.py +8 -0
  15. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/spawn.py +67 -11
  16. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/watchdog.py +33 -7
  17. swarph_cli-0.64.0/src/swarph_cli/dreaming/notify.py +96 -0
  18. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/dreaming/run.py +14 -0
  19. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/guide/GUIDE.md +151 -0
  20. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/main.py +18 -0
  21. swarph_cli-0.64.0/src/swarph_cli/source_text.py +129 -0
  22. {swarph_cli-0.62.0 → swarph_cli-0.64.0/src/swarph_cli.egg-info}/PKG-INFO +6 -2
  23. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli.egg-info/SOURCES.txt +10 -0
  24. swarph_cli-0.64.0/tests/test_126_pending_from_complete.py +116 -0
  25. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_397_gh_identity_router.py +4 -1
  26. swarph_cli-0.64.0/tests/test_547_verbs_from_registry.py +59 -0
  27. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_552_codegraph_gateway.py +15 -1
  28. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_556_relay_freshness.py +15 -1
  29. swarph_cli-0.64.0/tests/test_825_remaining_cap_shred_bind.py +165 -0
  30. swarph_cli-0.64.0/tests/test_866_doctrine_guide.py +71 -0
  31. swarph_cli-0.64.0/tests/test_872_identity_refuses_not_warns.py +129 -0
  32. swarph_cli-0.64.0/tests/test_874_source_text.py +116 -0
  33. swarph_cli-0.64.0/tests/test_888_opencode_data_dir_can_fail.py +71 -0
  34. swarph_cli-0.64.0/tests/test_937_dreaming_notify.py +120 -0
  35. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_board_card_thread.py +58 -2
  36. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_codegraph_hook.py +1 -1
  37. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_codegraph_mcp.py +15 -1
  38. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_monitor_state_split.py +2 -1
  39. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_opencode_membrane.py +45 -15
  40. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/LICENSE +0 -0
  41. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/setup.cfg +0 -0
  42. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/__main__.py +0 -0
  43. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/bench/__init__.py +0 -0
  44. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/bench/backends.py +0 -0
  45. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/bench/pack.py +0 -0
  46. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/bench/prices.py +0 -0
  47. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/bench/quality.py +0 -0
  48. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/bench/refresh_prices.py +0 -0
  49. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/bench/runner.py +0 -0
  50. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/bench/validate.py +0 -0
  51. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/caller.py +0 -0
  52. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/capture/__init__.py +0 -0
  53. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/capture/harden.py +0 -0
  54. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/capture/lineage.py +0 -0
  55. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/capture/liveness.py +0 -0
  56. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/capture/manifest.py +0 -0
  57. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/capture/paths.py +0 -0
  58. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/capture/verify.py +0 -0
  59. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/cell.py +0 -0
  60. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/chain_token.py +0 -0
  61. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/__init__.py +0 -0
  62. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/_content.py +0 -0
  63. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/_display.py +0 -0
  64. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/_gateway_client.py +0 -0
  65. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/add.py +0 -0
  66. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/bench.py +0 -0
  67. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/board_merge_check.py +0 -0
  68. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/brain.py +0 -0
  69. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/brain_ask.py +0 -0
  70. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/cell.py +0 -0
  71. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/cell_probe.py +0 -0
  72. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  73. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/channel.py +0 -0
  74. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/chat.py +0 -0
  75. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/codex_hook_output.py +0 -0
  76. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/codex_waker.py +0 -0
  77. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/compress.py +0 -0
  78. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/daemon.py +0 -0
  79. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/event.py +0 -0
  80. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/gateway.py +0 -0
  81. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/gh_route.py +0 -0
  82. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/group.py +0 -0
  83. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/highlight.py +0 -0
  84. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/hook_output.py +0 -0
  85. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/import_session.py +0 -0
  86. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/init.py +0 -0
  87. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
  88. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/install_hook.py +0 -0
  89. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  90. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
  91. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
  92. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/install_wake_hook.py +0 -0
  93. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/lane.py +0 -0
  94. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/memory.py +0 -0
  95. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
  96. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/memory_sync.py +0 -0
  97. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/okf_links.py +0 -0
  98. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
  99. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
  100. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/protocol_handler.py +0 -0
  101. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/ratify.py +0 -0
  102. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/schedule.py +0 -0
  103. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/security.py +0 -0
  104. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/service.py +0 -0
  105. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/timeline.py +0 -0
  106. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/version.py +0 -0
  107. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/commands/wake_hook_output.py +0 -0
  108. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/compress/__init__.py +0 -0
  109. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/compress/levers.py +0 -0
  110. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/compress/marker.py +0 -0
  111. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/compress/verify.py +0 -0
  112. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/console_safe.py +0 -0
  113. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/delivery_queue.py +0 -0
  114. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/dreaming/__init__.py +0 -0
  115. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/dreaming/candidates.py +0 -0
  116. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/dreaming/clone.py +0 -0
  117. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/dreaming/enrich.py +0 -0
  118. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/dreaming/index_check.py +0 -0
  119. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/dreaming/organize.py +0 -0
  120. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/dreaming/probes.py +0 -0
  121. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/dreaming/report.py +0 -0
  122. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/dreaming/slm.py +0 -0
  123. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/dreaming/transcripts.py +0 -0
  124. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/dreaming/verify.py +0 -0
  125. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/gateway/__init__.py +0 -0
  126. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/gateway/feature_registry.py +0 -0
  127. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/gateway/lanes_control.py +0 -0
  128. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/gateway/schema.sql +0 -0
  129. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/gateway/server.py +0 -0
  130. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/gateway/services_control.py +0 -0
  131. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/gateway_default.py +0 -0
  132. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/gh_identity.py +0 -0
  133. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/multiplexer.py +0 -0
  134. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/orphan_daemons.py +0 -0
  135. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/pane_probe.py +0 -0
  136. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/parsers/__init__.py +0 -0
  137. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/parsers/claude.py +0 -0
  138. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
  139. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
  140. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
  141. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
  142. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
  143. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
  144. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
  145. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
  146. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
  147. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
  148. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
  149. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
  150. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/peer_executor.py +0 -0
  151. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/peer_executor_windows.py +0 -0
  152. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/peer_reconciliation.py +0 -0
  153. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/peer_reply_outbox.py +0 -0
  154. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/peer_service_host.py +0 -0
  155. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/peer_service_payload.py +0 -0
  156. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/peer_service_reply.py +0 -0
  157. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/peer_staging.py +0 -0
  158. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/query_filters.py +0 -0
  159. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/scripts/__init__.py +0 -0
  160. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
  161. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  162. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
  163. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
  164. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/service/__init__.py +0 -0
  165. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/service/app.py +0 -0
  166. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/service/providers.py +0 -0
  167. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/session_bridge.py +0 -0
  168. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
  169. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
  170. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/stall_alert.py +0 -0
  171. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/systemd/codex-waker.default +0 -0
  172. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
  173. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
  174. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
  175. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
  176. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
  177. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
  178. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
  179. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
  180. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
  181. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
  182. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  183. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  184. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  185. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/timeline_paths.py +0 -0
  186. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli/tokens.py +0 -0
  187. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  188. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  189. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli.egg-info/requires.txt +0 -0
  190. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/src/swarph_cli.egg-info/top_level.txt +0 -0
  191. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_124_register_merge.py +0 -0
  192. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_132_ensure_monitor_state_dir.py +0 -0
  193. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_139_tool_probe.py +0 -0
  194. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_183_card_anchor.py +0 -0
  195. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_201_win32_bash_refusal.py +0 -0
  196. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_243_onboard_per_peer_token.py +0 -0
  197. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_307_task4_ask_reply.py +0 -0
  198. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
  199. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_319_refusal_exit_codes.py +0 -0
  200. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_323_mistral_backend.py +0 -0
  201. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_356_query_filters.py +0 -0
  202. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_397_router_preflight.py +0 -0
  203. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_467b_onboard_operator_credential.py +0 -0
  204. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_523_guide.py +0 -0
  205. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_524_conftest_hermeticity.py +0 -0
  206. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_532_accept_flag.py +0 -0
  207. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_535_version_capability.py +0 -0
  208. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_546_default_gateway.py +0 -0
  209. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_548_gateway_remainder.py +0 -0
  210. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_555_relay_row_shape.py +0 -0
  211. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_564_bootstrap_first_peer.py +0 -0
  212. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
  213. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_579_register_bootstrap_message.py +0 -0
  214. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_591_cli_step_graph.py +0 -0
  215. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_644_monitor_supervision.py +0 -0
  216. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_648_install_unit.py +0 -0
  217. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_654_reexec.py +0 -0
  218. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_656_owned_slm.py +0 -0
  219. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_684_dreaming_verb.py +0 -0
  220. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_689_pkg_version_order.py +0 -0
  221. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_716_timeline_one_path.py +0 -0
  222. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_724_ps1_ascii_or_bom.py +0 -0
  223. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_725_stdio_reconfigure.py +0 -0
  224. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_734_dreaming_enrich_degrade.py +0 -0
  225. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_740_edit_project.py +0 -0
  226. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_762_cards_edit_priority.py +0 -0
  227. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_801_confirm_flow_cli.py +0 -0
  228. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_802_stage_history.py +0 -0
  229. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_825_codegraph_hook_noise.py +0 -0
  230. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_829_counterfactual_column.py +0 -0
  231. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_830_hook_binding_version.py +0 -0
  232. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_84_cli_labels.py +0 -0
  233. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_add_security_gate.py +0 -0
  234. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_artifact_add.py +0 -0
  235. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_artifact_hash.py +0 -0
  236. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_artifact_lib.py +0 -0
  237. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_artifact_mcp.py +0 -0
  238. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_artifact_skill.py +0 -0
  239. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_artifact_tool.py +0 -0
  240. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_artifact_uri.py +0 -0
  241. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_bench_backends.py +0 -0
  242. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_bench_command.py +0 -0
  243. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_bench_pack.py +0 -0
  244. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_bench_prices.py +0 -0
  245. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_bench_quality.py +0 -0
  246. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_bench_runner.py +0 -0
  247. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_bench_validate.py +0 -0
  248. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_board_command.py +0 -0
  249. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_board_merge_check.py +0 -0
  250. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_board_obligation_close.py +0 -0
  251. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_brain_ask_command.py +0 -0
  252. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_brain_ask_env_diagnosis.py +0 -0
  253. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_brain_ask_identity_explicitness.py +0 -0
  254. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_brain_command.py +0 -0
  255. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_caller_meta_guard.py +0 -0
  256. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_capture_lineage.py +0 -0
  257. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_capture_liveness.py +0 -0
  258. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_capture_manifest.py +0 -0
  259. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_capture_paths.py +0 -0
  260. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_capture_security.py +0 -0
  261. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_cell_command_dispatch.py +0 -0
  262. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_cell_harden.py +0 -0
  263. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_cell_loader.py +0 -0
  264. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_cell_selfcheck.py +0 -0
  265. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_cell_verify.py +0 -0
  266. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_chain_token.py +0 -0
  267. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_channel_command.py +0 -0
  268. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_channel_poll_never_breaks_dms.py +0 -0
  269. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_channel_post.py +0 -0
  270. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_channel_wake_policy_filter.py +0 -0
  271. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_chat_command.py +0 -0
  272. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_claude_parser.py +0 -0
  273. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_claude_tmux_template.py +0 -0
  274. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_codegraph_caller_scope.py +0 -0
  275. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_codegraph_command.py +0 -0
  276. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_codex_waker.py +0 -0
  277. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_compress_caller_convention.py +0 -0
  278. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_compress_command.py +0 -0
  279. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_compress_levers.py +0 -0
  280. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_compress_marker.py +0 -0
  281. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_compress_verify.py +0 -0
  282. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_console_encoding_e2e.py +0 -0
  283. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_content_composition.py +0 -0
  284. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_cursor_membrane.py +0 -0
  285. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_daemon_command.py +0 -0
  286. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_daemon_console_encoding.py +0 -0
  287. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_daemon_delivery.py +0 -0
  288. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_delivery_queue.py +0 -0
  289. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_display_sanitize.py +0 -0
  290. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_ensure_monitor_no_peer_default.py +0 -0
  291. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_event_emit.py +0 -0
  292. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_feature_to_uri.py +0 -0
  293. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_gateway_brain.py +0 -0
  294. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_gateway_client.py +0 -0
  295. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_gateway_command.py +0 -0
  296. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_gateway_message_idempotency.py +0 -0
  297. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_gh_route_doctor_mesh_name.py +0 -0
  298. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_group_command.py +0 -0
  299. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_guide_commands_parse.py +0 -0
  300. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_guide_selfcheck.py +0 -0
  301. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_heartbeat_check.py +0 -0
  302. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_highlight_command.py +0 -0
  303. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_hook_output.py +0 -0
  304. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_hooks_activity_marker.py +0 -0
  305. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_hooks_add.py +0 -0
  306. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_hooks_bundle.py +0 -0
  307. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_hooks_init.py +0 -0
  308. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_hooks_legacy_generation_removal.py +0 -0
  309. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_hooks_lifecycle.py +0 -0
  310. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_hooks_merge.py +0 -0
  311. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_hooks_verify.py +0 -0
  312. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_hooks_win32_bash_wrapper.py +0 -0
  313. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_hooks_win32_command_path.py +0 -0
  314. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_import_command.py +0 -0
  315. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_inbox_identity_provenance.py +0 -0
  316. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_init_command.py +0 -0
  317. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_init_provider_menu.py +0 -0
  318. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_install_codex_hooks.py +0 -0
  319. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_install_hook.py +0 -0
  320. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_install_multiplexer_command.py +0 -0
  321. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_install_postcompact_hook.py +0 -0
  322. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_install_wake_hook.py +0 -0
  323. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_lane_command.py +0 -0
  324. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_main.py +0 -0
  325. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_main_unknown_verb_guard.py +0 -0
  326. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_mcp_missing_hint.py +0 -0
  327. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_mcp_server.py +0 -0
  328. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_memory_command.py +0 -0
  329. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_memory_emit_snippet.py +0 -0
  330. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_memory_gateway.py +0 -0
  331. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_memory_sync.py +0 -0
  332. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_memory_sync_membrane.py +0 -0
  333. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_mesh_command.py +0 -0
  334. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_mesh_inbox_mark_read.py +0 -0
  335. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_mesh_inbox_sanitize.py +0 -0
  336. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_mesh_send_unknown_peer.py +0 -0
  337. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_mesh_sidecar.py +0 -0
  338. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  339. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  340. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_meta_edge_identity.py +0 -0
  341. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_monitor_channel_poll.py +0 -0
  342. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_monitor_cli.py +0 -0
  343. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_monitor_daemon_collision.py +0 -0
  344. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_monitor_self_name_verification.py +0 -0
  345. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_multiplexer.py +0 -0
  346. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_muse_membrane.py +0 -0
  347. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_muse_own_cli.py +0 -0
  348. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_obligation_close_is_surfaced.py +0 -0
  349. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_okf_links.py +0 -0
  350. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_onboard_command.py +0 -0
  351. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_opencode_plugin.py +0 -0
  352. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_orphan_daemons.py +0 -0
  353. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_packaged_artifacts_exist.py +0 -0
  354. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_packaged_skills.py +0 -0
  355. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_peer_executor.py +0 -0
  356. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_peer_executor_windows.py +0 -0
  357. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_peer_reply_drain_command.py +0 -0
  358. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_peer_reply_drain_supervisor.py +0 -0
  359. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_peer_reply_outbox.py +0 -0
  360. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_peer_reply_transport.py +0 -0
  361. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_peer_service_e2e.py +0 -0
  362. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_peer_service_host.py +0 -0
  363. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_peer_service_payload.py +0 -0
  364. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_peer_service_reply.py +0 -0
  365. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_postcompact_recall.py +0 -0
  366. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_process_liveness.py +0 -0
  367. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_protocol_handler.py +0 -0
  368. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_providers_isolation.py +0 -0
  369. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_ratify_command.py +0 -0
  370. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_readme_claims_no_gateway_default.py +0 -0
  371. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_repl_caller_convention.py +0 -0
  372. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_schedule_command.py +0 -0
  373. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_schedule_create_contract.py +0 -0
  374. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_security.py +0 -0
  375. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_service_app.py +0 -0
  376. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_service_command.py +0 -0
  377. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_service_providers.py +0 -0
  378. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_session_bridge_inject.py +0 -0
  379. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_session_bridge_probe.py +0 -0
  380. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_session_bridge_resolve.py +0 -0
  381. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_session_path_role_gate.py +0 -0
  382. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_shipped_units_match_the_cli.py +0 -0
  383. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_smoke_chat.py +0 -0
  384. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_smoke_one_shot.py +0 -0
  385. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_smoke_phase_5_5.py +0 -0
  386. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_spawn_base_hoist.py +0 -0
  387. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_spawn_command.py +0 -0
  388. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_spawn_identity_stamp.py +0 -0
  389. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_spawn_live_pin.py +0 -0
  390. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_spawn_mitosis_lineage.py +0 -0
  391. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_spawn_tmux_session.py +0 -0
  392. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_spawn_windows_relaunch.py +0 -0
  393. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_stall_alert.py +0 -0
  394. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_subprocess_decode_encoding.py +0 -0
  395. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_timeline_command.py +0 -0
  396. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_tmux_wake_runstate_defer.py +0 -0
  397. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_tmux_wake_session_reanchor.py +0 -0
  398. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_tmux_wake_stale.py +0 -0
  399. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_tmux_wake_submit_verify.py +0 -0
  400. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_token_file_one_parser.py +0 -0
  401. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_token_precedence_explicit_first.py +0 -0
  402. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_unified_token_resolver.py +0 -0
  403. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_version_command.py +0 -0
  404. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_version_consistency.py +0 -0
  405. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_vibe_membrane.py +0 -0
  406. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_watchdog.py +0 -0
  407. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_watchdog_dm_wake.py +0 -0
  408. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  409. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  410. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_watchdog_emit_health.py +0 -0
  411. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_watchdog_install_flags.py +0 -0
  412. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_watchdog_model_rung.py +0 -0
  413. {swarph_cli-0.62.0 → swarph_cli-0.64.0}/tests/test_watchdog_stale_peers.py +0 -0
  414. {swarph_cli-0.62.0 → swarph_cli-0.64.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.62.0
3
+ Version: 0.64.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
@@ -148,6 +148,7 @@ swarph guide # the whole guide
148
148
  swarph guide channels # one topic
149
149
  swarph guide --list # every topic, with the commands it teaches
150
150
  swarph guide --search subscribe # find a topic by INTENT, not by its name
151
+ swarph guide doctrine # the standard of evidence a cell is held to (v0.63.0)
151
152
  ```
152
153
 
153
154
  Three ways in, because a new cell does not know which word it has:
@@ -291,9 +292,12 @@ Between-sessions memory pass: **VERIFY · ORGANIZE · ENRICH** against a **CLONE
291
292
  ```
292
293
  $ swarph dreaming run --corpus ~/.claude/projects/.../memory --out /tmp/dreaming-out
293
294
  $ swarph dreaming run --corpus <dir> --out <dir> --verify-only
294
- $ echo $? # 0 clean-and-adjudicated | 1 findings | 2 refused | 3 ran-but-adjudicated-nothing
295
+ $ swarph dreaming run --corpus <dir> --out <dir> --notify <cell>
296
+ $ echo $? # 0 clean-and-adjudicated | 1 findings | 2 refused | 3 ran-but-adjudicated-nothing | 4 notify send failed
295
297
  ```
296
298
 
299
+ `--notify <cell>` (#937) sends one mesh DM to `<cell>` **only when the finding set changes** (new and cleared findings named, with counts); an unchanged set sends nothing. State lives in `.last-findings.json` beside the `--out` dir and advances only after a successful send, so a failed send (exit 4) retries on the next run.
300
+
297
301
  **Scope is single-cell:** your corpus and your local transcripts. This is not the cross-agent multi-transcript dreaming from the Anthropic talk — that needs a shared transcript surface this release does not claim.
298
302
 
299
303
  #### The AI Router (why a tool, not a hook)
@@ -100,6 +100,7 @@ swarph guide # the whole guide
100
100
  swarph guide channels # one topic
101
101
  swarph guide --list # every topic, with the commands it teaches
102
102
  swarph guide --search subscribe # find a topic by INTENT, not by its name
103
+ swarph guide doctrine # the standard of evidence a cell is held to (v0.63.0)
103
104
  ```
104
105
 
105
106
  Three ways in, because a new cell does not know which word it has:
@@ -243,9 +244,12 @@ Between-sessions memory pass: **VERIFY · ORGANIZE · ENRICH** against a **CLONE
243
244
  ```
244
245
  $ swarph dreaming run --corpus ~/.claude/projects/.../memory --out /tmp/dreaming-out
245
246
  $ swarph dreaming run --corpus <dir> --out <dir> --verify-only
246
- $ echo $? # 0 clean-and-adjudicated | 1 findings | 2 refused | 3 ran-but-adjudicated-nothing
247
+ $ swarph dreaming run --corpus <dir> --out <dir> --notify <cell>
248
+ $ echo $? # 0 clean-and-adjudicated | 1 findings | 2 refused | 3 ran-but-adjudicated-nothing | 4 notify send failed
247
249
  ```
248
250
 
251
+ `--notify <cell>` (#937) sends one mesh DM to `<cell>` **only when the finding set changes** (new and cleared findings named, with counts); an unchanged set sends nothing. State lives in `.last-findings.json` beside the `--out` dir and advances only after a successful send, so a failed send (exit 4) retries on the next run.
252
+
249
253
  **Scope is single-cell:** your corpus and your local transcripts. This is not the cross-agent multi-transcript dreaming from the Anthropic talk — that needs a shared transcript surface this release does not claim.
250
254
 
251
255
  #### The AI Router (why a tool, not a hook)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "swarph-cli"
7
- version = "0.62.0"
7
+ version = "0.64.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.62.0"
19
+ __version__ = "0.64.0"
20
20
 
21
21
  __all__ = ["__version__"]
@@ -390,8 +390,38 @@ def _thread_url(gateway: str, card_id: int, *, limit: Optional[int] = None) -> s
390
390
  return f"{base}?limit={limit}" if limit else base
391
391
 
392
392
 
393
- def _thread_recipient(card: dict, explicit_to: Optional[str]) -> str:
394
- """Who a card-thread post is addressed to. `--to` wins, else the assignee.
393
+ def _project_owner_orchestrator(gw: str, token: str, project_id) -> Optional[str]:
394
+ """Resolve ``projects.owner_orchestrator`` for a card's project_id.
395
+
396
+ >>> THE CARD PAYLOAD DOES NOT CARRY THE OWNER (#864). <<< Measured
397
+ 2026-09-17: GET /board/cards/{id} has assignee/project_id and no owner
398
+ field; GET /board/projects rows carry ``owner_orchestrator`` (NOT NULL on
399
+ the server). A second call is required — inventing a sentinel peer is the
400
+ #259 failure mode this helper exists to avoid.
401
+ """
402
+ if project_id is None:
403
+ return None
404
+ st, data = _http_get_json(f"{gw.rstrip('/')}/board/projects", token)
405
+ if st != 200:
406
+ return None
407
+ rows = data if isinstance(data, list) else (data or {}).get("projects", [])
408
+ for row in rows or []:
409
+ if row.get("id") == project_id or str(row.get("id")) == str(project_id):
410
+ owner = (row.get("owner_orchestrator") or "").strip()
411
+ return owner or None
412
+ return None
413
+
414
+
415
+ def _thread_recipient(
416
+ card: dict,
417
+ explicit_to: Optional[str],
418
+ *,
419
+ owner_orchestrator: Optional[str] = None,
420
+ ) -> "tuple[str, str]":
421
+ """Who a card-thread post is addressed to, and why.
422
+
423
+ Order: ``--to`` > card assignee > project ``owner_orchestrator`` (#864).
424
+ Returns ``(to_node, reason)`` so the confirmation line can name the choice.
395
425
 
396
426
  >>> RAISES RATHER THAN INVENTING A SENTINEL. <<< POST /messages requires
397
427
  exactly one of {to_node, channel}, so a card post needs a real recipient. The
@@ -401,18 +431,21 @@ def _thread_recipient(card: dict, explicit_to: Optional[str]) -> str:
401
431
  A placeholder here would manufacture that defect once per card post.
402
432
  """
403
433
  if explicit_to:
404
- return explicit_to
434
+ return explicit_to, "--to"
405
435
  assignee = card.get("assignee")
406
436
  if assignee:
407
- return assignee
437
+ return assignee, "card assignee"
438
+ if owner_orchestrator:
439
+ return owner_orchestrator, "project owner_orchestrator"
408
440
  raise RuntimeError(
409
- f"card #{card.get('id')} has no assignee, so there is no default recipient "
410
- f"for a thread post — pass --to <peer>. (A card post is still a DM on the "
411
- f"wire; it needs someone to be addressed to.)"
441
+ f"card #{card.get('id')} has no assignee and project owner_orchestrator "
442
+ f"is unresolvable, so there is no default recipient for a thread post — "
443
+ f"pass --to <peer>. (A card post is still a DM on the wire; it needs "
444
+ f"someone to be addressed to.)"
412
445
  )
413
446
 
414
447
 
415
- def _say_line(resp: dict, card_id, to_node: str) -> str:
448
+ def _say_line(resp: dict, card_id, to_node: str, *, reason: Optional[str] = None) -> str:
416
449
  """The post confirmation — AND any obligation the post just DISCHARGED.
417
450
 
418
451
  >>> THE GATEWAY ALREADY SAID SO AND THIS CLI THREW IT AWAY. <<< POST
@@ -435,7 +468,11 @@ def _say_line(resp: dict, card_id, to_node: str) -> str:
435
468
  POLICY (that is #562's own question) -- it is the fix for the holder not
436
469
  being told, which is what made six hours of divergence possible.
437
470
  """
438
- line = f"posted id={resp.get('id')} onto card #{card_id} (to {to_node})"
471
+ if reason:
472
+ line = (f"posted id={resp.get('id')} onto card #{card_id} "
473
+ f"(to {to_node}; {reason})")
474
+ else:
475
+ line = f"posted id={resp.get('id')} onto card #{card_id} (to {to_node})"
439
476
  closed = resp.get("closed_obligations") or []
440
477
  if closed:
441
478
  ids = ", ".join(f"#{i}" for i in closed)
@@ -855,7 +892,8 @@ def _build_parser() -> argparse.ArgumentParser:
855
892
  add_content_args(cy)
856
893
  cy.add_argument("--kind", default="fyi", help="status|question|answer|unblock|fyi")
857
894
  cy.add_argument("--to", dest="to_node", default=None,
858
- help="recipient peer (default: the card's assignee)")
895
+ help="recipient peer (default: card assignee, else project "
896
+ "owner_orchestrator; refused if neither resolves)")
859
897
  _add_common(cy)
860
898
 
861
899
  ck = cards.add_parser(
@@ -1278,8 +1316,15 @@ def run_board(argv: list[str]) -> int:
1278
1316
  f"(it predates #181a). Run scripts/migrate_card_threads.py on "
1279
1317
  f"the gateway host.", file=sys.stderr)
1280
1318
  return 1
1319
+ # #864: card GET has no owner field — only fetch projects when the
1320
+ # cheaper defaults (explicit --to, card assignee) are absent.
1321
+ owner = None
1322
+ if not args.to_node and not card.get("assignee"):
1323
+ owner = _project_owner_orchestrator(gw, token, card.get("project_id"))
1281
1324
  try:
1282
- to_node = _thread_recipient(card, args.to_node)
1325
+ to_node, reason = _thread_recipient(
1326
+ card, args.to_node, owner_orchestrator=owner,
1327
+ )
1283
1328
  except RuntimeError as exc:
1284
1329
  print(f"swarph board cards say: {exc}", file=sys.stderr)
1285
1330
  return 1
@@ -1293,7 +1338,9 @@ def run_board(argv: list[str]) -> int:
1293
1338
  # explicit `propose` grant. `_out` passes `detail` through whole; it does
1294
1339
  # not need help, and a second copy of that logic is a second thing to
1295
1340
  # keep in sync.
1296
- return _out(st, d, lambda x: _say_line(x, args.id, to_node), aj)
1341
+ return _out(
1342
+ st, d, lambda x: _say_line(x, args.id, to_node, reason=reason), aj,
1343
+ )
1297
1344
  if args.command == "ready":
1298
1345
  st, d = _patch_json(f"{gw}/board/cards/{args.id}", {"actor": self_name, "move_ready": not args.clear}, token)
1299
1346
  return _out(st, d, lambda x: f"card #{x.get('id')} move_ready -> {x.get('move_ready')}", aj)
@@ -37,12 +37,65 @@ import sys
37
37
  from swarph_cli.commands.codegraph_hook import _match_quality
38
38
 
39
39
  DEFAULT_INDEX = os.path.expanduser("~/.swarph/codegraph/index.db")
40
- # NOTE: named DEFAULT_CALLER_CELL, not "*_CALLER" — this is an A8 caller-CELL
41
- # identity (sensitivity-gate scoping), not a SwarphCall `role.subrole.specific`
42
- # caller tag, and must NOT trip the repo-wide caller-convention meta-guard
43
- # (tests/test_caller_meta_guard.py) which validates every `*_CALLER` constant
44
- # against that unrelated dotted-slug convention.
45
- DEFAULT_CALLER_CELL = "lab-ovh"
40
+ # >>> THERE IS NO DEFAULT IDENTITY ANY MORE. THIS CONSTANT IS GONE ON PURPOSE. <<<
41
+ #
42
+ # It used to read `DEFAULT_CALLER_CELL = "lab-ovh"`, and that one line meant every
43
+ # unconfigured caller on lab-ovh queried with the ORCHESTRATOR'S READ AUTHORITY.
44
+ #
45
+ # MEASURED 2026-09-18 (science-claude, mesh 43183/43186/43188). The gateway was never
46
+ # bypassed and nothing was ever spoofed: `_query_via_gateway` sends NO caller field, so
47
+ # the server binds identity from the bearer token, correctly. What the default did was
48
+ # pick WHICH REAL TOKEN got presented — `_resolve_token(self_name, ...)` reads the file
49
+ # that name points at. A cell asking a question on this box authenticated AS lab-ovh,
50
+ # with lab-ovh's genuine credential, and got a full, plausible, correctly-authorised
51
+ # answer belonging to somebody else.
52
+ #
53
+ # >>> A WRONG-IDENTITY WRITE ANNOUNCES ITSELF — a DM arrives signed by the wrong cell
54
+ # and someone notices. A WRONG-IDENTITY READ ANNOUNCES NOTHING. <<< It was found only
55
+ # because a repo its owner KNEW they owned was missing from their own results.
56
+ #
57
+ # WHY REFUSE RATHER THAN WARN (commander, 2026-09-18 04:44Z): a warning on a read that
58
+ # returns a correct-looking answer is a line of text above a result the caller already
59
+ # believes. And the cost of refusing was measured first, not assumed — of 8 executable
60
+ # `swarph` invocations in systemd + cron on lab-ovh, 7 already pass `--as` or
61
+ # `--token-file`, and the 8th (`monitor reexec-on-change`) cannot authenticate at all:
62
+ # no --gateway, no --token-file, no --as exists on that verb. SCHEDULED WORK BREAKS IN
63
+ # ZERO PLACES. The population that pays is interactive use, which is the population
64
+ # that was silently mis-attributed.
65
+ #
66
+ # $SWARPH_SELF STILL WORKS AND IS THE INTENDED PATH. It is a DECLARED identity. What is
67
+ # refused is the *undeclared* case, where a package-level guess stood in for one.
68
+ IDENTITY_UNSET = object() # sentinel: argparse default when --caller-cell is omitted
69
+
70
+
71
+ class IdentityNotDeclared(RuntimeError):
72
+ """No caller identity was declared and there is no default to fall back to."""
73
+
74
+
75
+ def require_caller_cell(explicit=None, *, verb: str) -> str:
76
+ """Resolve the caller cell, or REFUSE.
77
+
78
+ Order: explicit flag -> $SWARPH_SELF -> $SWARPH_CELL -> refuse.
79
+
80
+ The refusal names the verb and every way to satisfy it, because an error that
81
+ says only "identity unset" moves the work to the reader.
82
+ """
83
+ if explicit not in (None, IDENTITY_UNSET) and str(explicit).strip():
84
+ return str(explicit).strip()
85
+ for var in ("SWARPH_SELF", "SWARPH_CELL"):
86
+ v = (os.environ.get(var) or "").strip()
87
+ if v:
88
+ return v
89
+ raise IdentityNotDeclared(
90
+ f"swarph {verb}: NO CALLER IDENTITY DECLARED, and there is no default.\n"
91
+ f" This used to fall back to 'lab-ovh', which meant an undeclared caller\n"
92
+ f" queried with that cell's read authority and got somebody else's answer.\n"
93
+ f" Declare one of:\n"
94
+ f" --caller-cell <cell> for this invocation\n"
95
+ f" SWARPH_SELF=<cell> for this shell or unit\n"
96
+ f" SWARPH_CELL=<cell> (MCP hosts)\n"
97
+ f" It is NOT guessable: this box hosts several cells under one uid."
98
+ )
46
99
 
47
100
  # NOT 1 and NOT 2. A distinct code so "nothing was searched" can never be confused
48
101
  # with a crash or a usage error, and so a caller can branch on it specifically.
@@ -332,7 +385,7 @@ def _query_via_gateway(gateway: str, query: str, limit: int, token_file):
332
385
  from swarph_cli.commands.mesh import _post_json, _resolve_token # local import: keeps
333
386
  # the local-only path free of any mesh dependency, so an offline cell still works.
334
387
 
335
- self_name = os.environ.get("SWARPH_SELF") or DEFAULT_CALLER_CELL
388
+ self_name = require_caller_cell(verb="codegraph")
336
389
  token = _resolve_token(self_name, token_file)
337
390
  body = {"query": query, "limit": max(1, min(int(limit), 25))}
338
391
  status, payload = _post_json(gateway.rstrip("/") + "/codegraph", body, token)
@@ -352,7 +405,9 @@ def run_codegraph(argv) -> int:
352
405
  "is configured (the server searches and returns the answer); falls back to a local index.")
353
406
  ap.add_argument("query", help="natural-language query, e.g. 'which function escapes HTML'")
354
407
  ap.add_argument("--index", default=DEFAULT_INDEX, help=f"index db (default {DEFAULT_INDEX})")
355
- ap.add_argument("--caller-cell", default=DEFAULT_CALLER_CELL, help="A8 caller identity (default lab-ovh)")
408
+ ap.add_argument("--caller-cell", default=IDENTITY_UNSET,
409
+ help="A8 caller identity. NO DEFAULT — else $SWARPH_SELF / $SWARPH_CELL, "
410
+ "else the command REFUSES rather than guessing (it used to guess lab-ovh).")
356
411
  ap.add_argument("--limit", type=int, default=8)
357
412
  ap.add_argument("--json", action="store_true", help="machine-readable JSON output")
358
413
  ap.add_argument("--gateway", default=None,
@@ -37,6 +37,10 @@ from swarph_cli.gateway_default import env_gateway
37
37
 
38
38
  TIMEOUT_S = 6
39
39
  MAX_ROWS = 6
40
+ # Probe one past the display cap so audit can tell "exactly 6" from "hit the
41
+ # clamp" (#825 / lab 48145: 90.5% of rows sat on match_count==6 because the
42
+ # query limit WAS the count).
43
+ _PROBE_LIMIT = MAX_ROWS + 1
40
44
 
41
45
  # Identifier token — used for prompt terms and for the Bash shred skip (no
42
46
  # useful symbol-shaped token left after cleaning).
@@ -89,6 +93,12 @@ def extract_raw_and_cleaned(cmd: str) -> Optional[tuple[str, str]]:
89
93
  if tok.startswith("-"):
90
94
  continue
91
95
  raw = tok.strip("'\"")
96
+ # Prefer the longest identifier STILL VISIBLE in the raw pattern
97
+ # before the shredding re.sub (#825). `foo_bar.*[0-9]+` must yield
98
+ # `foo_bar`, not skip-as-shredded or query regex debris.
99
+ idents = [t for t in _IDENT_TOKEN.findall(raw) if len(t) >= 3]
100
+ if idents:
101
+ return (raw, max(idents, key=len))
92
102
  cleaned = re.sub(r"[^\w\s.]", " ", raw).strip()
93
103
  if not cleaned:
94
104
  return None
@@ -308,7 +318,8 @@ def resolve_pending_outcome(self_name: str, subsequent: str,
308
318
 
309
319
  # ── gateway query ─────────────────────────────────────────────────────────
310
320
 
311
- def query_gateway(term: str, gateway: str, token: str, limit: int = MAX_ROWS) -> dict:
321
+ def query_gateway(term: str, gateway: str, token: str,
322
+ limit: int = _PROBE_LIMIT) -> dict:
312
323
  if not (gateway or "").strip():
313
324
  return {"error": "MESH_GATEWAY_URL is not set and swarph ships no default "
314
325
  "gateway host (#578) — the graph was never asked"}
@@ -411,6 +422,9 @@ def _query_and_emit(self_name: str, gateway: str, term: str, *,
411
422
  return
412
423
  env = query_gateway(term, gateway, token)
413
424
  rows = env.get("results") or [] if "error" not in env else []
425
+ capped = len(rows) > MAX_ROWS
426
+ # Audit sees the probe length (MAX_ROWS+1 ⇒ at-least / capped). Display
427
+ # still truncates via render()'s rows[:MAX_ROWS].
414
428
  name_hit = any_symbol_name_contains_term(term, rows) if rows else False
415
429
  fid = firing_id or str(uuid.uuid4())
416
430
  write_audit(self_name, {
@@ -424,13 +438,22 @@ def _query_and_emit(self_name: str, gateway: str, term: str, *,
424
438
  "raw_term": raw_term or term,
425
439
  "skipped": False,
426
440
  "match_count": len(rows),
441
+ "match_count_capped": capped,
427
442
  "any_name_contains_term": name_hit,
428
443
  "error": env.get("error"),
429
444
  })
430
445
  if trigger == "prompt":
431
446
  register_pending_firing(self_name, fid, session_id)
447
+ # Render only the display window so the prompt is not flooded.
448
+ if capped and "error" not in env:
449
+ env = {**env, "results": rows[:MAX_ROWS], "match_count_capped": True}
432
450
  text = render(term, env)
433
451
  if text:
452
+ if capped:
453
+ text = text.rstrip() + (
454
+ f"\n (display capped at {MAX_ROWS}; audit match_count="
455
+ f"{len(rows)} match_count_capped=true)"
456
+ )
434
457
  _emit(text, event_name)
435
458
 
436
459
 
@@ -67,6 +67,12 @@ def _build_parser() -> argparse.ArgumentParser:
67
67
  help="require enrich; refuse (rc=2) if SLM client unavailable (#734)",
68
68
  )
69
69
  run.add_argument("--verify-only", action="store_true", help="verify only; skip organize/enrich")
70
+ run.add_argument(
71
+ "--notify",
72
+ default=None,
73
+ metavar="CELL",
74
+ help="DM CELL when the finding set changes; no DM when it is unchanged (#937)",
75
+ )
70
76
  return p
71
77
 
72
78
 
@@ -95,4 +101,6 @@ def run_dreaming(argv: list[str] | None = None) -> int:
95
101
  forwarded.append("--enrich")
96
102
  if args.verify_only:
97
103
  forwarded.append("--verify-only")
104
+ if getattr(args, "notify", None):
105
+ forwarded.extend(["--notify", args.notify])
98
106
  return dreaming_run.main(forwarded)
@@ -130,6 +130,38 @@ def _commands_in(section: str) -> "list[str]":
130
130
  return out
131
131
 
132
132
 
133
+ def verbs_in_guide(topics: "dict[str, str] | None" = None) -> "set[str]":
134
+ """#547: the verbs the guide TEACHES, counted PER VERB (a topic count said 11 and
135
+ meant nothing). A verb is covered when some section's commands include it."""
136
+ topics = topics if topics is not None else _split_topics(_load_guide())
137
+ out: set[str] = set()
138
+ for body in topics.values():
139
+ for cmd in _commands_in(body):
140
+ parts = cmd.split()
141
+ if len(parts) >= 2:
142
+ out.add(parts[1])
143
+ return out
144
+
145
+
146
+ def _registry() -> "list[str]":
147
+ # Lazy: main dispatches handlers by dotted path and does not import this module,
148
+ # but keep the direction one-way anyway.
149
+ from swarph_cli.main import registered_verbs
150
+ return registered_verbs()
151
+
152
+
153
+ def _verb_entry(topics: "dict[str, str]", verb: str) -> "list[tuple[str, str]]":
154
+ """The line in each topic that teaches `swarph <verb>` -- one per topic, first hit."""
155
+ pat = re.compile(r"`swarph " + re.escape(verb) + r"(?:[ `])")
156
+ hits: list[tuple[str, str]] = []
157
+ for name, body in topics.items():
158
+ for raw in body.splitlines():
159
+ if pat.search(raw):
160
+ hits.append((name, raw.strip()))
161
+ break
162
+ return hits
163
+
164
+
133
165
  _TERM_RE = re.compile(r"\*\*(.+?)\*\*")
134
166
 
135
167
 
@@ -262,13 +294,21 @@ def run_guide(argv: "list[str]") -> int:
262
294
  # `apropos`: an LLM arrives with an INTENT ("subscribe to updates"), not the
263
295
  # topic's name ("channels"). Requiring the name is the #520 discovery defect
264
296
  # reproduced inside its own fix.
297
+ term = args.search.strip().lower()
298
+ # #547: a VERB name resolves to the verb's own entry first -- `--search highlight`
299
+ # used to answer with a topic list that did not contain it.
300
+ verb_hits = _verb_entry(topics, term) if term in _registry() else []
265
301
  hits = _search(topics, args.search)
266
- if not hits:
302
+ if not hits and not verb_hits:
267
303
  print_safe(f"swarph guide: nothing matches {args.search!r}. "
268
304
  f"Topics: {', '.join(topics)}", stream=sys.stderr)
269
305
  return 2
270
- for name, line in hits:
306
+ for name, line in verb_hits:
271
307
  print_safe(f"{name:<22} {line}")
308
+ shown = {name for name, _ in verb_hits}
309
+ for name, line in hits:
310
+ if name not in shown:
311
+ print_safe(f"{name:<22} {line}")
272
312
  return 0
273
313
 
274
314
  if not args.topic:
@@ -279,6 +319,19 @@ def run_guide(argv: "list[str]") -> int:
279
319
  if key in topics:
280
320
  print_safe(topics[key])
281
321
  return 0
322
+ if key in _registry():
323
+ # #547: every registered verb resolves, per verb (tests/test_547 pins the set):
324
+ # the lines that teach it, then -- when the name is also a topic's substring
325
+ # (`channel` -> channels) -- that one topic, as before.
326
+ for name, line in _verb_entry(topics, key):
327
+ print_safe(f"{name:<22} {line}")
328
+ near = [k for k in topics if key in k]
329
+ if len(near) == 1:
330
+ print_safe("")
331
+ print_safe(topics[near[0]])
332
+ else:
333
+ print_safe(f"\n more: swarph {key} --help")
334
+ return 0
282
335
 
283
336
  # Substring fallback before failing: `swarph guide channel` should find `channels`.
284
337
  hits = [k for k in topics if key in k]
@@ -1005,36 +1005,47 @@ def uninstall_hook(
1005
1005
  return 0
1006
1006
 
1007
1007
 
1008
- def _is_installed(settings: dict, command: str, bundle: HookBundle) -> bool:
1009
- """True when ALL of ``bundle``'s bindings' commands are present in settings."""
1008
+ def _event_binding_present(
1009
+ settings: dict, command: str, binding: HookBinding,
1010
+ ) -> bool:
1011
+ """True when one specific event/matcher carries our command."""
1010
1012
  hooks = settings.get("hooks")
1011
1013
  if not isinstance(hooks, dict):
1012
1014
  return False
1015
+ event_list = hooks.get(binding.event)
1016
+ if not isinstance(event_list, list):
1017
+ return False
1018
+ _known = _command_variants_for(command)
1019
+ for entry in event_list:
1020
+ if entry.get("matcher", "") != binding.matcher:
1021
+ continue
1022
+ return any(a.get("command") in _known for a in entry.get("hooks", []))
1023
+ return False
1024
+
1025
+
1026
+ def binding_coverage(
1027
+ settings: dict, command: str, bundle: HookBundle,
1028
+ ) -> tuple[list[str], list[str]]:
1029
+ """Return (present_events, missing_events) for ``bundle``'s bindings (#825).
1030
+
1031
+ A PostToolUse-only install of codegraph-on-grep used to report as plain
1032
+ ``available`` (not installed) while still firing 400 times — the steering
1033
+ half unbound and invisible. Coverage makes PARTIAL legible.
1034
+ """
1035
+ present, missing = [], []
1013
1036
  for b in bundle.bindings:
1014
- event_list = hooks.get(b.event)
1015
- if not isinstance(event_list, list):
1016
- return False
1017
- found = False
1018
- for entry in event_list:
1019
- if entry.get("matcher", "") == b.matcher:
1020
- # #216 review (Copilot, 2nd pass): THE THIRD SITE. Exact
1021
- # matching here makes a LEGACY backslash binding read as NOT
1022
- # INSTALLED, so a Windows cell's real, working hooks list as
1023
- # "unavailable" — the operator is told nothing is installed
1024
- # while the bindings sit right there in settings.json.
1025
- # The previous commit said "one helper, BOTH callers". There
1026
- # were THREE. A rule applied at N-1 sites is the defect this
1027
- # class keeps producing, and it produced it again inside the
1028
- # fix for it.
1029
- _known = _command_variants_for(command)
1030
- if any(
1031
- a.get("command") in _known for a in entry.get("hooks", [])
1032
- ):
1033
- found = True
1034
- break
1035
- if not found:
1036
- return False
1037
- return True
1037
+ label = b.event if not b.matcher else f"{b.event}:{b.matcher}"
1038
+ if _event_binding_present(settings, command, b):
1039
+ present.append(label)
1040
+ else:
1041
+ missing.append(label)
1042
+ return present, missing
1043
+
1044
+
1045
+ def _is_installed(settings: dict, command: str, bundle: HookBundle) -> bool:
1046
+ """True when ALL of ``bundle``'s bindings' commands are present in settings."""
1047
+ present, missing = binding_coverage(settings, command, bundle)
1048
+ return bool(present) and not missing
1038
1049
 
1039
1050
 
1040
1051
  def list_hooks(
@@ -1045,8 +1056,10 @@ def list_hooks(
1045
1056
  ) -> int:
1046
1057
  """List builtin hooks with install status. One greppable line per builtin::
1047
1058
 
1048
- name [installed|available] trust=builtin — description
1059
+ name [installed|partial|available] trust=builtin — description
1049
1060
 
1061
+ ``partial`` (#825): at least one binding present but not the full set —
1062
+ the lab-ovh codegraph case (PostToolUse only, UserPromptSubmit/Stop absent).
1050
1063
  When installed and the script carries a ``swarph-cli-bundle-version`` stamp
1051
1064
  (#830), a second line reports match / mismatch vs the running package.
1052
1065
  """
@@ -1054,14 +1067,26 @@ def list_hooks(
1054
1067
  hooks_home_p = Path(hooks_home).expanduser()
1055
1068
  for name in sorted(BUILTIN_HOOKS):
1056
1069
  bundle = BUILTIN_HOOKS[name]
1057
- command = _installed_command(bundle, hooks_home)
1058
- # Read side of the same ladder: a legacy install reported as "available"
1059
- # invites the reinstall that produces a DUPLICATE binding.
1060
- status = ("installed"
1061
- if any(_is_installed(settings, c, bundle)
1062
- for c in _installed_command_variants(bundle, hooks_home))
1063
- else "available")
1070
+ variants = _installed_command_variants(bundle, hooks_home)
1071
+ # Pick the first variant that has ANY binding coverage for reporting.
1072
+ present: list[str] = []
1073
+ missing: list[str] = [b.event for b in bundle.bindings]
1074
+ for c in variants:
1075
+ p, m = binding_coverage(settings, c, bundle)
1076
+ if len(p) > len(present):
1077
+ present, missing = p, m
1078
+ if present and not missing:
1079
+ status = "installed"
1080
+ elif present and missing:
1081
+ status = "partial"
1082
+ else:
1083
+ status = "available"
1064
1084
  out(f"{name} [{status}] trust=builtin — {bundle.description}")
1085
+ if status == "partial":
1086
+ out(f" bindings present: {', '.join(present) or '(none)'}")
1087
+ out(f" bindings MISSING: {', '.join(missing)} "
1088
+ f"— re-run: swarph hooks add {name} (#825 fleet bind)")
1089
+ continue
1065
1090
  if status != "installed":
1066
1091
  continue
1067
1092
  script = (hooks_home_p / bundle.script_name).expanduser()
@@ -151,7 +151,8 @@ def _codegraph_query(query: str, *, limit: int = 8) -> list[dict]:
151
151
 
152
152
  Delegates to :func:`codegraph.structural_query` against the module-level
153
153
  ``_CODEGRAPH_INDEX`` path, using ``SWARPH_CELL`` (else
154
- ``codegraph.DEFAULT_CALLER_CELL``) as the A8 caller identity and the
154
+ ``codegraph.require_caller_cell``, which REFUSES rather than defaulting) as
155
+ the A8 caller identity and the
155
156
  default operate-what-you-own allowlist (``allowlist=None``).
156
157
 
157
158
  NEVER raises — any exception (missing index, corrupt db, bad query, or
@@ -162,7 +163,13 @@ def _codegraph_query(query: str, *, limit: int = 8) -> list[dict]:
162
163
  to keep the tool output tight for the calling agent.
163
164
  """
164
165
  try:
165
- caller_cell = os.environ.get("SWARPH_CELL", codegraph.DEFAULT_CALLER_CELL)
166
+ # NO DEFAULT IDENTITY (2026-09-18). `codegraph.DEFAULT_CALLER_CELL` is gone;
167
+ # an undeclared caller used to become lab-ovh and receive that cell's read
168
+ # authority. require_caller_cell raises IdentityNotDeclared, which the
169
+ # NEVER-RAISES contract below turns into an empty result — FAIL-CLOSED: the
170
+ # MCP host gets nothing rather than somebody else's answer.
171
+ caller_cell = codegraph.require_caller_cell(
172
+ os.environ.get("SWARPH_CELL"), verb="mcp codegraph_query")
166
173
  rows = codegraph.structural_query(
167
174
  query,
168
175
  index_path=_CODEGRAPH_INDEX,