Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
236d5b4
ci(e2e): authorize NVIDIA-owned PR revisions
ericksoa Aug 17, 2026
889fe6d
Merge remote-tracking branch 'origin/main' into fix/e2e-authorized-br…
cv Aug 18, 2026
8ccda6c
fix(e2e): plan from trusted workflow source
cv Aug 18, 2026
a8e934b
fix(e2e): keep trusted planner dependencies local
cv Aug 18, 2026
0444124
docs(e2e): name the latest PR commit
cv Aug 18, 2026
b393f22
merge: incorporate current main into authorized E2E
cv Aug 18, 2026
7312b2f
test(e2e): satisfy growth checks
cv Aug 18, 2026
2aeb725
refactor(e2e): isolate trusted planner validation
cv Aug 18, 2026
7f8c339
Merge remote-tracking branch 'origin/main' into fix/e2e-authorized-br…
cv Aug 18, 2026
868d883
test(e2e): preserve Launchable concurrency
cv Aug 18, 2026
cbb6940
Merge remote-tracking branch 'origin/main' into fix/e2e-authorized-br…
cv Aug 18, 2026
4efaf06
fix(e2e): align authorized workflow contracts
cv Aug 18, 2026
b90b90f
test(e2e): align authorization fixtures
cv Aug 18, 2026
2cd4543
fix(e2e): authorize remaining PR credentials
cv Aug 18, 2026
72fdc79
test(e2e): clarify authorized credential cases
cv Aug 18, 2026
73d760e
test(e2e): name unauthorized credential case
cv Aug 18, 2026
dc86659
fix(e2e): restrict external default plan
cv Aug 18, 2026
b3e3d9c
test(e2e): use approved external selector
cv Aug 18, 2026
f7c0ee0
fix(e2e): enforce PR dispatch boundaries
cv Aug 18, 2026
1691596
test(e2e): name direct main authentication case
cv Aug 18, 2026
4bae484
merge(main): resolve E2E workflow conflicts
cv Aug 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 44 additions & 34 deletions .agents/skills/nemoclaw-maintainer-e2e/references/manual-pr.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,27 @@ check.
Before dispatch, read [Push and Manual PR E2E](../../../../test/e2e/README.md#push-and-manual-pr-e2e)
for the selected jobs' credential locations, access, lifetimes, and removal or cleanup boundaries.

Depending on its selection, a manual PR run can expose long-lived inference, Brave Search, and
messaging credentials to candidate-controlled jobs. Some jobs also expose a read-only job-scoped
`GITHUB_TOKEN`, messaging identifiers, or external resources.
An empty-selector NVIDIA-owned PR run can expose these values to candidate-controlled jobs:

- long-lived NVIDIA inference and Brave Search API keys;
- Docker Hub credentials through the job's temporary Docker configuration;
- long-lived Telegram, Discord, and Slack credentials;
- a read-only job-scoped `GITHUB_TOKEN` in jobs that need repository access; and
- messaging account and channel identifiers.

The workflow does not revoke long-lived credentials or erase identifiers copied by candidate code.
GitHub invalidates the job-scoped token after the job ends.

Before dispatch, review the complete candidate diff. After a failure:

- inspect artifacts;
- remove resources that cleanup left behind; and
- rotate or revoke exposed credentials when necessary.

`Exact staging Brev Launchable` is not available to manual PR runs. The checked-in workflow jobs
define narrower trusted-host boundaries for protected managed-image and native-runtime qualification.
`Exact staging Brev Launchable` is available only when the source is an NVIDIA-owned branch in
`NVIDIA/NemoClaw`. Its trusted host receives the Brev API key and image-dispatch token. The guest
receives the NVIDIA inference API key. The protected managed-image and native-runtime qualification
jobs define narrower trusted-host boundaries in the workflow.

## Resolve and Authorize the Revision

Expand All @@ -34,72 +43,70 @@ git fetch --prune origin main
WORKFLOW_SHA="$(git rev-parse origin/main)"
PR_JSON="$(gh api "repos/NVIDIA/NemoClaw/pulls/${PR_NUMBER}")"
test "$(jq -r .state <<<"$PR_JSON")" = open
test "$(jq -r .base.repo.full_name <<<"$PR_JSON")" = NVIDIA/NemoClaw
test "$(jq -r .base.ref <<<"$PR_JSON")" = main
HEAD_SHA="$(jq -r .head.sha <<<"$PR_JSON")"
BASE_SHA="$(jq -r .base.sha <<<"$PR_JSON")"
HEAD_REPOSITORY="$(jq -r .head.repo.full_name <<<"$PR_JSON")"
HEAD_OWNER="$(jq -r .head.repo.owner.login <<<"$PR_JSON")"
HEAD_OWNER_TYPE="$(jq -r .head.repo.owner.type <<<"$PR_JSON")"
[[ "$HEAD_SHA" =~ ^[0-9a-f]{40}$ ]]
[[ "$BASE_SHA" =~ ^[0-9a-f]{40}$ ]]
[[ "$WORKFLOW_SHA" =~ ^[0-9a-f]{40}$ ]]
```

Require a review reason containing 10 to 500 printable characters. Choose one allowed selection:
Choose the selection from the source owner:

- Empty jobs and targets: the trusted default PR selection.
- `jobs=inference-routing`.
- `jobs=managed-image-protected-runtime`. The candidate must contain `ci/protected-managed-image-multiarch-activation-v1.json` and `ci/protected-managed-image-runtime-activation-v1.json`.
- `jobs=native-runtime-qualification-producer` for a same-repository PR and first workflow attempt. The workflow SHA and PR base SHA must match. Set `NATIVE_RUNTIME_EPHEMERAL_RUNNER_POOL=enabled` and `NATIVE_RUNTIME_ARM64_GPU_RUNNER_LABEL` to the reviewed runner label. The candidate must contain `test/e2e/live/native-runtime-qualification-case.test.ts`.
- `targets=jetson-nvmap-gpu` with `allow_jetson_dispatch=true`, only after the operator confirms that
the service is available and compatible with HTTP contract version `1.0.0` and that
`JETSON_DISPATCH_URL` is set to the verified HTTPS origin. See
[Jetson Dispatch Controller](../../../../test/e2e/docs/jetson-dispatch.md).
- An NVIDIA-owned PR can use the full candidate plan and credential profiles. Empty selectors run
every default-enabled E2E. Any supported job or target selector is allowed.
- An external PR keeps the credential-free controller selection. Empty selectors run the trusted
default PR selection. The controller also permits `jobs=inference-routing`,
`jobs=managed-image-protected-runtime`, `jobs=native-runtime-qualification-producer`, or the
documented credential-free target selectors.

Do not rerun the same native-runtime workflow attempt.
Jetson and Launchable runs require a branch in `NVIDIA/NemoClaw`. Jetson also requires
`allow_jetson_dispatch=true` and the reviewed service configuration in
[Jetson Dispatch Controller](../../../../test/e2e/docs/jetson-dispatch.md).

Set one selector and its required flag, or leave all of them empty:
Set the requested selectors and flags, or leave them empty:

```bash
E2E_JOBS="${E2E_JOBS:-}"
E2E_TARGETS="${E2E_TARGETS:-}"
ALLOW_JETSON_DISPATCH="${ALLOW_JETSON_DISPATCH:-false}"
case "${E2E_JOBS}:${E2E_TARGETS}:${ALLOW_JETSON_DISPATCH}" in
::false | inference-routing::false | managed-image-protected-runtime::false | native-runtime-qualification-producer::false | :jetson-nvmap-gpu:true) ;;
*) echo "Unsupported manual PR E2E selection" >&2; exit 1 ;;
esac

REVIEW_REASON='Reviewed the latest PR commit and selected E2E boundary.'
INCLUDE_STAGING_BREV_LAUNCHABLE="${INCLUDE_STAGING_BREV_LAUNCHABLE:-false}"
CORRELATION_ID="$(python3 -c 'import uuid; print(uuid.uuid4())')"
gh workflow run .github/workflows/e2e.yaml \
--repo NVIDIA/NemoClaw \
--ref main \
-f "targets=${E2E_TARGETS}" \
-f "jobs=${E2E_JOBS}" \
-f inference_mode=mock \
-f include_staging_brev_launchable=false \
-f "include_staging_brev_launchable=${INCLUDE_STAGING_BREV_LAUNCHABLE}" \
-f "allow_jetson_dispatch=${ALLOW_JETSON_DISPATCH}" \
-f allow_dgx_spark_runner_queue=false \
-f "pr_number=${PR_NUMBER}" \
-f "checkout_sha=${HEAD_SHA}" \
-f "checkout_repository=${HEAD_REPOSITORY}" \
-f "base_sha=${BASE_SHA}" \
-f "workflow_sha=${WORKFLOW_SHA}" \
-f "review_reason=${REVIEW_REASON}" \
-f "correlation_id=${CORRELATION_ID}"
```

The trusted pre-checkout step requires the actor to have repository `maintain` or `admin` permission
at dispatch time. It validates:
GitHub's permission to dispatch the workflow authorizes the actor. The workflow does not repeat that
repository-role check. The trusted pre-checkout step validates:

- the actor;
- the open PR;
- the source repository;
- the target repository and branch;
- the source repository and owner;
- the latest PR commit SHA;
- the base SHA;
- the workflow SHA;
- the review reason; and
- the selector combination.
- the workflow SHA; and
- whether the source can receive the selected jobs and credentials.

It then records and uploads the immutable `nemoclaw-e2e-dispatch-v2` receipt before candidate
execution. A second validation after checkout rejects changed PR identity.
execution. The matrix planner and its dependencies come from the trusted workflow commit. A second
validation after checkout rejects changed PR identity or ownership.

## Find and Verify the Run

Expand All @@ -122,14 +129,16 @@ RUN_ID="$(jq -r '.[0].databaseId' <<<"$MATCHES")"
gh run watch "$RUN_ID" --repo NVIDIA/NemoClaw --exit-status
RUN_JSON="$(gh api "repos/NVIDIA/NemoClaw/actions/runs/${RUN_ID}")"
jq -e --arg sha "$WORKFLOW_SHA" '
.run_attempt == 1 and .head_sha == $sha and
.run_attempt >= 1 and .head_sha == $sha and
.status == "completed" and .conclusion == "success"
' <<<"$RUN_JSON" >/dev/null
CURRENT_PR="$(gh api "repos/NVIDIA/NemoClaw/pulls/${PR_NUMBER}")"
test "$(jq -r .state <<<"$CURRENT_PR")" = open
test "$(jq -r .head.sha <<<"$CURRENT_PR")" = "$HEAD_SHA"
test "$(jq -r .base.sha <<<"$CURRENT_PR")" = "$BASE_SHA"
test "$(jq -r .head.repo.full_name <<<"$CURRENT_PR")" = "$HEAD_REPOSITORY"
test "$(jq -r .head.repo.owner.login <<<"$CURRENT_PR")" = "$HEAD_OWNER"
test "$(jq -r .head.repo.owner.type <<<"$CURRENT_PR")" = "$HEAD_OWNER_TYPE"
```

If the run is not visible after bounded polling, do not dispatch again. Inspect GitHub Actions for
Expand All @@ -139,6 +148,7 @@ Return:

- the PR number;
- the source repository;
- the source repository owner;
- the latest PR commit SHA;
- the base SHA;
- the workflow SHA;
Expand All @@ -147,4 +157,4 @@ Return:
- the workflow URL; and
- the result.

A changed source repository, latest PR commit SHA, or base SHA invalidates the run claim.
A changed source repository, owner, latest PR commit SHA, or base SHA invalidates the run claim.
Loading
Loading