swarph-cli 0.69.7__tar.gz → 0.69.8__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.69.7/src/swarph_cli.egg-info → swarph_cli-0.69.8}/PKG-INFO +1 -1
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/pyproject.toml +1 -1
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/__init__.py +1 -1
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/scripts/ensure_monitor.sh +9 -61
- {swarph_cli-0.69.7 → swarph_cli-0.69.8/src/swarph_cli.egg-info}/PKG-INFO +1 -1
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli.egg-info/SOURCES.txt +1 -0
- swarph_cli-0.69.8/tests/test_1006_session_board.py +291 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_960_ensure_monitor_heartbeat.py +47 -16
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/LICENSE +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/README.md +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/setup.cfg +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/__main__.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/bench/__init__.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/bench/backends.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/bench/claude_cli.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/bench/pack.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/bench/prices.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/bench/providers.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/bench/quality.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/bench/refresh_prices.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/bench/rules.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/bench/runner.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/bench/validate.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/caller.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/capture/__init__.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/capture/harden.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/capture/lineage.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/capture/liveness.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/capture/manifest.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/capture/paths.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/capture/verify.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/cell.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/chain_token.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/channel.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/__init__.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/_content.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/_display.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/_gateway_client.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/add.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/bench.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/board.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/board_merge_check.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/brain.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/brain_ask.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/cell.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/cell_probe.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/channel.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/chat.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/codegraph.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/codegraph_hook.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/codex_hook_output.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/codex_waker.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/compress.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/daemon.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/dreaming.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/event.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/gateway.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/gh_route.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/group.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/guide.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/highlight.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/hook_interpreter.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/hook_output.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/hooks.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/import_session.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/init.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/install_hook.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/install_multiplexer.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/install_wake_hook.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/lane.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/mcp_server.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/memory.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/memory_sync.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/mesh.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/monitor.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/okf_links.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/onboard.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/protocol_handler.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/ratify.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/schedule.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/security.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/service.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/spawn.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/timeline.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/version.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/wake_hook_output.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/commands/watchdog.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/compress/__init__.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/compress/levers.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/compress/marker.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/compress/verify.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/console_safe.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/delivery_queue.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/dm_frame.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/dreaming/__init__.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/dreaming/candidates.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/dreaming/clone.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/dreaming/enrich.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/dreaming/index_check.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/dreaming/notify.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/dreaming/organize.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/dreaming/probes.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/dreaming/report.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/dreaming/run.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/dreaming/slm.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/dreaming/transcripts.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/dreaming/verify.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/gateway/__init__.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/gateway/feature_registry.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/gateway/lanes_control.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/gateway/schema.sql +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/gateway/server.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/gateway/services_control.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/gateway_default.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/gh_identity.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/guide/GUIDE.md +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/identity.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/main.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/multiplexer.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/orphan_daemons.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/pane_probe.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/parsers/__init__.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/parsers/claude.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/peer_executor.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/peer_executor_windows.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/peer_reconciliation.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/peer_reply_outbox.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/peer_service_host.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/peer_service_payload.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/peer_service_reply.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/peer_staging.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/query_filters.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/scripts/__init__.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/service/__init__.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/service/app.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/service/providers.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/session_bridge.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/source_text.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/stall_alert.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/systemd/codex-waker.default +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/timeline_paths.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli/tokens.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli.egg-info/entry_points.txt +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli.egg-info/requires.txt +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/src/swarph_cli.egg-info/top_level.txt +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_124_register_merge.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_126_pending_from_complete.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_132_ensure_monitor_state_dir.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_138_memory_navigate.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_139_tool_probe.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_183_card_anchor.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_184_notify_honesty.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_201_win32_bash_refusal.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_243_onboard_per_peer_token.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_307_task4_ask_reply.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_319_refusal_exit_codes.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_323_mistral_backend.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_332_report_organize_findings.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_356_query_filters.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_397_gh_identity_router.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_397_router_preflight.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_402_one_identity_resolver.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_467b_onboard_operator_credential.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_472_register_caller_binding.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_523_guide.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_524_conftest_hermeticity.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_532_accept_flag.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_535_version_capability.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_546_default_gateway.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_547_verbs_from_registry.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_548_gateway_remainder.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_552_codegraph_gateway.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_555_relay_row_shape.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_556_relay_freshness.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_559_edges_truncated.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_564_bootstrap_first_peer.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_579_register_bootstrap_message.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_591_cli_step_graph.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_644_monitor_supervision.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_648_install_unit.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_654_reexec.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_656_owned_slm.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_684_dreaming_verb.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_689_pkg_version_order.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_715_grok_wake.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_716_timeline_one_path.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_723_grok_0251.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_724_ps1_ascii_or_bom.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_725_stdio_reconfigure.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_726_grok_limit_screen.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_729_dm_once.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_729_wake_watchdog.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_734_dreaming_enrich_degrade.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_735_dm_mcp_tools.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_740_edit_project.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_762_cards_edit_priority.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_764_enrich_collapse.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_801_confirm_flow_cli.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_802_stage_history.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_825_codegraph_hook_noise.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_825_install_posttooluse_only.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_825_remaining_cap_shred_bind.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_829_counterfactual_column.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_830_hook_binding_version.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_84_cli_labels.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_866_doctrine_guide.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_872_identity_refuses_not_warns.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_874_source_text.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_937_dreaming_notify.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_938_organize_line_limit.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_941_bench_backends.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_941_harness_342.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_945_same_line_pairing.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_955_prompt_audit.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_957_auth_http.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_959_venv_interpreter.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_960_channel.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_963_plugin_manifest.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_964_channel_cell.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_965_mesh_wait.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_975_channel_hop.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_add_security_gate.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_artifact_add.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_artifact_hash.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_artifact_lib.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_artifact_mcp.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_artifact_skill.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_artifact_tool.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_artifact_uri.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_bench_backends.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_bench_claude_cli.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_bench_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_bench_pack.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_bench_prices.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_bench_quality.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_bench_runner.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_bench_validate.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_board_card_thread.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_board_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_board_merge_check.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_board_obligation_close.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_brain_ask_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_brain_ask_env_diagnosis.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_brain_ask_identity_explicitness.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_brain_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_caller_meta_guard.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_capture_lineage.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_capture_liveness.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_capture_manifest.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_capture_paths.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_capture_security.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_cell_command_dispatch.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_cell_harden.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_cell_loader.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_cell_selfcheck.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_cell_verify.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_chain_token.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_channel_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_channel_poll_never_breaks_dms.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_channel_post.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_channel_wake_policy_filter.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_chat_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_claude_parser.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_claude_tmux_template.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_codegraph_caller_scope.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_codegraph_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_codegraph_hook.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_codegraph_mcp.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_codex_waker.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_compress_caller_convention.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_compress_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_compress_levers.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_compress_marker.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_compress_verify.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_console_encoding_e2e.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_content_composition.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_cursor_membrane.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_daemon_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_daemon_console_encoding.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_daemon_delivery.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_delivery_queue.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_display_sanitize.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_ensure_monitor_no_peer_default.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_event_emit.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_feature_to_uri.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_gateway_brain.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_gateway_client.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_gateway_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_gateway_message_idempotency.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_gh_route_doctor_mesh_name.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_group_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_guide_commands_parse.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_guide_selfcheck.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_heartbeat_check.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_highlight_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_hook_output.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_hooks_activity_marker.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_hooks_add.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_hooks_bundle.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_hooks_init.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_hooks_legacy_generation_removal.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_hooks_lifecycle.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_hooks_merge.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_hooks_verify.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_hooks_win32_bash_wrapper.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_hooks_win32_command_path.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_import_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_inbox_identity_provenance.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_init_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_init_provider_menu.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_install_codex_hooks.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_install_hook.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_install_multiplexer_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_install_postcompact_hook.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_install_wake_hook.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_lane_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_main.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_main_unknown_verb_guard.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_mcp_missing_hint.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_mcp_server.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_memory_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_memory_emit_snippet.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_memory_gateway.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_memory_sync.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_memory_sync_membrane.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_mesh_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_mesh_inbox_mark_read.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_mesh_inbox_sanitize.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_mesh_originate_thread_623.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_mesh_send_unknown_peer.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_mesh_sidecar.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_meta_edge_identity.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_monitor_channel_poll.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_monitor_cli.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_monitor_daemon_collision.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_monitor_self_name_verification.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_monitor_state_split.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_multiplexer.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_muse_membrane.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_muse_own_cli.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_obligation_close_is_surfaced.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_okf_links.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_onboard_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_opencode_membrane.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_opencode_plugin.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_orphan_daemons.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_packaged_artifacts_exist.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_packaged_skills.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_peer_executor.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_peer_executor_windows.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_peer_reply_drain_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_peer_reply_drain_supervisor.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_peer_reply_outbox.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_peer_reply_transport.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_peer_service_e2e.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_peer_service_host.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_peer_service_payload.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_peer_service_reply.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_postcompact_recall.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_process_liveness.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_protocol_handler.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_providers_isolation.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_ratify_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_readme_claims_no_gateway_default.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_repl_caller_convention.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_schedule_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_schedule_create_contract.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_security.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_service_app.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_service_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_service_providers.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_session_bridge_inject.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_session_bridge_probe.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_session_bridge_resolve.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_session_path_role_gate.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_shipped_units_match_the_cli.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_smoke_chat.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_smoke_one_shot.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_smoke_phase_5_5.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_spawn_base_hoist.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_spawn_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_spawn_identity_stamp.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_spawn_live_pin.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_spawn_mitosis_lineage.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_spawn_tmux_session.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_spawn_windows_relaunch.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_stall_alert.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_subprocess_decode_encoding.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_timeline_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_tmux_wake_runstate_defer.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_tmux_wake_session_reanchor.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_tmux_wake_stale.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_tmux_wake_submit_verify.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_token_file_one_parser.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_token_precedence_explicit_first.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_unified_token_resolver.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_version_command.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_version_consistency.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_vibe_membrane.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_watchdog.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_watchdog_dm_wake.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_watchdog_dm_wake_wiring.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_watchdog_emit_health.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_watchdog_install_flags.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_watchdog_model_rung.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/tests/test_watchdog_stale_peers.py +0 -0
- {swarph_cli-0.69.7 → swarph_cli-0.69.8}/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.69.
|
|
3
|
+
Version: 0.69.8
|
|
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.69.
|
|
7
|
+
version = "0.69.8"
|
|
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" }
|
|
@@ -64,71 +64,19 @@ STATE_DIR="${1:-${SWARPH_STATE_DIR:-}}"
|
|
|
64
64
|
SD=()
|
|
65
65
|
[ -n "$STATE_DIR" ] && SD=(--state-dir "$STATE_DIR")
|
|
66
66
|
|
|
67
|
-
# >>> #960:
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
#
|
|
73
|
-
|
|
74
|
-
local f="$1"
|
|
75
|
-
[ -n "$f" ] && [ -f "$f" ] || return 1
|
|
76
|
-
command -v python3 >/dev/null 2>&1 || return 1
|
|
77
|
-
python3 -c 'import json,os,sys,time
|
|
78
|
-
try:
|
|
79
|
-
data=json.load(open(sys.argv[1],encoding="utf-8"))
|
|
80
|
-
ts=float(data.get("ts") or 0)
|
|
81
|
-
except Exception:
|
|
82
|
-
raise SystemExit(1)
|
|
83
|
-
age=time.time()-ts
|
|
84
|
-
# A clock a few seconds ahead is still a live writer. An hour ahead is not.
|
|
85
|
-
if age < -30 or age >= 180:
|
|
86
|
-
raise SystemExit(1)
|
|
87
|
-
pid=data.get("pid")
|
|
88
|
-
if pid is None:
|
|
89
|
-
raise SystemExit(0)
|
|
90
|
-
try:
|
|
91
|
-
pid=int(pid)
|
|
92
|
-
except (TypeError, ValueError):
|
|
93
|
-
raise SystemExit(1)
|
|
94
|
-
# pid 0 is this process group and pid -1 is every process we can signal.
|
|
95
|
-
# Neither is the channel server. os.kill would report both as alive.
|
|
96
|
-
if pid <= 0:
|
|
97
|
-
raise SystemExit(1)
|
|
98
|
-
try:
|
|
99
|
-
os.kill(pid, 0)
|
|
100
|
-
except PermissionError:
|
|
101
|
-
pass
|
|
102
|
-
except OSError:
|
|
103
|
-
raise SystemExit(1)
|
|
104
|
-
try:
|
|
105
|
-
raw=open("/proc/%s/cmdline" % pid, "rb").read()
|
|
106
|
-
except OSError:
|
|
107
|
-
# No command line to read (macOS, or a pid we may not inspect).
|
|
108
|
-
# A live pid still counts, which is the check this host can make.
|
|
109
|
-
raise SystemExit(0)
|
|
110
|
-
text=raw.replace(b"\x00", b" ").decode("utf-8", "replace")
|
|
111
|
-
if "channel-serve" in text or "swarph_cli.channel" in text:
|
|
112
|
-
raise SystemExit(0)
|
|
113
|
-
raise SystemExit(1)' "$f"
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
HB_ROOT="${STATE_DIR:-$HOME/swarph_state/$SELF}"
|
|
117
|
-
HB_FILE=""
|
|
118
|
-
if [ -f "$HB_ROOT/channel_heartbeat.json" ]; then
|
|
119
|
-
HB_FILE="$HB_ROOT/channel_heartbeat.json"
|
|
120
|
-
elif [ -f "$HB_ROOT/mesh-sidecar/channel_heartbeat.json" ]; then
|
|
121
|
-
HB_FILE="$HB_ROOT/mesh-sidecar/channel_heartbeat.json"
|
|
122
|
-
fi
|
|
123
|
-
|
|
67
|
+
# >>> #960: the heartbeat is the READER, the monitor is the WRITER. <<<
|
|
68
|
+
# channel-serve writes channel_heartbeat.json every 60s and only reads
|
|
69
|
+
# inbox.log. The pull monitor is the only writer of that log. A fresh
|
|
70
|
+
# heartbeat means the reader is up, not the writer. `monitor status` is
|
|
71
|
+
# the check for the writer. Not running: start one, even when the
|
|
72
|
+
# heartbeat is fresh. Running (status 0 or 1): start nothing. A second
|
|
73
|
+
# monitor is the duplicate. The heartbeat file is not that check.
|
|
124
74
|
"$SW" monitor status --as "$SELF" ${SD[@]+"${SD[@]}"} >/dev/null 2>&1
|
|
125
75
|
case $? in
|
|
126
76
|
0) ;; # running, nothing pending
|
|
127
77
|
1) ;; # running, DMs pending — reported below
|
|
128
|
-
*) # not running (2) or unknown.
|
|
129
|
-
if
|
|
130
|
-
echo "[monitor] channel heartbeat is under 180s old — not starting a monitor"
|
|
131
|
-
elif "$SW" monitor start --as "$SELF" --deliver pull ${SD[@]+"${SD[@]}"} >/tmp/.swarph-monitor-start.$$ 2>&1; then
|
|
78
|
+
*) # not running (2) or unknown. The heartbeat does not cover this.
|
|
79
|
+
if "$SW" monitor start --as "$SELF" --deliver pull ${SD[@]+"${SD[@]}"} >/tmp/.swarph-monitor-start.$$ 2>&1; then
|
|
132
80
|
echo "[monitor] started (--deliver pull)"
|
|
133
81
|
else
|
|
134
82
|
# LOUD, not silent: an auto-start that fails quietly rebuilds the exact
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarph-cli
|
|
3
|
-
Version: 0.69.
|
|
3
|
+
Version: 0.69.8
|
|
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
|
|
@@ -178,6 +178,7 @@ src/swarph_cli/systemd/swarph-peer-reply-drain@.timer
|
|
|
178
178
|
src/swarph_cli/systemd/swarph-watchdog.default
|
|
179
179
|
src/swarph_cli/systemd/swarph-watchdog.service
|
|
180
180
|
src/swarph_cli/systemd/swarph-watchdog.timer
|
|
181
|
+
tests/test_1006_session_board.py
|
|
181
182
|
tests/test_124_register_merge.py
|
|
182
183
|
tests/test_126_pending_from_complete.py
|
|
183
184
|
tests/test_132_ensure_monitor_state_dir.py
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"""card #1006 — CLI session board. The desktop tool names must not appear."""
|
|
2
|
+
import json
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
8
|
+
SKILL = ROOT / "plugins" / "swarph" / "skills" / "lej-session-board"
|
|
9
|
+
FIX = ROOT / "tests" / "fixtures" / "session-board"
|
|
10
|
+
SCRIPT = SKILL / "scripts" / "build_board.py"
|
|
11
|
+
|
|
12
|
+
# Names that exist only on Claude Desktop. Referencing one is a fail.
|
|
13
|
+
DESKTOP_TOOLS = (
|
|
14
|
+
"mcp__ccd_session_mgmt__",
|
|
15
|
+
"list_sessions",
|
|
16
|
+
"list_events",
|
|
17
|
+
"archive_session",
|
|
18
|
+
"set_pinned",
|
|
19
|
+
"get_session",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _skill_texts():
|
|
24
|
+
files = [SKILL / "SKILL.md", SKILL / "references" / "coordination-rule.md", SCRIPT]
|
|
25
|
+
return {p.relative_to(SKILL).as_posix(): p.read_text(encoding="utf-8") for p in files}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def desktop_hits(text: str) -> list[str]:
|
|
29
|
+
return [name for name in DESKTOP_TOOLS if name in text]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_desktop_tool_name_is_not_referenced():
|
|
33
|
+
for rel, text in _skill_texts().items():
|
|
34
|
+
assert desktop_hits(text) == [], rel
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_the_scanner_fails_when_a_desktop_tool_name_is_added():
|
|
38
|
+
poisoned = "call mcp__ccd_session_mgmt__list_sessions here"
|
|
39
|
+
assert "mcp__ccd_session_mgmt__" in desktop_hits(poisoned)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_license_and_attribution_kept():
|
|
43
|
+
lic = (SKILL / "LICENSE").read_text(encoding="utf-8")
|
|
44
|
+
assert "MIT License" in lic
|
|
45
|
+
assert "Jonathan Edwards" in lic
|
|
46
|
+
readme = (SKILL / "README.md").read_text(encoding="utf-8")
|
|
47
|
+
assert "Jonathan Edwards" in readme
|
|
48
|
+
assert "MIT" in readme
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_coordination_rule_uses_listagents_and_sendmessage_by_name():
|
|
52
|
+
text = (SKILL / "references" / "coordination-rule.md").read_text(encoding="utf-8")
|
|
53
|
+
assert "ListAgents" in text
|
|
54
|
+
assert "SendMessage" in text
|
|
55
|
+
assert "by the name" in text
|
|
56
|
+
assert "approves building, not deploying" in text
|
|
57
|
+
assert desktop_hits(text) == []
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _run_board():
|
|
61
|
+
proc = subprocess.run(
|
|
62
|
+
[sys.executable, str(SCRIPT),
|
|
63
|
+
"--listing", str(FIX / "listagents-lab.txt"),
|
|
64
|
+
"--rows", str(FIX / "commander-170.json")],
|
|
65
|
+
check=True, capture_output=True, text=True,
|
|
66
|
+
)
|
|
67
|
+
return json.loads(proc.stdout)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _validate(board: dict):
|
|
71
|
+
"""The fields references/data-schema.md says the page reads."""
|
|
72
|
+
schema = (SKILL / "references" / "data-schema.md").read_text(encoding="utf-8")
|
|
73
|
+
for field in ("updated", "projectFolder", "manager", "sessions", "groups"):
|
|
74
|
+
assert field in schema
|
|
75
|
+
assert field in board
|
|
76
|
+
assert isinstance(board["sessions"], list) and board["sessions"]
|
|
77
|
+
for sess in board["sessions"]:
|
|
78
|
+
for field in ("id", "name", "state", "tone", "where", "questions"):
|
|
79
|
+
assert field in sess
|
|
80
|
+
assert sess["tone"] in ("needs", "done", "")
|
|
81
|
+
assert 2 <= len(sess["where"]) <= 4
|
|
82
|
+
for q in sess["questions"]:
|
|
83
|
+
for field in ("id", "to", "title", "options", "to_node", "in_session"):
|
|
84
|
+
assert field in q
|
|
85
|
+
assert field in schema
|
|
86
|
+
assert "card" in schema and "obligation" in schema
|
|
87
|
+
assert q["to_node"] == q["to"]
|
|
88
|
+
assert isinstance(q["in_session"], bool)
|
|
89
|
+
assert 2 <= len(q["options"]) <= 3
|
|
90
|
+
assert sum(1 for opt in q["options"] if opt.get("rec")) == 1
|
|
91
|
+
for opt in q["options"]:
|
|
92
|
+
assert opt["label"] and opt["text"]
|
|
93
|
+
for group in board["groups"]:
|
|
94
|
+
assert "title" in group and "items" in group
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_skill_maps_a_name_through_the_sessions_file_and_lists_shell():
|
|
98
|
+
text = (SKILL / "SKILL.md").read_text(encoding="utf-8")
|
|
99
|
+
step = text.split("## 1. Take the roster", 1)[1].split("## 2.", 1)[0]
|
|
100
|
+
assert "~/.claude/sessions/<pid>.json" in step
|
|
101
|
+
assert "name" in step and "sessionId" in step and "cwd" in step
|
|
102
|
+
assert "`shell`" in step
|
|
103
|
+
assert "in progress" in step.lower() or "In progress" in step
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_shell_status_is_in_progress():
|
|
107
|
+
sys.path.insert(0, str(SCRIPT.parent))
|
|
108
|
+
import build_board
|
|
109
|
+
listing = "This session is lab-ovh [1e23f6]\npeer [abc123] · interactive · shell · running a command\n"
|
|
110
|
+
board = build_board.build(listing, [])
|
|
111
|
+
sess = next(s for s in board["sessions"] if s["name"] == "peer")
|
|
112
|
+
assert sess["bucket"] == "in_progress"
|
|
113
|
+
assert sess["tone"] == ""
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def test_schema_documents_the_four_new_question_fields():
|
|
117
|
+
schema = (SKILL / "references" / "data-schema.md").read_text(encoding="utf-8")
|
|
118
|
+
for field in ("to_node", "card", "obligation", "in_session"):
|
|
119
|
+
assert f"`{field}`" in schema
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def test_skill_builds_schema_valid_json_without_desktop_tools():
|
|
123
|
+
board = _run_board()
|
|
124
|
+
_validate(board)
|
|
125
|
+
assert board["manager"] == "lab-ovh"
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def test_lab_roster_sorts_offline_remote_control_into_stale():
|
|
129
|
+
listing = (FIX / "listagents-lab.txt").read_text(encoding="utf-8")
|
|
130
|
+
board = _run_board()
|
|
131
|
+
by_id = {s["id"]: s for s in board["sessions"]}
|
|
132
|
+
# every offline Remote Control row from the real listing is stale
|
|
133
|
+
seen = 0
|
|
134
|
+
for line in listing.splitlines():
|
|
135
|
+
if "Remote Control" in line and "offline" in line:
|
|
136
|
+
sid = line.split("[", 1)[1].split("]", 1)[0]
|
|
137
|
+
assert by_id[sid]["bucket"] == "stale"
|
|
138
|
+
assert by_id[sid]["tone"] == "done"
|
|
139
|
+
seen += 1
|
|
140
|
+
assert seen >= 40
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def test_droplet_section_carries_the_commander_row_with_a_recommendation():
|
|
144
|
+
board = _run_board()
|
|
145
|
+
droplet = next(s for s in board["sessions"] if s["name"] == "droplet")
|
|
146
|
+
assert droplet["bucket"] == "needs_followup"
|
|
147
|
+
assert droplet["tone"] == "needs"
|
|
148
|
+
q = droplet["questions"][0]
|
|
149
|
+
assert "687" in q["title"] or "rebalancer" in q["title"]
|
|
150
|
+
rec = [opt for opt in q["options"] if opt["rec"]]
|
|
151
|
+
assert len(rec) == 1
|
|
152
|
+
assert rec[0]["label"] == "Retire"
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def test_filling_the_template_does_not_change_the_markup():
|
|
156
|
+
sys.path.insert(0, str(SCRIPT.parent))
|
|
157
|
+
import build_board
|
|
158
|
+
template = (SKILL / "assets" / "board-template.html").read_text(encoding="utf-8")
|
|
159
|
+
board = _run_board()
|
|
160
|
+
# the page does not read the sort bucket
|
|
161
|
+
public = json.loads(json.dumps(board))
|
|
162
|
+
for sess in public["sessions"]:
|
|
163
|
+
sess.pop("bucket", None)
|
|
164
|
+
filled = build_board.fill_template(template, public)
|
|
165
|
+
start = template.find('<script type="application/json" id="board-data">')
|
|
166
|
+
end = template.find("</script>", start)
|
|
167
|
+
assert template[:start] == filled[:start]
|
|
168
|
+
assert template[end:] == filled[filled.find("</script>", start):]
|
|
169
|
+
data = json.loads(filled[filled.find(">", start) + 1:filled.find("</script>", start)])
|
|
170
|
+
_validate(data)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def test_envelope_header_and_new_fields():
|
|
174
|
+
sys.path.insert(0, str(SCRIPT.parent))
|
|
175
|
+
import build_board
|
|
176
|
+
board = _run_board()
|
|
177
|
+
body = build_board.envelope(board)
|
|
178
|
+
header, payload = body.split("\n", 1)
|
|
179
|
+
assert header == "SWARPH-BOARD v1"
|
|
180
|
+
parsed = json.loads(payload)
|
|
181
|
+
_validate(parsed)
|
|
182
|
+
droplet = next(s for s in parsed["sessions"] if s["name"] == "droplet")
|
|
183
|
+
q = droplet["questions"][0]
|
|
184
|
+
assert q["to_node"] == "droplet"
|
|
185
|
+
assert q["card"] == 687
|
|
186
|
+
assert q["obligation"] == 170
|
|
187
|
+
assert q["in_session"] is False
|
|
188
|
+
assert "bucket" not in droplet
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def test_unchanged_board_sends_nothing():
|
|
192
|
+
sys.path.insert(0, str(SCRIPT.parent))
|
|
193
|
+
import build_board
|
|
194
|
+
board = _run_board()
|
|
195
|
+
sent = []
|
|
196
|
+
assert build_board.publish_if_changed(board, build_board.envelope(board), sent.append) is False
|
|
197
|
+
assert sent == []
|
|
198
|
+
# a clock-only difference is not a change
|
|
199
|
+
later = json.loads(json.dumps(board))
|
|
200
|
+
later["updated"] = "2099-01-01 00:00 UTC"
|
|
201
|
+
assert build_board.publish_if_changed(later, build_board.envelope(board), sent.append) is False
|
|
202
|
+
assert sent == []
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def test_a_changed_board_sends_the_envelope_once():
|
|
206
|
+
sys.path.insert(0, str(SCRIPT.parent))
|
|
207
|
+
import build_board
|
|
208
|
+
board = _run_board()
|
|
209
|
+
changed = json.loads(json.dumps(board))
|
|
210
|
+
droplet = next(s for s in changed["sessions"] if s["name"] == "droplet")
|
|
211
|
+
droplet["questions"][0]["title"] = "A different question"
|
|
212
|
+
sent = []
|
|
213
|
+
assert build_board.publish_if_changed(changed, build_board.envelope(board), sent.append) is True
|
|
214
|
+
assert len(sent) == 1
|
|
215
|
+
assert sent[0].startswith("SWARPH-BOARD v1\n")
|
|
216
|
+
assert "A different question" in sent[0]
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def test_deploy_accept_sets_in_session_and_every_question_has_to_node():
|
|
220
|
+
sys.path.insert(0, str(SCRIPT.parent))
|
|
221
|
+
import build_board
|
|
222
|
+
listing = (FIX / "listagents-lab.txt").read_text(encoding="utf-8")
|
|
223
|
+
rows = json.loads((FIX / "commander-170.json").read_text(encoding="utf-8"))
|
|
224
|
+
rows.append({
|
|
225
|
+
"cell": "droplet",
|
|
226
|
+
"card_id": 9001,
|
|
227
|
+
"obligation_id": 9001,
|
|
228
|
+
"title": "Production deploy of the board tab",
|
|
229
|
+
"accept": "The commander types the production deploy go in that session. This row is a hard gate.",
|
|
230
|
+
"options": [
|
|
231
|
+
{"label": "Go", "text": "go", "rec": True},
|
|
232
|
+
{"label": "Hold", "text": "hold", "rec": False},
|
|
233
|
+
],
|
|
234
|
+
})
|
|
235
|
+
board = build_board.build(listing, rows)
|
|
236
|
+
questions = []
|
|
237
|
+
for sess in board["sessions"]:
|
|
238
|
+
questions.extend(sess["questions"])
|
|
239
|
+
assert questions
|
|
240
|
+
assert all(q.get("to_node") for q in questions)
|
|
241
|
+
deploy = next(q for q in questions if q["title"] == "Production deploy of the board tab")
|
|
242
|
+
retire = next(q for q in questions if q["obligation"] == 170)
|
|
243
|
+
assert deploy["in_session"] is True
|
|
244
|
+
assert deploy["to_node"] == "droplet"
|
|
245
|
+
assert deploy["card"] == 9001
|
|
246
|
+
assert retire["in_session"] is False
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def test_an_answer_dm_drops_that_question():
|
|
250
|
+
sys.path.insert(0, str(SCRIPT.parent))
|
|
251
|
+
import build_board
|
|
252
|
+
rows = json.loads((FIX / "commander-170.json").read_text(encoding="utf-8"))
|
|
253
|
+
title = rows[0]["title"]
|
|
254
|
+
kept = build_board.drop_answered(
|
|
255
|
+
rows, [("commander", f"Re: {title}\nRetire.\n")], commander="commander")
|
|
256
|
+
assert kept == []
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def test_only_the_commander_can_drop_a_question():
|
|
260
|
+
"""A Re: line from any other cell must leave the question up.
|
|
261
|
+
|
|
262
|
+
On 61c5f49 drop_answered took bare bodies and dropped the row for any sender.
|
|
263
|
+
"""
|
|
264
|
+
sys.path.insert(0, str(SCRIPT.parent))
|
|
265
|
+
import build_board
|
|
266
|
+
rows = json.loads((FIX / "commander-170.json").read_text(encoding="utf-8"))
|
|
267
|
+
title = rows[0]["title"]
|
|
268
|
+
body = f"Re: {title}\nRetire.\n"
|
|
269
|
+
kept = build_board.drop_answered(rows, [("lab-ovh", body)], commander="commander")
|
|
270
|
+
assert len(kept) == 1
|
|
271
|
+
assert kept[0]["title"] == title
|
|
272
|
+
cleared = build_board.drop_answered(rows, [("commander", body)], commander="commander")
|
|
273
|
+
assert cleared == []
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def test_redeploy_is_in_session_and_deployment_notes_are_not():
|
|
277
|
+
sys.path.insert(0, str(SCRIPT.parent))
|
|
278
|
+
import build_board
|
|
279
|
+
assert build_board._in_session({"accept": "redeploy the gateway"}) is True
|
|
280
|
+
assert build_board._in_session({"accept": "deployment notes for the board"}) is False
|
|
281
|
+
assert build_board._in_session({"accept": "production deploy go"}) is True
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def test_skill_step_4_names_the_sender_and_the_trigger_words():
|
|
285
|
+
text = (SKILL / "SKILL.md").read_text(encoding="utf-8")
|
|
286
|
+
step = text.split("## 4. Answers", 1)[1].split("## ", 1)[0]
|
|
287
|
+
assert "from_node" in step
|
|
288
|
+
assert "commander" in step
|
|
289
|
+
assert "any other cell" in step
|
|
290
|
+
for word in ("deploy", "redeploy", "hard gate"):
|
|
291
|
+
assert word in step
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
"""#960: a fresh channel heartbeat is
|
|
1
|
+
"""#960: a fresh channel heartbeat is the reader, not the writer.
|
|
2
2
|
|
|
3
|
-
channel-serve writes channel_heartbeat.json
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
starts only when the file is absent or older than 180s.
|
|
3
|
+
channel-serve writes channel_heartbeat.json and only reads inbox.log. The
|
|
4
|
+
pull monitor is the only writer. A fresh heartbeat must not stop that
|
|
5
|
+
monitor from starting. A live monitor (status 0) still starts nothing.
|
|
7
6
|
"""
|
|
8
7
|
from __future__ import annotations
|
|
9
8
|
|
|
@@ -61,11 +60,14 @@ def _run(tmp_path: Path, *, age: float | None):
|
|
|
61
60
|
return proc, calls
|
|
62
61
|
|
|
63
62
|
|
|
64
|
-
def
|
|
63
|
+
def test_fresh_heartbeat_with_no_monitor_starts(tmp_path):
|
|
64
|
+
"""The shim exits 2, so no monitor is running. A 10s-old heartbeat
|
|
65
|
+
still has to start the pull monitor. On main this asserts the opposite.
|
|
66
|
+
"""
|
|
65
67
|
proc, calls = _run(tmp_path, age=10)
|
|
66
68
|
assert proc.returncode == 0
|
|
67
|
-
assert "monitor start"
|
|
68
|
-
assert "under 180s" in proc.stdout
|
|
69
|
+
assert "monitor start" in calls
|
|
70
|
+
assert "under 180s" not in proc.stdout
|
|
69
71
|
|
|
70
72
|
|
|
71
73
|
def test_stale_heartbeat_still_starts(tmp_path):
|
|
@@ -107,10 +109,10 @@ def test_a_future_timestamp_an_hour_ahead_starts(tmp_path):
|
|
|
107
109
|
assert "monitor start" in calls
|
|
108
110
|
|
|
109
111
|
|
|
110
|
-
def
|
|
112
|
+
def test_a_timestamp_within_skew_still_starts_when_no_monitor(tmp_path):
|
|
111
113
|
proc, calls = _run_payload(tmp_path, {"ts": time.time() + 10})
|
|
112
114
|
assert proc.returncode == 0
|
|
113
|
-
assert "monitor start"
|
|
115
|
+
assert "monitor start" in calls
|
|
114
116
|
|
|
115
117
|
|
|
116
118
|
def test_a_fresh_heartbeat_with_a_dead_pid_starts(tmp_path):
|
|
@@ -120,12 +122,8 @@ def test_a_fresh_heartbeat_with_a_dead_pid_starts(tmp_path):
|
|
|
120
122
|
assert "monitor start" in calls
|
|
121
123
|
|
|
122
124
|
|
|
123
|
-
def
|
|
124
|
-
"""A live channel-
|
|
125
|
-
|
|
126
|
-
Where /proc is missing the command line cannot be checked, so any live pid
|
|
127
|
-
still counts, which is what this host can see.
|
|
128
|
-
"""
|
|
125
|
+
def test_a_fresh_heartbeat_with_a_live_reader_pid_still_starts(tmp_path):
|
|
126
|
+
"""A live channel-serve pid is the reader. It does not stand in for the monitor."""
|
|
129
127
|
child = None
|
|
130
128
|
if os.path.isdir("/proc/self"):
|
|
131
129
|
child = subprocess.Popen(
|
|
@@ -140,7 +138,40 @@ def test_a_fresh_heartbeat_with_a_live_pid_skips(tmp_path):
|
|
|
140
138
|
child.kill()
|
|
141
139
|
child.wait(timeout=5)
|
|
142
140
|
assert proc.returncode == 0
|
|
141
|
+
assert "monitor start" in calls
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def test_fresh_heartbeat_and_a_live_monitor_starts_nothing(tmp_path):
|
|
145
|
+
"""Status 0 is the writer. A fresh heartbeat beside it must not start a second one."""
|
|
146
|
+
bindir = tmp_path / "bin"
|
|
147
|
+
bindir.mkdir()
|
|
148
|
+
shim = bindir / "swarph"
|
|
149
|
+
shim.write_text(
|
|
150
|
+
"#!/usr/bin/env bash\n"
|
|
151
|
+
"printf '%s\\n' \"$*\" >> \"$FAKE_LOG\"\n"
|
|
152
|
+
"case \"$*\" in\n"
|
|
153
|
+
" *'monitor status'*) exit 0 ;;\n"
|
|
154
|
+
" *) exit 2 ;;\n"
|
|
155
|
+
"esac\n",
|
|
156
|
+
encoding="utf-8",
|
|
157
|
+
)
|
|
158
|
+
shim.chmod(0o755)
|
|
159
|
+
log = tmp_path / "calls.log"
|
|
160
|
+
side = tmp_path / "state" / "mesh-sidecar"
|
|
161
|
+
side.mkdir(parents=True)
|
|
162
|
+
(side / "channel_heartbeat.json").write_text(
|
|
163
|
+
json.dumps({"ts": time.time()}), encoding="utf-8")
|
|
164
|
+
env = dict(os.environ)
|
|
165
|
+
env["PATH"] = f"{bindir}{os.pathsep}{env.get('PATH', '')}"
|
|
166
|
+
env["SWARPH_SELF"] = "cursor-lin"
|
|
167
|
+
env["SWARPH_STATE_DIR"] = str(tmp_path / "state")
|
|
168
|
+
env["FAKE_LOG"] = str(log)
|
|
169
|
+
proc = subprocess.run(
|
|
170
|
+
[_bash(), str(SCRIPT)], env=env, capture_output=True, text=True, timeout=60)
|
|
171
|
+
calls = log.read_text(encoding="utf-8") if log.exists() else ""
|
|
172
|
+
assert proc.returncode == 0
|
|
143
173
|
assert "monitor start" not in calls
|
|
174
|
+
assert "monitor status" in calls
|
|
144
175
|
|
|
145
176
|
|
|
146
177
|
def test_pid_zero_starts(tmp_path):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|