Files
OpenShell/scripts/agents/runtime/supervisor_test.sh
45060f4492 feat(agents): add manifest-driven gator agent (#1826)
* chore(gator): add gator gate skill

* chore(gator): add sandbox launcher scaffold

* chore(gator): add codex image and docs checks

* chore(gator): fold approved provider policy rules

* chore(gator): add deterministic reviewer runner

* chore(gator): clarify ok-to-test comments

* chore(gator): structure launcher harnesses

* chore(gator): require e2e for dependabot

* chore(gator): add codex refresh profile

* chore(gator): wip manifest agent launcher

* feat(agents): supervise watch cycles in sandbox

* fix(agents): preserve gateway refresh state

* fix(gator): continue human response threads

* fix(agents): keep watch supervisor retrying

* fix(agents): use refreshed Codex credential aliases

* fix(gator): avoid misleading gh auth checks

* docs(agents): remove architecture build update

* fix(gator): use REST-backed GitHub writes

* fix(agents): bake immutable agent payloads

* fix(agents): upload writable agent workspace

* fix(agents): surface gator watch progress

* fix(agents): prevent codex stdin hang

* fix(agents): align codex subagent input

* fix(agents): heartbeat during active cycles

* fix(agents): clean up heartbeat sleep

* fix(agents): disable gh telemetry in codex harness

* fix(agents): reconcile closed gator PRs

* fix(agents): query closed gator PR labels separately

* fix(agents): tolerate rotated credential placeholders

* fix(agents): enforce gator same-sha comment guard

Signed-off-by: John Myers <johntmyers@users.noreply.github.com>

* docs(agents): scope gator trusted commentary

Signed-off-by: John Myers <johntmyers@users.noreply.github.com>

* fix(gator): treat reviewer failures as transient

Signed-off-by: Evan Lezar <elezar@nvidia.com>

* feat(agents): refine gator supervised workflow

Signed-off-by: John Myers <johntmyers@users.noreply.github.com>

* fix(agents): stream codex prompts via stdin

Signed-off-by: John Myers <johntmyers@users.noreply.github.com>

* docs(agents): clarify trusted gator responses

Signed-off-by: John Myers <johntmyers@users.noreply.github.com>

* refactor(agents): scope gator PR to scripts

Signed-off-by: John Myers <johntmyers@users.noreply.github.com>

---------

Signed-off-by: John Myers <johntmyers@users.noreply.github.com>
Signed-off-by: Evan Lezar <elezar@nvidia.com>
Co-authored-by: John Myers <johntmyers@users.noreply.github.com>
Co-authored-by: Evan Lezar <elezar@nvidia.com>
2026-07-01 10:22:40 -07:00

227 lines
7.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
set -euo pipefail
SUPERVISOR_UNDER_TEST="${SUPERVISOR_UNDER_TEST:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/supervisor.sh}"
fail() {
printf 'not ok - %s\n' "$*" >&2
exit 1
}
assert_contains() {
local file="$1"
local expected="$2"
if ! grep -Fq "$expected" "$file"; then
printf 'missing expected text: %s\n' "$expected" >&2
printf '%s\n' '--- output ---' >&2
sed -n '1,200p' "$file" >&2
fail "assert_contains failed"
fi
}
make_payload() {
local dir="$1"
local adapter_body="$2"
mkdir -p "$dir/runtime/harnesses/test"
printf 'test prompt\n' > "$dir/agent-prompt.md"
cp "$SUPERVISOR_UNDER_TEST" "$dir/runtime/supervisor.sh"
cat > "$dir/runtime/harnesses/test/exec.sh" <<EOF
#!/usr/bin/env bash
set -euo pipefail
$adapter_body
EOF
chmod +x "$dir/runtime/harnesses/test/exec.sh"
}
run_supervisor() {
local payload_dir="$1"
local mode="$2"
local output_file="$3"
set +e
OPENSHELL_AGENT_HARNESS=test \
OPENSHELL_AGENT_RUN_MODE="$mode" \
OPENSHELL_AGENT_POLL_INTERVAL_SECONDS="${OPENSHELL_AGENT_POLL_INTERVAL_SECONDS:-1}" \
OPENSHELL_AGENT_MAX_TRANSIENT_FAILURES=2 \
OPENSHELL_AGENT_HEARTBEAT_SECONDS="${OPENSHELL_AGENT_HEARTBEAT_SECONDS:-0}" \
OPENSHELL_AGENT_TEST_STATE="${OPENSHELL_AGENT_TEST_STATE:-}" \
bash "$payload_dir/runtime/supervisor.sh" > "$output_file" 2>&1
local status=$?
set -e
return "$status"
}
test_once_requires_sentinel() {
local tmp
tmp="$(mktemp -d)"
make_payload "$tmp/payload" "exit 0"
if run_supervisor "$tmp/payload" once "$tmp/output"; then
fail "once mode succeeded without sentinel"
fi
printf 'ok - once requires sentinel\n'
}
test_watch_retries_missing_sentinel_until_complete() {
local tmp
tmp="$(mktemp -d)"
make_payload "$tmp/payload" '
state_file="${OPENSHELL_AGENT_TEST_STATE:?}"
count=0
if [[ -f "$state_file" ]]; then
count="$(cat "$state_file")"
fi
count=$((count + 1))
printf "%s\n" "$count" > "$state_file"
if [[ "$count" -lt 3 ]]; then
printf "%s\n" "ERROR: stream disconnected before completion" >&2
exit 1
fi
printf "%s\n" "OPENSHELL_AGENT_RESULT {\"status\":\"complete\",\"reason\":\"done\"}"
'
OPENSHELL_AGENT_TEST_STATE="$tmp/state" run_supervisor "$tmp/payload" watch "$tmp/output"
assert_contains "$tmp/output" "transient watch failure 1"
assert_contains "$tmp/output" "transient watch failure 2"
assert_contains "$tmp/output" "openshell-agent: complete (done)"
printf 'ok - watch retries missing sentinel until complete\n'
}
test_watch_retries_invalid_status_until_complete() {
local tmp
tmp="$(mktemp -d)"
make_payload "$tmp/payload" '
state_file="${OPENSHELL_AGENT_TEST_STATE:?}"
count=0
if [[ -f "$state_file" ]]; then
count="$(cat "$state_file")"
fi
count=$((count + 1))
printf "%s\n" "$count" > "$state_file"
if [[ "$count" -lt 2 ]]; then
printf "%s\n" "OPENSHELL_AGENT_RESULT {\"status\":\"nonsense\",\"reason\":\"bad\"}"
exit 0
fi
printf "%s\n" "OPENSHELL_AGENT_RESULT {\"status\":\"complete\",\"reason\":\"done\"}"
'
OPENSHELL_AGENT_TEST_STATE="$tmp/state" run_supervisor "$tmp/payload" watch "$tmp/output"
assert_contains "$tmp/output" "invalid OPENSHELL_AGENT_RESULT status: nonsense"
assert_contains "$tmp/output" "openshell-agent: complete (done)"
printf 'ok - watch retries invalid status until complete\n'
}
test_watch_retries_malformed_terminal_json_until_complete() {
local tmp
tmp="$(mktemp -d)"
make_payload "$tmp/payload" '
state_file="${OPENSHELL_AGENT_TEST_STATE:?}"
count=0
if [[ -f "$state_file" ]]; then
count="$(cat "$state_file")"
fi
count=$((count + 1))
printf "%s\n" "$count" > "$state_file"
if [[ "$count" -lt 2 ]]; then
printf "%s\n" "OPENSHELL_AGENT_RESULT {\"status\":\"complete\""
exit 0
fi
printf "%s\n" "OPENSHELL_AGENT_RESULT {\"status\":\"complete\",\"reason\":\"done\"}"
'
OPENSHELL_AGENT_TEST_STATE="$tmp/state" run_supervisor "$tmp/payload" watch "$tmp/output"
assert_contains "$tmp/output" "malformed OPENSHELL_AGENT_RESULT JSON"
assert_contains "$tmp/output" "openshell-agent: complete (done)"
printf 'ok - watch retries malformed terminal JSON until complete\n'
}
test_watch_retries_failed_alias_until_complete() {
local tmp
tmp="$(mktemp -d)"
make_payload "$tmp/payload" '
state_file="${OPENSHELL_AGENT_TEST_STATE:?}"
count=0
if [[ -f "$state_file" ]]; then
count="$(cat "$state_file")"
fi
count=$((count + 1))
printf "%s\n" "$count" > "$state_file"
if [[ "$count" -lt 2 ]]; then
printf "%s\n" "OPENSHELL_AGENT_RESULT {\"status\":\"failed\",\"reason\":\"legacy\"}"
exit 0
fi
printf "%s\n" "OPENSHELL_AGENT_RESULT {\"status\":\"complete\",\"reason\":\"done\"}"
'
OPENSHELL_AGENT_TEST_STATE="$tmp/state" run_supervisor "$tmp/payload" watch "$tmp/output"
assert_contains "$tmp/output" "invalid OPENSHELL_AGENT_RESULT status: failed"
assert_contains "$tmp/output" "openshell-agent: complete (done)"
printf 'ok - watch retries failed alias until complete\n'
}
test_watch_terminal_failure_exits() {
local tmp
tmp="$(mktemp -d)"
make_payload "$tmp/payload" 'printf "%s\n" "OPENSHELL_AGENT_RESULT {\"status\":\"terminal_failure\",\"reason\":\"fatal\"}"'
if run_supervisor "$tmp/payload" watch "$tmp/output"; then
fail "watch mode succeeded after terminal failure"
fi
assert_contains "$tmp/output" "openshell-agent: terminal failure (fatal)"
printf 'ok - watch terminal failure exits\n'
}
test_watch_transient_failure_honors_next_poll_seconds() {
local tmp
tmp="$(mktemp -d)"
make_payload "$tmp/payload" '
state_file="${OPENSHELL_AGENT_TEST_STATE:?}"
count=0
if [[ -f "$state_file" ]]; then
count="$(cat "$state_file")"
fi
count=$((count + 1))
printf "%s\n" "$count" > "$state_file"
if [[ "$count" -lt 2 ]]; then
printf "%s\n" "OPENSHELL_AGENT_RESULT {\"status\":\"transient_failure\",\"reason\":\"github_transport_eof\",\"next_poll_seconds\":1}"
exit 0
fi
printf "%s\n" "OPENSHELL_AGENT_RESULT {\"status\":\"complete\",\"reason\":\"done\"}"
'
OPENSHELL_AGENT_TEST_STATE="$tmp/state" \
OPENSHELL_AGENT_POLL_INTERVAL_SECONDS=10 \
run_supervisor "$tmp/payload" watch "$tmp/output"
assert_contains "$tmp/output" "transient watch failure 1 (github_transport_eof); retrying in 1s"
assert_contains "$tmp/output" "openshell-agent: complete (done)"
printf 'ok - watch transient failure honors next_poll_seconds\n'
}
test_watch_prints_active_cycle_heartbeat() {
local tmp
tmp="$(mktemp -d)"
make_payload "$tmp/payload" '
sleep 2
printf "%s\n" "OPENSHELL_AGENT_RESULT {\"status\":\"complete\",\"reason\":\"done\"}"
'
OPENSHELL_AGENT_HEARTBEAT_SECONDS=1 run_supervisor "$tmp/payload" watch "$tmp/output"
assert_contains "$tmp/output" "openshell-agent: still running watch cycle 1 with harness test after 1s"
assert_contains "$tmp/output" "openshell-agent: complete (done)"
printf 'ok - watch prints active cycle heartbeat\n'
}
test_once_requires_sentinel
test_watch_retries_missing_sentinel_until_complete
test_watch_retries_invalid_status_until_complete
test_watch_retries_malformed_terminal_json_until_complete
test_watch_retries_failed_alias_until_complete
test_watch_terminal_failure_exits
test_watch_transient_failure_honors_next_poll_seconds
test_watch_prints_active_cycle_heartbeat