swarph-cli 0.72.41__tar.gz → 0.72.43__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 (573) hide show
  1. {swarph_cli-0.72.41/src/swarph_cli.egg-info → swarph_cli-0.72.43}/PKG-INFO +1 -1
  2. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/pyproject.toml +1 -1
  3. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/__init__.py +1 -1
  4. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/dev_extract.py +382 -19
  5. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/gh_route.py +10 -8
  6. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/mesh.py +391 -31
  7. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/gh_identity.py +19 -8
  8. {swarph_cli-0.72.41 → swarph_cli-0.72.43/src/swarph_cli.egg-info}/PKG-INFO +1 -1
  9. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli.egg-info/SOURCES.txt +6 -0
  10. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1065_release.py +174 -1
  11. swarph_cli-0.72.43/tests/test_1436_dev_extract_freshness.py +1207 -0
  12. swarph_cli-0.72.43/tests/test_1452_rework1_engine.py +300 -0
  13. swarph_cli-0.72.43/tests/test_1452_rework2_engine.py +283 -0
  14. swarph_cli-0.72.43/tests/test_1452_rework3_engine.py +264 -0
  15. swarph_cli-0.72.43/tests/test_1452_standing_wake_expiry.py +145 -0
  16. swarph_cli-0.72.43/tests/test_1470_gh_router_fail_closed.py +69 -0
  17. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_397_gh_identity_router.py +5 -7
  18. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_484_codex_queue_sink.py +1 -1
  19. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_mesh_sidecar_muse_sink.py +3 -2
  20. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_tmux_wake_runstate_defer.py +4 -0
  21. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/LICENSE +0 -0
  22. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/README.md +0 -0
  23. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/setup.cfg +0 -0
  24. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/__main__.py +0 -0
  25. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/bench/__init__.py +0 -0
  26. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/bench/backends.py +0 -0
  27. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/bench/claude_cli.py +0 -0
  28. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/bench/codex_cli.py +0 -0
  29. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/bench/pack.py +0 -0
  30. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/bench/prices.py +0 -0
  31. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/bench/providers.py +0 -0
  32. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/bench/quality.py +0 -0
  33. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/bench/refresh_prices.py +0 -0
  34. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/bench/rules.py +0 -0
  35. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/bench/runner.py +0 -0
  36. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/bench/signal_score.py +0 -0
  37. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/bench/validate.py +0 -0
  38. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/caller.py +0 -0
  39. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/capture/__init__.py +0 -0
  40. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/capture/harden.py +0 -0
  41. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/capture/lineage.py +0 -0
  42. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/capture/liveness.py +0 -0
  43. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/capture/manifest.py +0 -0
  44. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/capture/paths.py +0 -0
  45. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/capture/verify.py +0 -0
  46. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/cell.py +0 -0
  47. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/chain_token.py +0 -0
  48. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/channel.py +0 -0
  49. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/__init__.py +0 -0
  50. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/_content.py +0 -0
  51. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/_display.py +0 -0
  52. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/_gateway_client.py +0 -0
  53. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/add.py +0 -0
  54. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/bench.py +0 -0
  55. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/board.py +0 -0
  56. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/board_cost.py +0 -0
  57. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/board_merge_check.py +0 -0
  58. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/board_usage.py +0 -0
  59. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/brain.py +0 -0
  60. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/brain_ask.py +0 -0
  61. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/cell.py +0 -0
  62. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/cell_probe.py +0 -0
  63. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  64. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/channel.py +0 -0
  65. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/chat.py +0 -0
  66. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/codegraph.py +0 -0
  67. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/codegraph_hook.py +0 -0
  68. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/codex_hook_output.py +0 -0
  69. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/codex_waker.py +0 -0
  70. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/commander.py +0 -0
  71. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/committee.py +0 -0
  72. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/compress.py +0 -0
  73. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/daemon.py +0 -0
  74. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/dreaming.py +0 -0
  75. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/event.py +0 -0
  76. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/gateway.py +0 -0
  77. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/group.py +0 -0
  78. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/guide.py +0 -0
  79. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/highlight.py +0 -0
  80. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/hook_interpreter.py +0 -0
  81. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/hook_output.py +0 -0
  82. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/hooks.py +0 -0
  83. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/import_session.py +0 -0
  84. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/init.py +0 -0
  85. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
  86. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/install_hook.py +0 -0
  87. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  88. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
  89. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
  90. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/install_wake_hook.py +0 -0
  91. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/lane.py +0 -0
  92. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/mcp_server.py +0 -0
  93. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/memory.py +0 -0
  94. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
  95. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/memory_sync.py +0 -0
  96. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/monitor.py +0 -0
  97. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/okf_links.py +0 -0
  98. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/onboard.py +0 -0
  99. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
  100. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
  101. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/protocol_handler.py +0 -0
  102. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/ratify.py +0 -0
  103. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/roster.py +0 -0
  104. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/rowclear_hook.py +0 -0
  105. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/schedule.py +0 -0
  106. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/security.py +0 -0
  107. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/service.py +0 -0
  108. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/skunkworks.py +0 -0
  109. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/spawn.py +0 -0
  110. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/sweep.py +0 -0
  111. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/timeline.py +0 -0
  112. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/triage.py +0 -0
  113. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/version.py +0 -0
  114. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/wake_hook_output.py +0 -0
  115. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commands/watchdog.py +0 -0
  116. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/commit_trailers.py +0 -0
  117. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/compress/__init__.py +0 -0
  118. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/compress/levers.py +0 -0
  119. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/compress/marker.py +0 -0
  120. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/compress/verify.py +0 -0
  121. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/console_safe.py +0 -0
  122. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/delivery_queue.py +0 -0
  123. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/dm_frame.py +0 -0
  124. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/dreaming/__init__.py +0 -0
  125. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/dreaming/candidates.py +0 -0
  126. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/dreaming/clone.py +0 -0
  127. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/dreaming/enrich.py +0 -0
  128. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/dreaming/index_check.py +0 -0
  129. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/dreaming/notify.py +0 -0
  130. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/dreaming/organize.py +0 -0
  131. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/dreaming/probes.py +0 -0
  132. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/dreaming/report.py +0 -0
  133. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/dreaming/run.py +0 -0
  134. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/dreaming/slm.py +0 -0
  135. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/dreaming/transcripts.py +0 -0
  136. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/dreaming/verify.py +0 -0
  137. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/duplicate_session.py +0 -0
  138. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/gateway/__init__.py +0 -0
  139. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/gateway/feature_registry.py +0 -0
  140. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/gateway/lanes_control.py +0 -0
  141. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/gateway/schema.sql +0 -0
  142. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/gateway/server.py +0 -0
  143. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/gateway/services_control.py +0 -0
  144. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/gateway_default.py +0 -0
  145. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/guide/GUIDE.md +0 -0
  146. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/identity.py +0 -0
  147. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/launchd/swarph-codex-outbox.plist +0 -0
  148. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/launchd/swarph-codex-outbox.timer.plist +0 -0
  149. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/launchd/swarph-monitor.plist +0 -0
  150. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/launchd/swarph-watchdog.plist +0 -0
  151. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/launchd/swarph-watchdog.timer.plist +0 -0
  152. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/launchd_render.py +0 -0
  153. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/main.py +0 -0
  154. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/multiplexer.py +0 -0
  155. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/operator/__init__.py +0 -0
  156. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/operator/__main__.py +0 -0
  157. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/operator/api.py +0 -0
  158. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/operator/cli.py +0 -0
  159. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/operator/config.py +0 -0
  160. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/operator/fmt.py +0 -0
  161. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/orphan_daemons.py +0 -0
  162. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/pane_probe.py +0 -0
  163. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/parsers/__init__.py +0 -0
  164. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/parsers/claude.py +0 -0
  165. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
  166. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
  167. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
  168. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
  169. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
  170. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
  171. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
  172. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
  173. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
  174. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
  175. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
  176. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
  177. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/peer_executor.py +0 -0
  178. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/peer_executor_windows.py +0 -0
  179. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/peer_reconciliation.py +0 -0
  180. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/peer_reply_outbox.py +0 -0
  181. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/peer_service_host.py +0 -0
  182. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/peer_service_payload.py +0 -0
  183. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/peer_service_reply.py +0 -0
  184. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/peer_staging.py +0 -0
  185. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/query_filters.py +0 -0
  186. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/quota_sync.py +0 -0
  187. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/redact.py +0 -0
  188. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/rowclear.py +0 -0
  189. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/scripts/__init__.py +0 -0
  190. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
  191. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  192. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
  193. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
  194. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
  195. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/scrub.py +0 -0
  196. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/service/__init__.py +0 -0
  197. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/service/app.py +0 -0
  198. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/service/providers.py +0 -0
  199. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/session_bridge.py +0 -0
  200. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
  201. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
  202. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/source_text.py +0 -0
  203. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/stall_alert.py +0 -0
  204. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/systemd/codex-waker.default +0 -0
  205. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
  206. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
  207. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
  208. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
  209. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
  210. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
  211. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
  212. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
  213. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
  214. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
  215. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/systemd/swarph-stuck-sweep.service +0 -0
  216. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/systemd/swarph-stuck-sweep.timer +0 -0
  217. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/systemd/swarph-triage.service +0 -0
  218. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/systemd/swarph-triage.timer +0 -0
  219. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  220. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  221. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  222. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/the_ten.py +0 -0
  223. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/timeline_paths.py +0 -0
  224. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/tokens.py +0 -0
  225. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/triage/__init__.py +0 -0
  226. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/triage/backend.py +0 -0
  227. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/triage/job.py +0 -0
  228. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/triage/owners.json +0 -0
  229. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/triage/router.py +0 -0
  230. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/triage/rows.py +0 -0
  231. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli/triage/sensor.py +0 -0
  232. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  233. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  234. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli.egg-info/requires.txt +0 -0
  235. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/src/swarph_cli.egg-info/top_level.txt +0 -0
  236. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1006_board_publisher.py +0 -0
  237. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1006_session_board.py +0 -0
  238. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1010_strand_only.py +0 -0
  239. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1013_spawn_channel.py +0 -0
  240. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1017_muse_tui.py +0 -0
  241. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1034_copy_mode.py +0 -0
  242. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1050_surrogate_wake.py +0 -0
  243. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1052_opencode_deaf.py +0 -0
  244. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1059_watchdog_unread_env.py +0 -0
  245. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1061_roster_cli.py +0 -0
  246. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1064_stuck_sweep.py +0 -0
  247. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1066_quiet_wakes.py +0 -0
  248. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1068_structured_accept_cli.py +0 -0
  249. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1071_quota_sync.py +0 -0
  250. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1073_triage.py +0 -0
  251. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1073_triage_board.py +0 -0
  252. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1073_wheel_install.py +0 -0
  253. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1075_task_cli.py +0 -0
  254. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1083_openai_decisions.py +0 -0
  255. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1144_cell_yaml_github.py +0 -0
  256. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1197_commander_taps.py +0 -0
  257. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1203_swarph_me_refusal.py +0 -0
  258. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1206_offer_question.py +0 -0
  259. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1214_egress.py +0 -0
  260. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1219_egress_probe.py +0 -0
  261. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1221_quota_windows.py +0 -0
  262. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1222_maybe_start_cap.py +0 -0
  263. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1228_quota_reach.py +0 -0
  264. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1243_the_ten.py +0 -0
  265. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1248_ship_price_table.py +0 -0
  266. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_124_register_merge.py +0 -0
  267. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1250_price_cache.py +0 -0
  268. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1256_commander_wake.py +0 -0
  269. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1261_mcp_install.py +0 -0
  270. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1262_wake_followups.py +0 -0
  271. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1265_wake_note.py +0 -0
  272. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1269_triage_label.py +0 -0
  273. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_126_pending_from_complete.py +0 -0
  274. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1277_close_usage.py +0 -0
  275. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1278_dreaming_reach.py +0 -0
  276. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1286_dm_unread_cursor.py +0 -0
  277. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1297_commander.py +0 -0
  278. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1300_authorization_check.py +0 -0
  279. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1301_restart_skew.py +0 -0
  280. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1320_commit_trailers.py +0 -0
  281. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1321_layered_redaction.py +0 -0
  282. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_132_ensure_monitor_state_dir.py +0 -0
  283. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1340_router_rollout.py +0 -0
  284. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1346_shell_string_gh.py +0 -0
  285. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1347_shell_depth_cap.py +0 -0
  286. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1353_obligation_release_cli.py +0 -0
  287. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1385_duplicate_session_argv.py +0 -0
  288. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_138_memory_navigate.py +0 -0
  289. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1396_dev_extract.py +0 -0
  290. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_139_tool_probe.py +0 -0
  291. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1405_skunkworks_basic.py +0 -0
  292. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1405_skunkworks_cli.py +0 -0
  293. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_1427_membrane_workflow.py +0 -0
  294. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_183_card_anchor.py +0 -0
  295. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_184_notify_honesty.py +0 -0
  296. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_201_win32_bash_refusal.py +0 -0
  297. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_243_onboard_per_peer_token.py +0 -0
  298. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_307_task4_ask_reply.py +0 -0
  299. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
  300. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_3130_sender.py +0 -0
  301. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_3150_identity_router_install.py +0 -0
  302. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_319_refusal_exit_codes.py +0 -0
  303. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_323_mistral_backend.py +0 -0
  304. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_332_report_organize_findings.py +0 -0
  305. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_356_query_filters.py +0 -0
  306. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_397_router_preflight.py +0 -0
  307. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_402_one_identity_resolver.py +0 -0
  308. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_432_rowclear.py +0 -0
  309. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_467b_onboard_operator_credential.py +0 -0
  310. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_472_register_caller_binding.py +0 -0
  311. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_513_duplicate_session.py +0 -0
  312. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_523_guide.py +0 -0
  313. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_524_conftest_hermeticity.py +0 -0
  314. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_532_accept_flag.py +0 -0
  315. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_535_version_capability.py +0 -0
  316. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_538_signal_bench.py +0 -0
  317. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_546_default_gateway.py +0 -0
  318. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_547_verbs_from_registry.py +0 -0
  319. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_548_gateway_remainder.py +0 -0
  320. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_552_codegraph_gateway.py +0 -0
  321. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_555_relay_row_shape.py +0 -0
  322. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_556_relay_freshness.py +0 -0
  323. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_559_edges_truncated.py +0 -0
  324. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_564_bootstrap_first_peer.py +0 -0
  325. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
  326. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_579_register_bootstrap_message.py +0 -0
  327. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_591_cli_step_graph.py +0 -0
  328. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_644_monitor_supervision.py +0 -0
  329. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_648_install_unit.py +0 -0
  330. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_654_reexec.py +0 -0
  331. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_656_owned_slm.py +0 -0
  332. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_684_dreaming_verb.py +0 -0
  333. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_689_pkg_version_order.py +0 -0
  334. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_715_grok_wake.py +0 -0
  335. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_716_timeline_one_path.py +0 -0
  336. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_723_grok_0251.py +0 -0
  337. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_724_ps1_ascii_or_bom.py +0 -0
  338. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_725_stdio_reconfigure.py +0 -0
  339. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_726_grok_limit_screen.py +0 -0
  340. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_729_arm_check_delivery_gap.py +0 -0
  341. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_729_dm_once.py +0 -0
  342. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_729_wake_watchdog.py +0 -0
  343. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_734_dreaming_enrich_degrade.py +0 -0
  344. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_735_dm_mcp_tools.py +0 -0
  345. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_739_wake_writer.py +0 -0
  346. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_740_edit_project.py +0 -0
  347. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_762_cards_edit_priority.py +0 -0
  348. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_764_enrich_collapse.py +0 -0
  349. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_801_confirm_flow_cli.py +0 -0
  350. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_802_stage_history.py +0 -0
  351. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_825_codegraph_hook_noise.py +0 -0
  352. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_825_install_posttooluse_only.py +0 -0
  353. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_825_remaining_cap_shred_bind.py +0 -0
  354. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_829_counterfactual_column.py +0 -0
  355. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_830_hook_binding_version.py +0 -0
  356. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_84_cli_labels.py +0 -0
  357. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_866_doctrine_guide.py +0 -0
  358. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_872_identity_refuses_not_warns.py +0 -0
  359. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_874_source_text.py +0 -0
  360. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
  361. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_931_board_search.py +0 -0
  362. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_935_deadline_clock.py +0 -0
  363. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_937_dreaming_notify.py +0 -0
  364. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_938_organize_line_limit.py +0 -0
  365. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_941_bench_backends.py +0 -0
  366. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_941_harness_342.py +0 -0
  367. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_945_same_line_pairing.py +0 -0
  368. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_955_prompt_audit.py +0 -0
  369. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_957_auth_http.py +0 -0
  370. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_959_venv_interpreter.py +0 -0
  371. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_960_channel.py +0 -0
  372. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
  373. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_961_cursor_grok_footer.py +0 -0
  374. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_961_loud_stop.py +0 -0
  375. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_963_plugin_manifest.py +0 -0
  376. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_964_channel_cell.py +0 -0
  377. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_965_mesh_wait.py +0 -0
  378. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_975_channel_hop.py +0 -0
  379. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_add_security_gate.py +0 -0
  380. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_artifact_add.py +0 -0
  381. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_artifact_hash.py +0 -0
  382. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_artifact_lib.py +0 -0
  383. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_artifact_mcp.py +0 -0
  384. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_artifact_skill.py +0 -0
  385. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_artifact_tool.py +0 -0
  386. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_artifact_uri.py +0 -0
  387. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_bench_backends.py +0 -0
  388. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_bench_claude_cli.py +0 -0
  389. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_bench_codex_cli.py +0 -0
  390. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_bench_command.py +0 -0
  391. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_bench_pack.py +0 -0
  392. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_bench_prices.py +0 -0
  393. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_bench_quality.py +0 -0
  394. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_bench_runner.py +0 -0
  395. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_bench_validate.py +0 -0
  396. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_board_card_thread.py +0 -0
  397. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_board_command.py +0 -0
  398. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_board_merge_check.py +0 -0
  399. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_board_obligation_close.py +0 -0
  400. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_brain_ask_command.py +0 -0
  401. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_brain_ask_env_diagnosis.py +0 -0
  402. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_brain_ask_identity_explicitness.py +0 -0
  403. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_brain_command.py +0 -0
  404. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_caller_meta_guard.py +0 -0
  405. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_capture_lineage.py +0 -0
  406. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_capture_liveness.py +0 -0
  407. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_capture_manifest.py +0 -0
  408. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_capture_paths.py +0 -0
  409. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_capture_security.py +0 -0
  410. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_cell_command_dispatch.py +0 -0
  411. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_cell_harden.py +0 -0
  412. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_cell_loader.py +0 -0
  413. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_cell_selfcheck.py +0 -0
  414. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_cell_verify.py +0 -0
  415. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_chain_token.py +0 -0
  416. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_channel_command.py +0 -0
  417. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_channel_poll_never_breaks_dms.py +0 -0
  418. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_channel_post.py +0 -0
  419. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_channel_wake_policy_filter.py +0 -0
  420. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_chat_command.py +0 -0
  421. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_claude_parser.py +0 -0
  422. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_claude_tmux_template.py +0 -0
  423. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_codegraph_caller_scope.py +0 -0
  424. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_codegraph_command.py +0 -0
  425. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_codegraph_hook.py +0 -0
  426. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_codegraph_mcp.py +0 -0
  427. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_codex_waker.py +0 -0
  428. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_committee_command.py +0 -0
  429. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_compress_caller_convention.py +0 -0
  430. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_compress_command.py +0 -0
  431. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_compress_levers.py +0 -0
  432. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_compress_marker.py +0 -0
  433. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_compress_verify.py +0 -0
  434. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_console_encoding_e2e.py +0 -0
  435. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_content_composition.py +0 -0
  436. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_copilot_membrane.py +0 -0
  437. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_copilot_pane_reader.py +0 -0
  438. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_cursor_membrane.py +0 -0
  439. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_daemon_command.py +0 -0
  440. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_daemon_console_encoding.py +0 -0
  441. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_daemon_delivery.py +0 -0
  442. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_delivery_queue.py +0 -0
  443. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_display_sanitize.py +0 -0
  444. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_ensure_monitor_no_peer_default.py +0 -0
  445. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_event_emit.py +0 -0
  446. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_feature_to_uri.py +0 -0
  447. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_gateway_brain.py +0 -0
  448. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_gateway_client.py +0 -0
  449. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_gateway_command.py +0 -0
  450. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_gateway_message_idempotency.py +0 -0
  451. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_gh_route_doctor_mesh_name.py +0 -0
  452. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_group_command.py +0 -0
  453. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_guide_commands_parse.py +0 -0
  454. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_guide_selfcheck.py +0 -0
  455. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_heartbeat_check.py +0 -0
  456. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_highlight_command.py +0 -0
  457. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_hook_output.py +0 -0
  458. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_hooks_activity_marker.py +0 -0
  459. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_hooks_add.py +0 -0
  460. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_hooks_bundle.py +0 -0
  461. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_hooks_init.py +0 -0
  462. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_hooks_legacy_generation_removal.py +0 -0
  463. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_hooks_lifecycle.py +0 -0
  464. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_hooks_merge.py +0 -0
  465. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_hooks_verify.py +0 -0
  466. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_hooks_win32_bash_wrapper.py +0 -0
  467. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_hooks_win32_command_path.py +0 -0
  468. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_import_command.py +0 -0
  469. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_inbox_identity_provenance.py +0 -0
  470. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_init_command.py +0 -0
  471. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_init_provider_menu.py +0 -0
  472. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_install_codex_hooks.py +0 -0
  473. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_install_hook.py +0 -0
  474. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_install_multiplexer_command.py +0 -0
  475. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_install_postcompact_hook.py +0 -0
  476. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_install_wake_hook.py +0 -0
  477. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_lane_command.py +0 -0
  478. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_launchd_units.py +0 -0
  479. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_main.py +0 -0
  480. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_main_unknown_verb_guard.py +0 -0
  481. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_mcp_missing_hint.py +0 -0
  482. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_mcp_server.py +0 -0
  483. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_memory_command.py +0 -0
  484. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_memory_emit_snippet.py +0 -0
  485. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_memory_gateway.py +0 -0
  486. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_memory_sync.py +0 -0
  487. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_memory_sync_membrane.py +0 -0
  488. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_mesh_command.py +0 -0
  489. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_mesh_inbox_mark_read.py +0 -0
  490. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_mesh_inbox_sanitize.py +0 -0
  491. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_mesh_originate_thread_623.py +0 -0
  492. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_mesh_send_unknown_peer.py +0 -0
  493. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_mesh_sidecar.py +0 -0
  494. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  495. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  496. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_meta_edge_identity.py +0 -0
  497. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_monitor_channel_poll.py +0 -0
  498. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_monitor_cli.py +0 -0
  499. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_monitor_daemon_collision.py +0 -0
  500. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_monitor_self_name_verification.py +0 -0
  501. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_monitor_state_split.py +0 -0
  502. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_multiplexer.py +0 -0
  503. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_muse_membrane.py +0 -0
  504. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_muse_own_cli.py +0 -0
  505. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_obligation_close_is_surfaced.py +0 -0
  506. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_okf_links.py +0 -0
  507. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_onboard_command.py +0 -0
  508. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_opencode_membrane.py +0 -0
  509. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_opencode_plugin.py +0 -0
  510. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_operator_me.py +0 -0
  511. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_orphan_daemons.py +0 -0
  512. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_packaged_artifacts_exist.py +0 -0
  513. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_packaged_skills.py +0 -0
  514. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_peer_executor.py +0 -0
  515. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_peer_executor_windows.py +0 -0
  516. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_peer_reply_drain_command.py +0 -0
  517. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_peer_reply_drain_supervisor.py +0 -0
  518. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_peer_reply_outbox.py +0 -0
  519. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_peer_reply_transport.py +0 -0
  520. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_peer_service_e2e.py +0 -0
  521. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_peer_service_host.py +0 -0
  522. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_peer_service_payload.py +0 -0
  523. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_peer_service_reply.py +0 -0
  524. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_postcompact_recall.py +0 -0
  525. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_process_liveness.py +0 -0
  526. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_protocol_handler.py +0 -0
  527. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_providers_isolation.py +0 -0
  528. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_ratify_command.py +0 -0
  529. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_readme_claims_no_gateway_default.py +0 -0
  530. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_repl_caller_convention.py +0 -0
  531. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_schedule_command.py +0 -0
  532. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_schedule_create_contract.py +0 -0
  533. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_scrub.py +0 -0
  534. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_security.py +0 -0
  535. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_service_app.py +0 -0
  536. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_service_command.py +0 -0
  537. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_service_providers.py +0 -0
  538. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_session_bridge_inject.py +0 -0
  539. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_session_bridge_probe.py +0 -0
  540. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_session_bridge_resolve.py +0 -0
  541. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_session_path_role_gate.py +0 -0
  542. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_shipped_units_match_the_cli.py +0 -0
  543. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_smoke_chat.py +0 -0
  544. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_smoke_one_shot.py +0 -0
  545. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_smoke_phase_5_5.py +0 -0
  546. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_spawn_base_hoist.py +0 -0
  547. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_spawn_command.py +0 -0
  548. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_spawn_identity_stamp.py +0 -0
  549. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_spawn_live_pin.py +0 -0
  550. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_spawn_mitosis_lineage.py +0 -0
  551. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_spawn_tmux_session.py +0 -0
  552. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_spawn_windows_relaunch.py +0 -0
  553. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_stall_alert.py +0 -0
  554. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_subprocess_decode_encoding.py +0 -0
  555. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_timeline_command.py +0 -0
  556. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_tmux_wake_session_reanchor.py +0 -0
  557. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_tmux_wake_stale.py +0 -0
  558. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_tmux_wake_submit_verify.py +0 -0
  559. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_token_file_one_parser.py +0 -0
  560. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_token_precedence_explicit_first.py +0 -0
  561. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_unified_token_resolver.py +0 -0
  562. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_version_command.py +0 -0
  563. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_version_consistency.py +0 -0
  564. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_vibe_membrane.py +0 -0
  565. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_watchdog.py +0 -0
  566. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_watchdog_dm_wake.py +0 -0
  567. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  568. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  569. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_watchdog_emit_health.py +0 -0
  570. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_watchdog_install_flags.py +0 -0
  571. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_watchdog_model_rung.py +0 -0
  572. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_watchdog_stale_peers.py +0 -0
  573. {swarph_cli-0.72.41 → swarph_cli-0.72.43}/tests/test_windows_waker_installer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.72.41
3
+ Version: 0.72.43
4
4
  Summary: The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`.
5
5
  Author: Pierre Samson, Claude Opus
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "swarph-cli"
7
- version = "0.72.41"
7
+ version = "0.72.43"
8
8
  description = "The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -16,6 +16,6 @@ The architecture splits CLI from substrate so:
16
16
 
17
17
  from __future__ import annotations
18
18
 
19
- __version__ = "0.72.41"
19
+ __version__ = "0.72.43"
20
20
 
21
21
  __all__ = ["__version__"]
@@ -10,6 +10,25 @@ The extract JOINS two halves, each with its own as-of pin:
10
10
  the report header stamp plus ``Layer N:`` lines only, and deploy units
11
11
  for visible repos — no rels/depends-on/blast-radius/arrow/import lines.
12
12
  STALE when older than the last repo.merged for a repo.
13
+ A local ref is not evidence when it cannot be trusted (card #1436):
14
+ ``git fetch origin main`` fails, origin is not the index slug, or the
15
+ branch tracks a remote other than that slug. Slug compare is
16
+ case-insensitive. A missing FETCH_HEAD is not itself stale — the fetch
17
+ runs, including under ``--print-only``, and writes FETCH_HEAD and
18
+ ``refs/remotes/origin/main`` only (15s, one repo at a time). Other
19
+ ``refs/remotes/origin/*`` refs do not move. A recorded fetch older than
20
+ ``FETCH_TRUST_BOUND_HOURS`` is not evidence. FETCH_HEAD is
21
+ ``git rev-parse FETCH_HEAD``; unreadable FETCH_HEAD, or a commit that
22
+ is not the ref commit, is not evidence. ``fresh`` requires that same
23
+ commit on the compared ref, and a report stamp for it. The line names
24
+ the commit the report described and the ref compared. Printed origin
25
+ URLs drop every userinfo form (user-only, ``user:password``, scp
26
+ ``user@host:path``) and carry no query or fragment. Fetch errors are
27
+ redacted before the 180-character cut. A malformed origin URL reads
28
+ not fresh and does not abort the other repos. swarph-cli defines no
29
+ canonical-remote map; the codegraph index
30
+ ``repos.slug`` is the identity already stored, and an empty slug says
31
+ the canonical remote is not defined.
13
32
 
14
33
  Posting uses the board path when ``--card`` is given WITHOUT
15
34
  ``--print-only``; otherwise prints and posts nothing. Nothing is deployed.
@@ -39,6 +58,7 @@ import os
39
58
  import re
40
59
  import sqlite3
41
60
  import subprocess
61
+ import time
42
62
 
43
63
  DEFAULT_INDEX = os.path.expanduser("~/.swarph/codegraph/index.db")
44
64
  DEFAULT_FED_REPORT = os.path.expanduser("~/.swarph/codegraph/federation-report.txt")
@@ -58,6 +78,16 @@ DEFAULT_MAX_LINES = 60
58
78
  FLOOR_CALLERS = "0 callers (floor: HTTP callers not linked yet)"
59
79
  FLOOR_ROUTES = "0 routes (floor: route registry not linked for this symbol)"
60
80
 
81
+ # swarph-cli does not define a canonical git remote or a fetch-age bound.
82
+ # ``repos.slug`` in the codegraph index (owner/repo) is the identity that
83
+ # index already stores. A remote URL is that remote when its owner/repo
84
+ # matches the slug. A recorded fetch older than this many hours is not
85
+ # evidence the local ref is current (card #1436).
86
+ FETCH_TRUST_BOUND_HOURS = 24
87
+ COMPARED_REF = "refs/remotes/origin/main"
88
+ _REPO_STAMP_RE = re.compile(r"^# repo (\S+) ([0-9a-f]{7,40}) (\S+)\s*$")
89
+ _SHA_RE = re.compile(r"^[0-9a-f]{7,40}$")
90
+
61
91
  _TOKEN_RE = re.compile(r"`([^`]+)`")
62
92
  _SNAKE_RE = re.compile(r"(?<![A-Za-z0-9_])_?[a-z][a-z0-9]*(?:_[a-z0-9]+){1,}")
63
93
  _ROUTE_RE = re.compile(r"(?:POST|GET|PUT|PATCH|DELETE)\s+/\S+|/\S*chat\S*|/\S*sync\S*")
@@ -175,6 +205,14 @@ def repo_registry(con) -> dict:
175
205
  for name, p in paths.items():
176
206
  if name in out:
177
207
  out[name]["path"] = p
208
+ try:
209
+ slugs = dict(con.execute(
210
+ "SELECT name, slug FROM repos LIMIT 100").fetchall())
211
+ except sqlite3.Error:
212
+ slugs = {}
213
+ for name, slug in slugs.items():
214
+ if name in out and isinstance(slug, str):
215
+ out[name]["slug"] = slug
178
216
  return out
179
217
 
180
218
 
@@ -414,9 +452,15 @@ def read_fed_report(path: str,
414
452
  if not generated:
415
453
  return {"status": "unavailable: no header stamp"}
416
454
  layers = [ln.strip() for ln in lines if _LAYER_RE.match(ln)]
455
+ repo_commits = {}
456
+ for ln in lines:
457
+ m = _REPO_STAMP_RE.match(ln.strip())
458
+ if m:
459
+ repo_commits[m.group(1)] = m.group(2)
417
460
  return {
418
461
  "status": "ok", "generated": generated,
419
462
  "layers": layers, "cut": cut, "scanned": min(len(lines), line_cap),
463
+ "repo_commits": repo_commits,
420
464
  }
421
465
 
422
466
 
@@ -458,18 +502,287 @@ def read_structurizr(path: str, repos: list) -> dict:
458
502
  }
459
503
 
460
504
 
461
- def last_merged_at(repo_path: str | None) -> str | None:
462
- """origin/main HEAD commit time as the repo.merged proxy (None on failure)."""
463
- if not repo_path:
505
+ def _one_line(text: str, limit: int = 180) -> str:
506
+ line = " ".join((text or "").split())
507
+ return line[:limit]
508
+
509
+
510
+ def owner_repo(url: str) -> str | None:
511
+ """``owner/repo`` from a git remote URL, or None when it has no such pair."""
512
+ raw = (url or "").strip().rstrip("/")
513
+ if raw.endswith(".git"):
514
+ raw = raw[:-4]
515
+ if raw.startswith("file://"):
516
+ from urllib.parse import unquote
517
+ raw = unquote(raw[7:])
518
+ elif "://" in raw:
519
+ raw = raw.split("://", 1)[1]
520
+ host = raw.split("/", 1)[0]
521
+ if "@" in host:
522
+ raw = raw.split("@", 1)[1]
523
+ elif re.match(r"^[A-Za-z]:", raw):
524
+ pass # Windows path — drive letter is not scp-style host:path
525
+ elif ":" in raw:
526
+ raw = raw.split(":", 1)[1]
527
+ parts = [p for p in re.split(r"[/\\]+", raw) if p]
528
+ if len(parts) < 2:
464
529
  return None
530
+ return f"{parts[-2]}/{parts[-1]}"
531
+
532
+
533
+ def _same_slug(owner: str | None, slug: str) -> bool:
534
+ """Index slug vs remote owner/repo. Case-insensitive (PROVEN vs proven)."""
535
+ return owner is not None and owner.casefold() == slug.casefold()
536
+
537
+
538
+ _URL_USERINFO_RE = re.compile(
539
+ r"(?P<scheme>[a-z][a-z0-9+.-]*://)(?:[^/\s@]+)(?::[^/\s@]*)?@",
540
+ re.IGNORECASE,
541
+ )
542
+ # Query or fragment on a scheme URL inside a git error line.
543
+ _PRINTED_QUERY_RE = re.compile("(://[^\\s'\"]*?)[?#][^\\s'\"]*")
544
+ # ``user@host:`` as ssh prints it (``user@host: Permission denied``).
545
+ _SCP_USER_RE = re.compile("(?<![\\w./-])[^\\s/:@'\"]+@(?=[^\\s/:@'\"]+:)")
546
+
547
+
548
+ def _strip_scp_user(url: str) -> str:
549
+ """``user@host:path`` → ``host:path``. A Windows drive is not scp."""
550
+ if re.match(r"^[A-Za-z]:", url):
551
+ return url
552
+ if "@" not in url:
553
+ return url
554
+ _user, rest = url.split("@", 1)
555
+ if ":" not in rest or not rest.split(":", 1)[0]:
556
+ return url
557
+ return rest
558
+
559
+
560
+ def _strip_userinfo_parsed(url: str) -> str:
561
+ """Strip userinfo, query, and fragment. Raises ValueError on a bad URL."""
562
+ from urllib.parse import urlsplit, urlunsplit
563
+ if "://" not in url:
564
+ return re.split(r"[?#]", _strip_scp_user(url))[0]
565
+ parts = urlsplit(url)
566
+ # ``.port`` / ``.hostname`` raise ValueError for ``:notaport`` and ``[bad``.
567
+ if parts.username is None and parts.password is None:
568
+ return urlunsplit((parts.scheme, parts.netloc, parts.path, "", ""))
569
+ host = parts.hostname or ""
570
+ if ":" in host and not host.startswith("["):
571
+ host = f"[{host}]"
572
+ netloc = f"{host}:{parts.port}" if parts.port else host
573
+ return urlunsplit((parts.scheme, netloc, parts.path, "", ""))
574
+
575
+
576
+ def _strip_userinfo(url: str) -> str:
577
+ """Drop userinfo before an origin URL is printed.
578
+
579
+ Covers user-only (``https://TOKEN@host``, ``ssh://TOKEN@host``),
580
+ ``user:password``, and scp-style ``TOKEN@host:path``. A printed origin
581
+ keeps neither a query nor a fragment. A malformed URL (``ValueError``
582
+ from urllib) is redacted and does not propagate.
583
+ """
584
+ try:
585
+ return _strip_userinfo_parsed(url)
586
+ except ValueError:
587
+ return re.split(
588
+ r"[?#]", _URL_USERINFO_RE.sub(r"\g<scheme>", url or ""))[0]
589
+
590
+
591
+ def _redact_printed(text: str) -> str:
592
+ """Drop userinfo and any query or fragment embedded in a git error line.
593
+
594
+ Scheme URLs lose userinfo, then query and fragment. An scp denial
595
+ (``user@host: Permission denied``) loses the user too.
596
+ """
597
+ if not text:
598
+ return text
599
+ text = _PRINTED_QUERY_RE.sub(r"\1", _URL_USERINFO_RE.sub(r"\g<scheme>", text))
600
+ return _SCP_USER_RE.sub("", text)
601
+
602
+
603
+ def _same_commit(report_sha, ref_sha) -> bool:
604
+ """True only when both shas name one commit (full or unique prefix)."""
605
+ if not isinstance(report_sha, str) or not isinstance(ref_sha, str):
606
+ return False
607
+ a, b = report_sha.strip().lower(), ref_sha.strip().lower()
608
+ if not _SHA_RE.fullmatch(a) or not _SHA_RE.fullmatch(b):
609
+ return False
610
+ if a == b:
611
+ return True
612
+ short, long_ = (a, b) if len(a) < len(b) else (b, a)
613
+ return long_.startswith(short)
614
+
615
+
616
+ def _git(repo: str, args: list, timeout: int = 10):
617
+ """One git invocation. None on timeout or if git cannot be started."""
465
618
  try:
466
- out = subprocess.run(
467
- ["git", "log", "-1", "--format=%cI", "origin/main"],
468
- cwd=repo_path, capture_output=True, text=True, timeout=5,
469
- encoding="utf-8", errors="replace")
470
- return (out.stdout or "").strip() or None
471
- except Exception:
619
+ return subprocess.run(
620
+ ["git", "-C", repo, "-c", "core.hooksPath=/dev/null", *args],
621
+ capture_output=True, text=True, timeout=timeout,
622
+ encoding="utf-8", errors="replace",
623
+ env={**os.environ, "GIT_TERMINAL_PROMPT": "0"},
624
+ )
625
+ except (OSError, subprocess.TimeoutExpired):
626
+ return None
627
+
628
+
629
+ def _git_out(repo: str, args: list, timeout: int = 10) -> str | None:
630
+ proc = _git(repo, args, timeout=timeout)
631
+ if proc is None or proc.returncode != 0:
632
+ return None
633
+ text = (proc.stdout or "").strip()
634
+ return text or None
635
+
636
+
637
+ def _ref_snapshot(repo_path: str) -> dict:
638
+ """Local ``refs/remotes/origin/main`` sha and committer time, if present."""
639
+ sha = _git_out(repo_path, ["rev-parse", "--verify", COMPARED_REF])
640
+ if not sha or not _SHA_RE.fullmatch(sha):
641
+ sha = None
642
+ merged = _git_out(repo_path, ["log", "-1", "--format=%cI", COMPARED_REF])
643
+ return {"sha": sha, "merged_at": merged}
644
+
645
+
646
+ def _remote_untrusted(repo_path: str, canonical_slug: str | None) -> str | None:
647
+ """Origin or the upstream's remote is not the canonical slug.
648
+
649
+ The upstream check reads that remote's URL and compares owner/repo.
650
+ It does not match the literal ``fork/main``. ``upstream/main`` and
651
+ ``fork/dev`` are not fresh when those remotes are not canonical.
652
+ """
653
+ slug = (canonical_slug or "").strip()
654
+ if not slug:
655
+ return ("canonical remote is not defined "
656
+ "(swarph-cli has no canonical-remote map; index slug is empty)")
657
+ url = _git_out(repo_path, ["remote", "get-url", "origin"])
658
+ if not url:
659
+ return (f"origin is not the canonical remote {slug} "
660
+ "(origin URL unreadable)")
661
+ if not _same_slug(owner_repo(url), slug):
662
+ return f"origin {_strip_userinfo(url)} is not the canonical remote {slug}"
663
+ upstream = _git_out(
664
+ repo_path, ["rev-parse", "--abbrev-ref", "@{upstream}"])
665
+ if not upstream:
666
+ return None
667
+ short = upstream[len("refs/remotes/"):] if upstream.startswith(
668
+ "refs/remotes/") else upstream
669
+ remote_name = short.split("/", 1)[0]
670
+ remote_url = (_git_out(repo_path, ["remote", "get-url", remote_name])
671
+ if remote_name else None)
672
+ if not remote_url or not _same_slug(owner_repo(remote_url), slug):
673
+ return f"branch tracks {short}"
674
+ return None
675
+
676
+
677
+ def _fetch_age_untrusted(repo_path: str, now: float) -> str | None:
678
+ """Recorded fetch older than ``FETCH_TRUST_BOUND_HOURS``.
679
+
680
+ A missing FETCH_HEAD is not stale. A fresh clone or worktree falls
681
+ through so the fetch below can record one.
682
+ """
683
+ bound = f"{FETCH_TRUST_BOUND_HOURS}h"
684
+ rel = _git_out(repo_path, ["rev-parse", "--git-path", "FETCH_HEAD"])
685
+ path = None
686
+ if rel:
687
+ path = rel if os.path.isabs(rel) else os.path.join(repo_path, rel)
688
+ if not path or not os.path.isfile(path):
689
+ return None
690
+ mtime = os.path.getmtime(path)
691
+ if now - mtime > FETCH_TRUST_BOUND_HOURS * 3600:
692
+ import datetime as _dt
693
+ stamp = _dt.datetime.fromtimestamp(
694
+ mtime, _dt.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
695
+ return f"last fetch {stamp} is older than {bound}"
696
+ return None
697
+
698
+
699
+ def _fetch_untrusted(repo_path: str) -> str | None:
700
+ """``git fetch origin main`` failed, so a remote merge is invisible.
701
+
702
+ The refspec is ``origin main``, as merge_watch fetches it, so an
703
+ unrelated branch conflict does not fail a healthy main. This writes
704
+ FETCH_HEAD and the origin main ref in the checkout, including under
705
+ ``--print-only``. One repo at a time, 15s.
706
+ """
707
+ proc = _git(repo_path, ["fetch", "--quiet", "--no-tags", "origin", "main"],
708
+ timeout=15)
709
+ if proc is not None and proc.returncode == 0:
472
710
  return None
711
+ if proc is None:
712
+ detail = "git fetch origin main failed"
713
+ else:
714
+ raw = proc.stderr or proc.stdout or ""
715
+ detail = _one_line(_redact_printed(raw)) or "git fetch origin main failed"
716
+ return f"fetch failed: {detail}"
717
+
718
+
719
+ def _fetch_head_sha(repo_path: str) -> str | None:
720
+ """Sha from ``git rev-parse FETCH_HEAD``, or None when that fails."""
721
+ sha = (_git_out(repo_path, ["rev-parse", "FETCH_HEAD"]) or "").lower()
722
+ if not _SHA_RE.fullmatch(sha):
723
+ return None
724
+ return sha
725
+
726
+
727
+ def read_ref_trust(repo_path: str | None,
728
+ canonical_slug: str | None = None,
729
+ *, now: float | None = None) -> dict:
730
+ """Whether local origin/main may be compared to the federation build.
731
+
732
+ Untrusted results still name the local ref. A trusted result is a
733
+ canonical origin whose upstream remote is that same owner/repo,
734
+ fetched inside the bound, whose ``git fetch origin main`` succeeded
735
+ just now and whose FETCH_HEAD names that same ref. The ref is read
736
+ again after the fetch, including when the fetch fails.
737
+ """
738
+ moment = time.time() if now is None else now
739
+ base = {
740
+ "ref": COMPARED_REF, "ref_sha": None, "merged_at": None,
741
+ "trusted": False, "reason": None, "report_sha": None,
742
+ }
743
+ if not repo_path or not os.path.isdir(repo_path):
744
+ base["reason"] = "repo path is not a directory"
745
+ return base
746
+ snap = _ref_snapshot(repo_path)
747
+ base["ref_sha"] = snap["sha"]
748
+ base["merged_at"] = snap["merged_at"]
749
+ # Remote check (card #1436): origin, or the upstream's remote, is not canonical.
750
+ remote_reason = _remote_untrusted(repo_path, canonical_slug)
751
+ if remote_reason:
752
+ base["reason"] = remote_reason
753
+ return base
754
+ # Age check (card #1436): a recorded fetch older than the bound.
755
+ # Missing FETCH_HEAD falls through to the fetch.
756
+ age_reason = _fetch_age_untrusted(repo_path, moment)
757
+ if age_reason:
758
+ base["reason"] = age_reason
759
+ return base
760
+ # Fetch ``origin main``. Re-read the ref after the attempt, success or
761
+ # failure, so the compared sha is not the pre-fetch commit.
762
+ fetch_reason = _fetch_untrusted(repo_path)
763
+ snap = _ref_snapshot(repo_path)
764
+ base["ref_sha"] = snap["sha"]
765
+ base["merged_at"] = snap["merged_at"]
766
+ if fetch_reason:
767
+ base["reason"] = fetch_reason
768
+ return base
769
+ # A good fetch can still leave origin/main behind: no refspec, or a
770
+ # narrow one, writes FETCH_HEAD and does not move the ref. Those
771
+ # commits differ, so the ref is not fresh. Unreadable FETCH_HEAD is
772
+ # not fresh either — do not skip the compare.
773
+ fetched = _fetch_head_sha(repo_path)
774
+ if not fetched:
775
+ base["reason"] = "FETCH_HEAD unreadable after fetch"
776
+ return base
777
+ if snap["sha"] and not _same_commit(fetched, snap["sha"]):
778
+ base["reason"] = "fetched commit differs from the ref commit"
779
+ return base
780
+ if not snap["merged_at"]:
781
+ base["reason"] = "origin/main has no commit time after fetch"
782
+ return base
783
+ base["trusted"] = True
784
+ base["reason"] = None
785
+ return base
473
786
 
474
787
 
475
788
  def indexed_sha(repo_path: str | None) -> str | None:
@@ -533,7 +846,7 @@ def build_extract(*, tokens: list, index_path: str = DEFAULT_INDEX,
533
846
  card_allowlist_path: str | None = DEFAULT_CARD_ALLOWLIST,
534
847
  entitled: bool = False) -> dict:
535
848
  """Assemble the extract dict. Never raises on bad sources (unavailable)."""
536
- merged_lookup = merged_lookup or last_merged_at
849
+ merged_override = merged_lookup
537
850
  tokens = [_normalize_token(t) for t in (tokens or [])]
538
851
  tokens = [t for t in tokens if t and is_lookup_token(t)]
539
852
  fed_docs = fed_docs_state(fed_docs_db)
@@ -593,10 +906,28 @@ def build_extract(*, tokens: list, index_path: str = DEFAULT_INDEX,
593
906
  # Shape-only federation half: header stamp + `Layer N:` lines only.
594
907
  struct = read_structurizr(structurizr_ws, repos_hit)
595
908
  fed = read_fed_report(fed_report)
596
- pins = {repo: {"sha": indexed_sha((registry.get(repo) or {}).get("path")),
597
- "indexed_at": (registry.get(repo) or {}).get("indexed_at"),
598
- "merged_at": merged_lookup((registry.get(repo) or {}).get("path"))}
599
- for repo in repos_hit}
909
+ report_commits = fed.get("repo_commits") or {}
910
+ pins = {}
911
+ for repo in repos_hit:
912
+ reg = registry.get(repo) or {}
913
+ path = reg.get("path")
914
+ described = report_commits.get(repo)
915
+ pin = {"sha": indexed_sha(path),
916
+ "indexed_at": reg.get("indexed_at"),
917
+ "report_sha": described}
918
+ if merged_override is None:
919
+ trust = read_ref_trust(
920
+ path, reg.get("slug") if isinstance(reg.get("slug"), str) else None)
921
+ trust["report_sha"] = described
922
+ pin["ref_trust"] = trust
923
+ pin["merged_at"] = trust.get("merged_at") if trust.get("trusted") else None
924
+ else:
925
+ pin["merged_at"] = merged_override(path)
926
+ pins[repo] = pin
927
+ # repo_commits names every stamped repo, including withheld ones.
928
+ # Do not return that map past the pin loop (card #1436).
929
+ if isinstance(fed, dict):
930
+ fed.pop("repo_commits", None)
600
931
  fed_build = (_stamp_time(fed.get("generated"))
601
932
  if fed.get("status") == "ok" else None)
602
933
  return {
@@ -639,6 +970,18 @@ def is_stale_fed_build(fed_build, merged_at) -> bool | None:
639
970
  return None if fb is None or mg is None else fb < mg
640
971
 
641
972
 
973
+ def _ref_names(pin: dict) -> str:
974
+ """``; report described <sha>; compared <ref>@<sha>`` for a real read."""
975
+ trust = pin.get("ref_trust")
976
+ if not isinstance(trust, dict):
977
+ return ""
978
+ described = trust.get("report_sha") or pin.get("report_sha") or "no commit"
979
+ ref = trust.get("ref") or COMPARED_REF
980
+ sha = trust.get("ref_sha")
981
+ compared = f"{ref}@{sha}" if sha else ref
982
+ return f"; report described {described}; compared {compared}"
983
+
984
+
642
985
  def render(ex: dict, max_lines: int = DEFAULT_MAX_LINES,
643
986
  post_safe: bool = False) -> str:
644
987
  """Render the extract text; post-safe mode names no withheld/missing repo."""
@@ -774,23 +1117,43 @@ def render(ex: dict, max_lines: int = DEFAULT_MAX_LINES,
774
1117
  for repo, p in ex.get("pins", {}).items():
775
1118
  merged = p.get("merged_at")
776
1119
  fb = ex.get("fed_build")
1120
+ names = _ref_names(p)
1121
+ trust = p.get("ref_trust")
1122
+ if isinstance(trust, dict) and not trust.get("trusted"):
1123
+ reason = trust.get("reason") or "local ref is not trusted"
1124
+ tail.append(f"- {repo}: not fresh ({reason}){names}")
1125
+ continue
777
1126
  if not fb:
778
- tail.append(f"- {repo}: freshness unavailable (no federation build time)")
1127
+ tail.append(f"- {repo}: freshness unavailable (no federation build time)"
1128
+ f"{names}")
779
1129
  elif not merged:
780
1130
  tail.append(f"- {repo}: freshness unavailable "
781
- "(last repo.merged unknown for this repo)")
1131
+ "(last repo.merged unknown for this repo)"
1132
+ f"{names}")
782
1133
  else:
783
1134
  stale = is_stale_fed_build(fb, merged)
1135
+ report_sha = p.get("report_sha")
1136
+ ref_sha = None
1137
+ if isinstance(trust, dict):
1138
+ report_sha = trust.get("report_sha") or report_sha
1139
+ ref_sha = trust.get("ref_sha")
784
1140
  if stale is None:
785
1141
  tail.append(f"- {repo}: freshness unavailable "
786
- "(unparseable federation-build or repo.merged time)")
1142
+ "(unparseable federation-build or repo.merged time)"
1143
+ f"{names}")
787
1144
  elif stale:
788
1145
  tail.append(f"- {repo}: STALE (federation build {fb} older than "
789
1146
  f"last repo.merged {merged}; merge-time is an "
790
- "origin/main-HEAD proxy)")
1147
+ f"origin/main-HEAD proxy){names}")
1148
+ elif isinstance(trust, dict) and not _same_commit(report_sha, ref_sha):
1149
+ # Same commit, and only that, reads fresh. A commit dated
1150
+ # before the report and pushed after it is not fresh.
1151
+ tail.append(f"- {repo}: not fresh "
1152
+ "(report commit differs from the ref commit)"
1153
+ f"{names}")
791
1154
  else:
792
1155
  tail.append(f"- {repo}: fresh (federation build {fb} >= "
793
- f"last repo.merged {merged})")
1156
+ f"last repo.merged {merged}){names}")
794
1157
  if len(head) + len(tail) + (1 if cut_notes else 0) > max_lines:
795
1158
  cut_notes.append(f"output capped at {max_lines} lines "
796
1159
  "(render budget; freshness + withheld kept)")
@@ -51,17 +51,19 @@ def _allow_unchanged() -> int:
51
51
 
52
52
 
53
53
  def run_hook(stdin_text: Optional[str] = None) -> int:
54
+ try:
55
+ return _run_hook(stdin_text)
56
+ except Exception as exc:
57
+ # A hook crash otherwise leaves gh on the ambient shared identity.
58
+ return _deny(f"identity routing failed ({type(exc).__name__}); "
59
+ "correct the command or hook payload and retry")
60
+
61
+
62
+ def _run_hook(stdin_text: Optional[str] = None) -> int:
54
63
  raw = stdin_text if stdin_text is not None else sys.stdin.read()
55
64
  # PS 5.1 native pipes prefix a (double) UTF-8 BOM; strip before parse.
56
65
  raw = (raw or "").lstrip("\ufeff")
57
- try:
58
- payload = json.loads(raw or "{}")
59
- except Exception:
60
- # >>> FAIL OPEN ON A MALFORMED PAYLOAD, NOT CLOSED. <<< A router that cannot
61
- # parse its input has learned nothing about the command, so denying would
62
- # brick every Bash call on a harness change. It is the UNMAPPED CELL that
63
- # must fail closed, not the unreadable envelope.
64
- return _allow_unchanged()
66
+ payload = json.loads(raw or "{}")
65
67
 
66
68
  command = (payload.get("tool_input") or {}).get("command") or ""
67
69
  if not ghid.targets_gh(command):