agent-control-plane 0.1.1 → 0.1.3

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 (184) hide show
  1. package/README.md +6 -0
  2. package/npm/bin/agent-control-plane.js +149 -5
  3. package/package.json +2 -3
  4. package/tools/vendor/codex-quota/README.md +8 -16
  5. package/tools/vendor/codex-quota/lib/claude-accounts.js +2 -68
  6. package/tools/vendor/codex-quota/lib/claude-usage.js +76 -667
  7. package/tools/vendor/codex-quota/lib/display.js +6 -14
  8. package/tools/vendor/codex-quota/lib/handlers.js +32 -117
  9. package/tools/vendor/codex-quota/lib/sync.js +9 -14
  10. package/tools/tests/test-agent-control-plane-npm-cli.sh +0 -280
  11. package/tools/tests/test-agent-github-update-labels-falls-back-to-repository-id.sh +0 -56
  12. package/tools/tests/test-agent-project-claude-session-wrapper-clears-stale-sandbox-artifacts.sh +0 -89
  13. package/tools/tests/test-agent-project-claude-session-wrapper-does-not-retry-provider-quota.sh +0 -82
  14. package/tools/tests/test-agent-project-claude-session-wrapper-retries-transient-failures.sh +0 -90
  15. package/tools/tests/test-agent-project-claude-session-wrapper-times-out.sh +0 -73
  16. package/tools/tests/test-agent-project-claude-session-wrapper.sh +0 -103
  17. package/tools/tests/test-agent-project-cleanup-session-orphan-fallback.sh +0 -90
  18. package/tools/tests/test-agent-project-cleanup-session-skip-worktree-cleanup.sh +0 -90
  19. package/tools/tests/test-agent-project-codex-live-thread-persist.sh +0 -76
  20. package/tools/tests/test-agent-project-codex-recovery.sh +0 -731
  21. package/tools/tests/test-agent-project-codex-session-wrapper-clears-stale-sandbox-artifacts.sh +0 -105
  22. package/tools/tests/test-agent-project-codex-session-wrapper.sh +0 -97
  23. package/tools/tests/test-agent-project-open-pr-worktree-config-prefix.sh +0 -81
  24. package/tools/tests/test-agent-project-openclaw-session-wrapper-clears-stale-sandbox-artifacts.sh +0 -109
  25. package/tools/tests/test-agent-project-openclaw-session-wrapper-infers-blocked-result-contract.sh +0 -89
  26. package/tools/tests/test-agent-project-openclaw-session-wrapper-recovers-literal-env-artifacts.sh +0 -113
  27. package/tools/tests/test-agent-project-openclaw-session-wrapper-recovers-version-mismatch.sh +0 -135
  28. package/tools/tests/test-agent-project-openclaw-session-wrapper-resident.sh +0 -179
  29. package/tools/tests/test-agent-project-openclaw-session-wrapper-reuses-existing-agent-after-add-race.sh +0 -119
  30. package/tools/tests/test-agent-project-openclaw-session-wrapper-terminates-rate-limit-hang.sh +0 -91
  31. package/tools/tests/test-agent-project-openclaw-session-wrapper.sh +0 -117
  32. package/tools/tests/test-agent-project-publish-issue-pr-prunes-stale-worktree-entry.sh +0 -148
  33. package/tools/tests/test-agent-project-publish-issue-pr-reads-archived-session.sh +0 -146
  34. package/tools/tests/test-agent-project-publish-issue-pr-recovers-final-head.sh +0 -145
  35. package/tools/tests/test-agent-project-publish-issue-pr-reuses-existing-worktree.sh +0 -147
  36. package/tools/tests/test-agent-project-reconcile-failure-reason.sh +0 -456
  37. package/tools/tests/test-agent-project-reconcile-issue-archived-session-fallback.sh +0 -96
  38. package/tools/tests/test-agent-project-reconcile-issue-before-blocked.sh +0 -90
  39. package/tools/tests/test-agent-project-reconcile-issue-host-verification-recovery-uses-recovered-worktree.sh +0 -212
  40. package/tools/tests/test-agent-project-reconcile-issue-host-verification-recovery.sh +0 -207
  41. package/tools/tests/test-agent-project-reconcile-issue-provider-quota-schedules-provider-cooldown.sh +0 -101
  42. package/tools/tests/test-agent-project-reconcile-issue-session-backfills-lane-metadata-from-worker-key.sh +0 -113
  43. package/tools/tests/test-agent-project-reconcile-issue-session-clears-stale-failed-summary.sh +0 -117
  44. package/tools/tests/test-agent-project-reconcile-issue-session-initializes-shared-agent-home.sh +0 -55
  45. package/tools/tests/test-agent-project-reconcile-issue-session-normalizes-runner-state.sh +0 -125
  46. package/tools/tests/test-agent-project-reconcile-issue-session-records-invalid-contract-summary.sh +0 -118
  47. package/tools/tests/test-agent-project-reconcile-issue-session-skips-duplicate-blocked-comment.sh +0 -144
  48. package/tools/tests/test-agent-project-reconcile-issue-session-standardizes-no-commits-blocker.sh +0 -145
  49. package/tools/tests/test-agent-project-reconcile-issue-session-synthesizes-blocked-comment.sh +0 -139
  50. package/tools/tests/test-agent-project-reconcile-pr-blocked-host-recovery.sh +0 -242
  51. package/tools/tests/test-agent-project-reconcile-pr-guard-blocked-no-commit.sh +0 -142
  52. package/tools/tests/test-agent-project-reconcile-pr-provider-quota-schedules-provider-cooldown.sh +0 -106
  53. package/tools/tests/test-agent-project-reconcile-pr-session-initializes-shared-agent-home.sh +0 -66
  54. package/tools/tests/test-agent-project-reconcile-pr-updated-branch-noop.sh +0 -129
  55. package/tools/tests/test-audit-agent-worktrees-active-launch-skips-git-inspection.sh +0 -69
  56. package/tools/tests/test-audit-agent-worktrees-broken-worktree.sh +0 -43
  57. package/tools/tests/test-audit-agent-worktrees-pending-launch-owner.sh +0 -46
  58. package/tools/tests/test-audit-agent-worktrees-unreconciled-owner.sh +0 -79
  59. package/tools/tests/test-audit-issue-routing-managed-branch-globs.sh +0 -56
  60. package/tools/tests/test-branch-verification-guard-generated-artifacts.sh +0 -72
  61. package/tools/tests/test-branch-verification-guard-targeted-coverage.sh +0 -125
  62. package/tools/tests/test-codex-quota-manager-failure-driven-rotation.sh +0 -178
  63. package/tools/tests/test-codex-quota-wrapper.sh +0 -37
  64. package/tools/tests/test-contribution-docs.sh +0 -18
  65. package/tools/tests/test-control-plane-dashboard-runtime-smoke.sh +0 -343
  66. package/tools/tests/test-create-follow-up-issue.sh +0 -73
  67. package/tools/tests/test-dashboard-launchd-bootstrap.sh +0 -55
  68. package/tools/tests/test-flow-export-execution-env-exports-repo-id.sh +0 -30
  69. package/tools/tests/test-flow-export-github-cli-auth-env-prefers-git-credential.sh +0 -48
  70. package/tools/tests/test-flow-github-api-repo-fallback-preserves-input.sh +0 -85
  71. package/tools/tests/test-flow-github-api-repo-prefers-explicit-repository-id.sh +0 -60
  72. package/tools/tests/test-flow-github-issue-list-falls-back-to-repository-id.sh +0 -64
  73. package/tools/tests/test-flow-github-pr-list-falls-back-to-repository-id.sh +0 -77
  74. package/tools/tests/test-flow-resident-can-reuse-does-not-leak-metadata.sh +0 -52
  75. package/tools/tests/test-flow-resident-reap-stale-controllers.sh +0 -63
  76. package/tools/tests/test-flow-resolve-codex-quota-tools.sh +0 -104
  77. package/tools/tests/test-flow-runtime-doctor-profile-selection.sh +0 -27
  78. package/tools/tests/test-heartbeat-codex-pr-linked-issue-exclusion.sh +0 -79
  79. package/tools/tests/test-heartbeat-hooks-enqueue-resident-issue-for-idle-controller.sh +0 -115
  80. package/tools/tests/test-heartbeat-hooks-enqueue-resident-issue-for-live-lane-controller.sh +0 -117
  81. package/tools/tests/test-heartbeat-hooks-start-resident-issue-loop-claude.sh +0 -96
  82. package/tools/tests/test-heartbeat-hooks-start-resident-issue-loop-codex.sh +0 -96
  83. package/tools/tests/test-heartbeat-hooks-start-resident-issue-loop.sh +0 -96
  84. package/tools/tests/test-heartbeat-loop-auth-wait-does-not-consume-capacity.sh +0 -170
  85. package/tools/tests/test-heartbeat-loop-blocked-recovery-lane.sh +0 -201
  86. package/tools/tests/test-heartbeat-loop-blocked-recovery-vs-pr-reservation.sh +0 -201
  87. package/tools/tests/test-heartbeat-loop-idle-resident-controller-does-not-block-launches.sh +0 -160
  88. package/tools/tests/test-heartbeat-loop-pr-launch-dedup.sh +0 -133
  89. package/tools/tests/test-heartbeat-loop-provider-cooldown-suppresses-launches.sh +0 -157
  90. package/tools/tests/test-heartbeat-loop-reaps-stale-resident-controller.sh +0 -181
  91. package/tools/tests/test-heartbeat-loop-waiting-provider-resident-controller-does-not-block-launches.sh +0 -160
  92. package/tools/tests/test-heartbeat-ready-issues-blocked-recovery.sh +0 -134
  93. package/tools/tests/test-heartbeat-safe-auto-dynamic-concurrency.sh +0 -162
  94. package/tools/tests/test-heartbeat-safe-auto-no-tmux-sessions.sh +0 -136
  95. package/tools/tests/test-heartbeat-safe-auto-openclaw-skips-codex-quota.sh +0 -139
  96. package/tools/tests/test-heartbeat-safe-auto-quota-health-signal.sh +0 -119
  97. package/tools/tests/test-heartbeat-safe-auto-stale-shared-loop-pid-does-not-skip.sh +0 -140
  98. package/tools/tests/test-heartbeat-safe-auto-static-capacity-without-quota-cache.sh +0 -142
  99. package/tools/tests/test-heartbeat-safe-auto-zero-healthy-pools.sh +0 -141
  100. package/tools/tests/test-heartbeat-sync-issue-labels-empty-schedule.sh +0 -65
  101. package/tools/tests/test-heartbeat-sync-open-agent-prs-terminal-clears-running.sh +0 -179
  102. package/tools/tests/test-install-dashboard-launchd.sh +0 -78
  103. package/tools/tests/test-install-project-launchd-adds-tool-paths.sh +0 -87
  104. package/tools/tests/test-install-project-launchd.sh +0 -110
  105. package/tools/tests/test-issue-local-workspace-install-policy.sh +0 -81
  106. package/tools/tests/test-issue-publish-scope-guard-docs-signal.sh +0 -70
  107. package/tools/tests/test-issue-reconcile-hooks-success-clears-blocked.sh +0 -36
  108. package/tools/tests/test-kick-scheduler-requires-explicit-profile.sh +0 -47
  109. package/tools/tests/test-label-follow-up-issues-falls-back-to-repository-id.sh +0 -132
  110. package/tools/tests/test-manual-operator-entrypoints-require-explicit-profile.sh +0 -64
  111. package/tools/tests/test-package-funding-metadata.sh +0 -21
  112. package/tools/tests/test-package-public-metadata.sh +0 -62
  113. package/tools/tests/test-placeholder-worker-adapters.sh +0 -38
  114. package/tools/tests/test-pr-reconcile-hooks-refreshes-recurring-issue-checklist.sh +0 -110
  115. package/tools/tests/test-pr-risk-cohesive-mobile-locale-scope.sh +0 -70
  116. package/tools/tests/test-pr-risk-fix-label-semantics.sh +0 -114
  117. package/tools/tests/test-pr-risk-local-first-no-checks.sh +0 -70
  118. package/tools/tests/test-prepare-worktree-simple-repo-baseline.sh +0 -67
  119. package/tools/tests/test-profile-activate.sh +0 -33
  120. package/tools/tests/test-profile-adopt-allow-missing-repo.sh +0 -68
  121. package/tools/tests/test-profile-adopt-skip-workspace-sync-missing-file.sh +0 -61
  122. package/tools/tests/test-profile-adopt-syncs-anchor-and-workspace.sh +0 -90
  123. package/tools/tests/test-profile-smoke-collision.sh +0 -44
  124. package/tools/tests/test-profile-smoke-invalid-claude-config.sh +0 -31
  125. package/tools/tests/test-profile-smoke-invalid-provider-pool.sh +0 -68
  126. package/tools/tests/test-profile-smoke-repo-slug-mismatch.sh +0 -36
  127. package/tools/tests/test-profile-smoke.sh +0 -45
  128. package/tools/tests/test-project-init-force-and-skip-sync.sh +0 -61
  129. package/tools/tests/test-project-init-repo-slug-mismatch.sh +0 -29
  130. package/tools/tests/test-project-init.sh +0 -66
  131. package/tools/tests/test-project-launchd-bootstrap.sh +0 -66
  132. package/tools/tests/test-project-remove.sh +0 -150
  133. package/tools/tests/test-project-runtime-supervisor.sh +0 -47
  134. package/tools/tests/test-project-runtimectl-launchd.sh +0 -115
  135. package/tools/tests/test-project-runtimectl-missing-profile.sh +0 -54
  136. package/tools/tests/test-project-runtimectl-start-falls-back-to-bootstrap.sh +0 -108
  137. package/tools/tests/test-project-runtimectl-status-reports-supervisor-as-heartbeat-parent.sh +0 -95
  138. package/tools/tests/test-project-runtimectl-status-supervisor-running.sh +0 -59
  139. package/tools/tests/test-project-runtimectl-stop-cancels-pending-kick.sh +0 -85
  140. package/tools/tests/test-project-runtimectl-stop-clears-running-labels.sh +0 -78
  141. package/tools/tests/test-project-runtimectl.sh +0 -212
  142. package/tools/tests/test-provider-cooldown-state-prefers-runtime-worker-context.sh +0 -39
  143. package/tools/tests/test-provider-cooldown-state.sh +0 -59
  144. package/tools/tests/test-public-repo-docs.sh +0 -160
  145. package/tools/tests/test-reconcile-pr-worker-acp-config-routing.sh +0 -75
  146. package/tools/tests/test-render-dashboard-snapshot.sh +0 -149
  147. package/tools/tests/test-render-flow-config-demo-profile.sh +0 -36
  148. package/tools/tests/test-render-flow-config-provider-pool-fallback.sh +0 -81
  149. package/tools/tests/test-render-flow-config.sh +0 -52
  150. package/tools/tests/test-run-codex-task-claude-routing.sh +0 -125
  151. package/tools/tests/test-run-codex-task-codex-resident-routing.sh +0 -108
  152. package/tools/tests/test-run-codex-task-kilo-routing.sh +0 -98
  153. package/tools/tests/test-run-codex-task-openclaw-resident-routing.sh +0 -117
  154. package/tools/tests/test-run-codex-task-openclaw-routing.sh +0 -113
  155. package/tools/tests/test-run-codex-task-opencode-routing.sh +0 -98
  156. package/tools/tests/test-run-codex-task-provider-pool-fallback-routing.sh +0 -146
  157. package/tools/tests/test-scaffold-profile.sh +0 -108
  158. package/tools/tests/test-serve-dashboard.sh +0 -93
  159. package/tools/tests/test-start-issue-worker-blocked-context.sh +0 -129
  160. package/tools/tests/test-start-issue-worker-blocks-complete-recurring-checklist.sh +0 -189
  161. package/tools/tests/test-start-issue-worker-local-install-routing.sh +0 -157
  162. package/tools/tests/test-start-issue-worker-profile-template-routing.sh +0 -149
  163. package/tools/tests/test-start-issue-worker-recurring-resident-reuse-codex.sh +0 -212
  164. package/tools/tests/test-start-issue-worker-recurring-resident-reuse.sh +0 -219
  165. package/tools/tests/test-start-issue-worker-renders-verification-snippet.sh +0 -155
  166. package/tools/tests/test-start-issue-worker-resident-reuse-falls-back-to-new-worktree.sh +0 -199
  167. package/tools/tests/test-start-pr-fix-worker-host-blocker-context.sh +0 -275
  168. package/tools/tests/test-start-resident-issue-loop-adopts-next-recurring-issue.sh +0 -185
  169. package/tools/tests/test-start-resident-issue-loop-clears-pending-while-waiting-due.sh +0 -152
  170. package/tools/tests/test-start-resident-issue-loop-consumes-queued-lease.sh +0 -186
  171. package/tools/tests/test-start-resident-issue-loop-fails-over-provider-pool.sh +0 -212
  172. package/tools/tests/test-start-resident-issue-loop-immediate-cycles.sh +0 -148
  173. package/tools/tests/test-start-resident-issue-loop-waits-for-provider.sh +0 -194
  174. package/tools/tests/test-start-resident-issue-loop-waits-for-terminal-reconcile-status.sh +0 -198
  175. package/tools/tests/test-start-resident-issue-loop-yields-to-live-lane-controller.sh +0 -145
  176. package/tools/tests/test-sync-pr-labels-fix-lane-uses-repair-queued.sh +0 -67
  177. package/tools/tests/test-sync-recurring-issue-checklist-backfills-workflow-complete-blocker.sh +0 -70
  178. package/tools/tests/test-sync-recurring-issue-checklist.sh +0 -95
  179. package/tools/tests/test-sync-shared-agent-home-local-source-root.sh +0 -66
  180. package/tools/tests/test-sync-shared-agent-home-preserves-unrelated-workflow-catalog-skill.sh +0 -47
  181. package/tools/tests/test-test-smoke.sh +0 -86
  182. package/tools/tests/test-uninstall-project-launchd.sh +0 -37
  183. package/tools/tests/test-update-github-labels-prefers-sibling-helper.sh +0 -49
  184. package/tools/tests/test-workflow-catalog.sh +0 -43
@@ -1,343 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
-
4
- FLOW_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
5
- SNAPSHOT_BIN="${FLOW_ROOT}/tools/bin/render-dashboard-snapshot.py"
6
- SERVER_BIN="${FLOW_ROOT}/tools/bin/serve-dashboard.sh"
7
- RUNTIMECTL_BIN="${FLOW_ROOT}/tools/bin/project-runtimectl.sh"
8
-
9
- tmpdir="$(mktemp -d)"
10
- server_pid=""
11
- supervisor_pid=""
12
- heartbeat_pid=""
13
- shared_loop_pid=""
14
- controller_pid=""
15
- cleanup() {
16
- local pid=""
17
- for pid in "${server_pid}" "${shared_loop_pid}" "${controller_pid}" "${heartbeat_pid}" "${supervisor_pid}"; do
18
- [[ -n "${pid}" ]] || continue
19
- kill "${pid}" >/dev/null 2>&1 || true
20
- wait "${pid}" >/dev/null 2>&1 || true
21
- done
22
- rm -rf "${tmpdir}"
23
- }
24
- trap cleanup EXIT
25
-
26
- profile_registry_root="${tmpdir}/profiles"
27
- profile_dir="${profile_registry_root}/demo"
28
- runs_root="${tmpdir}/runtime/demo/runs"
29
- state_root="${tmpdir}/runtime/demo/state"
30
- recurring_run_dir="${runs_root}/demo-issue-2"
31
- scheduled_run_dir="${runs_root}/demo-issue-3"
32
- recurring_worker_key="issue-lane-recurring-general-openclaw-safe"
33
- scheduled_worker_key="issue-lane-scheduled-1800-openclaw-safe"
34
- repo_slug="example/demo-control-plane"
35
- port="18766"
36
- heartbeat_child_pid_file="${tmpdir}/heartbeat-child.pid"
37
- heartbeat_script="${tmpdir}/heartbeat-safe-auto.sh"
38
- supervisor_script="${tmpdir}/project-runtime-supervisor.sh"
39
- shared_loop_script="${tmpdir}/agent-project-heartbeat-loop"
40
-
41
- mkdir -p \
42
- "${profile_dir}" \
43
- "${recurring_run_dir}" \
44
- "${scheduled_run_dir}" \
45
- "${state_root}/heartbeat-loop.lock" \
46
- "${state_root}/resident-workers/issues/2" \
47
- "${state_root}/resident-workers/issues/${recurring_worker_key}" \
48
- "${state_root}/resident-workers/issues/${scheduled_worker_key}" \
49
- "${state_root}/resident-workers/issue-queue/pending" \
50
- "${state_root}/retries/providers" \
51
- "${state_root}/scheduled-issues"
52
-
53
- cat >"${profile_dir}/control-plane.yaml" <<EOF
54
- schema_version: "1"
55
- id: "demo"
56
- repo:
57
- slug: "${repo_slug}"
58
- root: "${tmpdir}/repo"
59
- default_branch: "main"
60
- runtime:
61
- orchestrator_agent_root: "${tmpdir}/runtime/demo"
62
- worktree_root: "${tmpdir}/worktrees"
63
- agent_repo_root: "${tmpdir}/repo"
64
- runs_root: "${runs_root}"
65
- state_root: "${state_root}"
66
- history_root: "${tmpdir}/runtime/demo/history"
67
- retained_repo_root: "${tmpdir}/repo"
68
- vscode_workspace_file: "${tmpdir}/demo.code-workspace"
69
- session_naming:
70
- issue_prefix: "demo-issue-"
71
- pr_prefix: "demo-pr-"
72
- execution:
73
- coding_worker: "openclaw"
74
- openclaw:
75
- model: "primary/model"
76
- thinking: "adaptive"
77
- timeout_seconds: 900
78
- EOF
79
-
80
- cat >"${recurring_run_dir}/run.env" <<EOF
81
- TASK_KIND=issue
82
- TASK_ID=2
83
- SESSION=demo-issue-2
84
- MODE=safe
85
- STARTED_AT=2026-03-27T11:00:00Z
86
- CODING_WORKER=openclaw
87
- WORKTREE=${tmpdir}/worktrees/issue-2
88
- BRANCH=agent/demo/issue-2
89
- RESIDENT_WORKER_KEY=${recurring_worker_key}
90
- OPENCLAW_MODEL=primary/model
91
- EOF
92
-
93
- cat >"${recurring_run_dir}/runner.env" <<'EOF'
94
- RUNNER_STATE=succeeded
95
- THREAD_ID=thread-demo-2
96
- LAST_EXIT_CODE=0
97
- LAST_FAILURE_REASON=''
98
- UPDATED_AT=2026-03-27T11:03:00Z
99
- EOF
100
-
101
- cat >"${recurring_run_dir}/result.env" <<'EOF'
102
- OUTCOME=implemented
103
- ACTION=host-publish-issue-pr
104
- EOF
105
-
106
- cat >"${scheduled_run_dir}/run.env" <<EOF
107
- TASK_KIND=issue
108
- TASK_ID=3
109
- SESSION=demo-issue-3
110
- MODE=safe
111
- STARTED_AT=2026-03-27T11:10:00Z
112
- CODING_WORKER=openclaw
113
- WORKTREE=${tmpdir}/worktrees/issue-3
114
- BRANCH=agent/demo/issue-3
115
- RESIDENT_WORKER_KEY=${scheduled_worker_key}
116
- OPENCLAW_MODEL=primary/model
117
- EOF
118
-
119
- cat >"${scheduled_run_dir}/runner.env" <<'EOF'
120
- RUNNER_STATE=succeeded
121
- THREAD_ID=thread-demo-3
122
- LAST_EXIT_CODE=0
123
- LAST_FAILURE_REASON=''
124
- UPDATED_AT=2026-03-27T11:12:00Z
125
- EOF
126
-
127
- cat >"${scheduled_run_dir}/result.env" <<'EOF'
128
- OUTCOME=reported
129
- ACTION=host-comment-scheduled-report
130
- EOF
131
-
132
- cat >"${heartbeat_script}" <<'EOF'
133
- #!/usr/bin/env bash
134
- set -euo pipefail
135
- sleep 60
136
- EOF
137
- chmod +x "${heartbeat_script}"
138
-
139
- cat >"${supervisor_script}" <<EOF
140
- #!/usr/bin/env bash
141
- set -euo pipefail
142
- bash "${heartbeat_script}" >/dev/null 2>&1 &
143
- child_pid=\$!
144
- printf '%s\n' "\${child_pid}" >"${heartbeat_child_pid_file}"
145
- wait "\${child_pid}"
146
- EOF
147
- chmod +x "${supervisor_script}"
148
-
149
- cat >"${shared_loop_script}" <<'EOF'
150
- #!/usr/bin/env bash
151
- set -euo pipefail
152
- sleep 60
153
- EOF
154
- chmod +x "${shared_loop_script}"
155
-
156
- bash "${supervisor_script}" >/dev/null 2>&1 &
157
- supervisor_pid="$!"
158
-
159
- for _ in $(seq 1 50); do
160
- if [[ -s "${heartbeat_child_pid_file}" ]]; then
161
- break
162
- fi
163
- sleep 0.1
164
- done
165
-
166
- heartbeat_pid="$(tr -d '[:space:]' <"${heartbeat_child_pid_file}")"
167
- if [[ -z "${heartbeat_pid}" ]]; then
168
- echo "failed to capture heartbeat pid" >&2
169
- exit 1
170
- fi
171
-
172
- printf '%s\n' "${heartbeat_pid}" >"${state_root}/heartbeat-loop.lock/pid"
173
- printf '%s\n' "${supervisor_pid}" >"${state_root}/runtime-supervisor.pid"
174
-
175
- bash "${shared_loop_script}" --repo-slug "${repo_slug}" >/dev/null 2>&1 &
176
- shared_loop_pid="$!"
177
- printf '%s\n' "${shared_loop_pid}" >"${state_root}/shared-heartbeat-loop.pid"
178
-
179
- sleep 60 >/dev/null 2>&1 &
180
- controller_pid="$!"
181
-
182
- cat >"${state_root}/resident-workers/issues/2/controller.env" <<EOF
183
- ISSUE_ID=2
184
- SESSION=demo-issue-2
185
- CONTROLLER_PID=${controller_pid}
186
- CONTROLLER_MODE=safe
187
- CONTROLLER_LOOP_COUNT=4
188
- CONTROLLER_STATE=waiting-provider
189
- CONTROLLER_REASON=provider-cooldown
190
- ACTIVE_RESIDENT_WORKER_KEY=${recurring_worker_key}
191
- ACTIVE_RESIDENT_LANE_KIND=recurring
192
- ACTIVE_RESIDENT_LANE_VALUE=general
193
- ACTIVE_PROVIDER_BACKEND=openclaw
194
- ACTIVE_PROVIDER_MODEL=primary/model
195
- ACTIVE_PROVIDER_KEY=openclaw-primary-model
196
- PROVIDER_SWITCH_COUNT=2
197
- PROVIDER_FAILOVER_COUNT=1
198
- PROVIDER_WAIT_COUNT=3
199
- PROVIDER_WAIT_TOTAL_SECONDS=90
200
- PROVIDER_LAST_WAIT_SECONDS=30
201
- UPDATED_AT=2026-03-27T11:13:00Z
202
- EOF
203
-
204
- cat >"${state_root}/resident-workers/issues/${recurring_worker_key}/metadata.env" <<EOF
205
- RESIDENT_WORKER_KIND=issue
206
- RESIDENT_WORKER_SCOPE=lane
207
- RESIDENT_WORKER_KEY=${recurring_worker_key}
208
- RESIDENT_LANE_KIND=recurring
209
- RESIDENT_LANE_VALUE=general
210
- ISSUE_ID=2
211
- CODING_WORKER=openclaw
212
- TASK_COUNT=5
213
- LAST_STATUS=SUCCEEDED
214
- LAST_STARTED_AT=2026-03-27T11:00:00Z
215
- LAST_FINISHED_AT=2026-03-27T11:03:00Z
216
- LAST_RUN_SESSION=demo-issue-2
217
- LAST_OUTCOME=implemented
218
- LAST_ACTION=host-publish-issue-pr
219
- LAST_FAILURE_REASON=
220
- EOF
221
-
222
- cat >"${state_root}/resident-workers/issues/${scheduled_worker_key}/metadata.env" <<EOF
223
- RESIDENT_WORKER_KIND=issue
224
- RESIDENT_WORKER_SCOPE=lane
225
- RESIDENT_WORKER_KEY=${scheduled_worker_key}
226
- RESIDENT_LANE_KIND=scheduled
227
- RESIDENT_LANE_VALUE=1800
228
- ISSUE_ID=3
229
- CODING_WORKER=openclaw
230
- TASK_COUNT=2
231
- LAST_STATUS=SUCCEEDED
232
- LAST_STARTED_AT=2026-03-27T11:10:00Z
233
- LAST_FINISHED_AT=2026-03-27T11:12:00Z
234
- LAST_RUN_SESSION=demo-issue-3
235
- LAST_OUTCOME=reported
236
- LAST_ACTION=host-comment-scheduled-report
237
- LAST_FAILURE_REASON=
238
- EOF
239
-
240
- cat >"${state_root}/scheduled-issues/3.env" <<'EOF'
241
- INTERVAL_SECONDS=1800
242
- LAST_STARTED_AT=2026-03-27T11:10:00Z
243
- NEXT_DUE_AT=2026-03-27T11:40:00Z
244
- UPDATED_AT=2026-03-27T11:12:00Z
245
- EOF
246
-
247
- cat >"${state_root}/retries/providers/openclaw-primary-model.env" <<'EOF'
248
- ATTEMPTS=2
249
- NEXT_ATTEMPT_EPOCH=4102444800
250
- NEXT_ATTEMPT_AT=2100-01-01T00:00:00Z
251
- LAST_REASON=provider-quota-limit
252
- UPDATED_AT=2026-03-27T11:13:00Z
253
- EOF
254
-
255
- cat >"${state_root}/resident-workers/issue-queue/pending/issue-6.env" <<'EOF'
256
- ISSUE_ID=6
257
- SESSION=demo-issue-6
258
- UPDATED_AT=2026-03-27T11:13:30Z
259
- EOF
260
-
261
- snapshot_file="${tmpdir}/snapshot.json"
262
- api_snapshot_file="${tmpdir}/api-snapshot.json"
263
-
264
- ACP_PROFILE_REGISTRY_ROOT="${profile_registry_root}" python3 "${SNAPSHOT_BIN}" --pretty >"${snapshot_file}"
265
-
266
- bash "${SERVER_BIN}" --host 127.0.0.1 --port "${port}" --registry-root "${profile_registry_root}" >"${tmpdir}/server.log" 2>&1 &
267
- server_pid="$!"
268
-
269
- for _ in $(seq 1 10); do
270
- if curl -fsS "http://127.0.0.1:${port}/api/snapshot.json" >/dev/null 2>&1; then
271
- break
272
- fi
273
- sleep 1
274
- done
275
-
276
- curl -fsS "http://127.0.0.1:${port}/api/snapshot.json" >"${api_snapshot_file}"
277
- html="$(curl -fsS "http://127.0.0.1:${port}/")"
278
-
279
- status_output="$(
280
- ACP_PROFILE_REGISTRY_ROOT="${profile_registry_root}" \
281
- ACP_PROJECT_RUNTIME_LAUNCHCTL_BIN="/nonexistent" \
282
- bash "${RUNTIMECTL_BIN}" status --profile-id demo
283
- )"
284
-
285
- grep -q '^RUNTIME_STATUS=running$' <<<"${status_output}"
286
- grep -q "^HEARTBEAT_PID=${heartbeat_pid}$" <<<"${status_output}"
287
- grep -q "^HEARTBEAT_PARENT_PID=${supervisor_pid}$" <<<"${status_output}"
288
- grep -q "^SHARED_LOOP_PID=${shared_loop_pid}$" <<<"${status_output}"
289
- grep -q "^SUPERVISOR_PID=${supervisor_pid}$" <<<"${status_output}"
290
- grep -q '^CONTROLLER_COUNT=1$' <<<"${status_output}"
291
-
292
- grep -q 'ACP Worker Dashboard' <<<"${html}"
293
- grep -q 'Track active runs, resident controllers, queue pressure, and provider failover in one place.' <<<"${html}"
294
-
295
- python3 - "${snapshot_file}" "${api_snapshot_file}" <<'PY'
296
- import json
297
- import sys
298
-
299
- snapshot_path, api_path = sys.argv[1], sys.argv[2]
300
- snapshot = json.load(open(snapshot_path, encoding="utf-8"))
301
- api_snapshot = json.load(open(api_path, encoding="utf-8"))
302
-
303
- assert snapshot["profile_count"] == 1
304
- assert api_snapshot["profile_count"] == 1
305
-
306
- profile = snapshot["profiles"][0]
307
- api_profile = api_snapshot["profiles"][0]
308
-
309
- assert profile["id"] == "demo"
310
- assert profile["repo_slug"] == "example/demo-control-plane"
311
- assert profile["counts"]["implemented_runs"] == 1
312
- assert profile["counts"]["reported_runs"] == 1
313
- assert profile["counts"]["blocked_runs"] == 0
314
- assert profile["counts"]["live_resident_controllers"] == 1
315
- assert profile["counts"]["resident_workers"] == 2
316
- assert profile["counts"]["queued_issues"] == 1
317
- assert profile["counts"]["provider_cooldowns"] == 1
318
- assert profile["counts"]["scheduled_issues"] == 1
319
-
320
- runs = {item["session"]: item for item in profile["runs"]}
321
- assert runs["demo-issue-2"]["result_kind"] == "implemented"
322
- assert runs["demo-issue-2"]["action"] == "host-publish-issue-pr"
323
- assert runs["demo-issue-3"]["result_kind"] == "reported"
324
- assert runs["demo-issue-3"]["action"] == "host-comment-scheduled-report"
325
-
326
- controllers = {item["issue_id"]: item for item in profile["resident_controllers"]}
327
- controller = controllers["2"]
328
- assert controller["state"] == "waiting-provider"
329
- assert controller["controller_live"] is True
330
- assert controller["provider_failover_count"] == 1
331
- assert controller["provider_wait_total_seconds"] == 90
332
-
333
- workers = {item["key"]: item for item in profile["resident_workers"]}
334
- assert workers["issue-lane-recurring-general-openclaw-safe"]["last_action"] == "host-publish-issue-pr"
335
- assert workers["issue-lane-scheduled-1800-openclaw-safe"]["last_action"] == "host-comment-scheduled-report"
336
-
337
- assert api_profile["counts"]["implemented_runs"] == 1
338
- assert api_profile["counts"]["reported_runs"] == 1
339
- assert api_profile["counts"]["live_resident_controllers"] == 1
340
- assert api_profile["counts"]["scheduled_issues"] == 1
341
- PY
342
-
343
- echo "control plane dashboard runtime smoke test passed"
@@ -1,73 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
-
4
- FLOW_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
5
- SCRIPT="${FLOW_ROOT}/tools/bin/create-follow-up-issue.sh"
6
-
7
- tmpdir="$(mktemp -d)"
8
- trap 'rm -rf "$tmpdir"' EXIT
9
-
10
- bin_dir="$tmpdir/bin"
11
- labels_log="$tmpdir/labels.log"
12
- body_capture="$tmpdir/body.txt"
13
- mkdir -p "$bin_dir"
14
-
15
- cat >"$bin_dir/gh" <<'EOF'
16
- #!/usr/bin/env bash
17
- set -euo pipefail
18
-
19
- if [[ "${1:-}" == "issue" && "${2:-}" == "create" ]]; then
20
- body_file=""
21
- while [[ $# -gt 0 ]]; do
22
- case "$1" in
23
- --body-file)
24
- body_file="${2:-}"
25
- shift 2
26
- ;;
27
- *)
28
- shift
29
- ;;
30
- esac
31
- done
32
- cat "$body_file" >"${TEST_BODY_CAPTURE:?}"
33
- printf 'https://github.com/example/repo/issues/912\n'
34
- exit 0
35
- fi
36
-
37
- echo "unexpected gh args: $*" >&2
38
- exit 1
39
- EOF
40
- chmod +x "$bin_dir/gh"
41
-
42
- cat >"$bin_dir/agent-github-update-labels" <<'EOF'
43
- #!/usr/bin/env bash
44
- set -euo pipefail
45
- printf '%s\n' "$*" >"${TEST_LABELS_LOG:?}"
46
- EOF
47
- chmod +x "$bin_dir/agent-github-update-labels"
48
-
49
- body_file="$tmpdir/follow-up.md"
50
- cat >"$body_file" <<'EOF'
51
- Target slice: mobile settings language cluster
52
-
53
- - Keep PR to one route family
54
- - Reuse account cluster shell
55
- EOF
56
-
57
- TEST_BODY_CAPTURE="$body_capture" \
58
- TEST_LABELS_LOG="$labels_log" \
59
- UPDATE_LABELS_BIN="$bin_dir/agent-github-update-labels" \
60
- PATH="$bin_dir:$PATH" \
61
- bash "$SCRIPT" \
62
- --parent 421 \
63
- --title "Mobile settings: split language cluster from umbrella" \
64
- --body-file "$body_file" \
65
- --label agent-e2e-heavy >"$tmpdir/out.txt"
66
-
67
- grep -q '^ISSUE_NUMBER=912$' "$tmpdir/out.txt"
68
- grep -q '^ISSUE_URL=https://github.com/example/repo/issues/912$' "$tmpdir/out.txt"
69
- grep -q '^Parent issue: #421$' "$body_capture"
70
- grep -q 'Target slice: mobile settings language cluster' "$body_capture"
71
- grep -q -- '--number 912 --add agent-e2e-heavy' "$labels_log"
72
-
73
- echo "create follow-up issue test passed"
@@ -1,55 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
-
4
- FLOW_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
5
- BOOTSTRAP_BIN="${FLOW_ROOT}/tools/bin/dashboard-launchd-bootstrap.sh"
6
-
7
- tmpdir="$(mktemp -d)"
8
- trap 'rm -rf "$tmpdir"' EXIT
9
-
10
- home_dir="$tmpdir/home"
11
- runtime_home="$tmpdir/runtime-home"
12
- profile_registry_root="$tmpdir/profiles"
13
- capture_dir="$tmpdir/capture"
14
- sync_script="$tmpdir/sync.sh"
15
- runtime_serve_script="$runtime_home/skills/openclaw/agent-control-plane/tools/bin/serve-dashboard.sh"
16
-
17
- mkdir -p "$home_dir" "$profile_registry_root" "$(dirname "$runtime_serve_script")" "$capture_dir"
18
-
19
- cat >"$sync_script" <<'EOF'
20
- #!/usr/bin/env bash
21
- set -euo pipefail
22
- printf 'SYNC_SOURCE=%s\n' "$1" >"${ACP_DASHBOARD_CAPTURE_DIR}/sync.log"
23
- printf 'SYNC_TARGET=%s\n' "$2" >>"${ACP_DASHBOARD_CAPTURE_DIR}/sync.log"
24
- EOF
25
- chmod +x "$sync_script"
26
-
27
- cat >"$runtime_serve_script" <<'EOF'
28
- #!/usr/bin/env bash
29
- set -euo pipefail
30
- printf 'ARGS=%s %s %s %s\n' "$1" "$2" "$3" "$4" >"${ACP_DASHBOARD_CAPTURE_DIR}/serve.log"
31
- printf 'PROFILE_REGISTRY_ROOT=%s\n' "${ACP_PROFILE_REGISTRY_ROOT:-}" >>"${ACP_DASHBOARD_CAPTURE_DIR}/serve.log"
32
- printf 'HOME=%s\n' "${HOME:-}" >>"${ACP_DASHBOARD_CAPTURE_DIR}/serve.log"
33
- printf 'PYTHONDONTWRITEBYTECODE=%s\n' "${PYTHONDONTWRITEBYTECODE:-}" >>"${ACP_DASHBOARD_CAPTURE_DIR}/serve.log"
34
- EOF
35
- chmod +x "$runtime_serve_script"
36
-
37
- ACP_DASHBOARD_HOME_DIR="$home_dir" \
38
- ACP_DASHBOARD_SOURCE_HOME="$tmpdir/source-home" \
39
- ACP_DASHBOARD_RUNTIME_HOME="$runtime_home" \
40
- ACP_DASHBOARD_PROFILE_REGISTRY_ROOT="$profile_registry_root" \
41
- ACP_DASHBOARD_HOST="127.0.0.1" \
42
- ACP_DASHBOARD_PORT="9911" \
43
- ACP_DASHBOARD_SYNC_SCRIPT="$sync_script" \
44
- ACP_DASHBOARD_RUNTIME_SERVE_SCRIPT="$runtime_serve_script" \
45
- ACP_DASHBOARD_CAPTURE_DIR="$capture_dir" \
46
- bash "$BOOTSTRAP_BIN"
47
-
48
- grep -q "^SYNC_SOURCE=$tmpdir/source-home$" "$capture_dir/sync.log"
49
- grep -q "^SYNC_TARGET=$runtime_home$" "$capture_dir/sync.log"
50
- grep -q '^ARGS=--host 127.0.0.1 --port 9911$' "$capture_dir/serve.log"
51
- grep -q "^PROFILE_REGISTRY_ROOT=$profile_registry_root$" "$capture_dir/serve.log"
52
- grep -q "^HOME=$home_dir$" "$capture_dir/serve.log"
53
- grep -q '^PYTHONDONTWRITEBYTECODE=1$' "$capture_dir/serve.log"
54
-
55
- echo "dashboard launchd bootstrap test passed"
@@ -1,30 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
-
4
- FLOW_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
5
- CONFIG_LIB="${FLOW_ROOT}/tools/bin/flow-config-lib.sh"
6
-
7
- tmpdir="$(mktemp -d)"
8
- trap 'rm -rf "$tmpdir"' EXIT
9
-
10
- config_file="$tmpdir/control-plane.yaml"
11
-
12
- cat >"$config_file" <<'EOF'
13
- schema_version: "1"
14
- id: "demo"
15
- repo:
16
- slug: "example/repo"
17
- id: "123"
18
- default_branch: "main"
19
- EOF
20
-
21
- output="$(
22
- bash -lc 'source "'"$CONFIG_LIB"'"; flow_export_execution_env "'"$config_file"'"; printf "ACP_REPO_ID=%s\nF_LOSNING_REPO_ID=%s\nACP_GITHUB_REPOSITORY_ID=%s\nF_LOSNING_GITHUB_REPOSITORY_ID=%s\n" "${ACP_REPO_ID:-}" "${F_LOSNING_REPO_ID:-}" "${ACP_GITHUB_REPOSITORY_ID:-}" "${F_LOSNING_GITHUB_REPOSITORY_ID:-}"'
23
- )"
24
-
25
- grep -q '^ACP_REPO_ID=123$' <<<"$output"
26
- grep -q '^F_LOSNING_REPO_ID=123$' <<<"$output"
27
- grep -q '^ACP_GITHUB_REPOSITORY_ID=123$' <<<"$output"
28
- grep -q '^F_LOSNING_GITHUB_REPOSITORY_ID=123$' <<<"$output"
29
-
30
- echo "flow export execution env exports repo id test passed"
@@ -1,48 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
-
4
- FLOW_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
5
- REAL_CONFIG_LIB="${FLOW_ROOT}/tools/bin/flow-config-lib.sh"
6
- REAL_SHELL_LIB="${FLOW_ROOT}/tools/bin/flow-shell-lib.sh"
7
-
8
- tmpdir="$(mktemp -d)"
9
- trap 'rm -rf "$tmpdir"' EXIT
10
-
11
- bin_dir="$tmpdir/bin"
12
- mkdir -p "$bin_dir"
13
-
14
- cp "$REAL_CONFIG_LIB" "$bin_dir/flow-config-lib.sh"
15
- cp "$REAL_SHELL_LIB" "$bin_dir/flow-shell-lib.sh"
16
-
17
- cat >"$bin_dir/git" <<'EOF'
18
- #!/usr/bin/env bash
19
- set -euo pipefail
20
- if [[ "${1:-}" == "credential" && "${2:-}" == "fill" ]]; then
21
- cat >/dev/null
22
- printf 'protocol=https\n'
23
- printf 'host=github.com\n'
24
- printf 'username=test-user\n'
25
- printf 'password=repo-specific-token\n'
26
- exit 0
27
- fi
28
- exit 1
29
- EOF
30
- chmod +x "$bin_dir/git"
31
-
32
- output="$(
33
- LIB_PATH="$bin_dir/flow-config-lib.sh" \
34
- PATH="$bin_dir:/usr/bin:/bin:/usr/sbin:/sbin" \
35
- GITHUB_PERSONAL_ACCESS_TOKEN="env-fallback-token" \
36
- bash <<'EOF'
37
- set -euo pipefail
38
- source "$LIB_PATH"
39
- unset GH_TOKEN
40
- unset GITHUB_TOKEN
41
- flow_export_github_cli_auth_env "owner/repo"
42
- printf 'GH_TOKEN=%s\n' "${GH_TOKEN:-}"
43
- EOF
44
- )"
45
-
46
- grep -q '^GH_TOKEN=repo-specific-token$' <<<"$output"
47
-
48
- echo "flow export github cli auth env prefers git credential test passed"
@@ -1,85 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
-
4
- FLOW_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
5
- REAL_CONFIG_LIB="${FLOW_ROOT}/tools/bin/flow-config-lib.sh"
6
- REAL_SHELL_LIB="${FLOW_ROOT}/tools/bin/flow-shell-lib.sh"
7
-
8
- tmpdir="$(mktemp -d)"
9
- trap 'rm -rf "$tmpdir"' EXIT
10
-
11
- bin_dir="$tmpdir/bin"
12
- mkdir -p "$bin_dir"
13
-
14
- cp "$REAL_CONFIG_LIB" "$bin_dir/flow-config-lib.sh"
15
- cp "$REAL_SHELL_LIB" "$bin_dir/flow-shell-lib.sh"
16
-
17
- captured_payload_file="$tmpdir/captured-payload.json"
18
- cat >"$bin_dir/gh" <<'EOF'
19
- #!/usr/bin/env bash
20
- set -euo pipefail
21
-
22
- if [[ "${1:-}" != "api" ]]; then
23
- echo "unexpected gh invocation: $*" >&2
24
- exit 1
25
- fi
26
-
27
- route="${2:-}"
28
- shift 2 || true
29
-
30
- input_file=""
31
- while [[ $# -gt 0 ]]; do
32
- case "$1" in
33
- --input)
34
- input_file="${2:-}"
35
- shift 2
36
- ;;
37
- *)
38
- shift
39
- ;;
40
- esac
41
- done
42
-
43
- case "$route" in
44
- repos/example/repo/issues/42)
45
- if [[ -n "$input_file" && -f "$input_file" ]]; then
46
- cat "$input_file" >/dev/null
47
- fi
48
- exit 1
49
- ;;
50
- repositories/123/issues/42)
51
- if [[ -z "$input_file" || ! -f "$input_file" ]]; then
52
- echo "missing preserved input file" >&2
53
- exit 97
54
- fi
55
- cp "$input_file" "${TEST_CAPTURED_PAYLOAD_FILE:?}"
56
- printf '{"number":42,"state":"open"}\n'
57
- exit 0
58
- ;;
59
- *)
60
- echo "unexpected gh api route: $route" >&2
61
- exit 1
62
- ;;
63
- esac
64
- EOF
65
-
66
- chmod +x "$bin_dir/flow-config-lib.sh" "$bin_dir/flow-shell-lib.sh" "$bin_dir/gh"
67
-
68
- output="$(
69
- LIB_PATH="$bin_dir/flow-config-lib.sh" \
70
- PATH="$bin_dir:/usr/bin:/bin:/usr/sbin:/sbin" \
71
- ACP_REPO_ID="123" \
72
- ACP_REPO_SLUG="example/repo" \
73
- TEST_CAPTURED_PAYLOAD_FILE="$captured_payload_file" \
74
- bash <<'EOF'
75
- set -euo pipefail
76
- source "$LIB_PATH"
77
- printf '{"body":"patched body"}' | flow_github_api_repo "example/repo" "issues/42" --method PATCH --input -
78
- EOF
79
- )"
80
-
81
- test "$(jq -r '.number' <<<"$output")" = "42"
82
- test "$(jq -r '.state' <<<"$output")" = "open"
83
- test "$(jq -r '.body' "$captured_payload_file")" = "patched body"
84
-
85
- echo "flow github api repo fallback preserves input test passed"