swarph-cli 0.62.0__tar.gz → 0.63.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 (411) hide show
  1. {swarph_cli-0.62.0/src/swarph_cli.egg-info → swarph_cli-0.63.0}/PKG-INFO +2 -1
  2. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/README.md +1 -0
  3. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/pyproject.toml +1 -1
  4. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/__init__.py +1 -1
  5. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/board.py +59 -12
  6. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/codegraph.py +63 -8
  7. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/guide.py +55 -2
  8. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/mcp_server.py +9 -2
  9. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/mesh.py +24 -7
  10. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/monitor.py +4 -2
  11. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/onboard.py +8 -0
  12. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/spawn.py +67 -11
  13. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/watchdog.py +33 -7
  14. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/guide/GUIDE.md +151 -0
  15. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/main.py +18 -0
  16. swarph_cli-0.63.0/src/swarph_cli/source_text.py +129 -0
  17. {swarph_cli-0.62.0 → swarph_cli-0.63.0/src/swarph_cli.egg-info}/PKG-INFO +2 -1
  18. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli.egg-info/SOURCES.txt +7 -0
  19. swarph_cli-0.63.0/tests/test_126_pending_from_complete.py +116 -0
  20. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_397_gh_identity_router.py +4 -1
  21. swarph_cli-0.63.0/tests/test_547_verbs_from_registry.py +59 -0
  22. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_552_codegraph_gateway.py +15 -1
  23. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_556_relay_freshness.py +15 -1
  24. swarph_cli-0.63.0/tests/test_866_doctrine_guide.py +71 -0
  25. swarph_cli-0.63.0/tests/test_872_identity_refuses_not_warns.py +129 -0
  26. swarph_cli-0.63.0/tests/test_874_source_text.py +116 -0
  27. swarph_cli-0.63.0/tests/test_888_opencode_data_dir_can_fail.py +71 -0
  28. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_board_card_thread.py +58 -2
  29. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_codegraph_mcp.py +15 -1
  30. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_monitor_state_split.py +2 -1
  31. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_opencode_membrane.py +45 -15
  32. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/LICENSE +0 -0
  33. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/setup.cfg +0 -0
  34. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/__main__.py +0 -0
  35. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/bench/__init__.py +0 -0
  36. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/bench/backends.py +0 -0
  37. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/bench/pack.py +0 -0
  38. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/bench/prices.py +0 -0
  39. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/bench/quality.py +0 -0
  40. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/bench/refresh_prices.py +0 -0
  41. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/bench/runner.py +0 -0
  42. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/bench/validate.py +0 -0
  43. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/caller.py +0 -0
  44. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/capture/__init__.py +0 -0
  45. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/capture/harden.py +0 -0
  46. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/capture/lineage.py +0 -0
  47. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/capture/liveness.py +0 -0
  48. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/capture/manifest.py +0 -0
  49. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/capture/paths.py +0 -0
  50. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/capture/verify.py +0 -0
  51. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/cell.py +0 -0
  52. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/chain_token.py +0 -0
  53. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/__init__.py +0 -0
  54. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/_content.py +0 -0
  55. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/_display.py +0 -0
  56. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/_gateway_client.py +0 -0
  57. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/add.py +0 -0
  58. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/bench.py +0 -0
  59. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/board_merge_check.py +0 -0
  60. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/brain.py +0 -0
  61. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/brain_ask.py +0 -0
  62. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/cell.py +0 -0
  63. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/cell_probe.py +0 -0
  64. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  65. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/channel.py +0 -0
  66. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/chat.py +0 -0
  67. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/codegraph_hook.py +0 -0
  68. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/codex_hook_output.py +0 -0
  69. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/codex_waker.py +0 -0
  70. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/compress.py +0 -0
  71. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/daemon.py +0 -0
  72. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/dreaming.py +0 -0
  73. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/event.py +0 -0
  74. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/gateway.py +0 -0
  75. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/gh_route.py +0 -0
  76. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/group.py +0 -0
  77. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/highlight.py +0 -0
  78. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/hook_output.py +0 -0
  79. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/hooks.py +0 -0
  80. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/import_session.py +0 -0
  81. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/init.py +0 -0
  82. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
  83. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/install_hook.py +0 -0
  84. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  85. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
  86. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
  87. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/install_wake_hook.py +0 -0
  88. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/lane.py +0 -0
  89. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/memory.py +0 -0
  90. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
  91. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/memory_sync.py +0 -0
  92. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/okf_links.py +0 -0
  93. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
  94. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
  95. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/protocol_handler.py +0 -0
  96. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/ratify.py +0 -0
  97. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/schedule.py +0 -0
  98. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/security.py +0 -0
  99. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/service.py +0 -0
  100. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/timeline.py +0 -0
  101. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/version.py +0 -0
  102. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/wake_hook_output.py +0 -0
  103. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/compress/__init__.py +0 -0
  104. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/compress/levers.py +0 -0
  105. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/compress/marker.py +0 -0
  106. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/compress/verify.py +0 -0
  107. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/console_safe.py +0 -0
  108. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/delivery_queue.py +0 -0
  109. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/dreaming/__init__.py +0 -0
  110. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/dreaming/candidates.py +0 -0
  111. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/dreaming/clone.py +0 -0
  112. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/dreaming/enrich.py +0 -0
  113. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/dreaming/index_check.py +0 -0
  114. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/dreaming/organize.py +0 -0
  115. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/dreaming/probes.py +0 -0
  116. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/dreaming/report.py +0 -0
  117. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/dreaming/run.py +0 -0
  118. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/dreaming/slm.py +0 -0
  119. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/dreaming/transcripts.py +0 -0
  120. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/dreaming/verify.py +0 -0
  121. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/gateway/__init__.py +0 -0
  122. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/gateway/feature_registry.py +0 -0
  123. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/gateway/lanes_control.py +0 -0
  124. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/gateway/schema.sql +0 -0
  125. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/gateway/server.py +0 -0
  126. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/gateway/services_control.py +0 -0
  127. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/gateway_default.py +0 -0
  128. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/gh_identity.py +0 -0
  129. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/multiplexer.py +0 -0
  130. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/orphan_daemons.py +0 -0
  131. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/pane_probe.py +0 -0
  132. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/parsers/__init__.py +0 -0
  133. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/parsers/claude.py +0 -0
  134. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
  135. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
  136. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
  137. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
  138. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
  139. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
  140. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
  141. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
  142. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
  143. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
  144. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
  145. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
  146. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/peer_executor.py +0 -0
  147. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/peer_executor_windows.py +0 -0
  148. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/peer_reconciliation.py +0 -0
  149. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/peer_reply_outbox.py +0 -0
  150. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/peer_service_host.py +0 -0
  151. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/peer_service_payload.py +0 -0
  152. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/peer_service_reply.py +0 -0
  153. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/peer_staging.py +0 -0
  154. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/query_filters.py +0 -0
  155. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/scripts/__init__.py +0 -0
  156. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
  157. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  158. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
  159. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
  160. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/service/__init__.py +0 -0
  161. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/service/app.py +0 -0
  162. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/service/providers.py +0 -0
  163. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/session_bridge.py +0 -0
  164. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
  165. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
  166. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/stall_alert.py +0 -0
  167. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/codex-waker.default +0 -0
  168. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
  169. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
  170. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
  171. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
  172. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
  173. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
  174. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
  175. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
  176. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
  177. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
  178. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  179. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  180. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  181. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/timeline_paths.py +0 -0
  182. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli/tokens.py +0 -0
  183. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  184. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  185. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli.egg-info/requires.txt +0 -0
  186. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/src/swarph_cli.egg-info/top_level.txt +0 -0
  187. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_124_register_merge.py +0 -0
  188. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_132_ensure_monitor_state_dir.py +0 -0
  189. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_139_tool_probe.py +0 -0
  190. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_183_card_anchor.py +0 -0
  191. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_201_win32_bash_refusal.py +0 -0
  192. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_243_onboard_per_peer_token.py +0 -0
  193. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_307_task4_ask_reply.py +0 -0
  194. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
  195. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_319_refusal_exit_codes.py +0 -0
  196. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_323_mistral_backend.py +0 -0
  197. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_356_query_filters.py +0 -0
  198. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_397_router_preflight.py +0 -0
  199. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_467b_onboard_operator_credential.py +0 -0
  200. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_523_guide.py +0 -0
  201. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_524_conftest_hermeticity.py +0 -0
  202. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_532_accept_flag.py +0 -0
  203. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_535_version_capability.py +0 -0
  204. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_546_default_gateway.py +0 -0
  205. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_548_gateway_remainder.py +0 -0
  206. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_555_relay_row_shape.py +0 -0
  207. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_564_bootstrap_first_peer.py +0 -0
  208. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
  209. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_579_register_bootstrap_message.py +0 -0
  210. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_591_cli_step_graph.py +0 -0
  211. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_644_monitor_supervision.py +0 -0
  212. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_648_install_unit.py +0 -0
  213. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_654_reexec.py +0 -0
  214. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_656_owned_slm.py +0 -0
  215. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_684_dreaming_verb.py +0 -0
  216. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_689_pkg_version_order.py +0 -0
  217. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_716_timeline_one_path.py +0 -0
  218. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_724_ps1_ascii_or_bom.py +0 -0
  219. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_725_stdio_reconfigure.py +0 -0
  220. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_734_dreaming_enrich_degrade.py +0 -0
  221. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_740_edit_project.py +0 -0
  222. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_762_cards_edit_priority.py +0 -0
  223. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_801_confirm_flow_cli.py +0 -0
  224. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_802_stage_history.py +0 -0
  225. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_825_codegraph_hook_noise.py +0 -0
  226. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_829_counterfactual_column.py +0 -0
  227. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_830_hook_binding_version.py +0 -0
  228. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_84_cli_labels.py +0 -0
  229. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_add_security_gate.py +0 -0
  230. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_artifact_add.py +0 -0
  231. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_artifact_hash.py +0 -0
  232. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_artifact_lib.py +0 -0
  233. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_artifact_mcp.py +0 -0
  234. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_artifact_skill.py +0 -0
  235. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_artifact_tool.py +0 -0
  236. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_artifact_uri.py +0 -0
  237. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_bench_backends.py +0 -0
  238. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_bench_command.py +0 -0
  239. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_bench_pack.py +0 -0
  240. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_bench_prices.py +0 -0
  241. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_bench_quality.py +0 -0
  242. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_bench_runner.py +0 -0
  243. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_bench_validate.py +0 -0
  244. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_board_command.py +0 -0
  245. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_board_merge_check.py +0 -0
  246. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_board_obligation_close.py +0 -0
  247. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_brain_ask_command.py +0 -0
  248. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_brain_ask_env_diagnosis.py +0 -0
  249. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_brain_ask_identity_explicitness.py +0 -0
  250. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_brain_command.py +0 -0
  251. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_caller_meta_guard.py +0 -0
  252. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_capture_lineage.py +0 -0
  253. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_capture_liveness.py +0 -0
  254. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_capture_manifest.py +0 -0
  255. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_capture_paths.py +0 -0
  256. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_capture_security.py +0 -0
  257. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_cell_command_dispatch.py +0 -0
  258. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_cell_harden.py +0 -0
  259. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_cell_loader.py +0 -0
  260. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_cell_selfcheck.py +0 -0
  261. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_cell_verify.py +0 -0
  262. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_chain_token.py +0 -0
  263. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_channel_command.py +0 -0
  264. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_channel_poll_never_breaks_dms.py +0 -0
  265. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_channel_post.py +0 -0
  266. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_channel_wake_policy_filter.py +0 -0
  267. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_chat_command.py +0 -0
  268. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_claude_parser.py +0 -0
  269. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_claude_tmux_template.py +0 -0
  270. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_codegraph_caller_scope.py +0 -0
  271. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_codegraph_command.py +0 -0
  272. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_codegraph_hook.py +0 -0
  273. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_codex_waker.py +0 -0
  274. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_compress_caller_convention.py +0 -0
  275. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_compress_command.py +0 -0
  276. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_compress_levers.py +0 -0
  277. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_compress_marker.py +0 -0
  278. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_compress_verify.py +0 -0
  279. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_console_encoding_e2e.py +0 -0
  280. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_content_composition.py +0 -0
  281. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_cursor_membrane.py +0 -0
  282. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_daemon_command.py +0 -0
  283. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_daemon_console_encoding.py +0 -0
  284. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_daemon_delivery.py +0 -0
  285. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_delivery_queue.py +0 -0
  286. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_display_sanitize.py +0 -0
  287. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_ensure_monitor_no_peer_default.py +0 -0
  288. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_event_emit.py +0 -0
  289. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_feature_to_uri.py +0 -0
  290. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_gateway_brain.py +0 -0
  291. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_gateway_client.py +0 -0
  292. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_gateway_command.py +0 -0
  293. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_gateway_message_idempotency.py +0 -0
  294. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_gh_route_doctor_mesh_name.py +0 -0
  295. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_group_command.py +0 -0
  296. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_guide_commands_parse.py +0 -0
  297. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_guide_selfcheck.py +0 -0
  298. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_heartbeat_check.py +0 -0
  299. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_highlight_command.py +0 -0
  300. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_hook_output.py +0 -0
  301. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_hooks_activity_marker.py +0 -0
  302. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_hooks_add.py +0 -0
  303. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_hooks_bundle.py +0 -0
  304. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_hooks_init.py +0 -0
  305. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_hooks_legacy_generation_removal.py +0 -0
  306. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_hooks_lifecycle.py +0 -0
  307. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_hooks_merge.py +0 -0
  308. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_hooks_verify.py +0 -0
  309. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_hooks_win32_bash_wrapper.py +0 -0
  310. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_hooks_win32_command_path.py +0 -0
  311. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_import_command.py +0 -0
  312. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_inbox_identity_provenance.py +0 -0
  313. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_init_command.py +0 -0
  314. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_init_provider_menu.py +0 -0
  315. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_install_codex_hooks.py +0 -0
  316. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_install_hook.py +0 -0
  317. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_install_multiplexer_command.py +0 -0
  318. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_install_postcompact_hook.py +0 -0
  319. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_install_wake_hook.py +0 -0
  320. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_lane_command.py +0 -0
  321. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_main.py +0 -0
  322. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_main_unknown_verb_guard.py +0 -0
  323. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_mcp_missing_hint.py +0 -0
  324. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_mcp_server.py +0 -0
  325. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_memory_command.py +0 -0
  326. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_memory_emit_snippet.py +0 -0
  327. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_memory_gateway.py +0 -0
  328. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_memory_sync.py +0 -0
  329. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_memory_sync_membrane.py +0 -0
  330. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_mesh_command.py +0 -0
  331. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_mesh_inbox_mark_read.py +0 -0
  332. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_mesh_inbox_sanitize.py +0 -0
  333. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_mesh_send_unknown_peer.py +0 -0
  334. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_mesh_sidecar.py +0 -0
  335. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  336. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  337. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_meta_edge_identity.py +0 -0
  338. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_monitor_channel_poll.py +0 -0
  339. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_monitor_cli.py +0 -0
  340. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_monitor_daemon_collision.py +0 -0
  341. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_monitor_self_name_verification.py +0 -0
  342. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_multiplexer.py +0 -0
  343. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_muse_membrane.py +0 -0
  344. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_muse_own_cli.py +0 -0
  345. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_obligation_close_is_surfaced.py +0 -0
  346. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_okf_links.py +0 -0
  347. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_onboard_command.py +0 -0
  348. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_opencode_plugin.py +0 -0
  349. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_orphan_daemons.py +0 -0
  350. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_packaged_artifacts_exist.py +0 -0
  351. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_packaged_skills.py +0 -0
  352. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_peer_executor.py +0 -0
  353. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_peer_executor_windows.py +0 -0
  354. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_peer_reply_drain_command.py +0 -0
  355. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_peer_reply_drain_supervisor.py +0 -0
  356. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_peer_reply_outbox.py +0 -0
  357. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_peer_reply_transport.py +0 -0
  358. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_peer_service_e2e.py +0 -0
  359. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_peer_service_host.py +0 -0
  360. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_peer_service_payload.py +0 -0
  361. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_peer_service_reply.py +0 -0
  362. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_postcompact_recall.py +0 -0
  363. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_process_liveness.py +0 -0
  364. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_protocol_handler.py +0 -0
  365. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_providers_isolation.py +0 -0
  366. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_ratify_command.py +0 -0
  367. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_readme_claims_no_gateway_default.py +0 -0
  368. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_repl_caller_convention.py +0 -0
  369. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_schedule_command.py +0 -0
  370. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_schedule_create_contract.py +0 -0
  371. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_security.py +0 -0
  372. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_service_app.py +0 -0
  373. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_service_command.py +0 -0
  374. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_service_providers.py +0 -0
  375. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_session_bridge_inject.py +0 -0
  376. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_session_bridge_probe.py +0 -0
  377. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_session_bridge_resolve.py +0 -0
  378. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_session_path_role_gate.py +0 -0
  379. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_shipped_units_match_the_cli.py +0 -0
  380. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_smoke_chat.py +0 -0
  381. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_smoke_one_shot.py +0 -0
  382. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_smoke_phase_5_5.py +0 -0
  383. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_spawn_base_hoist.py +0 -0
  384. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_spawn_command.py +0 -0
  385. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_spawn_identity_stamp.py +0 -0
  386. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_spawn_live_pin.py +0 -0
  387. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_spawn_mitosis_lineage.py +0 -0
  388. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_spawn_tmux_session.py +0 -0
  389. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_spawn_windows_relaunch.py +0 -0
  390. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_stall_alert.py +0 -0
  391. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_subprocess_decode_encoding.py +0 -0
  392. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_timeline_command.py +0 -0
  393. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_tmux_wake_runstate_defer.py +0 -0
  394. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_tmux_wake_session_reanchor.py +0 -0
  395. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_tmux_wake_stale.py +0 -0
  396. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_tmux_wake_submit_verify.py +0 -0
  397. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_token_file_one_parser.py +0 -0
  398. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_token_precedence_explicit_first.py +0 -0
  399. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_unified_token_resolver.py +0 -0
  400. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_version_command.py +0 -0
  401. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_version_consistency.py +0 -0
  402. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_vibe_membrane.py +0 -0
  403. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_watchdog.py +0 -0
  404. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_watchdog_dm_wake.py +0 -0
  405. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  406. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  407. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_watchdog_emit_health.py +0 -0
  408. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_watchdog_install_flags.py +0 -0
  409. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_watchdog_model_rung.py +0 -0
  410. {swarph_cli-0.62.0 → swarph_cli-0.63.0}/tests/test_watchdog_stale_peers.py +0 -0
  411. {swarph_cli-0.62.0 → swarph_cli-0.63.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.63.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:
@@ -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:
@@ -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.63.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.63.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,
@@ -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]
@@ -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,
@@ -1172,8 +1172,14 @@ class PullSink(Sink):
1172
1172
  return True
1173
1173
 
1174
1174
  def pending_label(self, count: int) -> str:
1175
+ # #126: this is NOT gateway-unread. It is inbox.log entries newer than
1176
+ # last_delivered_id — on a frozen ledger it grows at the cell's DM rate
1177
+ # forever. Naming it "unread" taught cells to distrust status.
1175
1178
  plural = "s" if count != 1 else ""
1176
- return f"{count} unread DM{plural}"
1179
+ return (
1180
+ f"{count} DM{plural} newer than last delivery "
1181
+ f"(inbox.log after ledger — not gateway unread)"
1182
+ )
1177
1183
 
1178
1184
 
1179
1185
  class NoneSink(Sink):
@@ -1590,17 +1596,25 @@ def _write_ledgers_atomic(path: Path, ledgers: dict) -> None:
1590
1596
 
1591
1597
  def _replay_from_inbox_log(
1592
1598
  path: Path, after_id: int, limit: int
1593
- ) -> tuple[list, int]:
1594
- """Owed DMs newer than `after_id`, newest `limit` kept. Returns (dms, skipped).
1599
+ ) -> tuple[list, int, list[str]]:
1600
+ """Owed DMs newer than `after_id`, newest `limit` kept.
1601
+
1602
+ Returns ``(dms, skipped, pending_from)``.
1603
+
1604
+ ``pending_from`` is the distinct-sender set over EVERY matching log entry,
1605
+ not over the capped ``dms`` list (#126). The deque bounds returned LINES;
1606
+ the sender set was taken from it by convenience and understated who was
1607
+ waiting (evicting the longest-waiting senders first).
1595
1608
 
1596
1609
  This is BOTH the restart-retry path and the late-attached-sink replay path.
1597
1610
  They are the same question — "what does this ledger still owe" — so they get
1598
1611
  one mechanism; two would drift.
1599
1612
  """
1600
1613
  if not path.exists():
1601
- return [], 0
1614
+ return [], 0, []
1602
1615
  keep: deque = deque(maxlen=max(0, limit))
1603
1616
  total = 0
1617
+ from_nodes: set[str] = set()
1604
1618
  try:
1605
1619
  with path.open("r", encoding="utf-8") as fp:
1606
1620
  for raw in fp:
@@ -1615,10 +1629,13 @@ def _replay_from_inbox_log(
1615
1629
  continue
1616
1630
  total += 1
1617
1631
  keep.append(dm)
1632
+ src = dm.get("from_node")
1633
+ if src is not None and str(src):
1634
+ from_nodes.add(str(src))
1618
1635
  except OSError as exc:
1619
1636
  print(f"[monitor] cannot replay {path}: {exc}", file=sys.stderr, flush=True)
1620
- return [], 0
1621
- return list(keep), total - len(keep)
1637
+ return [], 0, []
1638
+ return list(keep), total - len(keep), sorted(from_nodes)
1622
1639
 
1623
1640
 
1624
1641
  class MonitorState:
@@ -2198,7 +2215,7 @@ def _monitor_deliver(state: MonitorState) -> None:
2198
2215
  f"{sink.name} (cursor advanced; delivery still pending)",
2199
2216
  flush=True)
2200
2217
  continue
2201
- dms, skipped = _replay_from_inbox_log(
2218
+ dms, skipped, _pending_from = _replay_from_inbox_log(
2202
2219
  state.inbox_log_path, delivered, state.replay_limit
2203
2220
  )
2204
2221
  if skipped:
@@ -678,7 +678,7 @@ def _collect(args: argparse.Namespace) -> dict:
678
678
  continue
679
679
  led = ledgers.get(sink.name)
680
680
  delivered = int(led["last_delivered_id"]) if led else 0
681
- dms, skipped = mesh._replay_from_inbox_log(
681
+ dms, skipped, pending_from = mesh._replay_from_inbox_log(
682
682
  inbox_log, delivered, mesh._MONITOR_REPLAY_LIMIT
683
683
  )
684
684
  rows.append({
@@ -690,7 +690,9 @@ def _collect(args: argparse.Namespace) -> dict:
690
690
  "consecutive_failures": int(led["consecutive_failures"]) if led else 0,
691
691
  "ledger_missing": led is None,
692
692
  "pending": len(dms) + skipped,
693
- "pending_from": sorted({str(d.get("from_node")) for d in dms}),
693
+ # #126: same set as `pending` — distinct senders across EVERY log
694
+ # entry newer than last_delivered_id, not over the 50-entry deque.
695
+ "pending_from": pending_from,
694
696
  "label": sink.pending_label(len(dms) + skipped),
695
697
  })
696
698
 
@@ -1123,4 +1123,12 @@ def run_onboard(argv: list[str]) -> int:
1123
1123
  f" swarph ratify {canonical} --reason \"<short text>\"\n"
1124
1124
  f" to flip ratified=true.\n"
1125
1125
  )
1126
+ # #866: mechanics without the standard of evidence is half an onboard.
1127
+ # The doctrine topic is bundled; print the command, do not paste the body
1128
+ # here (the guide is the record -- a paraphrase in this banner would drift).
1129
+ print_safe(
1130
+ "\n[doctrine] the standard of evidence ships in the guide, not only the verbs:\n"
1131
+ " swarph guide doctrine\n"
1132
+ " Authority: proven/docs/THE_TEN.md -- approved extract: card #866 post 42503.\n"
1133
+ )
1126
1134
  return 0
@@ -2704,19 +2704,70 @@ class MuseMembrane(ClaudeMembrane):
2704
2704
  )
2705
2705
 
2706
2706
 
2707
- #: Opencode cell subdir, created INSIDE the cell cwd, holding the relocated
2708
- #: config (XDG_CONFIG_HOME) and data (XDG_DATA_HOME). Both relocations are
2709
- #: grounded against real opencode 1.18.30 by execution: `XDG_DATA_HOME=/tmp/x
2710
- #: opencode --pure db path` -> `/tmp/x/opencode/opencode.db`, and
2711
- #: `XDG_CONFIG_HOME=/tmp/x opencode --pure debug config` -> `"plugin": []`.
2707
+ #: Opencode cell CONFIG subdir, created INSIDE the cell cwd, holding the
2708
+ #: relocated config (XDG_CONFIG_HOME). Grounded against real opencode 1.18.30 by
2709
+ #: execution: `XDG_CONFIG_HOME=/tmp/x opencode --pure debug config` ->
2710
+ #: `"plugin": []`.
2712
2711
  _OPENCODE_CELL_SUBDIR = ".opencode-cell"
2713
2712
 
2714
2713
 
2715
2714
  def _opencode_cell_dir(cell: Cell) -> Path:
2716
- """The cell's private opencode root (``<cwd>/.opencode-cell``)."""
2715
+ """The cell's private opencode CONFIG root (``<cwd>/.opencode-cell``)."""
2717
2716
  return cell.cwd / _OPENCODE_CELL_SUBDIR
2718
2717
 
2719
2718
 
2719
+ def _opencode_data_dir(cell: Cell) -> Path:
2720
+ """The cell's private opencode DATA dir, OUTSIDE the cell cwd.
2721
+
2722
+ opencode snapshots its work-tree with a git-dir UNDER this data dir. If the
2723
+ data dir sits inside the work-tree (the pre-fix shape, ``<cwd>/.opencode-cell/
2724
+ data``), the snapshot repo snapshots its own object store — a self-referential
2725
+ explosion measured 2026-09-19 at 9.1 GB of objects and a 319k-entry index,
2726
+ with every turn blocked at ``loop step=0`` on the resulting ``git add``. Kept
2727
+ under $HOME so no cell work-tree can ever contain it. Grounded:
2728
+ ``XDG_DATA_HOME=/tmp/x opencode --pure db path`` -> ``/tmp/x/opencode/opencode.db``.
2729
+ """
2730
+ return Path.home() / ".local" / "share" / "swarph" / "opencode" / cell.name
2731
+
2732
+
2733
+ def _migrate_opencode_data(cell: Cell) -> None:
2734
+ """Move a pre-fix in-tree data dir to the out-of-tree root, best-effort.
2735
+
2736
+ Session continuity: the ``opencode.db`` holds the cell's session, keyed by
2737
+ directory, so relocating it preserves the resume. Never raises; the failure
2738
+ direction is a lost resume, not a refused spawn.
2739
+ """
2740
+ old = _opencode_cell_dir(cell) / "data"
2741
+ new = _opencode_data_dir(cell)
2742
+ if not old.is_dir():
2743
+ return
2744
+ if new.exists():
2745
+ # Measured 2026-09-19 11:2xZ on lab-ovh: a cell process spawned BEFORE the fix
2746
+ # kept running with the in-tree env and wrote a second data dir there after
2747
+ # the first migration. It is not merged (two opencode.db files cannot be);
2748
+ # say so where the operator is looking instead of skipping in silence.
2749
+ print(f"[spawn] opencode: legacy in-tree data at {old} left in place -- {new} "
2750
+ f"already exists, sessions written in-tree after the first migration are "
2751
+ f"NOT merged; respawn the cell so no process still writes there",
2752
+ file=sys.stderr)
2753
+ return
2754
+ try:
2755
+ new.parent.mkdir(parents=True, exist_ok=True)
2756
+ # #888 half (2): the in-tree data dir may carry the SELF-REFERENTIAL snapshot
2757
+ # repo (the specimen: 9.0 GB of objects, a 319,621-entry index). It is
2758
+ # opencode's own work-tree snapshot cache, recreated on the next turn, so
2759
+ # drop it rather than carry it out of the tree. The PROJECT index is never
2760
+ # touched here -- on lab-ovh nothing was ever tracked (git ls-files
2761
+ # .opencode-cell = 0), and a blind reset of the clone that backs the
2762
+ # editable install would be a worse defect than this one.
2763
+ snap = old / "opencode" / "snapshot"
2764
+ if snap.is_dir():
2765
+ shutil.rmtree(snap, ignore_errors=True)
2766
+ shutil.move(str(old), str(new))
2767
+ except OSError:
2768
+ pass
2769
+
2770
+
2720
2771
  def _scrub_opencode_namespace(env: dict[str, str]) -> None:
2721
2772
  """DENY-BY-DEFAULT scrub of the ``OPENCODE_*`` namespace.
2722
2773
 
@@ -2768,9 +2819,13 @@ def _opencode_env(cell: Cell) -> dict[str, str]:
2768
2819
  """Subscription env for a local ``opencode`` CELL.
2769
2820
 
2770
2821
  On top of the canonical billing scrub, the cell relocates opencode's CONFIG
2771
- (XDG_CONFIG_HOME) and DATA (XDG_DATA_HOME) into ``<cwd>/.opencode-cell``, and
2772
- links the operator's ``auth.json`` in, so the cell's sessions/plugins/auth
2773
- never mix with the operator's. ``$HOME`` is deliberately NOT relocated — the
2822
+ (XDG_CONFIG_HOME) to ``<cwd>/.opencode-cell/config`` and its DATA
2823
+ (XDG_DATA_HOME) to ``~/.local/share/swarph/opencode/<cell>`` — data OUTSIDE
2824
+ the work-tree, because opencode's snapshot git-dir lives under the data dir
2825
+ and would otherwise snapshot its own object store (the 2026-09-19 9 GB
2826
+ self-referential explosion). It links the operator's ``auth.json`` in, so the
2827
+ cell's sessions/plugins/auth never mix with the operator's. ``$HOME`` is
2828
+ deliberately NOT relocated — the
2774
2829
  vibe/cursor lesson: a fake $HOME costs the cell ``~/.config/swarph/<self>.
2775
2830
  peer_token``, ``~/.swarph/secrets.toml`` and the codegraph hook, and opencode's
2776
2831
  XDG-honoring knobs let us pay nothing to keep it.
@@ -2788,7 +2843,8 @@ def _opencode_env(cell: Cell) -> dict[str, str]:
2788
2843
  # ORDER IS LOAD-BEARING: the scrub removes an inherited OPENCODE_* var and this
2789
2844
  # sets the cell's own AFTER, so the membrane's values are authoritative.
2790
2845
  _scrub_opencode_namespace(env)
2791
- data_dir = _opencode_cell_dir(cell) / "data"
2846
+ _migrate_opencode_data(cell)
2847
+ data_dir = _opencode_data_dir(cell)
2792
2848
  config_dir = _opencode_cell_dir(cell) / "config"
2793
2849
  data_dir.mkdir(parents=True, exist_ok=True)
2794
2850
  config_dir.mkdir(parents=True, exist_ok=True)
@@ -2855,7 +2911,7 @@ def _opencode_prior_session(cell: Cell) -> Optional[str]:
2855
2911
  if binary is None:
2856
2912
  return None
2857
2913
  env = dict(os.environ)
2858
- env["XDG_DATA_HOME"] = str(_opencode_cell_dir(cell) / "data")
2914
+ env["XDG_DATA_HOME"] = str(_opencode_data_dir(cell))
2859
2915
  env["OPENCODE_DISABLE_AUTOUPDATE"] = "1"
2860
2916
  try:
2861
2917
  out = subprocess.run(
@@ -641,10 +641,39 @@ def _fetch_peers(gateway: str, token: str) -> list[dict]:
641
641
  return []
642
642
 
643
643
 
644
+ def _ppid(cur: int) -> int:
645
+ """Parent of ``cur``.
646
+
647
+ PRODUCTION, not a test helper (#515 / #492). ``_process_alive`` calls
648
+ ``_pid_under``, which calls this. Linux reads ``/proc/<pid>/stat`` —
649
+ ``comm`` can contain spaces and parens, so PPID is the field after the
650
+ final ``)``. Darwin has no ``/proc``; ``ps -o ppid=`` is the same fact.
651
+ A missing source raises ``OSError`` and the walker treats that as
652
+ "not under", which is the old Linux behaviour when ``/proc`` cannot
653
+ be read. ``monitor.py`` and ``orphan_daemons.py`` have their own
654
+ ``/proc`` readers and are not this function.
655
+ """
656
+ try:
657
+ with open(f"/proc/{cur}/stat", encoding="utf-8") as f:
658
+ data = f.read()
659
+ except OSError:
660
+ if sys.platform != "darwin":
661
+ raise
662
+ r = subprocess.run(
663
+ ["ps", "-o", "ppid=", "-p", str(cur)],
664
+ capture_output=True, text=True, encoding="utf-8", errors="replace",
665
+ timeout=5,
666
+ )
667
+ if r.returncode != 0 or not r.stdout.strip():
668
+ raise OSError(f"ps could not read ppid of {cur}")
669
+ return int(r.stdout.strip())
670
+ after = data[data.rfind(")") + 2:].split()
671
+ return int(after[1]) # stat field 4 (PPID): state, ppid, ...
672
+
673
+
644
674
  def _pid_under(pid: int, ancestors: set, _max_depth: int = 40) -> bool:
645
675
  """Walk the PPID chain up from ``pid``; True if any ancestor is in
646
- ``ancestors``. Reads ``/proc/<pid>/stat`` (Linux). The ``comm`` field can
647
- contain spaces/parens, so parse PPID after the final ``)``."""
676
+ ``ancestors``. See ``_ppid`` for the platform split."""
648
677
  cur = pid
649
678
  for _ in range(_max_depth):
650
679
  if cur in ancestors:
@@ -652,11 +681,8 @@ def _pid_under(pid: int, ancestors: set, _max_depth: int = 40) -> bool:
652
681
  if cur <= 1:
653
682
  return False
654
683
  try:
655
- with open(f"/proc/{cur}/stat", encoding="utf-8") as f:
656
- data = f.read()
657
- after = data[data.rfind(")") + 2:].split()
658
- cur = int(after[1]) # stat field 4 (PPID): state, ppid, ...
659
- except (OSError, ValueError, IndexError):
684
+ cur = _ppid(cur)
685
+ except (OSError, ValueError, IndexError, subprocess.TimeoutExpired):
660
686
  return False
661
687
  return cur in ancestors
662
688