swarph-cli 0.72.14__tar.gz → 0.72.16__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.72.14/src/swarph_cli.egg-info → swarph_cli-0.72.16}/PKG-INFO +1 -1
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/pyproject.toml +1 -1
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/__init__.py +1 -1
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/board.py +45 -3
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/triage.py +16 -3
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/triage/backend.py +76 -21
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/triage/owners.json +2 -1
- {swarph_cli-0.72.14 → swarph_cli-0.72.16/src/swarph_cli.egg-info}/PKG-INFO +1 -1
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli.egg-info/SOURCES.txt +2 -0
- swarph_cli-0.72.16/tests/test_1073_triage_board.py +254 -0
- swarph_cli-0.72.16/tests/test_1075_task_cli.py +24 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/LICENSE +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/README.md +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/setup.cfg +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/__main__.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/__init__.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/backends.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/claude_cli.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/codex_cli.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/pack.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/prices.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/providers.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/quality.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/refresh_prices.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/rules.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/runner.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/signal_score.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/bench/validate.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/caller.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/capture/__init__.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/capture/harden.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/capture/lineage.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/capture/liveness.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/capture/manifest.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/capture/paths.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/capture/verify.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/cell.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/chain_token.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/channel.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/__init__.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/_content.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/_display.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/_gateway_client.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/add.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/bench.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/board_merge_check.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/brain.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/brain_ask.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/cell.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/cell_probe.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/channel.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/chat.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/codegraph.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/codegraph_hook.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/codex_hook_output.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/codex_waker.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/compress.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/daemon.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/dreaming.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/event.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/gateway.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/gh_route.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/group.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/guide.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/highlight.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/hook_interpreter.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/hook_output.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/hooks.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/import_session.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/init.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/install_hook.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/install_multiplexer.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/install_wake_hook.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/lane.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/mcp_server.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/memory.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/memory_sync.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/mesh.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/monitor.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/okf_links.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/onboard.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/protocol_handler.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/ratify.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/roster.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/rowclear_hook.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/schedule.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/security.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/service.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/spawn.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/sweep.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/timeline.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/version.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/wake_hook_output.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/commands/watchdog.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/compress/__init__.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/compress/levers.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/compress/marker.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/compress/verify.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/console_safe.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/delivery_queue.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dm_frame.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/__init__.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/candidates.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/clone.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/enrich.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/index_check.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/notify.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/organize.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/probes.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/report.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/run.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/slm.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/transcripts.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/dreaming/verify.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/gateway/__init__.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/gateway/feature_registry.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/gateway/lanes_control.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/gateway/schema.sql +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/gateway/server.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/gateway/services_control.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/gateway_default.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/gh_identity.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/guide/GUIDE.md +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/identity.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/main.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/multiplexer.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/operator/__init__.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/operator/__main__.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/operator/api.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/operator/cli.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/operator/config.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/operator/fmt.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/orphan_daemons.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/pane_probe.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/parsers/__init__.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/parsers/claude.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/peer_executor.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/peer_executor_windows.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/peer_reconciliation.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/peer_reply_outbox.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/peer_service_host.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/peer_service_payload.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/peer_service_reply.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/peer_staging.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/query_filters.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/rowclear.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/scripts/__init__.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/scrub.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/service/__init__.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/service/app.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/service/providers.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/session_bridge.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/source_text.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/stall_alert.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/codex-waker.default +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-stuck-sweep.service +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-stuck-sweep.timer +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-triage.service +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-triage.timer +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/timeline_paths.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/tokens.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/triage/__init__.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/triage/job.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/triage/router.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/triage/rows.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli/triage/sensor.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli.egg-info/entry_points.txt +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli.egg-info/requires.txt +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/src/swarph_cli.egg-info/top_level.txt +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1006_board_publisher.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1006_session_board.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1010_strand_only.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1013_spawn_channel.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1017_muse_tui.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1050_surrogate_wake.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1052_opencode_deaf.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1059_watchdog_unread_env.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1061_roster_cli.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1064_stuck_sweep.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1065_release.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1066_quiet_wakes.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1073_triage.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_1073_wheel_install.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_124_register_merge.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_126_pending_from_complete.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_132_ensure_monitor_state_dir.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_138_memory_navigate.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_139_tool_probe.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_183_card_anchor.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_184_notify_honesty.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_201_win32_bash_refusal.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_243_onboard_per_peer_token.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_307_task4_ask_reply.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_319_refusal_exit_codes.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_323_mistral_backend.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_332_report_organize_findings.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_356_query_filters.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_397_gh_identity_router.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_397_router_preflight.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_402_one_identity_resolver.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_432_rowclear.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_467b_onboard_operator_credential.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_472_register_caller_binding.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_484_codex_queue_sink.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_523_guide.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_524_conftest_hermeticity.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_532_accept_flag.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_535_version_capability.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_538_signal_bench.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_546_default_gateway.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_547_verbs_from_registry.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_548_gateway_remainder.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_552_codegraph_gateway.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_555_relay_row_shape.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_556_relay_freshness.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_559_edges_truncated.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_564_bootstrap_first_peer.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_579_register_bootstrap_message.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_591_cli_step_graph.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_644_monitor_supervision.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_648_install_unit.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_654_reexec.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_656_owned_slm.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_684_dreaming_verb.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_689_pkg_version_order.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_715_grok_wake.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_716_timeline_one_path.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_723_grok_0251.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_724_ps1_ascii_or_bom.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_725_stdio_reconfigure.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_726_grok_limit_screen.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_729_arm_check_delivery_gap.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_729_dm_once.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_729_wake_watchdog.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_734_dreaming_enrich_degrade.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_735_dm_mcp_tools.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_739_wake_writer.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_740_edit_project.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_762_cards_edit_priority.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_764_enrich_collapse.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_801_confirm_flow_cli.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_802_stage_history.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_825_codegraph_hook_noise.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_825_install_posttooluse_only.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_825_remaining_cap_shred_bind.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_829_counterfactual_column.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_830_hook_binding_version.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_84_cli_labels.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_866_doctrine_guide.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_872_identity_refuses_not_warns.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_874_source_text.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_937_dreaming_notify.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_938_organize_line_limit.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_941_bench_backends.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_941_harness_342.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_945_same_line_pairing.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_955_prompt_audit.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_957_auth_http.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_959_venv_interpreter.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_960_channel.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_961_cursor_grok_footer.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_961_loud_stop.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_963_plugin_manifest.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_964_channel_cell.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_965_mesh_wait.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_975_channel_hop.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_add_security_gate.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_artifact_add.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_artifact_hash.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_artifact_lib.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_artifact_mcp.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_artifact_skill.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_artifact_tool.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_artifact_uri.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_bench_backends.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_bench_claude_cli.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_bench_codex_cli.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_bench_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_bench_pack.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_bench_prices.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_bench_quality.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_bench_runner.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_bench_validate.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_board_card_thread.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_board_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_board_merge_check.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_board_obligation_close.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_brain_ask_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_brain_ask_env_diagnosis.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_brain_ask_identity_explicitness.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_brain_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_caller_meta_guard.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_capture_lineage.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_capture_liveness.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_capture_manifest.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_capture_paths.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_capture_security.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_cell_command_dispatch.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_cell_harden.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_cell_loader.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_cell_selfcheck.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_cell_verify.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_chain_token.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_channel_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_channel_poll_never_breaks_dms.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_channel_post.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_channel_wake_policy_filter.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_chat_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_claude_parser.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_claude_tmux_template.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_codegraph_caller_scope.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_codegraph_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_codegraph_hook.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_codegraph_mcp.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_codex_waker.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_compress_caller_convention.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_compress_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_compress_levers.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_compress_marker.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_compress_verify.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_console_encoding_e2e.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_content_composition.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_copilot_membrane.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_cursor_membrane.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_daemon_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_daemon_console_encoding.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_daemon_delivery.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_delivery_queue.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_display_sanitize.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_ensure_monitor_no_peer_default.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_event_emit.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_feature_to_uri.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_gateway_brain.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_gateway_client.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_gateway_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_gateway_message_idempotency.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_gh_route_doctor_mesh_name.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_group_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_guide_commands_parse.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_guide_selfcheck.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_heartbeat_check.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_highlight_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_hook_output.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_hooks_activity_marker.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_hooks_add.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_hooks_bundle.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_hooks_init.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_hooks_legacy_generation_removal.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_hooks_lifecycle.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_hooks_merge.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_hooks_verify.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_hooks_win32_bash_wrapper.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_hooks_win32_command_path.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_import_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_inbox_identity_provenance.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_init_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_init_provider_menu.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_install_codex_hooks.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_install_hook.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_install_multiplexer_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_install_postcompact_hook.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_install_wake_hook.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_lane_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_main.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_main_unknown_verb_guard.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_mcp_missing_hint.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_mcp_server.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_memory_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_memory_emit_snippet.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_memory_gateway.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_memory_sync.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_memory_sync_membrane.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_mesh_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_mesh_inbox_mark_read.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_mesh_inbox_sanitize.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_mesh_originate_thread_623.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_mesh_send_unknown_peer.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_mesh_sidecar.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_mesh_sidecar_muse_sink.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_meta_edge_identity.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_monitor_channel_poll.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_monitor_cli.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_monitor_daemon_collision.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_monitor_self_name_verification.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_monitor_state_split.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_multiplexer.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_muse_membrane.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_muse_own_cli.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_obligation_close_is_surfaced.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_okf_links.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_onboard_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_opencode_membrane.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_opencode_plugin.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_operator_me.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_orphan_daemons.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_packaged_artifacts_exist.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_packaged_skills.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_peer_executor.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_peer_executor_windows.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_peer_reply_drain_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_peer_reply_drain_supervisor.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_peer_reply_outbox.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_peer_reply_transport.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_peer_service_e2e.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_peer_service_host.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_peer_service_payload.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_peer_service_reply.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_postcompact_recall.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_process_liveness.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_protocol_handler.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_providers_isolation.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_ratify_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_readme_claims_no_gateway_default.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_repl_caller_convention.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_schedule_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_schedule_create_contract.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_scrub.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_security.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_service_app.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_service_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_service_providers.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_session_bridge_inject.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_session_bridge_probe.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_session_bridge_resolve.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_session_path_role_gate.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_shipped_units_match_the_cli.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_smoke_chat.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_smoke_one_shot.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_smoke_phase_5_5.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_spawn_base_hoist.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_spawn_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_spawn_identity_stamp.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_spawn_live_pin.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_spawn_mitosis_lineage.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_spawn_tmux_session.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_spawn_windows_relaunch.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_stall_alert.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_subprocess_decode_encoding.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_timeline_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_tmux_wake_runstate_defer.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_tmux_wake_session_reanchor.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_tmux_wake_stale.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_tmux_wake_submit_verify.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_token_file_one_parser.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_token_precedence_explicit_first.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_unified_token_resolver.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_version_command.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_version_consistency.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_vibe_membrane.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_watchdog.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_watchdog_dm_wake.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_watchdog_dm_wake_wiring.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_watchdog_emit_health.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_watchdog_install_flags.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_watchdog_model_rung.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_watchdog_stale_peers.py +0 -0
- {swarph_cli-0.72.14 → swarph_cli-0.72.16}/tests/test_windows_waker_installer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarph-cli
|
|
3
|
-
Version: 0.72.
|
|
3
|
+
Version: 0.72.16
|
|
4
4
|
Summary: The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`.
|
|
5
5
|
Author: Pierre Samson, Claude Opus
|
|
6
6
|
License: MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "swarph-cli"
|
|
7
|
-
version = "0.72.
|
|
7
|
+
version = "0.72.16"
|
|
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" }
|
|
@@ -631,7 +631,8 @@ def _step_list(values) -> list[str]:
|
|
|
631
631
|
|
|
632
632
|
|
|
633
633
|
def _ask_payload(self_name, what, *, holder=None, step=None, needs=None, hours=None,
|
|
634
|
-
done=None, accept=None, kind="action", timeout_hours=None
|
|
634
|
+
done=None, accept=None, kind="action", timeout_hours=None,
|
|
635
|
+
task=None, task_needs=None) -> dict:
|
|
635
636
|
"""POST /board/cards/{id}/ask body. None = not mentioned = key ABSENT (#191's
|
|
636
637
|
rule; test_532 pins that a null `accept` on the wire would overwrite the
|
|
637
638
|
gateway's own normalization). No holder → `requested`; `me` → the caller's
|
|
@@ -653,6 +654,10 @@ def _ask_payload(self_name, what, *, holder=None, step=None, needs=None, hours=N
|
|
|
653
654
|
p["accept"] = accept
|
|
654
655
|
if timeout_hours is not None:
|
|
655
656
|
p["timeout_hours"] = timeout_hours
|
|
657
|
+
if task is not None:
|
|
658
|
+
p["task"] = task
|
|
659
|
+
if task_needs is not None and _step_list(task_needs):
|
|
660
|
+
p["task_needs"] = _step_list(task_needs)
|
|
656
661
|
return p
|
|
657
662
|
|
|
658
663
|
|
|
@@ -680,6 +685,8 @@ def _ask_line(d) -> str:
|
|
|
680
685
|
return _format_ask(d)
|
|
681
686
|
n = d.get("id"); card = d.get("card_id")
|
|
682
687
|
step = _s(d.get("step")) or "unstepped"
|
|
688
|
+
if d.get("task"):
|
|
689
|
+
step = f"{step} {_s(d.get('task'))}"
|
|
683
690
|
holder = _s(d.get("holder")); take = _s(d.get("take_with"))
|
|
684
691
|
missing = ", ".join(_s(m) for m in d.get("still_missing") or []) # the gateway's field (mesh-gateway #157)
|
|
685
692
|
if state == "requested":
|
|
@@ -980,6 +987,17 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
980
987
|
ck.add_argument("--done", default=None, metavar="EVIDENCE",
|
|
981
988
|
help="mint + take + close in ONE act (needs --holder me); the "
|
|
982
989
|
"container the step names — a ref, >=40 words, or a verdict")
|
|
990
|
+
ck.add_argument("--task", default=None, metavar="task:<slug>",
|
|
991
|
+
help="task id, one token task:<slug> (letters, digits, dots, "
|
|
992
|
+
"underscores, hyphens). A pull-request task is "
|
|
993
|
+
"task:pr-<repo>-<digits>. pr:<repo>#<n> is refused. "
|
|
994
|
+
"Set at mint; amend cannot add it. Two live rows of one "
|
|
995
|
+
"step need different task ids. Omit it for the one live "
|
|
996
|
+
"unsplit row")
|
|
997
|
+
ck.add_argument("--task-needs", action="append", dest="task_needs", default=None,
|
|
998
|
+
metavar="task:<slug>",
|
|
999
|
+
help="sibling task id on this same step (repeatable). The "
|
|
1000
|
+
"gateway stores the edge and refuses a cycle")
|
|
983
1001
|
ck.add_argument("--json", action="store_true"); _add_common(ck)
|
|
984
1002
|
|
|
985
1003
|
cf = cards.add_parser(
|
|
@@ -1055,7 +1073,7 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
1055
1073
|
"list", help="list obligations — the READ half (#590): an obligation "
|
|
1056
1074
|
"that can be minted and closed but never read makes the "
|
|
1057
1075
|
"holder the only auditor")
|
|
1058
|
-
ol.add_argument("--status", choices=["open", "closed", "fallback_fired"])
|
|
1076
|
+
ol.add_argument("--status", choices=["open", "closed", "fallback_fired", "withdrawn"])
|
|
1059
1077
|
ol.add_argument("--holder", help="only obligations this peer owes")
|
|
1060
1078
|
ol.add_argument("--card", type=int, dest="card_id", help="only this card")
|
|
1061
1079
|
ol.add_argument("--overdue", action="store_true",
|
|
@@ -1092,6 +1110,15 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
1092
1110
|
help="the contract's signed spelling: `+step` adds, `-step` removes "
|
|
1093
1111
|
"(write the latter `--needs=-step`; a bare `-step` reads as a flag)")
|
|
1094
1112
|
oa.add_argument("--json", action="store_true"); _add_common(oa)
|
|
1113
|
+
ow = obl.add_parser(
|
|
1114
|
+
"withdraw",
|
|
1115
|
+
help="WITHDRAW an untaken row (asker, card assignee, or orchestrator). "
|
|
1116
|
+
"A taken row is closed or declined, not withdrawn. Status becomes "
|
|
1117
|
+
"withdrawn, which is neither pass nor fail")
|
|
1118
|
+
ow.add_argument("id", type=int, help="the obligation id")
|
|
1119
|
+
ow.add_argument("--reason", required=True,
|
|
1120
|
+
help="why the row is withdrawn; whitespace is refused")
|
|
1121
|
+
ow.add_argument("--json", action="store_true"); _add_common(ow)
|
|
1095
1122
|
return p
|
|
1096
1123
|
|
|
1097
1124
|
|
|
@@ -1194,6 +1221,20 @@ def run_board(argv: list[str]) -> int:
|
|
|
1194
1221
|
return 2
|
|
1195
1222
|
st, d = _patch_json(f"{gw}/board/obligations/{args.id}/amend", patch, token)
|
|
1196
1223
|
return _out(st, d, _obligation_act_line, aj)
|
|
1224
|
+
if args.command == "withdraw":
|
|
1225
|
+
reason = args.reason
|
|
1226
|
+
if not reason.strip():
|
|
1227
|
+
print("swarph board obligations withdraw: --reason is "
|
|
1228
|
+
"whitespace-only", file=sys.stderr)
|
|
1229
|
+
return 2
|
|
1230
|
+
st, d = _post_json(
|
|
1231
|
+
f"{gw}/board/obligations/{args.id}/withdraw",
|
|
1232
|
+
{"reason": reason}, token)
|
|
1233
|
+
return _out(st, d, lambda x: (
|
|
1234
|
+
f"obligation #{_s(x.get('id', args.id))} WITHDRAWN by "
|
|
1235
|
+
f"{_s(x.get('closed_by', self_name))}"
|
|
1236
|
+
+ (f" task {_s(x.get('task'))}" if x.get("task") else "")
|
|
1237
|
+
), aj)
|
|
1197
1238
|
|
|
1198
1239
|
if args.group == "cards":
|
|
1199
1240
|
if args.command == "list":
|
|
@@ -1350,7 +1391,8 @@ def run_board(argv: list[str]) -> int:
|
|
|
1350
1391
|
body = _ask_payload(self_name, args.what, holder=holder,
|
|
1351
1392
|
step=args.step, needs=args.needs, hours=args.hours,
|
|
1352
1393
|
done=args.done, accept=args.accept, kind=args.kind,
|
|
1353
|
-
timeout_hours=args.timeout_hours
|
|
1394
|
+
timeout_hours=args.timeout_hours,
|
|
1395
|
+
task=args.task, task_needs=args.task_needs)
|
|
1354
1396
|
st, d = _post_json(f"{gw}/board/cards/{args.id}/ask", body, token)
|
|
1355
1397
|
return _out(st, d, _ask_line, args.json)
|
|
1356
1398
|
if args.command == "confirm-flow":
|
|
@@ -212,6 +212,7 @@ def run_triage(*, gateway, token, sender, owners_map, state_file,
|
|
|
212
212
|
sent[key] = now_iso
|
|
213
213
|
return True
|
|
214
214
|
|
|
215
|
+
from swarph_cli.triage.backend import BackendError
|
|
215
216
|
from swarph_cli.triage.rows import TriageStore
|
|
216
217
|
store = TriageStore(store_backend)
|
|
217
218
|
if dry_run:
|
|
@@ -219,8 +220,14 @@ def run_triage(*, gateway, token, sender, owners_map, state_file,
|
|
|
219
220
|
print(f"DRY {record['source_organ']} {record['type']} "
|
|
220
221
|
f"sharp={record['sharp']} item={record['item_id']}")
|
|
221
222
|
return 0, {"dry_run_records": len(records)}
|
|
222
|
-
|
|
223
|
-
|
|
223
|
+
try:
|
|
224
|
+
summary = _job.route_and_store(store, owners_map, records,
|
|
225
|
+
dm_sender=dm_sender)
|
|
226
|
+
except BackendError as exc:
|
|
227
|
+
# A failed create or append names itself here and exits non-zero
|
|
228
|
+
# — never the bare 'accept line not added' assertion (#1446).
|
|
229
|
+
print(f"swarph triage: board write failed: {exc}", file=sys.stderr)
|
|
230
|
+
return 1, {"board_error": str(exc)}
|
|
224
231
|
_save_state(state_file, sent)
|
|
225
232
|
return 0, summary
|
|
226
233
|
|
|
@@ -262,6 +269,9 @@ def _build_parser():
|
|
|
262
269
|
p.add_argument("--owners-file", default=None,
|
|
263
270
|
help="local owner-map override (PROVEN calibration "
|
|
264
271
|
"writes here; deep-merges over shipped owners.json)")
|
|
272
|
+
p.add_argument("--project-id", type=int, default=None,
|
|
273
|
+
help="board project for triage-row cards (default: "
|
|
274
|
+
"project_id from the owner map, 9)")
|
|
265
275
|
p.add_argument("--json", action="store_true")
|
|
266
276
|
p.add_argument("--as", dest="self_name", default=None,
|
|
267
277
|
help="DM sender identity")
|
|
@@ -298,9 +308,12 @@ def run_triage_cmd(argv):
|
|
|
298
308
|
return 2
|
|
299
309
|
state_file = args.state_file or os.environ.get(
|
|
300
310
|
"SWARPH_SWEEP_STATE", _sweep._default_state_file())
|
|
311
|
+
project_id = (args.project_id if args.project_id is not None
|
|
312
|
+
else owners_map.get("project_id", 9))
|
|
301
313
|
backend = _backend.BoardBackend(
|
|
302
314
|
args.gateway, resolution.token, sender,
|
|
303
|
-
_mesh._http_get_json, _mesh._post_json
|
|
315
|
+
_mesh._http_get_json, _mesh._post_json,
|
|
316
|
+
project_id=project_id)
|
|
304
317
|
rc, summary = run_triage(
|
|
305
318
|
gateway=args.gateway, token=resolution.token, sender=sender,
|
|
306
319
|
owners_map=owners_map, state_file=state_file,
|
|
@@ -5,20 +5,36 @@ protocol; production rows live on board cards titled "<owner> triage".
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
7
|
|
|
8
|
+
class BackendError(RuntimeError):
|
|
9
|
+
"""A named board failure: what was attempted, the HTTP status, and
|
|
10
|
+
the gateway's own detail. Raised, never a row with id None."""
|
|
11
|
+
|
|
12
|
+
|
|
8
13
|
class MemoryBackend:
|
|
9
|
-
"""In-memory double for tests: rows are dicts, posts are dicts.
|
|
14
|
+
"""In-memory double for tests: rows are dicts, posts are dicts.
|
|
15
|
+
|
|
16
|
+
Strict where the gateway is strict (card #1073, #1446): a create
|
|
17
|
+
without a project_id is refused, and a post to a thread that does
|
|
18
|
+
not exist is refused — the old permissive double is what let the
|
|
19
|
+
0.72.14 live failure pass every test.
|
|
20
|
+
"""
|
|
10
21
|
|
|
11
|
-
def __init__(self):
|
|
22
|
+
def __init__(self, project_id=9):
|
|
12
23
|
self.rows: dict[str, dict] = {}
|
|
13
24
|
self.posts: dict[str, list[dict]] = {}
|
|
14
25
|
self.next_id = 1
|
|
26
|
+
self.project_id = project_id
|
|
15
27
|
|
|
16
28
|
def find_open_row(self, title):
|
|
17
29
|
return self.rows.get(title)
|
|
18
30
|
|
|
19
31
|
def create_row(self, title, owner):
|
|
32
|
+
if self.project_id is None:
|
|
33
|
+
raise BackendError(
|
|
34
|
+
f"create '{title}': project_id is required — the gateway "
|
|
35
|
+
f"refuses a card create without one (BoardCardCreate)")
|
|
20
36
|
row = {"id": self.next_id, "title": title, "owner": owner,
|
|
21
|
-
"status": "open"}
|
|
37
|
+
"project_id": self.project_id, "status": "open"}
|
|
22
38
|
self.next_id += 1
|
|
23
39
|
self.rows[title] = row
|
|
24
40
|
self.posts[title] = []
|
|
@@ -28,6 +44,10 @@ class MemoryBackend:
|
|
|
28
44
|
return list(self.posts.get(row["title"], []))
|
|
29
45
|
|
|
30
46
|
def append(self, row, body, kind="item"):
|
|
47
|
+
if row["title"] not in self.posts:
|
|
48
|
+
raise BackendError(
|
|
49
|
+
f"post to '{row['title']}': no such thread — the row was "
|
|
50
|
+
f"never created")
|
|
31
51
|
post = {"id": self.next_id, "body": body, "kind": kind}
|
|
32
52
|
self.next_id += 1
|
|
33
53
|
self.posts[row["title"]].append(post)
|
|
@@ -49,12 +69,26 @@ class BoardBackend:
|
|
|
49
69
|
gateway errors by returning None/[] (unevaluable, never invented).
|
|
50
70
|
"""
|
|
51
71
|
|
|
52
|
-
def __init__(self, gateway, token, sender, http_get, http_post
|
|
72
|
+
def __init__(self, gateway, token, sender, http_get, http_post,
|
|
73
|
+
project_id=9):
|
|
53
74
|
self.gateway = gateway.rstrip("/")
|
|
54
75
|
self.token = token
|
|
55
76
|
self.sender = sender
|
|
56
77
|
self._http_get = http_get
|
|
57
78
|
self._http_post = http_post
|
|
79
|
+
self.project_id = project_id
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def _detail(body) -> str:
|
|
83
|
+
if isinstance(body, dict):
|
|
84
|
+
detail = body.get("detail")
|
|
85
|
+
if isinstance(detail, str) and detail.strip():
|
|
86
|
+
return detail.strip()
|
|
87
|
+
return "<no detail>"
|
|
88
|
+
|
|
89
|
+
def _refused(self, what: str, status, body) -> BackendError:
|
|
90
|
+
return BackendError(
|
|
91
|
+
f"{what}: gateway refused {status}: {self._detail(body)}")
|
|
58
92
|
|
|
59
93
|
def find_open_row(self, title):
|
|
60
94
|
try:
|
|
@@ -76,31 +110,50 @@ class BoardBackend:
|
|
|
76
110
|
return None
|
|
77
111
|
|
|
78
112
|
def create_row(self, title, owner):
|
|
113
|
+
# BoardCardCreate REQUIRES project_id (card #1073, #1446: the
|
|
114
|
+
# 0.72.14 live run POSTed title+body only, was refused, and the
|
|
115
|
+
# old code carried the refusal forward as a row with id None).
|
|
116
|
+
# The owner is the card assignee, so the row lands in a queue.
|
|
117
|
+
payload = {"actor": self.sender, "project_id": self.project_id,
|
|
118
|
+
"title": title,
|
|
119
|
+
"body": f"Daily triage row for {owner} (card #1073). "
|
|
120
|
+
"Items are appended by the triage job; "
|
|
121
|
+
"owners work them off the row.",
|
|
122
|
+
"assignee": owner}
|
|
79
123
|
try:
|
|
80
124
|
status, body = self._http_post(
|
|
81
|
-
f"{self.gateway}/board/cards",
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
self.token)
|
|
87
|
-
except Exception:
|
|
88
|
-
return {"id": None, "title": title, "status": "unknown"}
|
|
125
|
+
f"{self.gateway}/board/cards", payload, self.token)
|
|
126
|
+
except Exception as exc:
|
|
127
|
+
raise BackendError(f"create '{title}': {exc}") from exc
|
|
128
|
+
if status is None or not 200 <= status < 300:
|
|
129
|
+
raise self._refused(f"create '{title}'", status, body)
|
|
89
130
|
card = body if isinstance(body, dict) else {}
|
|
131
|
+
if not isinstance(card.get("id"), int):
|
|
132
|
+
raise BackendError(
|
|
133
|
+
f"create '{title}': gateway replied {status} with no card "
|
|
134
|
+
f"id: {self._detail(body)}")
|
|
90
135
|
return {"id": card.get("id"), "title": title, "owner": owner,
|
|
91
136
|
"status": "open"}
|
|
92
137
|
|
|
93
138
|
def _thread_uuid(self, row):
|
|
94
139
|
if not row.get("id"):
|
|
95
|
-
|
|
140
|
+
raise BackendError(
|
|
141
|
+
f"post to '{row.get('title')}': the row has no board id — "
|
|
142
|
+
f"its create failed and was not reported")
|
|
96
143
|
try:
|
|
97
144
|
status, body = self._http_get(
|
|
98
145
|
f"{self.gateway}/board/cards/{row['id']}", self.token)
|
|
99
|
-
except Exception:
|
|
100
|
-
|
|
146
|
+
except Exception as exc:
|
|
147
|
+
raise BackendError(
|
|
148
|
+
f"thread of '{row.get('title')}': {exc}") from exc
|
|
101
149
|
if status != 200 or not isinstance(body, dict):
|
|
102
|
-
|
|
103
|
-
|
|
150
|
+
raise self._refused(f"thread of '{row.get('title')}'", status,
|
|
151
|
+
body)
|
|
152
|
+
thread_uuid = body.get("thread_uuid")
|
|
153
|
+
if not thread_uuid:
|
|
154
|
+
raise BackendError(
|
|
155
|
+
f"thread of '{row.get('title')}': card has no thread")
|
|
156
|
+
return thread_uuid
|
|
104
157
|
|
|
105
158
|
def list_posts(self, row):
|
|
106
159
|
if not row.get("id"):
|
|
@@ -121,8 +174,6 @@ class BoardBackend:
|
|
|
121
174
|
|
|
122
175
|
def append(self, row, body, kind="item"):
|
|
123
176
|
thread_uuid = self._thread_uuid(row)
|
|
124
|
-
if thread_uuid is None:
|
|
125
|
-
return {"id": None, "body": body, "kind": kind}
|
|
126
177
|
try:
|
|
127
178
|
status, resp = self._http_post(
|
|
128
179
|
f"{self.gateway}/messages",
|
|
@@ -131,7 +182,11 @@ class BoardBackend:
|
|
|
131
182
|
"kind": "answer" if kind == "security" else "fyi",
|
|
132
183
|
"content": body, "thread_id": thread_uuid},
|
|
133
184
|
self.token)
|
|
134
|
-
except Exception:
|
|
135
|
-
|
|
185
|
+
except Exception as exc:
|
|
186
|
+
raise BackendError(
|
|
187
|
+
f"append to '{row.get('title')}': {exc}") from exc
|
|
188
|
+
if status is None or not 200 <= status < 300:
|
|
189
|
+
raise self._refused(f"append to '{row.get('title')}'", status,
|
|
190
|
+
resp)
|
|
136
191
|
msg = resp if isinstance(resp, dict) else {}
|
|
137
192
|
return {"id": msg.get("id"), "body": body, "kind": kind}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 1,
|
|
3
|
-
"comment": "Organ triage owner map + calibration thresholds (card #1073, rulings #1413). DATA, not code: the router reads this file and contains no owner or threshold literals. Thresholds are written by a PROVEN calibration; until an organ has one, every model result for it is split. Owners keyed here route to '<owner> triage' rows; an organ with no entry routes to lab-ovh and is counted.",
|
|
3
|
+
"comment": "Organ triage owner map + calibration thresholds (card #1073, rulings #1413). DATA, not code: the router reads this file and contains no owner or threshold literals. Thresholds are written by a PROVEN calibration; until an organ has one, every model result for it is split. Owners keyed here route to '<owner> triage' rows; an organ with no entry routes to lab-ovh and is counted. project_id is the board project triage-row cards are created under (BoardCardCreate requires it; #1446).",
|
|
4
4
|
"owners": {
|
|
5
5
|
"stuck-work": {
|
|
6
6
|
"owner": null,
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"note": "pattern: detector calibration -> science-claude"
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
|
+
"project_id": 9,
|
|
22
23
|
"thresholds": {},
|
|
23
24
|
"label_sets": {
|
|
24
25
|
"stuck-work": ["real-stall", "deliberate-wait", "needed-row-closed-race"],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarph-cli
|
|
3
|
-
Version: 0.72.
|
|
3
|
+
Version: 0.72.16
|
|
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
|
|
@@ -216,7 +216,9 @@ tests/test_1064_stuck_sweep.py
|
|
|
216
216
|
tests/test_1065_release.py
|
|
217
217
|
tests/test_1066_quiet_wakes.py
|
|
218
218
|
tests/test_1073_triage.py
|
|
219
|
+
tests/test_1073_triage_board.py
|
|
219
220
|
tests/test_1073_wheel_install.py
|
|
221
|
+
tests/test_1075_task_cli.py
|
|
220
222
|
tests/test_124_register_merge.py
|
|
221
223
|
tests/test_126_pending_from_complete.py
|
|
222
224
|
tests/test_132_ensure_monitor_state_dir.py
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"""card #1073 rework (#1446): the router's first live run died on the
|
|
2
|
+
board it never wrote to in tests.
|
|
3
|
+
|
|
4
|
+
0.72.14 POSTed a card create with title+body only; the gateway refused
|
|
5
|
+
it (BoardCardCreate requires project_id); the old code carried the
|
|
6
|
+
refusal forward as a row with id None and died on the bare 'accept
|
|
7
|
+
line not added' assertion. These tests pin the fix: the create sends
|
|
8
|
+
project_id + assignee, every non-2xx names its status and detail, the
|
|
9
|
+
run exits non-zero saying so, and the double rejects what the gateway
|
|
10
|
+
rejects (a create without project_id, a post to a missing thread).
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import pytest
|
|
15
|
+
|
|
16
|
+
from swarph_cli.commands import triage as _triage_cmd
|
|
17
|
+
from swarph_cli.triage import backend as _backend
|
|
18
|
+
from swarph_cli.triage import router as _router
|
|
19
|
+
|
|
20
|
+
NOW = 1_790_000_000.0
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _iso(age_s):
|
|
24
|
+
from datetime import datetime, timezone
|
|
25
|
+
return datetime.fromtimestamp(NOW - age_s, timezone.utc).isoformat()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class _FakeHttp:
|
|
29
|
+
"""Records bodies; scripted (status, body) per URL suffix.
|
|
30
|
+
|
|
31
|
+
Enforces caller-binding like the gateway (card #1073, #1451):
|
|
32
|
+
POST /board/cards requires actor == the sender, POST /messages
|
|
33
|
+
requires from_node == the sender; anything else is refused 403
|
|
34
|
+
with the gateway's shape. Enforcement runs before the scripted
|
|
35
|
+
responses, so a mutant dropping the identity fails every test.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, sender="lab-ovh"):
|
|
39
|
+
self.posts = []
|
|
40
|
+
self.scripts = {}
|
|
41
|
+
self.sender = sender
|
|
42
|
+
self.card_threads = {}
|
|
43
|
+
self.thread_messages = {}
|
|
44
|
+
self.next_id = 501
|
|
45
|
+
|
|
46
|
+
def on_post(self, suffix, status, body):
|
|
47
|
+
self.scripts[("POST", suffix)] = (status, body)
|
|
48
|
+
|
|
49
|
+
def on_get(self, suffix, status, body):
|
|
50
|
+
self.scripts[("GET", suffix)] = (status, body)
|
|
51
|
+
|
|
52
|
+
def _binding_refusal(self, field, claimed):
|
|
53
|
+
return (403, {"detail":
|
|
54
|
+
f"caller-binding: authenticated peer {self.sender!r} "
|
|
55
|
+
f"may not act as {field}={claimed!r}"})
|
|
56
|
+
|
|
57
|
+
def post(self, url, payload, token):
|
|
58
|
+
self.posts.append((url, dict(payload)))
|
|
59
|
+
if url.endswith("/board/cards"):
|
|
60
|
+
actor = payload.get("actor")
|
|
61
|
+
if actor != self.sender:
|
|
62
|
+
return self._binding_refusal("actor", actor)
|
|
63
|
+
if url.endswith("/messages"):
|
|
64
|
+
sender = payload.get("from_node")
|
|
65
|
+
if sender != self.sender:
|
|
66
|
+
return self._binding_refusal("from_node", sender)
|
|
67
|
+
for (method, suffix), response in self.scripts.items():
|
|
68
|
+
if method == "POST" and url.endswith(suffix):
|
|
69
|
+
status, body = response
|
|
70
|
+
if url.endswith("/messages") and status is not None and (
|
|
71
|
+
200 <= status < 300):
|
|
72
|
+
# Like the gateway, a thread-bound post lands in the
|
|
73
|
+
# card thread a later GET .../thread reads back.
|
|
74
|
+
self.thread_messages.setdefault(
|
|
75
|
+
payload.get("thread_id"), []).append(
|
|
76
|
+
{"id": self.next_id,
|
|
77
|
+
"content": payload.get("content"),
|
|
78
|
+
"kind": payload.get("kind")})
|
|
79
|
+
self.next_id += 1
|
|
80
|
+
return response
|
|
81
|
+
raise AssertionError(f"unscripted POST {url}")
|
|
82
|
+
|
|
83
|
+
def get(self, url, token):
|
|
84
|
+
import re
|
|
85
|
+
thread = re.search(r"/board/cards/(\d+)/thread$", url)
|
|
86
|
+
if thread:
|
|
87
|
+
uuid = self.card_threads.get(thread.group(1))
|
|
88
|
+
return 200, {"card_id": int(thread.group(1)),
|
|
89
|
+
"messages": list(
|
|
90
|
+
self.thread_messages.get(uuid, []))}
|
|
91
|
+
for (method, suffix), response in self.scripts.items():
|
|
92
|
+
if method == "GET" and url.endswith(suffix):
|
|
93
|
+
card = re.search(r"/board/cards/(\d+)$", url)
|
|
94
|
+
if card and isinstance(response[1], dict):
|
|
95
|
+
uuid = response[1].get("thread_uuid")
|
|
96
|
+
if uuid:
|
|
97
|
+
self.card_threads[card.group(1)] = uuid
|
|
98
|
+
return response
|
|
99
|
+
raise AssertionError(f"unscripted GET {url}")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _board(project_id=9):
|
|
103
|
+
http = _FakeHttp()
|
|
104
|
+
return _backend.BoardBackend("http://gw:9", "tok", "lab-ovh",
|
|
105
|
+
http.get, http.post,
|
|
106
|
+
project_id=project_id), http
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def test_create_sends_project_id_and_assignee():
|
|
110
|
+
backend, http = _board(project_id=9)
|
|
111
|
+
http.on_post("/board/cards", 200, {"id": 77})
|
|
112
|
+
row = backend.create_row("lab-ovh triage", "lab-ovh")
|
|
113
|
+
assert row["id"] == 77
|
|
114
|
+
_url, payload = http.posts[-1]
|
|
115
|
+
assert payload["project_id"] == 9
|
|
116
|
+
assert payload["assignee"] == "lab-ovh"
|
|
117
|
+
assert payload["title"] == "lab-ovh triage"
|
|
118
|
+
assert payload["actor"] == "lab-ovh"
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def test_actor_none_is_refused_like_the_live_run():
|
|
122
|
+
"""The 0.72.15 shape: no actor identity on the create is a 403
|
|
123
|
+
naming caller-binding, not a silent row."""
|
|
124
|
+
backend, http = _board()
|
|
125
|
+
backend.sender = None
|
|
126
|
+
with pytest.raises(_backend.BackendError) as exc:
|
|
127
|
+
backend.create_row("lab-ovh triage", "lab-ovh")
|
|
128
|
+
assert "403" in str(exc.value) and "caller-binding" in str(exc.value)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def test_append_without_sender_is_refused():
|
|
132
|
+
backend, http = _board()
|
|
133
|
+
http.on_get("/board/cards/77", 200, {"id": 77,
|
|
134
|
+
"thread_uuid": "thread-1"})
|
|
135
|
+
backend.sender = None
|
|
136
|
+
row = {"id": 77, "title": "lab-ovh triage", "owner": "lab-ovh",
|
|
137
|
+
"status": "open"}
|
|
138
|
+
with pytest.raises(_backend.BackendError) as exc:
|
|
139
|
+
backend.append(row, "ACCEPT x y")
|
|
140
|
+
assert "403" in str(exc.value) and "from_node" in str(exc.value)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def test_create_422_names_status_and_detail():
|
|
144
|
+
backend, http = _board()
|
|
145
|
+
http.on_post("/board/cards", 422, {"detail": "project_id: field required"})
|
|
146
|
+
with pytest.raises(_backend.BackendError) as exc:
|
|
147
|
+
backend.create_row("lab-ovh triage", "lab-ovh")
|
|
148
|
+
assert "422" in str(exc.value)
|
|
149
|
+
assert "project_id: field required" in str(exc.value)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def test_append_422_names_status_and_detail():
|
|
153
|
+
backend, http = _board()
|
|
154
|
+
http.on_get("/board/cards/77", 200, {"id": 77,
|
|
155
|
+
"thread_uuid": "thread-1"})
|
|
156
|
+
http.on_post("/messages", 422, {"detail": "unknown thread"})
|
|
157
|
+
row = {"id": 77, "title": "lab-ovh triage", "owner": "lab-ovh",
|
|
158
|
+
"status": "open"}
|
|
159
|
+
with pytest.raises(_backend.BackendError) as exc:
|
|
160
|
+
backend.append(row, "ACCEPT x y")
|
|
161
|
+
assert "422" in str(exc.value) and "unknown thread" in str(exc.value)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def test_failed_create_is_never_a_row_with_id_none():
|
|
165
|
+
backend, http = _board()
|
|
166
|
+
http.on_post("/board/cards", 500, {"detail": "boom"})
|
|
167
|
+
with pytest.raises(_backend.BackendError):
|
|
168
|
+
backend.create_row("lab-ovh triage", "lab-ovh")
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def test_memory_double_rejects_create_without_project_id():
|
|
172
|
+
backend = _backend.MemoryBackend(project_id=None)
|
|
173
|
+
with pytest.raises(_backend.BackendError) as exc:
|
|
174
|
+
backend.create_row("lab-ovh triage", "lab-ovh")
|
|
175
|
+
assert "project_id" in str(exc.value)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def test_memory_double_rejects_post_to_missing_thread():
|
|
179
|
+
backend = _backend.MemoryBackend()
|
|
180
|
+
with pytest.raises(_backend.BackendError) as exc:
|
|
181
|
+
backend.append({"id": 999, "title": "ghost triage",
|
|
182
|
+
"owner": "lab-ovh", "status": "open"}, "ACCEPT x y")
|
|
183
|
+
assert "no such thread" in str(exc.value)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _board_fixture():
|
|
187
|
+
old = _iso(10 * 86400)
|
|
188
|
+
waiter = {"id": 1, "status": "open", "taken_at": old, "step": "build",
|
|
189
|
+
"card_id": 7, "holder": "waiter"}
|
|
190
|
+
dep = {"id": 2, "status": "open", "holder": "lab-ovh",
|
|
191
|
+
"holder_known": True, "created_at": old, "card_id": 8}
|
|
192
|
+
return [waiter], {1: waiter, 2: dep}, {"lab-ovh"}
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _live_http():
|
|
196
|
+
http = _FakeHttp()
|
|
197
|
+
http.on_get("/board/cards/7/graph", 200, {"steps": [
|
|
198
|
+
{"step": "build", "needs": [
|
|
199
|
+
{"step": "dep", "row_id": 2, "satisfied": False}]}]})
|
|
200
|
+
http.on_get("/board/cards/8", 200, {"id": 8, "due_at": None})
|
|
201
|
+
return http
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def test_run_names_the_refusal_and_exits_nonzero(capsys, tmp_path):
|
|
205
|
+
"""The 0.72.14 shape end to end: the create is refused, the run
|
|
206
|
+
says so (status + detail) and exits 1 — no bare assertion."""
|
|
207
|
+
backend, http = _board()
|
|
208
|
+
http.on_post("/board/cards", 422,
|
|
209
|
+
{"detail": "project_id: field required"})
|
|
210
|
+
live = _live_http()
|
|
211
|
+
rows, by_id, peers = _board_fixture()
|
|
212
|
+
rc, summary = _triage_cmd.run_triage(
|
|
213
|
+
gateway="http://gw:9", token="tok", sender="lab-ovh",
|
|
214
|
+
owners_map=_router.load_owners(_triage_cmd._OWNERS_DEFAULT),
|
|
215
|
+
state_file=str(tmp_path / "state.json"), store_backend=backend,
|
|
216
|
+
peers_fetcher=None, board_fetcher=lambda: (rows, by_id, peers),
|
|
217
|
+
http_get=live.get, http_post=http.post, now=NOW)
|
|
218
|
+
err = capsys.readouterr().err
|
|
219
|
+
assert rc == 1
|
|
220
|
+
assert "422" in err and "project_id: field required" in err
|
|
221
|
+
assert "accept line not added" not in err
|
|
222
|
+
assert "board_error" in summary
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def test_run_dm_carries_sender_identity(tmp_path):
|
|
226
|
+
"""Third write path (card #1073, #1451): the one-a-day DM POSTs
|
|
227
|
+
/messages with from_node == the sender. Caller-binding enforcement
|
|
228
|
+
is active on the fake, so a mutant dropping the identity fails
|
|
229
|
+
here (append raises first) or starves dm_sent (DM refused)."""
|
|
230
|
+
http = _FakeHttp()
|
|
231
|
+
http.on_get("/board/cards", 200, {"cards": []})
|
|
232
|
+
http.on_get("/board/cards/7/graph", 200, {"steps": [
|
|
233
|
+
{"step": "build", "needs": [
|
|
234
|
+
{"step": "dep", "row_id": 2, "satisfied": False}]}]})
|
|
235
|
+
http.on_get("/board/cards/8", 200, {"id": 8, "due_at": None})
|
|
236
|
+
http.on_get("/board/cards/77", 200, {"id": 77,
|
|
237
|
+
"thread_uuid": "thread-1"})
|
|
238
|
+
http.on_post("/board/cards", 200, {"id": 77})
|
|
239
|
+
http.on_post("/messages", 200, {"id": 501})
|
|
240
|
+
backend = _backend.BoardBackend(
|
|
241
|
+
"http://gw:9", "tok", "lab-ovh", http.get, http.post,
|
|
242
|
+
project_id=9)
|
|
243
|
+
rows, by_id, peers = _board_fixture()
|
|
244
|
+
rc, summary = _triage_cmd.run_triage(
|
|
245
|
+
gateway="http://gw:9", token="tok", sender="lab-ovh",
|
|
246
|
+
owners_map=_router.load_owners(_triage_cmd._OWNERS_DEFAULT),
|
|
247
|
+
state_file=str(tmp_path / "state.json"), store_backend=backend,
|
|
248
|
+
peers_fetcher=None, board_fetcher=lambda: (rows, by_id, peers),
|
|
249
|
+
http_get=http.get, http_post=http.post, now=NOW)
|
|
250
|
+
assert rc == 0
|
|
251
|
+
assert summary["dm_sent"] == 1
|
|
252
|
+
message_posts = [p for u, p in http.posts if u.endswith("/messages")]
|
|
253
|
+
assert message_posts, "no /messages write happened at all"
|
|
254
|
+
assert all(p.get("from_node") == "lab-ovh" for p in message_posts)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""card #1075 — the ask carries a task id, and withdraw is its own act."""
|
|
2
|
+
from swarph_cli.commands import board
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def test_ask_payload_sends_task_and_omits_it_when_absent():
|
|
6
|
+
p = board._ask_payload(
|
|
7
|
+
"cursor-lin", "wire it", holder="me", step="build",
|
|
8
|
+
task="task:pr-lab-orchestrator-160",
|
|
9
|
+
task_needs=["task:pr-lab-orchestrator-159"])
|
|
10
|
+
assert p["task"] == "task:pr-lab-orchestrator-160"
|
|
11
|
+
assert p["task_needs"] == ["task:pr-lab-orchestrator-159"]
|
|
12
|
+
assert p["holder"] == "cursor-lin"
|
|
13
|
+
bare = board._ask_payload("cursor-lin", "wire it", step="build")
|
|
14
|
+
assert "task" not in bare and "task_needs" not in bare
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_withdraw_is_a_command():
|
|
18
|
+
parser = board._build_parser()
|
|
19
|
+
args = parser.parse_args(
|
|
20
|
+
["obligations", "withdraw", "1422", "--reason", "asker pulls it"])
|
|
21
|
+
assert args.group == "obligations"
|
|
22
|
+
assert args.command == "withdraw"
|
|
23
|
+
assert args.id == 1422
|
|
24
|
+
assert args.reason == "asker pulls it"
|