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