Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
5ea601f
feat(openshell): add external gateway status
apurvvkumaria Aug 29, 2026
a37b42d
fix(openshell): repair runner package checks
apurvvkumaria Aug 30, 2026
3621714
merge: resolve conflicts with main
github-actions[bot] Aug 30, 2026
81c82f5
test(openshell): lock runner package fixture
apurvvkumaria Aug 30, 2026
cb3c067
test(openshell): stabilize runner qualification
apurvvkumaria Aug 30, 2026
323b9be
merge(main): refresh #9872 candidate
apurvvkumaria Aug 31, 2026
094b2db
fix(blueprint): close external health review gaps
apurvvkumaria Aug 31, 2026
5a4f641
fix(e2e): fail closed external health qualification
apurvvkumaria Aug 31, 2026
9d04da2
merge(main): refresh #9872 before qualification
apurvvkumaria Aug 31, 2026
f5b57e1
fix(blueprint): align external target boundaries
apurvvkumaria Aug 31, 2026
ba61734
docs(schema): describe external target status
apurvvkumaria Aug 31, 2026
5e3707a
merge(main): refresh external target status
apurvvkumaria Aug 31, 2026
1434ed2
merge(main): refresh external target status
apurvvkumaria Aug 31, 2026
259d474
test(blueprint): replace external status shape checks
apurvvkumaria Aug 31, 2026
b70d242
docs(blueprint): document external gateway status
apurvvkumaria Aug 31, 2026
74edab1
merge(main): refresh external target status
apurvvkumaria Aug 31, 2026
53ab70b
docs(blueprint): clarify external gateway safety
apurvvkumaria Aug 31, 2026
0af779b
test(e2e): cover packaged blueprint runner
apurvvkumaria Aug 31, 2026
b766a2d
merge(main): refresh external target status
apurvvkumaria Aug 31, 2026
fe87d15
merge(main): refresh external target status
apurvvkumaria Aug 31, 2026
fdc218c
merge(main): refresh external target status
apurvvkumaria Aug 31, 2026
051333d
test(blueprint): remove redundant deadline assertion
apurvvkumaria Aug 31, 2026
c3328ba
merge(main): restore compiled CI inputs
apurvvkumaria Aug 31, 2026
ce7bff8
test(blueprint): require missing health credential
apurvvkumaria Aug 31, 2026
43d9a8b
merge(main): refresh trusted review workflow
apurvvkumaria Aug 31, 2026
1796ea6
fix(blueprint): avoid credentials during public health
apurvvkumaria Aug 31, 2026
61a7906
fix(blueprint): align credential-free status contract
apurvvkumaria Aug 31, 2026
24f96d4
test(schema): remove wording-only assertion
apurvvkumaria Sep 1, 2026
2dec43b
docs(openshell): clarify status recovery
apurvvkumaria Sep 1, 2026
b8f63bb
merge(main): refresh review advisor base
apurvvkumaria Sep 1, 2026
16d2db8
merge(main): refresh repaired advisor base
apurvvkumaria Sep 1, 2026
656a043
merge(main): refresh final advisor base
apurvvkumaria Sep 1, 2026
e3bced5
merge(main): refresh repaired e2e base
apurvvkumaria Sep 1, 2026
abed18e
docs(openshell): clarify public health path
apurvvkumaria Sep 1, 2026
5897d16
fix(openshell): address advisor findings
cv Sep 1, 2026
1cdac43
refactor(openshell): narrow gateway health ownership
apurvvkumaria Sep 1, 2026
548ef5f
merge(review): reconcile verified advisor fixes
apurvvkumaria Sep 1, 2026
b1ae72e
docs(openshell): cite external target trust decision
cv Sep 1, 2026
dcd34b9
fix(openshell): complete advisor repairs
cv Sep 1, 2026
0587cd5
chore: apply review repair formatting
cv Sep 1, 2026
ed46fb3
test(openshell): keep package contracts deterministic
cv Sep 1, 2026
482f75e
fix(openshell): complete validation before effects
cv Sep 1, 2026
d623c3d
fix(e2e): clarify artifact presence check
cv Sep 1, 2026
dd138be
fix(e2e): verify external target modules
cv Sep 1, 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
2 changes: 2 additions & 0 deletions .github/actions/ci-build-typecheck/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ runs:
steps:
- name: Verify compiled package contracts
shell: bash
env:
NPM_CONFIG_CACHE: ${{ runner.temp }}/npm
run: npx vitest run --project package-contract

- name: Typecheck CLI + tests
Expand Down
170 changes: 2 additions & 168 deletions .github/actions/restore-e2e-cli-artifact/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,76 +18,7 @@ runs:
CALLER_WORKFLOW_SHA: ${{ github.workflow_sha }}
PROVENANCE_JSON: ${{ inputs.provenance-json }}
shell: bash
run: |
set -euo pipefail
jq -e '
type == "object" and
(keys | sort) == [
"artifactDigest",
"artifactId",
"artifactName",
"candidateRepository",
"candidateSha",
"kind",
"payloadSha256",
"runAttempt",
"runId",
"workflowSha"
] and
.kind == "nemoclaw-e2e-cli-provenance-v1" and
(.artifactId | strings | test("^[1-9][0-9]*$")) and
(.artifactDigest | strings | test("^[a-f0-9]{64}$")) and
(.artifactName | strings) and
(.candidateRepository | strings | test("^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$")) and
(.candidateSha | strings | test("^[a-f0-9]{40}$")) and
(.payloadSha256 | strings | test("^[a-f0-9]{64}$")) and
(.workflowSha | strings | test("^[a-f0-9]{40}$")) and
(.runId | strings | test("^[1-9][0-9]*$")) and
(.runAttempt | strings | test("^[1-9][0-9]*$")) and
.artifactName == ("nemoclaw-cli-" + .candidateSha + "-" + .payloadSha256)
' <<<"$PROVENANCE_JSON" >/dev/null ||
{ echo "::error::producer CLI artifact provenance is invalid"; exit 1; }

artifact_id="$(jq -r '.artifactId' <<<"$PROVENANCE_JSON")"
candidate_repository="$(jq -r '.candidateRepository' <<<"$PROVENANCE_JSON")"
candidate_sha="$(jq -r '.candidateSha' <<<"$PROVENANCE_JSON")"
run_attempt="$(jq -r '.runAttempt' <<<"$PROVENANCE_JSON")"
run_id="$(jq -r '.runId' <<<"$PROVENANCE_JSON")"
workflow_sha="$(jq -r '.workflowSha' <<<"$PROVENANCE_JSON")"
[[ "$(git rev-parse --verify HEAD)" == "$candidate_sha" ]] ||
{ echo "::error::consumer checkout does not match the producer candidate SHA"; exit 1; }
[[ "$workflow_sha" == "$CALLER_WORKFLOW_SHA" ]] ||
{ echo "::error::consumer and producer workflow SHAs differ"; exit 1; }
[[ "$GITHUB_RUN_ATTEMPT" =~ ^[1-9][0-9]*$ ]] ||
{ echo "::error::consumer workflow run attempt is invalid"; exit 1; }
[[ "$run_id" == "$GITHUB_RUN_ID" ]] ||
{ echo "::error::consumer and producer workflow run IDs differ"; exit 1; }
(( run_attempt <= GITHUB_RUN_ATTEMPT )) ||
{ echo "::error::producer workflow attempt is newer than the consumer attempt"; exit 1; }

remote_url="$(git remote get-url origin)"
case "$remote_url" in
https://github.com/*) remote_repository="${remote_url#https://github.com/}" ;;
git@github.com:*) remote_repository="${remote_url#git@github.com:}" ;;
*) echo "::error::consumer checkout repository URL is invalid"; exit 1 ;;
esac
remote_repository="${remote_repository%.git}"
[[ "$remote_repository" == "$candidate_repository" ]] ||
{ echo "::error::consumer checkout repository does not match producer provenance"; exit 1; }

jq -r '
"artifact_digest=" + .artifactDigest,
"artifact_id=" + .artifactId,
"artifact_name=" + .artifactName,
"candidate_repository=" + .candidateRepository,
"candidate_sha=" + .candidateSha,
"payload_sha256=" + .payloadSha256,
"producer_run_attempt=" + .runAttempt,
"run_attempt=" + .runAttempt,
"run_id=" + .runId,
"workflow_sha=" + .workflowSha
' <<<"$PROVENANCE_JSON" >>"$GITHUB_OUTPUT"
printf 'consumer_run_attempt=%s\n' "$GITHUB_RUN_ATTEMPT" >>"$GITHUB_OUTPUT"
run: "$GITHUB_ACTION_PATH/../../../scripts/e2e/validate-cli-artifact-identity.sh"

- name: Download exact-commit CLI artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
Expand All @@ -106,101 +37,4 @@ runs:
RUN_ID: ${{ steps.identity.outputs.run_id }}
WORKFLOW_SHA: ${{ steps.identity.outputs.workflow_sha }}
shell: bash
run: |
set -euo pipefail
artifact_dir="${RUNNER_TEMP}/nemoclaw-cli-artifact"
manifest="$artifact_dir/manifest.json"
payload="$artifact_dir/nemoclaw-cli.tar"
test -s "$manifest" && test -s "$payload" ||
{ echo "::error::exact-commit CLI artifact is incomplete"; exit 1; }
[[ "$(node --version)" =~ ^v22\.[0-9]+\.[0-9]+$ ]] ||
{ echo "::error::consumer must restore the CLI under the pinned Node 22 toolchain"; exit 1; }

sha256_file() {
node --input-type=module --eval '
import { createHash } from "node:crypto";
import { createReadStream } from "node:fs";
const hash = createHash("sha256");
for await (const chunk of createReadStream(process.argv[1])) hash.update(chunk);
process.stdout.write(hash.digest("hex"));
' "$1"
}

source_tree="$(git rev-parse 'HEAD^{tree}')"
lockfile_sha256="$(sha256_file package-lock.json)"
jq -e \
--arg artifactName "$ARTIFACT_NAME" \
--arg candidateRepository "$CANDIDATE_REPOSITORY" \
--arg candidateSha "$CANDIDATE_SHA" \
--arg lockfileSha256 "$lockfile_sha256" \
--arg payloadSha256 "$PAYLOAD_SHA256" \
--arg runAttempt "$PRODUCER_RUN_ATTEMPT" \
--arg runId "$RUN_ID" \
--arg sourceTree "$source_tree" \
--arg workflowSha "$WORKFLOW_SHA" \
'
.kind == "nemoclaw-e2e-cli-artifact-v1" and
.artifactName == $artifactName and
.candidate.repository == $candidateRepository and
.candidate.sha == $candidateSha and
.candidate.sourceTree == $sourceTree and
.candidate.lockfileSha256 == $lockfileSha256 and
.workflow.sha == $workflowSha and
.workflow.runId == $runId and
.workflow.runAttempt == $runAttempt and
(.toolchain.node | strings | test("^v22\\.[0-9]+\\.[0-9]+$")) and
(.toolchain.npm | strings | test("^[0-9]+\\.[0-9]+\\.[0-9]+$")) and
.toolchain.runnerOs == "Linux" and
.toolchain.runnerArch == "X64" and
.build.command == "npm run build:cli" and
.build.sourceRevision == $candidateSha and
.payload.file == "nemoclaw-cli.tar" and
.payload.sha256 == $payloadSha256
' "$manifest" >/dev/null ||
{ echo "::error::exact-commit CLI artifact provenance mismatch"; exit 1; }
actual_payload_sha256="$(sha256_file "$payload")"
[[ "$actual_payload_sha256" == "$PAYLOAD_SHA256" ]] ||
{ echo "::error::exact-commit CLI artifact payload digest mismatch"; exit 1; }
while IFS= read -r member; do
case "$member" in
dist | dist/* | nemoclaw/dist/shared | nemoclaw/dist/shared/*) ;;
*) echo "::error::CLI artifact contains an unsafe member: $member"; exit 1 ;;
esac
case "/$member/" in
*"/../"* | *"/./"*) echo "::error::CLI artifact contains traversal: $member"; exit 1 ;;
esac
done < <(tar -tf "$payload")
tar -tvf "$payload" |
awk 'substr($1, 1, 1) != "-" && substr($1, 1, 1) != "d" { exit 1 }' ||
{ echo "::error::CLI artifact contains a link or special file"; exit 1; }
[[ ! -e "$GITHUB_WORKSPACE/dist" && ! -L "$GITHUB_WORKSPACE/dist" ]] ||
{ echo "::error::consumer unexpectedly built dist before artifact restore"; exit 1; }
[[ -d "$GITHUB_WORKSPACE/nemoclaw" && ! -L "$GITHUB_WORKSPACE/nemoclaw" ]] ||
{ echo "::error::consumer nemoclaw directory must be a non-symlink directory"; exit 1; }

[[ ! -e "$GITHUB_WORKSPACE/nemoclaw/dist" && ! -L "$GITHUB_WORKSPACE/nemoclaw/dist" ]] ||
{ echo "::error::consumer unexpectedly built nemoclaw/dist before artifact restore"; exit 1; }
restore_dir="$(mktemp -d "${RUNNER_TEMP}/nemoclaw-cli-restore.XXXXXX")"
trap 'rm -rf -- "$restore_dir"' EXIT
tar --no-same-owner --no-same-permissions -xf "$payload" -C "$restore_dir"
cli_entrypoint="$restore_dir/dist/nemoclaw.js"
[[ -f "$cli_entrypoint" && ! -L "$cli_entrypoint" && -s "$cli_entrypoint" ]] ||
{ echo "::error::restored CLI artifact entry point is missing or is not a nonempty regular file"; exit 1; }
for boundary in \
openshell-policy-boundary.cjs \
sandbox-name.cjs \
snapshot-sanitizer-boundary.cjs; do
boundary_path="$restore_dir/nemoclaw/dist/shared/$boundary"
[[ -f "$boundary_path" && ! -L "$boundary_path" && -s "$boundary_path" ]] ||
{ echo "::error::restored CLI artifact shared module is missing or is not a nonempty regular file: $boundary"; exit 1; }
done
jq -e --arg candidateSha "$CANDIDATE_SHA" '
type == "object" and
(keys | sort) == ["nemoclawVersion", "sourceRevision"] and
(.nemoclawVersion | strings | length > 0) and
.sourceRevision == $candidateSha
' "$restore_dir/dist/build-identity.json" >/dev/null ||
{ echo "::error::restored CLI build identity does not match the candidate SHA"; exit 1; }
mv "$restore_dir/nemoclaw/dist" "$GITHUB_WORKSPACE/nemoclaw/dist"
mv "$restore_dir/dist" "$GITHUB_WORKSPACE/dist"
node "$GITHUB_WORKSPACE/bin/nemoclaw.js" --version >/dev/null
run: "$GITHUB_ACTION_PATH/../../../scripts/e2e/restore-cli-artifact.sh"
112 changes: 3 additions & 109 deletions .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -748,114 +748,7 @@ jobs:
RUN_ID: ${{ github.run_id }}
WORKFLOW_SHA: ${{ github.workflow_sha }}
shell: bash
run: |
set -euo pipefail
[[ "$CANDIDATE_REPOSITORY" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]] ||
{ echo "::error::candidate repository is invalid"; exit 1; }
[[ "$CANDIDATE_SHA" =~ ^[a-f0-9]{40}$ ]] ||
{ echo "::error::candidate SHA must be a lowercase 40-character SHA"; exit 1; }
[[ "$WORKFLOW_SHA" =~ ^[a-f0-9]{40}$ ]] ||
{ echo "::error::workflow SHA must be a lowercase 40-character SHA"; exit 1; }
[[ "$(git rev-parse --verify HEAD)" == "$CANDIDATE_SHA" ]] ||
{ echo "::error::checked-out commit does not match the artifact candidate SHA"; exit 1; }
[[ "$RUN_ATTEMPT" =~ ^[1-9][0-9]*$ && "$RUN_ID" =~ ^[1-9][0-9]*$ ]] ||
{ echo "::error::workflow run identity is invalid"; exit 1; }
for required_file in dist/nemoclaw.js dist/build-identity.json; do
[[ -f "$required_file" && ! -L "$required_file" && -s "$required_file" ]] ||
{ echo "::error::candidate CLI build output is missing or is not a nonempty regular file: $required_file"; exit 1; }
done
for boundary in \
openshell-policy-boundary.cjs \
sandbox-name.cjs \
snapshot-sanitizer-boundary.cjs; do
boundary_path="nemoclaw/dist/shared/$boundary"
[[ -f "$boundary_path" && ! -L "$boundary_path" && -s "$boundary_path" ]] ||
{ echo "::error::candidate CLI build shared module is missing or is not a nonempty regular file: $boundary"; exit 1; }
done

jq -e --arg candidateSha "$CANDIDATE_SHA" '
type == "object" and
(keys | sort) == ["nemoclawVersion", "sourceRevision"] and
(.nemoclawVersion | strings | length > 0) and
.sourceRevision == $candidateSha
' dist/build-identity.json >/dev/null ||
{ echo "::error::candidate CLI build identity does not match the candidate commit SHA"; exit 1; }

artifact_catalog="dist/e2e-managed-image-catalog.json"
[[ ! -e "$artifact_catalog" && ! -L "$artifact_catalog" ]] || {
echo "::error::candidate build created the managed-image catalog path" >&2
exit 1
}

artifact_dir="${RUNNER_TEMP}/nemoclaw-cli-artifact"
install -d -m 0700 "$artifact_dir"
payload="$artifact_dir/nemoclaw-cli.tar"
manifest="$artifact_dir/manifest.json"
tar \
--sort=name \
--mtime=@0 \
--owner=0 \
--group=0 \
--numeric-owner \
-cf "$payload" \
dist \
nemoclaw/dist/shared
payload_sha256="$(sha256sum "$payload" | awk '{print $1}')"
source_tree="$(git rev-parse 'HEAD^{tree}')"
lockfile_sha256="$(sha256sum package-lock.json | awk '{print $1}')"
node_version="$(node --version)"
npm_version="$(npm --version)"
artifact_name="nemoclaw-cli-${CANDIDATE_SHA}-${payload_sha256}"

jq -n \
--arg artifactName "$artifact_name" \
--arg buildCommand "npm run build:cli" \
--arg candidateRepository "$CANDIDATE_REPOSITORY" \
--arg candidateSha "$CANDIDATE_SHA" \
--arg lockfileSha256 "$lockfile_sha256" \
--arg nodeVersion "$node_version" \
--arg npmVersion "$npm_version" \
--arg payloadFile "nemoclaw-cli.tar" \
--arg payloadSha256 "$payload_sha256" \
--arg runAttempt "$RUN_ATTEMPT" \
--arg runId "$RUN_ID" \
--arg runnerArch "$RUNNER_ARCH" \
--arg runnerOs "$RUNNER_OS" \
--arg sourceTree "$source_tree" \
--arg workflowSha "$WORKFLOW_SHA" \
'{
kind: "nemoclaw-e2e-cli-artifact-v1",
artifactName: $artifactName,
candidate: {
repository: $candidateRepository,
sha: $candidateSha,
sourceTree: $sourceTree,
lockfileSha256: $lockfileSha256
},
workflow: {
sha: $workflowSha,
runId: $runId,
runAttempt: $runAttempt
},
toolchain: {
node: $nodeVersion,
npm: $npmVersion,
runnerOs: $runnerOs,
runnerArch: $runnerArch
},
build: {
command: $buildCommand,
sourceRevision: $candidateSha
},
payload: {
file: $payloadFile,
sha256: $payloadSha256
}
}' >"$manifest"
chmod 0600 "$manifest" "$payload"
printf 'artifact_name=%s\n' "$artifact_name" >>"$GITHUB_OUTPUT"
printf 'candidate_sha=%s\n' "$CANDIDATE_SHA" >>"$GITHUB_OUTPUT"
printf 'payload_sha256=%s\n' "$payload_sha256" >>"$GITHUB_OUTPUT"
run: scripts/e2e/package-cli-artifact.sh

- id: upload_cli_artifact
name: Publish content-addressed CLI artifact
Expand Down Expand Up @@ -3360,12 +3253,13 @@ jobs:
E2E_DEFAULT_ENABLED: "0"
E2E_TARGET_ID: "external-gateway-health"
E2E_AGENT_RUNTIME: "none"
E2E_OBSERVABLE_OUTCOME: "The reviewed SDK observes exact public gateway health over explicit HTTPS and CA"
E2E_OBSERVABLE_OUTCOME: "The exact Blueprint Runner observes public gateway health over explicit HTTPS and CA"
E2E_ENVIRONMENT_OR_INFERENCE_ENDPOINT: "Ubuntu host with OpenShell 0.0.106; no inference endpoint"
E2E_ARTIFACT_DIR: ${{ github.workspace }}/e2e-artifacts/live/external-gateway-health
NEMOCLAW_RUN_LIVE_E2E: "1"
NEMOCLAW_NON_INTERACTIVE: "1"
NEMOCLAW_OPENSHELL_PIN_VERSION: "0.0.106"
NEMOCLAW_E2E_REQUIRE_EXECUTED_TEST: "1"
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
__pycache__/
coverage/
dist/
nemoclaw/runner-dist/
artifacts/pr-review-advisor-local/
docs/_build/
node_modules/
Expand Down
2 changes: 1 addition & 1 deletion ci/source-architecture-budget.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"src/lib/messaging/channels/index.ts": 25,
"src/lib/onboard/gateway-binding.ts": 53,
"src/lib/runner.ts": 85,
"src/lib/security/redact.ts": 54,
"src/lib/security/redact.ts": 55,
"src/lib/state/mcp-lifecycle-lock.ts": 21,
"src/lib/state/onboard-session.ts": 35,
"src/lib/state/registry.ts": 97,
Expand Down
Loading
Loading