ctrlrelay 0.20.1__tar.gz → 0.20.2__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.
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/CHANGELOG.md +13 -1
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/PKG-INFO +1 -1
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/pyproject.toml +1 -1
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/usage.py +10 -2
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_usage.py +24 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/.github/dependabot.yml +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/.github/workflows/build.yml +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/.github/workflows/cla.yml +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/.github/workflows/pages.yml +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/.github/workflows/publish.yml +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/.github/workflows/test.yml +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/.gitignore +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/CODE_OF_CONDUCT.md +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/CONTRIBUTING.md +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/LICENSE +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/README.md +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/SECURITY.md +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/config/orchestrator.yaml.example +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/docs/Gemfile +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/docs/_config.yml +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/docs/adding-a-machine.md +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/docs/architecture.md +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/docs/bridge.md +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/docs/cli.md +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/docs/configuration.md +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/docs/development.md +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/docs/feedback-loop.md +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/docs/getting-started.md +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/docs/index.md +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/docs/llms.txt +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/docs/operations.md +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/docs/personalization.md +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/docs/registered-mode.md +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/docs/superpowers/plans/2026-10-30-status-page.md +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/docs/superpowers/specs/2026-10-30-status-page-design.md +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/__init__.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/bridge/__init__.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/bridge/__main__.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/bridge/factory.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/bridge/handler.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/bridge/mattermost_handler.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/bridge/node_tag.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/bridge/protocol.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/bridge/server.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/bridge/telegram_handler.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/cli.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/__init__.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/agent_env.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/archived.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/audit.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/binaries.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/checkpoint.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/claims.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/code_review.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/config.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/dispatch_health.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/dispatcher.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/events.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/github.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/github_budget.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/heartbeat.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/job_claims.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/network.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/node_check.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/obs.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/orphans.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/poller.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/pr_verifier.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/pr_watcher.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/publish.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/question_expiry.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/redact.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/scheduler.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/state.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/core/worktree.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/dashboard/__init__.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/dashboard/client.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/gh_protocol.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/install.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/personalization/__init__.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/personalization/manager.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/personalization/merge.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/personalization/paths.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/pipelines/__init__.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/pipelines/base.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/pipelines/dev.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/pipelines/post_merge.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/pipelines/secops.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/pipelines/task.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/setup.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/templates/__init__.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/templates/global-CLAUDE.md.snippet +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/templates/launchd/__init__.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/templates/launchd/bridge.plist.template +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/templates/launchd/poller.plist.template +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/templates/systemd/__init__.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/templates/systemd/bridge.service.template +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/templates/systemd/poller.service.template +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/templates/systemd/web.service.template +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/transports/__init__.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/transports/base.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/transports/file_mock.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/transports/socket_client.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/web/__init__.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/web/fleet.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/web/health.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/web/links.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/web/numbers.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/web/server.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/web/static/index.html +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/web/status.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/web/transcript.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/web/waiting.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/__init__.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/_poller_fakes.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/_publish_fakes.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/conftest.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/fixtures/gh_search_issues.json +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/fixtures/stream_json_rate_limit.jsonl +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/fixtures/transcript_sample.jsonl +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_agent_backend_wiring.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_agent_no_keys.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_agent_routing.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_agent_sandbox.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_archived.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_audit.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_binary_resolution.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_bridge_protocol.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_bridge_server.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_bridge_start_command.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_chat_machines.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_checkpoint.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_claim_reconciliation.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_cli_ci_wait.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_cli_dev.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_cli_repos.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_cli_secops.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_cli_start.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_cli_version.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_code_review.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_config.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_dashboard_client.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_dev_integration.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_dev_pipeline.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_dev_plan_step.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_dev_step_switches.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_dev_work_steps.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_dispatch_health.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_dispatch_outcome_hook.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_dispatcher.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_docs_site.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_event_emission.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_events.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_fleet.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_gh_protocol.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_github.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_github_budget.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_install.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_issue_outcome_announcement.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_job_claims.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_mattermost_handler.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_network.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_node_check.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_node_claims.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_notify_transport.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_obs.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_opencode_adapter.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_orphans.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_personalization.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_personalization_merge.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_pipeline_base.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_poller.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_poller_recheck.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_poller_search.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_post_merge.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_pr_verifier.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_pr_watcher.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_pre_dispatch_retry.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_publish_failure_retry.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_question_backoff.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_question_expiry.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_question_survives_restart.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_redact.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_relay_alert_off_lock.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_relay_git_263.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_resume_prompt_contract.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_review_copy.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_scheduler.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_secops_integration.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_secops_pipeline.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_setup.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_state.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_task_pipeline.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_telegram_handler.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_timeout_save.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_transport.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_transport_resolution.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_unreviewed_pr_announcement.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_web_health.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_web_links.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_web_numbers.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_web_peer_links.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_web_server.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_web_status.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_web_transcript.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_web_waiting.py +0 -0
- {ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/tests/test_worktree.py +0 -0
|
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.20.2] - 2026-11-04
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- A node no longer stops taking work when the agent CLI reports its usage
|
|
15
|
+
status as `allowed_warning`. That status means a usage window has passed
|
|
16
|
+
a warning line, and calls are still served; it was treated as a refusal,
|
|
17
|
+
so nodes at 26% of the 7-day window paused until that window reset, a
|
|
18
|
+
week away. How close a window is to its limit is still decided by the
|
|
19
|
+
pause threshold, and any other status still pauses (#338).
|
|
20
|
+
|
|
10
21
|
## [0.20.1] - 2026-11-04
|
|
11
22
|
|
|
12
23
|
### Fixed
|
|
@@ -2348,7 +2359,8 @@ pipeline).
|
|
|
2348
2359
|
per-phase implementation plans (Phase 0 through Phase 4).
|
|
2349
2360
|
- `docs/Claude_Code_Project_Guide.md` — project development guide.
|
|
2350
2361
|
|
|
2351
|
-
[Unreleased]: https://github.com/AInvirion/ctrlrelay/compare/v0.20.
|
|
2362
|
+
[Unreleased]: https://github.com/AInvirion/ctrlrelay/compare/v0.20.2...HEAD
|
|
2363
|
+
[0.20.2]: https://github.com/AInvirion/ctrlrelay/compare/v0.20.1...v0.20.2
|
|
2352
2364
|
[0.20.1]: https://github.com/AInvirion/ctrlrelay/compare/v0.20.0...v0.20.1
|
|
2353
2365
|
[0.20.0]: https://github.com/AInvirion/ctrlrelay/compare/v0.19.1...v0.20.0
|
|
2354
2366
|
[0.19.1]: https://github.com/AInvirion/ctrlrelay/compare/v0.19.0...v0.19.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ctrlrelay
|
|
3
|
-
Version: 0.20.
|
|
3
|
+
Version: 0.20.2
|
|
4
4
|
Summary: Local-first orchestrator for headless coding agents across multiple GitHub repos
|
|
5
5
|
Project-URL: Homepage, https://github.com/AInvirion/ctrlrelay
|
|
6
6
|
Project-URL: Documentation, https://ainvirion.github.io/ctrlrelay/
|
|
@@ -43,6 +43,14 @@ UNKNOWN_RESET_PAUSE_SECONDS = 3600
|
|
|
43
43
|
# recursion limit (RecursionError). Any of them is unreadable, not fatal.
|
|
44
44
|
_UNPARSEABLE = (ValueError, RecursionError)
|
|
45
45
|
ALLOWED = "allowed"
|
|
46
|
+
# Statuses that still let a run through. The agent CLI reports
|
|
47
|
+
# ``allowed_warning`` once a window passes a warning line well short of
|
|
48
|
+
# its limit (seen at 26% of the 7-day window), and the call is served.
|
|
49
|
+
# Pausing on it idled machines for the rest of the 7-day window, so it
|
|
50
|
+
# counts as allowed; how close a window is to its limit is the
|
|
51
|
+
# threshold's question, not the status's. Any other status, including
|
|
52
|
+
# one not seen before, still pauses.
|
|
53
|
+
RUNNABLE_STATUSES = frozenset({ALLOWED, "allowed_warning"})
|
|
46
54
|
|
|
47
55
|
|
|
48
56
|
@dataclass(frozen=True)
|
|
@@ -225,7 +233,7 @@ def pause_for(
|
|
|
225
233
|
) -> Pause | None:
|
|
226
234
|
"""Whether to stop taking new work.
|
|
227
235
|
|
|
228
|
-
Paused when the status is not ``
|
|
236
|
+
Paused when the status is not one of ``RUNNABLE_STATUSES`` or a window is at or above
|
|
229
237
|
``threshold``, until that window's reset (for the status, the event's
|
|
230
238
|
own reset). A reading whose reset time has passed says nothing about
|
|
231
239
|
now, so it does not pause. When the reset time is missing or cannot
|
|
@@ -243,7 +251,7 @@ def pause_for(
|
|
|
243
251
|
# From the reading, or from now if the reading claims to be from the
|
|
244
252
|
# future (a clock set back): an hour, never longer.
|
|
245
253
|
fallback = int(min(reading.read_at, now)) + UNKNOWN_RESET_PAUSE_SECONDS
|
|
246
|
-
if reading.status
|
|
254
|
+
if reading.status not in RUNNABLE_STATUSES:
|
|
247
255
|
until = reading.resets_at if reading.resets_at is not None else fallback
|
|
248
256
|
hits.append((f"usage status is {reading.status}", until))
|
|
249
257
|
for name, w in reading.windows.items():
|
|
@@ -110,6 +110,30 @@ class TestPause:
|
|
|
110
110
|
p = pause_for(_reading(status="rejected"), threshold=0.9, now=NOW)
|
|
111
111
|
assert p is not None and p.until == NOW + 3600
|
|
112
112
|
|
|
113
|
+
def test_allowed_warning_below_the_threshold_does_not_pause(self) -> None:
|
|
114
|
+
# The usage.json alkamax wrote on 2026-11-04: a warning status at
|
|
115
|
+
# 26% of the 7-day window, which had paused it for a week.
|
|
116
|
+
r = UsageReading.from_json(json.loads(
|
|
117
|
+
'{"status": "allowed_warning", "read_at": 1793857204, '
|
|
118
|
+
'"resets_at": 1794412800, '
|
|
119
|
+
'"seven_day": {"used": 0.26, "resets_at": 1794412800}}'
|
|
120
|
+
))
|
|
121
|
+
assert r is not None and r.status == "allowed_warning"
|
|
122
|
+
assert pause_for(r, threshold=0.9, now=1793857204) is None
|
|
123
|
+
|
|
124
|
+
def test_allowed_warning_at_the_threshold_still_pauses(self) -> None:
|
|
125
|
+
p = pause_for(
|
|
126
|
+
_reading(status="allowed_warning",
|
|
127
|
+
windows={"seven_day": Window(0.95, NOW + 86400)}),
|
|
128
|
+
threshold=0.9, now=NOW,
|
|
129
|
+
)
|
|
130
|
+
assert p is not None and p.until == NOW + 86400
|
|
131
|
+
assert "status" not in p.reason
|
|
132
|
+
|
|
133
|
+
@pytest.mark.parametrize("status", ["rejected", "allowed_later", "", "ALLOWED"])
|
|
134
|
+
def test_any_other_status_pauses(self, status: str) -> None:
|
|
135
|
+
assert pause_for(_reading(status=status), threshold=0.9, now=NOW) is not None
|
|
136
|
+
|
|
113
137
|
def test_after_the_reset_the_old_reading_does_not_pause(self) -> None:
|
|
114
138
|
r = _reading(windows={"five_hour": Window(0.99, NOW + 600)})
|
|
115
139
|
assert pause_for(r, threshold=0.9, now=NOW + 600) is None
|
|
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
|
{ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/docs/superpowers/specs/2026-10-30-status-page-design.md
RENAMED
|
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
|
|
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
|
{ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/templates/systemd/bridge.service.template
RENAMED
|
File without changes
|
{ctrlrelay-0.20.1 → ctrlrelay-0.20.2}/src/ctrlrelay/templates/systemd/poller.service.template
RENAMED
|
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
|
|
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
|
|
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
|