ctrlrelay 0.18.0__tar.gz → 0.19.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/CHANGELOG.md +77 -1
  2. ctrlrelay-0.19.1/PKG-INFO +164 -0
  3. ctrlrelay-0.19.1/README.md +123 -0
  4. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/config/orchestrator.yaml.example +17 -0
  5. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/_config.yml +7 -2
  6. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/adding-a-machine.md +9 -6
  7. ctrlrelay-0.19.1/docs/architecture.md +231 -0
  8. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/bridge.md +115 -87
  9. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/cli.md +32 -21
  10. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/configuration.md +131 -15
  11. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/development.md +18 -15
  12. ctrlrelay-0.19.1/docs/feedback-loop.md +209 -0
  13. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/getting-started.md +32 -21
  14. ctrlrelay-0.19.1/docs/index.md +89 -0
  15. ctrlrelay-0.19.1/docs/llms.txt +36 -0
  16. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/operations.md +17 -10
  17. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/personalization.md +142 -10
  18. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/pyproject.toml +1 -1
  19. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/cli.py +47 -0
  20. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/code_review.py +16 -2
  21. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/config.py +190 -0
  22. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/worktree.py +280 -34
  23. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/personalization/manager.py +470 -6
  24. ctrlrelay-0.19.1/src/ctrlrelay/personalization/merge.py +568 -0
  25. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/pipelines/dev.py +625 -261
  26. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/_publish_fakes.py +2 -0
  27. ctrlrelay-0.19.1/tests/test_dev_step_switches.py +1004 -0
  28. ctrlrelay-0.19.1/tests/test_dev_work_steps.py +214 -0
  29. ctrlrelay-0.19.1/tests/test_personalization_merge.py +1626 -0
  30. ctrlrelay-0.19.1/tests/test_relay_git_263.py +621 -0
  31. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_review_copy.py +24 -9
  32. ctrlrelay-0.18.0/PKG-INFO +0 -263
  33. ctrlrelay-0.18.0/README.md +0 -222
  34. ctrlrelay-0.18.0/docs/architecture.md +0 -222
  35. ctrlrelay-0.18.0/docs/feedback-loop.md +0 -197
  36. ctrlrelay-0.18.0/docs/index.md +0 -62
  37. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  38. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  39. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  40. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/.github/dependabot.yml +0 -0
  41. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/.github/workflows/build.yml +0 -0
  42. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/.github/workflows/cla.yml +0 -0
  43. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/.github/workflows/pages.yml +0 -0
  44. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/.github/workflows/publish.yml +0 -0
  45. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/.github/workflows/test.yml +0 -0
  46. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/.gitignore +0 -0
  47. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/CODE_OF_CONDUCT.md +0 -0
  48. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/CONTRIBUTING.md +0 -0
  49. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/LICENSE +0 -0
  50. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/SECURITY.md +0 -0
  51. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/Gemfile +0 -0
  52. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/registered-mode.md +0 -0
  53. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/superpowers/plans/2026-10-30-status-page.md +0 -0
  54. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/superpowers/specs/2026-10-30-status-page-design.md +0 -0
  55. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/__init__.py +0 -0
  56. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/bridge/__init__.py +0 -0
  57. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/bridge/__main__.py +0 -0
  58. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/bridge/factory.py +0 -0
  59. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/bridge/handler.py +0 -0
  60. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/bridge/mattermost_handler.py +0 -0
  61. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/bridge/node_tag.py +0 -0
  62. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/bridge/protocol.py +0 -0
  63. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/bridge/server.py +0 -0
  64. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/bridge/telegram_handler.py +0 -0
  65. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/__init__.py +0 -0
  66. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/agent_env.py +0 -0
  67. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/archived.py +0 -0
  68. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/audit.py +0 -0
  69. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/binaries.py +0 -0
  70. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/checkpoint.py +0 -0
  71. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/claims.py +0 -0
  72. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/dispatch_health.py +0 -0
  73. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/dispatcher.py +0 -0
  74. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/events.py +0 -0
  75. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/github.py +0 -0
  76. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/github_budget.py +0 -0
  77. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/heartbeat.py +0 -0
  78. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/job_claims.py +0 -0
  79. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/network.py +0 -0
  80. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/node_check.py +0 -0
  81. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/obs.py +0 -0
  82. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/poller.py +0 -0
  83. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/pr_verifier.py +0 -0
  84. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/pr_watcher.py +0 -0
  85. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/publish.py +0 -0
  86. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/question_expiry.py +0 -0
  87. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/redact.py +0 -0
  88. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/scheduler.py +0 -0
  89. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/state.py +0 -0
  90. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/usage.py +0 -0
  91. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/dashboard/__init__.py +0 -0
  92. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/dashboard/client.py +0 -0
  93. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/gh_protocol.py +0 -0
  94. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/install.py +0 -0
  95. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/personalization/__init__.py +0 -0
  96. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/personalization/paths.py +0 -0
  97. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/pipelines/__init__.py +0 -0
  98. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/pipelines/base.py +0 -0
  99. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/pipelines/post_merge.py +0 -0
  100. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/pipelines/secops.py +0 -0
  101. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/pipelines/task.py +0 -0
  102. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/setup.py +0 -0
  103. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/templates/__init__.py +0 -0
  104. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/templates/global-CLAUDE.md.snippet +0 -0
  105. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/templates/launchd/__init__.py +0 -0
  106. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/templates/launchd/bridge.plist.template +0 -0
  107. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/templates/launchd/poller.plist.template +0 -0
  108. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/templates/systemd/__init__.py +0 -0
  109. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/templates/systemd/bridge.service.template +0 -0
  110. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/templates/systemd/poller.service.template +0 -0
  111. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/templates/systemd/web.service.template +0 -0
  112. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/transports/__init__.py +0 -0
  113. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/transports/base.py +0 -0
  114. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/transports/file_mock.py +0 -0
  115. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/transports/socket_client.py +0 -0
  116. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/web/__init__.py +0 -0
  117. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/web/fleet.py +0 -0
  118. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/web/health.py +0 -0
  119. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/web/links.py +0 -0
  120. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/web/numbers.py +0 -0
  121. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/web/server.py +0 -0
  122. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/web/static/index.html +0 -0
  123. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/web/status.py +0 -0
  124. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/web/transcript.py +0 -0
  125. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/web/waiting.py +0 -0
  126. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/__init__.py +0 -0
  127. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/_poller_fakes.py +0 -0
  128. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/conftest.py +0 -0
  129. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/fixtures/gh_search_issues.json +0 -0
  130. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/fixtures/stream_json_rate_limit.jsonl +0 -0
  131. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/fixtures/transcript_sample.jsonl +0 -0
  132. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_agent_backend_wiring.py +0 -0
  133. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_agent_no_keys.py +0 -0
  134. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_agent_routing.py +0 -0
  135. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_agent_sandbox.py +0 -0
  136. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_archived.py +0 -0
  137. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_audit.py +0 -0
  138. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_binary_resolution.py +0 -0
  139. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_bridge_protocol.py +0 -0
  140. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_bridge_server.py +0 -0
  141. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_bridge_start_command.py +0 -0
  142. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_chat_machines.py +0 -0
  143. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_checkpoint.py +0 -0
  144. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_claim_reconciliation.py +0 -0
  145. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_cli_ci_wait.py +0 -0
  146. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_cli_dev.py +0 -0
  147. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_cli_repos.py +0 -0
  148. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_cli_secops.py +0 -0
  149. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_cli_start.py +0 -0
  150. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_cli_version.py +0 -0
  151. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_code_review.py +0 -0
  152. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_config.py +0 -0
  153. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_dashboard_client.py +0 -0
  154. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_dev_integration.py +0 -0
  155. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_dev_pipeline.py +0 -0
  156. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_dispatch_health.py +0 -0
  157. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_dispatch_outcome_hook.py +0 -0
  158. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_dispatcher.py +0 -0
  159. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_docs_site.py +0 -0
  160. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_event_emission.py +0 -0
  161. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_events.py +0 -0
  162. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_fleet.py +0 -0
  163. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_gh_protocol.py +0 -0
  164. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_github.py +0 -0
  165. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_github_budget.py +0 -0
  166. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_install.py +0 -0
  167. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_issue_outcome_announcement.py +0 -0
  168. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_job_claims.py +0 -0
  169. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_mattermost_handler.py +0 -0
  170. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_network.py +0 -0
  171. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_node_check.py +0 -0
  172. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_node_claims.py +0 -0
  173. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_notify_transport.py +0 -0
  174. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_obs.py +0 -0
  175. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_opencode_adapter.py +0 -0
  176. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_personalization.py +0 -0
  177. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_pipeline_base.py +0 -0
  178. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_poller.py +0 -0
  179. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_poller_search.py +0 -0
  180. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_post_merge.py +0 -0
  181. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_pr_verifier.py +0 -0
  182. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_pr_watcher.py +0 -0
  183. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_pre_dispatch_retry.py +0 -0
  184. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_publish_failure_retry.py +0 -0
  185. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_question_backoff.py +0 -0
  186. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_question_expiry.py +0 -0
  187. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_redact.py +0 -0
  188. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_relay_alert_off_lock.py +0 -0
  189. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_resume_prompt_contract.py +0 -0
  190. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_scheduler.py +0 -0
  191. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_secops_integration.py +0 -0
  192. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_secops_pipeline.py +0 -0
  193. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_setup.py +0 -0
  194. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_state.py +0 -0
  195. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_task_pipeline.py +0 -0
  196. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_telegram_handler.py +0 -0
  197. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_transport.py +0 -0
  198. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_transport_resolution.py +0 -0
  199. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_unreviewed_pr_announcement.py +0 -0
  200. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_usage.py +0 -0
  201. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_web_health.py +0 -0
  202. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_web_links.py +0 -0
  203. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_web_numbers.py +0 -0
  204. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_web_server.py +0 -0
  205. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_web_status.py +0 -0
  206. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_web_transcript.py +0 -0
  207. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_web_waiting.py +0 -0
  208. {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_worktree.py +0 -0
@@ -7,6 +7,76 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.19.1] - 2026-11-03
11
+
12
+ ### Fixed
13
+
14
+ - A synced or live `settings.json` nested more than 64 levels deep is now
15
+ refused right after it is read. On Python 3.14 the parser accepts about
16
+ 65,000 levels, and writing that back out used all the memory on the
17
+ machine instead of failing (#313).
18
+
19
+ ## [0.19.0] - 2026-11-03
20
+
21
+ ### Added
22
+
23
+ - `repos[].steps` sets each dev pipeline work step (`code`, `publish`,
24
+ `verify`, `review`) to `off`, `on` or `required`, and `repos[].max_fix_attempts`
25
+ caps the CI fix rounds. `required` stops the run on a failure, including a
26
+ reviewer that cannot run or CI that never finished; the PR stays open and the
27
+ run is announced as failed. Unset keys keep today's behaviour, and settings
28
+ the pipeline could not honour (`code: off`, `verify: on`, a review switch
29
+ that disagrees with `code_review.method`) are refused at load (#226).
30
+ - `mode: merge` for a personalization path keeps the live file real and, on
31
+ pull, writes only the top-level JSON keys listed in the local `merge_keys`,
32
+ so `~/.claude/settings.json` can carry fleet policy while the keys the app
33
+ writes on its own stay on that machine and no longer dirty the checkout. The
34
+ synced file cannot widen the list; it must be a JSON object without
35
+ duplicate keys; links (at the file or any directory under `$HOME` on the
36
+ way), non-regular and foreign-owned files are refused; writes are atomic at
37
+ mode 0600 or stricter and are redone once if the app writes the file
38
+ meanwhile; push refuses while the checkout copy differs from HEAD (`migrate
39
+ --reset-copy` drops the change) and a configuration with no merge entry
40
+ pushes exactly as before; logs name keys, never values, and a change to
41
+ `hooks`, `env`, `permissions`, `apiKeyHelper` or
42
+ `skipDangerousModePermissionPrompt` logs a warning. `ctrlrelay
43
+ personalization migrate` converts the old symlink once, on request, and
44
+ resets the checkout copy to the fleet's version; pull and auto-pull refuse
45
+ the symlink instead (#287).
46
+
47
+ ### Fixed
48
+
49
+ - A personalization pull on a machine still wired the old way (`~/.claude/settings.json`
50
+ a symlink into the checkout, with the app having written through it) failed
51
+ with "rebase hit conflicts" and never mentioned the fix. Pull and auto-pull
52
+ now add: run `ctrlrelay personalization migrate` (#287)
53
+
54
+ ### Security
55
+
56
+ - The relay's own git touches of agent-written trees no longer reach past
57
+ the agent sandbox (#263).
58
+ - Every `git worktree add` the relay runs now reads no global or system
59
+ git config and runs no hooks or fsmonitor, whether or not the sandbox is
60
+ on. A committed or planted attributes file therefore cannot select a
61
+ filter driver the operator's config defines. For that checkout this
62
+ drops `lfs.*` and `filter.*` drivers, post-checkout hooks,
63
+ `safe.directory`, `core.autocrlf`/`core.eol`, `core.longpaths` and
64
+ `url.*.insteadOf` (not needed: the add does no fetch).
65
+ - A repository using git-lfs now gives the agent pointer files. None of
66
+ the repositories the relay currently works on uses LFS: 0 of 90 bare
67
+ clones have `filter=lfs` in HEAD's `.gitattributes`.
68
+ - The bare clone's own config is still read. The relay writes it, and the
69
+ sandbox keeps it read-only.
70
+ - The `CLAUDE.md` exclude line is written only to the worktree's git
71
+ directory found from the path the relay made (never from the
72
+ worktree's `.git` file): `<clone>.git/worktrees/<basename>[N]`, whose
73
+ back-pointer must name this worktree. It is opened without following
74
+ any link, a hard-linked file is refused, and every refusal is logged
75
+ and skipped rather than failing the resume.
76
+ - The reviewer's base branch and its commit come from GitHub rather than
77
+ the bare clone, are pinned in the review copy, and are named in the
78
+ review comment.
79
+
10
80
  ## [0.18.0] - 2026-11-03
11
81
 
12
82
  ### Added
@@ -38,6 +108,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
38
108
 
39
109
  ### Changed
40
110
 
111
+ - Internal, no behaviour change: the dev pipeline's work (code, publish,
112
+ verify, review) runs as a fixed, ordered list of steps inside a frame that
113
+ keeps the lock, claim, worktree, cleanup and claim reconciliation as they
114
+ were. Groundwork for per-repo step switches (refs #226)
41
115
  - The subprocess machinery every CLI-driven adapter shares — the timeout, the
42
116
  kill on cancel, the replacing decode, the checkpoint read and #204's outcome
43
117
  report on every exit path — moved from `ClaudeDispatcher` to a
@@ -2192,7 +2266,9 @@ pipeline).
2192
2266
  per-phase implementation plans (Phase 0 through Phase 4).
2193
2267
  - `docs/Claude_Code_Project_Guide.md` — project development guide.
2194
2268
 
2195
- [Unreleased]: https://github.com/AInvirion/ctrlrelay/compare/v0.18.0...HEAD
2269
+ [Unreleased]: https://github.com/AInvirion/ctrlrelay/compare/v0.19.1...HEAD
2270
+ [0.19.1]: https://github.com/AInvirion/ctrlrelay/compare/v0.19.0...v0.19.1
2271
+ [0.19.0]: https://github.com/AInvirion/ctrlrelay/compare/v0.18.0...v0.19.0
2196
2272
  [0.18.0]: https://github.com/AInvirion/ctrlrelay/compare/v0.17.1...v0.18.0
2197
2273
  [0.17.1]: https://github.com/AInvirion/ctrlrelay/compare/v0.17.0...v0.17.1
2198
2274
  [0.17.0]: https://github.com/AInvirion/ctrlrelay/compare/v0.16.4...v0.17.0
@@ -0,0 +1,164 @@
1
+ Metadata-Version: 2.5
2
+ Name: ctrlrelay
3
+ Version: 0.19.1
4
+ Summary: Local-first orchestrator for headless coding agents across multiple GitHub repos
5
+ Project-URL: Homepage, https://github.com/AInvirion/ctrlrelay
6
+ Project-URL: Documentation, https://ainvirion.github.io/ctrlrelay/
7
+ Project-URL: Repository, https://github.com/AInvirion/ctrlrelay
8
+ Project-URL: Issues, https://github.com/AInvirion/ctrlrelay/issues
9
+ Project-URL: Changelog, https://github.com/AInvirion/ctrlrelay/blob/main/CHANGELOG.md
10
+ Author-email: Oscar Valenzuela <oscar.valenzuela.b@gmail.com>
11
+ License-Expression: Apache-2.0
12
+ License-File: LICENSE
13
+ Keywords: automation,claude,github,orchestrator
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Environment :: Console
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: License :: OSI Approved :: Apache Software License
18
+ Classifier: Operating System :: MacOS
19
+ Classifier: Operating System :: POSIX :: Linux
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Topic :: Software Development :: Build Tools
24
+ Classifier: Topic :: Software Development :: Version Control :: Git
25
+ Classifier: Topic :: System :: Systems Administration
26
+ Requires-Python: >=3.12
27
+ Requires-Dist: apscheduler>=3.10.0
28
+ Requires-Dist: httpx>=0.27.0
29
+ Requires-Dist: pydantic>=2.0.0
30
+ Requires-Dist: python-telegram-bot>=21.0
31
+ Requires-Dist: pyyaml>=6.0.0
32
+ Requires-Dist: rich>=13.0.0
33
+ Requires-Dist: typer>=0.12.0
34
+ Requires-Dist: websockets>=12.0
35
+ Provides-Extra: dev
36
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
37
+ Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
38
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
39
+ Requires-Dist: ruff>=0.4.0; extra == 'dev'
40
+ Description-Content-Type: text/markdown
41
+
42
+ # ctrlrelay
43
+
44
+ [![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
45
+ [![PyPI](https://img.shields.io/pypi/v/ctrlrelay.svg)](https://pypi.org/project/ctrlrelay/)
46
+ [![Python](https://img.shields.io/badge/python-3.12%20%7C%203.13%20%7C%203.14-blue)](https://pypi.org/project/ctrlrelay/)
47
+ [![Tests and lint](https://github.com/AInvirion/ctrlrelay/actions/workflows/test.yml/badge.svg)](https://github.com/AInvirion/ctrlrelay/actions/workflows/test.yml)
48
+
49
+ **Assign an issue. Get a reviewed pull request. Answer questions from your phone.**
50
+
51
+ ctrlrelay turns the coding agent you already use into a teammate that works
52
+ your GitHub backlog in the background. It picks up issues, writes the code in
53
+ a clean branch, opens a pull request, waits for CI, gets the change reviewed,
54
+ and pings you on **Telegram** or **Mattermost** only when it truly needs you.
55
+
56
+ It runs on your own machine. Your code, your credentials, your agent
57
+ subscription. No hosted service in the middle.
58
+
59
+ **[Documentation](https://ainvirion.github.io/ctrlrelay/)** ·
60
+ **[Get started](https://ainvirion.github.io/ctrlrelay/getting-started/)** ·
61
+ **[Why ctrlrelay](https://ainvirion.github.io/ctrlrelay/#why-ctrlrelay)**
62
+
63
+ ## Why ctrlrelay
64
+
65
+ Coding agents are great when you sit and watch them. ctrlrelay is for the
66
+ work you don't want to sit and watch.
67
+
68
+ - **Your backlog is the to-do list.** No new tool to learn. Assign an issue
69
+ (or add a label) and the work starts. The result is a normal pull request
70
+ your team already knows how to review.
71
+ - **Runs where your code already lives.** ctrlrelay is a small program on your
72
+ laptop or server — not a cloud service. Nothing leaves your machine except
73
+ the calls your agent and GitHub already make. No per-seat platform fee on
74
+ top of the agent you already pay for.
75
+ - **It asks instead of guessing.** When the agent is stuck, you get the
76
+ question in chat. You reply from your phone and the *same* session picks up
77
+ where it stopped.
78
+ - **Safe by default.** Every issue gets its own isolated copy of the repo.
79
+ One job per repo at a time. Optional Linux sandbox that hides your secrets
80
+ from the agent. Nothing merges without your rules allowing it.
81
+ - **Done means done.** It waits for CI, fixes failing checks, and posts an
82
+ independent review on the pull request — so what reaches you is ready to
83
+ read, not a first draft.
84
+ - **Pick your agent per repo.** Claude Code today, or OpenCode — which can
85
+ run on a local model — for the repos where that fits.
86
+ - **Grows with you.** Start on one laptop. Add more machines later; they share
87
+ the work without stepping on each other.
88
+ - **Open source.** Apache 2.0. Read every line of what runs on your machine.
89
+
90
+ ## What it does
91
+
92
+ - **Issues → pull requests.** Watches every repo you choose and works each new
93
+ issue end to end.
94
+ - **Questions in your chat.** Telegram (a private bot) or Mattermost (a channel
95
+ on your own server).
96
+ - **Automatic review.** Every agent pull request gets a second-opinion review
97
+ comment before you look at it.
98
+ - **Daily security sweep.** Reviews Dependabot alerts and pull requests across
99
+ your repos every morning.
100
+ - **Status page.** See what each agent is doing, what is waiting, and what
101
+ needs you — at a glance.
102
+ - **Many machines, one account.** Spread work across machines, with one chat
103
+ and one status page.
104
+ - **Settings follow you.** Optional sync of your agent setup across machines
105
+ through a private GitHub repo.
106
+
107
+ ## How it works
108
+
109
+ ```mermaid
110
+ flowchart LR
111
+ GH[("GitHub<br/>issues")] -->|new issue| CR["ctrlrelay<br/>on your machine"]
112
+ CR -->|works in a clean branch| AG["Coding agent<br/>Claude Code · OpenCode"]
113
+ AG -->|stuck?| CH["Your chat<br/>Telegram · Mattermost"]
114
+ CH -->|your answer| AG
115
+ AG -->|opens| PR["Pull request"]
116
+ PR -->|CI + review| YOU(["You merge"])
117
+ ```
118
+
119
+ 1. **You assign an issue** to yourself, or add a label you pick.
120
+ 2. **ctrlrelay starts the agent** in a fresh, isolated copy of the repo.
121
+ 3. **If the agent has a question**, it lands in your chat. Your reply goes
122
+ straight back to the same session.
123
+ 4. **A pull request opens.** ctrlrelay waits for CI, fixes what fails, and
124
+ posts a review.
125
+ 5. **You merge** — or ask for changes, like with any teammate.
126
+
127
+ ## Quick start
128
+
129
+ ```bash
130
+ pipx install ctrlrelay
131
+ ctrlrelay setup # finds your repos and writes your config
132
+ ctrlrelay poller start # starts working assigned issues
133
+ ```
134
+
135
+ You need Python 3.12+, the GitHub CLI, and a coding agent CLI. The
136
+ **[getting started guide](https://ainvirion.github.io/ctrlrelay/getting-started/)**
137
+ covers prerequisites, chat setup and running it as a background service.
138
+
139
+ ## Documentation
140
+
141
+ Everything lives at **[ainvirion.github.io/ctrlrelay](https://ainvirion.github.io/ctrlrelay/)**:
142
+
143
+ - [Getting started](https://ainvirion.github.io/ctrlrelay/getting-started/) — install and first run
144
+ - [Chat bridge](https://ainvirion.github.io/ctrlrelay/bridge/) — set up Telegram or Mattermost
145
+ - [Configuration](https://ainvirion.github.io/ctrlrelay/configuration/) — every setting
146
+ - [Operations](https://ainvirion.github.io/ctrlrelay/operations/) — background services, status page, several machines
147
+ - [CLI reference](https://ainvirion.github.io/ctrlrelay/cli/) — every command
148
+
149
+ ## Contributing
150
+
151
+ Contributions are welcome. Read the [contributing guide](CONTRIBUTING.md) and
152
+ the [code of conduct](CODE_OF_CONDUCT.md). First-time contributors sign a
153
+ one-time Contributor Assignment Agreement in the pull request.
154
+
155
+ ## Security
156
+
157
+ Found a vulnerability? Please follow the [security policy](SECURITY.md) and open
158
+ a private advisory — not a public issue.
159
+
160
+ ## License
161
+
162
+ Apache License 2.0 — see [LICENSE](LICENSE).
163
+
164
+ Copyright (c) 2026 AInvirion LLC. All Rights Reserved.
@@ -0,0 +1,123 @@
1
+ # ctrlrelay
2
+
3
+ [![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
4
+ [![PyPI](https://img.shields.io/pypi/v/ctrlrelay.svg)](https://pypi.org/project/ctrlrelay/)
5
+ [![Python](https://img.shields.io/badge/python-3.12%20%7C%203.13%20%7C%203.14-blue)](https://pypi.org/project/ctrlrelay/)
6
+ [![Tests and lint](https://github.com/AInvirion/ctrlrelay/actions/workflows/test.yml/badge.svg)](https://github.com/AInvirion/ctrlrelay/actions/workflows/test.yml)
7
+
8
+ **Assign an issue. Get a reviewed pull request. Answer questions from your phone.**
9
+
10
+ ctrlrelay turns the coding agent you already use into a teammate that works
11
+ your GitHub backlog in the background. It picks up issues, writes the code in
12
+ a clean branch, opens a pull request, waits for CI, gets the change reviewed,
13
+ and pings you on **Telegram** or **Mattermost** only when it truly needs you.
14
+
15
+ It runs on your own machine. Your code, your credentials, your agent
16
+ subscription. No hosted service in the middle.
17
+
18
+ **[Documentation](https://ainvirion.github.io/ctrlrelay/)** ·
19
+ **[Get started](https://ainvirion.github.io/ctrlrelay/getting-started/)** ·
20
+ **[Why ctrlrelay](https://ainvirion.github.io/ctrlrelay/#why-ctrlrelay)**
21
+
22
+ ## Why ctrlrelay
23
+
24
+ Coding agents are great when you sit and watch them. ctrlrelay is for the
25
+ work you don't want to sit and watch.
26
+
27
+ - **Your backlog is the to-do list.** No new tool to learn. Assign an issue
28
+ (or add a label) and the work starts. The result is a normal pull request
29
+ your team already knows how to review.
30
+ - **Runs where your code already lives.** ctrlrelay is a small program on your
31
+ laptop or server — not a cloud service. Nothing leaves your machine except
32
+ the calls your agent and GitHub already make. No per-seat platform fee on
33
+ top of the agent you already pay for.
34
+ - **It asks instead of guessing.** When the agent is stuck, you get the
35
+ question in chat. You reply from your phone and the *same* session picks up
36
+ where it stopped.
37
+ - **Safe by default.** Every issue gets its own isolated copy of the repo.
38
+ One job per repo at a time. Optional Linux sandbox that hides your secrets
39
+ from the agent. Nothing merges without your rules allowing it.
40
+ - **Done means done.** It waits for CI, fixes failing checks, and posts an
41
+ independent review on the pull request — so what reaches you is ready to
42
+ read, not a first draft.
43
+ - **Pick your agent per repo.** Claude Code today, or OpenCode — which can
44
+ run on a local model — for the repos where that fits.
45
+ - **Grows with you.** Start on one laptop. Add more machines later; they share
46
+ the work without stepping on each other.
47
+ - **Open source.** Apache 2.0. Read every line of what runs on your machine.
48
+
49
+ ## What it does
50
+
51
+ - **Issues → pull requests.** Watches every repo you choose and works each new
52
+ issue end to end.
53
+ - **Questions in your chat.** Telegram (a private bot) or Mattermost (a channel
54
+ on your own server).
55
+ - **Automatic review.** Every agent pull request gets a second-opinion review
56
+ comment before you look at it.
57
+ - **Daily security sweep.** Reviews Dependabot alerts and pull requests across
58
+ your repos every morning.
59
+ - **Status page.** See what each agent is doing, what is waiting, and what
60
+ needs you — at a glance.
61
+ - **Many machines, one account.** Spread work across machines, with one chat
62
+ and one status page.
63
+ - **Settings follow you.** Optional sync of your agent setup across machines
64
+ through a private GitHub repo.
65
+
66
+ ## How it works
67
+
68
+ ```mermaid
69
+ flowchart LR
70
+ GH[("GitHub<br/>issues")] -->|new issue| CR["ctrlrelay<br/>on your machine"]
71
+ CR -->|works in a clean branch| AG["Coding agent<br/>Claude Code · OpenCode"]
72
+ AG -->|stuck?| CH["Your chat<br/>Telegram · Mattermost"]
73
+ CH -->|your answer| AG
74
+ AG -->|opens| PR["Pull request"]
75
+ PR -->|CI + review| YOU(["You merge"])
76
+ ```
77
+
78
+ 1. **You assign an issue** to yourself, or add a label you pick.
79
+ 2. **ctrlrelay starts the agent** in a fresh, isolated copy of the repo.
80
+ 3. **If the agent has a question**, it lands in your chat. Your reply goes
81
+ straight back to the same session.
82
+ 4. **A pull request opens.** ctrlrelay waits for CI, fixes what fails, and
83
+ posts a review.
84
+ 5. **You merge** — or ask for changes, like with any teammate.
85
+
86
+ ## Quick start
87
+
88
+ ```bash
89
+ pipx install ctrlrelay
90
+ ctrlrelay setup # finds your repos and writes your config
91
+ ctrlrelay poller start # starts working assigned issues
92
+ ```
93
+
94
+ You need Python 3.12+, the GitHub CLI, and a coding agent CLI. The
95
+ **[getting started guide](https://ainvirion.github.io/ctrlrelay/getting-started/)**
96
+ covers prerequisites, chat setup and running it as a background service.
97
+
98
+ ## Documentation
99
+
100
+ Everything lives at **[ainvirion.github.io/ctrlrelay](https://ainvirion.github.io/ctrlrelay/)**:
101
+
102
+ - [Getting started](https://ainvirion.github.io/ctrlrelay/getting-started/) — install and first run
103
+ - [Chat bridge](https://ainvirion.github.io/ctrlrelay/bridge/) — set up Telegram or Mattermost
104
+ - [Configuration](https://ainvirion.github.io/ctrlrelay/configuration/) — every setting
105
+ - [Operations](https://ainvirion.github.io/ctrlrelay/operations/) — background services, status page, several machines
106
+ - [CLI reference](https://ainvirion.github.io/ctrlrelay/cli/) — every command
107
+
108
+ ## Contributing
109
+
110
+ Contributions are welcome. Read the [contributing guide](CONTRIBUTING.md) and
111
+ the [code of conduct](CODE_OF_CONDUCT.md). First-time contributors sign a
112
+ one-time Contributor Assignment Agreement in the pull request.
113
+
114
+ ## Security
115
+
116
+ Found a vulnerability? Please follow the [security policy](SECURITY.md) and open
117
+ a private advisory — not a public issue.
118
+
119
+ ## License
120
+
121
+ Apache License 2.0 — see [LICENSE](LICENSE).
122
+
123
+ Copyright (c) 2026 AInvirion LLC. All Rights Reserved.
@@ -254,3 +254,20 @@ repos: []
254
254
  # # generated while blocked in this call, so a slow default here
255
255
  # # just adds idle wall-clock time. Defaults to 600 (10 minutes).
256
256
  # ci_wait_timeout_seconds: 600
257
+ # # How strict each dev pipeline work step is for this repo. The
258
+ # # order (code, publish, verify, review) is fixed. Each key is
259
+ # # off, on or required; leave a key out for today's behaviour.
260
+ # # required: a failure stops the run, and nothing skips the step
261
+ # # (a reviewer that cannot run is not a pass, and CI
262
+ # # that never finished is not a pass)
263
+ # # on: a failure is reported and the PR is handed over
264
+ # # off: the step does not run
265
+ # # code and publish accept only required. verify accepts off or
266
+ # # required. review: off is the same as code_review.method: off;
267
+ # # set both only if they agree.
268
+ # steps:
269
+ # verify: required
270
+ # review: required
271
+ # # Fix rounds for a PR whose CI is red or whose branch conflicts,
272
+ # # before the run fails. Defaults to 3; 0 means none.
273
+ # max_fix_attempts: 3
@@ -1,7 +1,8 @@
1
1
  title: ctrlrelay
2
2
  description: >-
3
- Local-first orchestrator for headless coding agents across multiple GitHub
4
- repos — operator guides and architecture reference.
3
+ Assign a GitHub issue, get a reviewed pull request. ctrlrelay runs your
4
+ coding agent in the background and asks you on Telegram or Mattermost
5
+ when it needs you.
5
6
  url: "https://ainvirion.github.io"
6
7
  baseurl: "/ctrlrelay"
7
8
 
@@ -26,6 +27,10 @@ search:
26
27
 
27
28
  heading_anchors: true
28
29
 
30
+ # Render ```mermaid fences as diagrams
31
+ mermaid:
32
+ version: "10.9.1"
33
+
29
34
  # Aux links appear in the top right of the layout
30
35
  aux_links:
31
36
  "GitHub":
@@ -39,10 +39,12 @@ A token copied between machines is revoked for all of them at once.
39
39
 
40
40
  ## 3. Its own agent login
41
41
 
42
- Log the agent CLI in on this machine. Each machine can use its own
43
- subscription; it pauses on its own when its usage nears the limit
44
- (`usage.pause_at`). Two machines on one subscription share one set of usage
45
- numbers and pause together, which is correct.
42
+ Log the agent CLI in on this machine — whichever your repos use (`claude`,
43
+ `opencode` or both, per `agent.type` and `repos[].agent_type`). For Claude Code, each
44
+ machine can use its own subscription; it pauses on its own when its usage
45
+ nears the limit (`usage.pause_at`). Two machines on one subscription share one
46
+ set of usage numbers and pause together, which is correct. OpenCode has no
47
+ usage reading of its own.
46
48
 
47
49
  ## 4. A config with its own `node_id`
48
50
 
@@ -103,8 +105,9 @@ ctrlrelay node check
103
105
  ✓ peer http://node-a:8787: reachable, node-a
104
106
  ```
105
107
 
106
- `--no-agent` skips the agent check, which makes one tiny call on the
107
- subscription. A peer that answers with this machine's own name means two
108
+ `--no-agent` skips the agent check, which makes one tiny `claude` call on the
109
+ subscription. The row is skipped when no repo routes to `claude`, and it never
110
+ checks `opencode`: run that CLI once by hand. A peer that answers with this machine's own name means two
108
111
  machines share a `node_id` (or the URL points back here).
109
112
 
110
113
  ## 8. Start it