From 6e83d4b8210e3e200456aeffa9e89d5454de1144 Mon Sep 17 00:00:00 2001 From: Taylor Mutch Date: Fri, 16 Oct 2026 13:41:16 -0700 Subject: [PATCH 1/2] fix(agents): validate sandbox names before Gator launch setup Signed-off-by: Taylor Mutch --- .../skills/launch-openshell-gator/SKILL.md | 41 ++++++---- scripts/agents/README.md | 7 ++ scripts/agents/gator/README.md | 1 + scripts/agents/run.sh | 6 +- scripts/agents/run_test.sh | 82 +++++++++++++++++++ 5 files changed, 118 insertions(+), 19 deletions(-) create mode 100755 scripts/agents/run_test.sh diff --git a/.agents/skills/launch-openshell-gator/SKILL.md b/.agents/skills/launch-openshell-gator/SKILL.md index 2a516754ee..85553a4f01 100644 --- a/.agents/skills/launch-openshell-gator/SKILL.md +++ b/.agents/skills/launch-openshell-gator/SKILL.md @@ -109,7 +109,7 @@ gateway_name="" openshell --gateway "$gateway_name" sandbox list ``` -Look for names like `gator-pr--supervised`. If one exists, inspect its log before deleting or relaunching. +Look for names like `gtr-pr--watch`. If one exists, inspect its log before deleting or relaunching. ## Input Normalization @@ -129,11 +129,14 @@ pr_number="" [[ "$pr_number" =~ ^[0-9]+$ ]] || { echo "invalid PR number" >&2; exit 1; } ``` -Use the portable Kubernetes DNS-1123 sandbox-name format even when the selected gateway currently uses another driver: +Sandbox names must be DNS-1123 labels of at most **19 characters**, including +any suffix. The gateway reserves room for workspace, sandbox, and service +segments in one DNS label; this limit applies to every compute driver. Use +short names and validate them before launching: ```bash -sandbox_name="gator-pr-${pr_number}-supervised" -[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } +sandbox_name="gtr-pr-${pr_number}-watch" +[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,17}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } ``` For local image contexts passed to `--from`, use an agent-created path such as `mktemp -d`; do not pass raw user-supplied paths without validating that they are expected local Dockerfile contexts. @@ -149,8 +152,8 @@ gateway_name="" pr_number="" [[ "$gateway_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid gateway name" >&2; exit 1; } [[ "$pr_number" =~ ^[0-9]+$ ]] || { echo "invalid PR number" >&2; exit 1; } -sandbox_name="gator-pr-${pr_number}-supervised" -[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } +sandbox_name="gtr-pr-${pr_number}-watch" +[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,17}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } ./scripts/agents/run.sh \ --agent gator \ @@ -171,8 +174,8 @@ gateway_name="" issue_number="" [[ "$gateway_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid gateway name" >&2; exit 1; } [[ "$issue_number" =~ ^[0-9]+$ ]] || { echo "invalid issue number" >&2; exit 1; } -sandbox_name="gator-issue-${issue_number}-supervised" -[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } +sandbox_name="gtr-i-${issue_number}-watch" +[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,17}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } ./scripts/agents/run.sh \ --agent gator \ @@ -191,8 +194,8 @@ issue_number="" [[ "$gateway_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid gateway name" >&2; exit 1; } [[ "$pr_number" =~ ^[0-9]+$ ]] || { echo "invalid PR number" >&2; exit 1; } [[ "$issue_number" =~ ^[0-9]+$ ]] || { echo "invalid issue number" >&2; exit 1; } -sandbox_name="gator-pr-${pr_number}-supervised" -[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } +sandbox_name="gtr-pr-${pr_number}-watch" +[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,17}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } ./scripts/agents/run.sh \ --agent gator \ @@ -211,8 +214,8 @@ gateway_name="" pr_number="" [[ "$gateway_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid gateway name" >&2; exit 1; } [[ "$pr_number" =~ ^[0-9]+$ ]] || { echo "invalid PR number" >&2; exit 1; } -sandbox_name="gator-pr-${pr_number}-supervised" -[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } +sandbox_name="gtr-pr-${pr_number}-watch" +[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,17}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } ./scripts/agents/run.sh \ --agent gator \ @@ -231,8 +234,8 @@ gateway_name="" pr_number="" [[ "$gateway_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid gateway name" >&2; exit 1; } [[ "$pr_number" =~ ^[0-9]+$ ]] || { echo "invalid PR number" >&2; exit 1; } -sandbox_name="gator-pr-${pr_number}-gpt56sol-supervised" -[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } +sandbox_name="gtr-pr-${pr_number}-exp" +[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,17}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } CODEX_MODEL=gpt-5.6-sol \ ./scripts/agents/run.sh \ @@ -244,6 +247,8 @@ CODEX_MODEL=gpt-5.6-sol \ ``` If the installed Codex CLI is too old for a model, create a temporary copy of `scripts/agents/gator/`, adjust only that temporary Dockerfile, and launch with that generated context. Keep the repo Dockerfile unchanged unless the version bump is the intended code change. +The short `-exp` suffix below identifies an experimental watcher; keep its full +name within 19 characters. Example shape: @@ -252,8 +257,8 @@ gateway_name="" pr_number="" [[ "$gateway_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid gateway name" >&2; exit 1; } [[ "$pr_number" =~ ^[0-9]+$ ]] || { echo "invalid PR number" >&2; exit 1; } -sandbox_name="gator-pr-${pr_number}-gpt56sol-supervised" -[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } +sandbox_name="gtr-pr-${pr_number}-exp" +[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,17}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } tmp_context="$(mktemp -d "${TMPDIR:-/tmp}/gator-codex-XXXXXX")" cp -R scripts/agents/gator/. "$tmp_context"/ @@ -298,7 +303,7 @@ The launcher streams image-build and provisioning output to the terminal. Import gateway_name="" sandbox_name="" [[ "$gateway_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid gateway name" >&2; exit 1; } -[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } +[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,17}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } openshell --gateway "$gateway_name" sandbox list openshell --gateway "$gateway_name" sandbox get "$sandbox_name" @@ -338,7 +343,7 @@ Before deleting, check that the sandbox is truly stale or that the operator aske gateway_name="" sandbox_name="" [[ "$gateway_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid gateway name" >&2; exit 1; } -[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } +[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,17}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } openshell --gateway "$gateway_name" sandbox delete "$sandbox_name" ./scripts/agents/run.sh \ diff --git a/scripts/agents/README.md b/scripts/agents/README.md index 6c8735f569..3f878160e1 100644 --- a/scripts/agents/README.md +++ b/scripts/agents/README.md @@ -28,6 +28,13 @@ prompt templates, provider profiles, policies, and references to skills or subagents. They should not contain `harnesses/codex`, `harnesses/opencode`, or similar runtime code. +Sandbox names supplied through `--name`, `GATOR_SANDBOX_NAME`, or generated +from the manifest prefix must be DNS-1123 labels of at most 19 characters. +The launcher validates them before building images or changing gateway state. +For example, use `gtr-pr-4633-watch` for a supervised Gator watcher. Generated +names append a hyphen and a 14-digit timestamp, leaving four characters for +the manifest's name prefix. + ## Agent Manifest Each agent has an `agent.yaml` manifest. The launcher currently reads these diff --git a/scripts/agents/gator/README.md b/scripts/agents/gator/README.md index 350c0dd595..c7630707ec 100644 --- a/scripts/agents/gator/README.md +++ b/scripts/agents/gator/README.md @@ -71,6 +71,7 @@ The launcher preserves existing gateway-owned Codex refresh material by default ## Tests ```shell +bash scripts/agents/run_test.sh bash scripts/agents/gator/bin/gh_guard_test.sh bash scripts/agents/gator/bin/review_feedback_ledger_test.sh bash scripts/agents/gator/bin/resolve_gator_review_threads_test.sh diff --git a/scripts/agents/run.sh b/scripts/agents/run.sh index d778d33a72..2275aa69a3 100755 --- a/scripts/agents/run.sh +++ b/scripts/agents/run.sh @@ -32,7 +32,7 @@ usage() { Options: --agent NAME|PATH Agent manifest directory or name under scripts/agents/ --gateway NAME Gateway name to use - --name NAME Sandbox name + --name NAME Sandbox name (DNS-1123 label, at most 19 characters) --from DOCKERFILE|DIR Local Dockerfile source for the sandbox image --harness NAME Agent harness to run --github-provider NAME Override the github-gator provider instance name @@ -513,6 +513,10 @@ configure_provider_refresh() { GATEWAY="${GATEWAY_OVERRIDE:-${GATOR_GATEWAY:-$GATEWAY_DEFAULT}}" SANDBOX_NAME="${SANDBOX_NAME_OVERRIDE:-${GATOR_SANDBOX_NAME:-$SANDBOX_NAME_PREFIX-$(date +%Y%m%d%H%M%S)}}" +# Match the gateway's MAX_ROUTABLE_NAME_LEN: workspace, sandbox, and service +# segments must fit together in one DNS label. Check before any build or writes. +[[ ${#SANDBOX_NAME} -le 19 ]] || fail "invalid sandbox name '$SANDBOX_NAME': maximum length is 19 characters" +[[ "$SANDBOX_NAME" =~ ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ ]] || fail "invalid sandbox name '$SANDBOX_NAME': expected a DNS-1123 label (lowercase letters, digits, internal hyphens)" SANDBOX_FROM="${SANDBOX_FROM_OVERRIDE:-${GATOR_SANDBOX_FROM:-$(resolve_manifest_path "$SANDBOX_FROM_DEFAULT")}}" RUN_MODE="${RUN_MODE_OVERRIDE:-$RUNTIME_MODE}" POLL_INTERVAL_SECONDS="${POLL_INTERVAL_OVERRIDE:-$RUNTIME_POLL_INTERVAL_SECONDS}" diff --git a/scripts/agents/run_test.sh b/scripts/agents/run_test.sh new file mode 100755 index 0000000000..eec55cbc56 --- /dev/null +++ b/scripts/agents/run_test.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash + +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +TEST_DIR="$(mktemp -d)" +trap 'rm -rf "$TEST_DIR"' EXIT + +mkdir -p "$TEST_DIR/agent" +cat > "$TEST_DIR/agent/agent.yaml" <<'YAML' +id: test +sandbox: + name_prefix: gtr + from: agent://. +harness: + default: codex + supported: + codex: {} +prompt_template: prompt.md +YAML +printf 'FROM scratch\n' > "$TEST_DIR/agent/Dockerfile" +printf '{{USER_PROMPT}}\n' > "$TEST_DIR/agent/prompt.md" + +# Stop at the first gateway call, before any image build or gateway mutation. +cat > "$TEST_DIR/openshell" <<'MOCK' +#!/usr/bin/env bash +printf '%s\n' "$*" >> "$TEST_CALLS" +exit 1 +MOCK +chmod +x "$TEST_DIR/openshell" +export TEST_CALLS="$TEST_DIR/calls" +export OPENSHELL_BIN="$TEST_DIR/openshell" +unset GATOR_SANDBOX_NAME GATOR_SANDBOX_FROM OPENSHELL_AGENT_DIR + +reject_name() { + local expected="$1" + shift + : > "$TEST_CALLS" + if bash "$SCRIPT_DIR/run.sh" --agent "$TEST_DIR/agent" "$@" 'test prompt' > "$TEST_DIR/output" 2>&1; then + echo 'expected invalid sandbox name to fail' >&2 + exit 1 + fi + grep -q "$expected" "$TEST_DIR/output" + [[ ! -s "$TEST_CALLS" ]] + if grep -q 'Preparing\|Building\|Configuring' "$TEST_DIR/output"; then + echo 'name validation ran after launch preparation' >&2 + exit 1 + fi +} + +reject_name 'maximum length is 19' --name gator-pr-4633-supervised +reject_name 'maximum length is 19' --name abcdefghijklmnopqrst +for name in Gator gator_pr gator.pr -gator gator- 'gator name'; do + reject_name 'expected a DNS-1123 label' --name "$name" +done +GATOR_SANDBOX_NAME=gator-pr-4633-supervised reject_name 'maximum length is 19' +# An oversized manifest prefix also produces an invalid generated name. +sed 's/name_prefix: gtr/name_prefix: toolong/' "$TEST_DIR/agent/agent.yaml" > "$TEST_DIR/manifest" +cp "$TEST_DIR/manifest" "$TEST_DIR/agent/agent.yaml" +reject_name 'maximum length is 19' +sed 's/name_prefix: toolong/name_prefix: gtr/' "$TEST_DIR/agent/agent.yaml" > "$TEST_DIR/manifest" +cp "$TEST_DIR/manifest" "$TEST_DIR/agent/agent.yaml" +printf '%s\n' 'ok - invalid explicit, environment, and generated names fail before launch work' + +for name in a abcdefghijklmnopqrs gtr-pr-4633-watch; do + : > "$TEST_CALLS" + if bash "$SCRIPT_DIR/run.sh" --agent "$TEST_DIR/agent" --name "$name" 'test prompt' > "$TEST_DIR/output" 2>&1; then + echo 'expected gateway stub to stop launch' >&2 + exit 1 + fi + grep -q 'gateway info --output json' "$TEST_CALLS" +done +: > "$TEST_CALLS" +if bash "$SCRIPT_DIR/run.sh" --agent "$TEST_DIR/agent" 'test prompt' > "$TEST_DIR/output" 2>&1; then + echo 'expected gateway stub to stop launch' >&2 + exit 1 +fi +grep -q 'gateway info --output json' "$TEST_CALLS" +printf '%s\n' 'ok - one-character, 19-character, documented, and default generated names reach gateway preflight' From 827f053f08e52b6292869ae27c7517991cc94139 Mon Sep 17 00:00:00 2001 From: Taylor Mutch Date: Fri, 16 Oct 2026 13:44:12 -0700 Subject: [PATCH 2/2] chore(agents): remove standalone launcher test script Signed-off-by: Taylor Mutch --- scripts/agents/gator/README.md | 1 - scripts/agents/run_test.sh | 82 ---------------------------------- 2 files changed, 83 deletions(-) delete mode 100755 scripts/agents/run_test.sh diff --git a/scripts/agents/gator/README.md b/scripts/agents/gator/README.md index c7630707ec..350c0dd595 100644 --- a/scripts/agents/gator/README.md +++ b/scripts/agents/gator/README.md @@ -71,7 +71,6 @@ The launcher preserves existing gateway-owned Codex refresh material by default ## Tests ```shell -bash scripts/agents/run_test.sh bash scripts/agents/gator/bin/gh_guard_test.sh bash scripts/agents/gator/bin/review_feedback_ledger_test.sh bash scripts/agents/gator/bin/resolve_gator_review_threads_test.sh diff --git a/scripts/agents/run_test.sh b/scripts/agents/run_test.sh deleted file mode 100755 index eec55cbc56..0000000000 --- a/scripts/agents/run_test.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env bash - -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -TEST_DIR="$(mktemp -d)" -trap 'rm -rf "$TEST_DIR"' EXIT - -mkdir -p "$TEST_DIR/agent" -cat > "$TEST_DIR/agent/agent.yaml" <<'YAML' -id: test -sandbox: - name_prefix: gtr - from: agent://. -harness: - default: codex - supported: - codex: {} -prompt_template: prompt.md -YAML -printf 'FROM scratch\n' > "$TEST_DIR/agent/Dockerfile" -printf '{{USER_PROMPT}}\n' > "$TEST_DIR/agent/prompt.md" - -# Stop at the first gateway call, before any image build or gateway mutation. -cat > "$TEST_DIR/openshell" <<'MOCK' -#!/usr/bin/env bash -printf '%s\n' "$*" >> "$TEST_CALLS" -exit 1 -MOCK -chmod +x "$TEST_DIR/openshell" -export TEST_CALLS="$TEST_DIR/calls" -export OPENSHELL_BIN="$TEST_DIR/openshell" -unset GATOR_SANDBOX_NAME GATOR_SANDBOX_FROM OPENSHELL_AGENT_DIR - -reject_name() { - local expected="$1" - shift - : > "$TEST_CALLS" - if bash "$SCRIPT_DIR/run.sh" --agent "$TEST_DIR/agent" "$@" 'test prompt' > "$TEST_DIR/output" 2>&1; then - echo 'expected invalid sandbox name to fail' >&2 - exit 1 - fi - grep -q "$expected" "$TEST_DIR/output" - [[ ! -s "$TEST_CALLS" ]] - if grep -q 'Preparing\|Building\|Configuring' "$TEST_DIR/output"; then - echo 'name validation ran after launch preparation' >&2 - exit 1 - fi -} - -reject_name 'maximum length is 19' --name gator-pr-4633-supervised -reject_name 'maximum length is 19' --name abcdefghijklmnopqrst -for name in Gator gator_pr gator.pr -gator gator- 'gator name'; do - reject_name 'expected a DNS-1123 label' --name "$name" -done -GATOR_SANDBOX_NAME=gator-pr-4633-supervised reject_name 'maximum length is 19' -# An oversized manifest prefix also produces an invalid generated name. -sed 's/name_prefix: gtr/name_prefix: toolong/' "$TEST_DIR/agent/agent.yaml" > "$TEST_DIR/manifest" -cp "$TEST_DIR/manifest" "$TEST_DIR/agent/agent.yaml" -reject_name 'maximum length is 19' -sed 's/name_prefix: toolong/name_prefix: gtr/' "$TEST_DIR/agent/agent.yaml" > "$TEST_DIR/manifest" -cp "$TEST_DIR/manifest" "$TEST_DIR/agent/agent.yaml" -printf '%s\n' 'ok - invalid explicit, environment, and generated names fail before launch work' - -for name in a abcdefghijklmnopqrs gtr-pr-4633-watch; do - : > "$TEST_CALLS" - if bash "$SCRIPT_DIR/run.sh" --agent "$TEST_DIR/agent" --name "$name" 'test prompt' > "$TEST_DIR/output" 2>&1; then - echo 'expected gateway stub to stop launch' >&2 - exit 1 - fi - grep -q 'gateway info --output json' "$TEST_CALLS" -done -: > "$TEST_CALLS" -if bash "$SCRIPT_DIR/run.sh" --agent "$TEST_DIR/agent" 'test prompt' > "$TEST_DIR/output" 2>&1; then - echo 'expected gateway stub to stop launch' >&2 - exit 1 -fi -grep -q 'gateway info --output json' "$TEST_CALLS" -printf '%s\n' 'ok - one-character, 19-character, documented, and default generated names reach gateway preflight'