Skip to content

fix(ci): harden trusted OpenShell release upgrades - #6744

Merged
cv merged 8 commits into
mainfrom
chore/installer-hash-multi-release
Jul 13, 2026
Merged

fix(ci): harden trusted OpenShell release upgrades#6744
cv merged 8 commits into
mainfrom
chore/installer-hash-multi-release

Conversation

@ericksoa

@ericksoa ericksoa commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Summary

This prerequisite makes future OpenShell release bumps verifiable by code owned by the pull request base, while NemoClaw remains pinned to OpenShell 0.0.72. It also rejects unsafe release archives before extraction so the eventual 0.0.82 pin can change only reviewed release data rather than installer behavior.

Related Issue

Related to #6379.

Changes

  • Trust release-specific checksum-manifest digests and exact consumed asset sets instead of accepting a single hard-coded release shape.
  • Bind installer, Brev, and blueprint OpenShell selectors to the release version parsed by the base-owned checker.
  • SHA-256 lock every byte of both installer scripts after normalizing only the strictly parsed pin-table function and exact stable selector literal; comments, dead branches, indirect overrides, alternate URLs/assets, hash bypasses, and extra extraction logic fail closed.
  • Validate every selected archive contains exactly one expected regular binary before any extraction; reject absolute paths, traversal, duplicates, extras, links, and devices.
  • Model the two-step trust bootstrap: the old base rejects an unknown release without executing PR code, then the prerequisite base permits a later table/selector-only release change.
  • Keep every runtime selector and trusted release entry on 0.0.72; this PR does not perform the dependency upgrade.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with doc updates
  • Doc only (prose changes, no code sample modifications)
  • Doc only (includes code sample changes)

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — justification:
  • Tests not applicable — justification:
  • Docs updated for user-facing behavior changes
  • Docs not applicable — justification: this changes installer validation and CI trust enforcement without changing the supported OpenShell version or user workflow.
  • Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging)
  • Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification:
  • Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue:

Verification

  • PR description includes the DCO sign-off declaration and every commit appears as Verified in GitHub
  • Normal pre-commit, commit-msg, and pre-push hooks passed, or npm run check:diff passed when hooks were skipped or unavailable
  • Targeted behavior tests pass for the current change set, or tests are marked not applicable above — command/result or justification: 146 integration and installer-integration tests passed; the focused normalized-template suite passed 59/59; the live v0.0.72 release-manifest check passed all 13 entries.
  • Applicable broad gate passed — npm test for broad runtime/test-harness changes; npm run check for repo-wide validation/coverage changes — command/result:
  • Quality Gates section completed with required justifications or waivers
  • No secrets, API keys, or credentials committed
  • npm run docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Signed-off-by: Aaron Erickson aerickson@nvidia.com

Summary by CodeRabbit

  • Security Improvements

    • Added pre-extraction OpenShell archive safety checks to reject unsafe/unexpected archive contents and shapes before installation proceeds.
    • Strengthened installer integrity verification by using versioned release-manifest SHA-256 allowlists and tighter pin-table/runtime consistency validation.
  • Bug Fixes

    • Improved mismatch detection and clearer failure reporting for installer pins, release-manifest allowlists, and runtime-consumption constraints across installation paths.
  • Tests

    • Expanded parameterized coverage for unsafe archive shapes and incomplete allowlists.
    • Refreshed fixtures and test stubs to assert the updated fail-closed behaviors.

ericksoa added 6 commits July 12, 2026 21:07
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 7470f1e4-da7e-4b2a-b7a1-4df7674ddd94

📥 Commits

Reviewing files that changed from the base of the PR and between 546712a and 5259ddb.

📒 Files selected for processing (1)
  • test/install-openshell-version-check.test.ts

📝 Walkthrough

Walkthrough

The change derives OpenShell release versions from trusted installer and Brev pin tables, validates versioned release-manifest digests and asset mappings, and rejects unsafe tar archives before extraction. Tests cover allowlist drift, template mutations, archive layouts, and extraction behavior.

Changes

Release integrity controls

Layer / File(s) Summary
Trusted pin extraction and runtime consistency
scripts/checks/extract-installer-pins.mts
Pin extraction derives release versions from shell tables, validates trusted templates and exact asset sets, and checks installer, Brev, and blueprint version consistency.
Versioned release-manifest verification
scripts/check-installer-hash.sh, scripts/checks/dependency-pins.ts, test/installer-hash-check.test.ts, test/dependency-pins-check.test.ts, .github/workflows/installer-hash-check.yaml
Release-manifest digests are selected from a versioned allowlist, verified after download, and tested for complete entries, asset mappings, and prerequisite allowlist behavior.
OpenShell archive validation
scripts/install-openshell.sh, scripts/brev-launchable-ci-cpu.sh, test/brev-launchable-ci-cpu-checksum.test.ts, test/install-openshell-version-check.test.ts, test/runner.test.ts
Downloaded tarballs are checked for exactly one expected regular-file member before extraction, with tests covering unsafe paths, duplicate or extra entries, links, device nodes, and wrapper extraction.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant InstallerAndBrev
  participant ExtractInstallerPins
  participant CheckInstallerHash
  participant ReleaseManifests
  participant Blueprint
  InstallerAndBrev->>ExtractInstallerPins: provide pin tables
  ExtractInstallerPins->>Blueprint: compare releaseVersion with max_openshell_version
  ExtractInstallerPins-->>CheckInstallerHash: return releaseVersion and pinned assets
  CheckInstallerHash->>ReleaseManifests: download checksum manifests
  ReleaseManifests-->>CheckInstallerHash: return manifest contents
  CheckInstallerHash->>CheckInstallerHash: verify manifest digests and pinned assets
Loading

Suggested labels: security, dependencies, area: ci

Suggested reviewers: cv, jyaunches

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.42% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the CI hardening around trusted OpenShell release upgrades.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/installer-hash-multi-release

Comment @coderabbitai help to get the list of available commands.

@github-code-quality

github-code-quality Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage remains at 96%, unchanged from the main branch.

TypeScript / code-coverage/cli

The overall coverage in the chore/installer-hash... branch remains at 79%, unchanged from the main branch.

Show a code coverage summary of the most impacted files.
File main deed1aa chore/installer-hash... 5259ddb +/-
src/lib/messagi...ridge-health.ts 79% 68% -11%
src/lib/messagi.../token-paste.ts 92% 82% -10%
src/lib/messagi...onfig-prompt.ts 97% 91% -6%
src/lib/platform.ts 86% 80% -6%
src/lib/security/redact.ts 100% 95% -5%
src/lib/messagi...ridge-health.ts 96% 91% -5%
src/lib/state/config-io.ts 93% 90% -3%
src/lib/adapters/http/probe.ts 90% 89% -1%
src/lib/securit...ntial-filter.ts 97% 99% +2%
src/lib/messagi...annel-config.ts 96% 99% +3%

Updated July 13, 2026 05:08 UTC
Code Coverage is in Public Preview. Learn more and provide us with your feedback.

@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

E2E Advisor Recommendation

Required E2E: cloud-onboard
Optional E2E: launchable-smoke

Dispatch hint: cloud-onboard,launchable-smoke

Workflow run

Full advisor summary

E2E Recommendation Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E

  • cloud-onboard: Required deterministic platform-install floor: validate that a clean supported host installs the pinned OpenShell runtime and completes a usable hosted onboarding flow after installer and integrity-boundary changes.

Optional E2E

  • launchable-smoke: The touched Brev CPU bootstrap has a dedicated live test that runs the launchable installer, then verifies onboarding, hosted inference routing, and an OpenClaw agent turn.

New E2E recommendations

  • security-boundaries (medium): Current changed tests emulate malformed archives, while live onboarding necessarily downloads trusted upstream releases. Add a controlled end-to-end installer fixture that serves a checksum-valid malformed archive and proves both standard and Brev installers reject it before extraction.
    • Suggested test: A credential-free live installer-boundary test using a local controlled release endpoint/archive fixture for absolute-path, extra-member, and link archive rejection.

Dispatch hint

  • Workflow: .github/workflows/e2e.yaml
  • jobs input: cloud-onboard,launchable-smoke

@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — No blocking findings

Merge posture: No blocking advisor findings
Primary next action: Review the warnings below.
Findings: 0 required · 1 warning · 0 optional suggestions
Since last review: 0 prior items resolved · 0 still apply · 1 new item found

1 warning · 0 optional suggestions

Warnings

These merit maintainer attention but do not block by themselves.

PRA-1 Warning — Cover archive rejection on the opted-in dev install path

  • Location: test/install-openshell-version-check.test.ts:559
  • Category: tests
  • Problem: The new archive validation is unconditional in `scripts/install-openshell.sh`, including the explicitly opted-in dev channel, but the changed unsafe-archive matrix executes only with `NEMOCLAW_OPENSHELL_CHANNEL: "stable"`. Existing dev tests cover opt-in and version behavior but do not prove a malformed dev archive is rejected before extraction.
  • Impact: A future change could place the dev path after extraction or otherwise bypass archive validation while preserving stable-path tests, allowing an explicitly opted-in but unverified dev archive to write unsafe members.
  • Recommendation: Add one opted-in dev-channel reinstall case with an unsafe archive shape and assert it fails before `tar xzf` and before installation.
  • Verification: Read the archive matrix environment at test/install-openshell-version-check.test.ts:559 and compare it with the unconditional validation loop at scripts/install-openshell.sh:768-778; search existing dev tests for an archive-shape assertion.
  • Test coverage: Run `install-openshell.sh` with `NEMOCLAW_OPENSHELL_CHANNEL=dev`, `NEMOCLAW_ACCEPT_DEV_UNVERIFIED_INSTALL=1`, and a fake archive listing such as `../openshell`; assert nonzero status and no `tar xzf` or install invocation.
  • Evidence: scripts/install-openshell.sh:768-778 derives expected members and calls validate_openshell_archive for every downloaded asset without excluding RELEASE_TAG=dev. test/install-openshell-version-check.test.ts:425-576 covers unsafe archive shapes but sets NEMOCLAW_OPENSHELL_CHANNEL to stable at line 559. Existing dev-path test references in test/install-openshell-version-check.test.ts cover channel opt-in/version behavior, not malformed archive extraction.

Workflow run details

This is an automated review. Required findings need action before merge. Warnings and optional suggestions do not require a response or follow-up. A human maintainer makes the final merge decision.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
scripts/checks/extract-installer-pins.mts (1)

389-430: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting the shared brace-matching walk.

functionDefinitionSourceRanges (389-430) duplicates the token/brace-depth traversal of functionBodyRanges (351-387); they differ only in whether they return token index ranges or source-char SourceEdits. Since both drive security-critical decisions (pin extraction vs. template normalization), keeping two independent copies risks them silently disagreeing on what counts as a function definition. A single helper returning {definitionStart, bodyStart, bodyCursor} that both wrap would eliminate that drift risk.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/checks/extract-installer-pins.mts` around lines 389 - 430, Extract
the shared function-definition and brace-matching traversal from
functionBodyRanges and functionDefinitionSourceRanges into one helper returning
definitionStart, bodyStart, and bodyCursor token indices. Update both callers to
derive their existing token ranges or SourceEdit values from that helper,
preserving their current outputs and validation for unmatched or unavailable
ranges.
test/brev-launchable-ci-cpu-checksum.test.ts (1)

381-404: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefer a filesystem existence check over parsing sudoLog.

This test confirms non-installation by regex-matching fake.sudoLog for an install -m 755 ...openshell line. Since the fake sudo install copies the binary to path.join(fake.fakeBin, "openshell") (lines 172-177), a direct fs.existsSync(path.join(fake.fakeBin, "openshell")) check would be a more robust observable-outcome assertion than parsing the logged command string, and is less coupled to the exact install invocation syntax.

♻️ Proposed diff
       const tarCalls = fs.readFileSync(fake.tarLog, "utf-8");
       expect(tarCalls).not.toMatch(/^xzf /m);
-      expect(fs.existsSync(fake.sudoLog) ? fs.readFileSync(fake.sudoLog, "utf-8") : "").not.toMatch(
-        /^install -m 755 .*openshell/m,
-      );
+      expect(fs.existsSync(path.join(fake.fakeBin, "openshell"))).toBe(false);

As per path instructions, tests should "Prefer observable outcomes through the public boundary over source-text, private-shape, or mock-call assertions."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/brev-launchable-ci-cpu-checksum.test.ts` around lines 381 - 404, Update
the unsafe archive test around runLaunchable to verify non-installation by
checking fs.existsSync(path.join(fake.fakeBin, "openshell")) is false. Remove
the sudoLog regex assertion while preserving the existing status, output, and
tar extraction checks.

Source: Path instructions

test/runner.test.ts (1)

744-772: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the duplicated tar-mock case-statement into a shared helper.

The exact same ~29-line tar mock (basename classification + -tzf/-tvzf/xzf handling) is repeated verbatim in both the curl-direct and curl-fallback test setups. Consolidating into one shared shell-fragment/helper (e.g., a template string constant interpolated into both heredocs) would remove the risk of the two copies silently drifting apart as the production mapping evolves.

Also applies to: 846-874

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/runner.test.ts` around lines 744 - 772, Extract the duplicated tar mock
case statement, including archive basename classification and -tzf, -tvzf, and
xzf handling, into one shared shell fragment or helper. Reuse that shared
definition in both the curl-direct and curl-fallback test setups, preserving the
existing behavior and avoiding separate copies that can drift.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@scripts/checks/extract-installer-pins.mts`:
- Around line 389-430: Extract the shared function-definition and brace-matching
traversal from functionBodyRanges and functionDefinitionSourceRanges into one
helper returning definitionStart, bodyStart, and bodyCursor token indices.
Update both callers to derive their existing token ranges or SourceEdit values
from that helper, preserving their current outputs and validation for unmatched
or unavailable ranges.

In `@test/brev-launchable-ci-cpu-checksum.test.ts`:
- Around line 381-404: Update the unsafe archive test around runLaunchable to
verify non-installation by checking fs.existsSync(path.join(fake.fakeBin,
"openshell")) is false. Remove the sudoLog regex assertion while preserving the
existing status, output, and tar extraction checks.

In `@test/runner.test.ts`:
- Around line 744-772: Extract the duplicated tar mock case statement, including
archive basename classification and -tzf, -tvzf, and xzf handling, into one
shared shell fragment or helper. Reuse that shared definition in both the
curl-direct and curl-fallback test setups, preserving the existing behavior and
avoiding separate copies that can drift.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 8822097e-4aed-4b26-a41d-796362ea3d88

📥 Commits

Reviewing files that changed from the base of the PR and between deed1aa and f9e2495.

📒 Files selected for processing (11)
  • .github/workflows/installer-hash-check.yaml
  • scripts/brev-launchable-ci-cpu.sh
  • scripts/check-installer-hash.sh
  • scripts/checks/dependency-pins.ts
  • scripts/checks/extract-installer-pins.mts
  • scripts/install-openshell.sh
  • test/brev-launchable-ci-cpu-checksum.test.ts
  • test/dependency-pins-check.test.ts
  • test/install-openshell-version-check.test.ts
  • test/installer-hash-check.test.ts
  • test/runner.test.ts

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
test/install-openshell-version-check.test.ts (1)

568-573: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Boolean-short-circuit assertions are only meaningful for the unsafe branch.

expect(expected.unsafe && fs.existsSync(...)).toBe(false) and the similar tar.log check reduce to a no-op tautology (expect(false).toBe(false)) whenever expected.unsafe is false, so these two lines assert nothing for the safe case (presumably covered by the later download-content checks, per the line-range summary). The pattern works but is easy to misread as verifying both branches.

♻️ Suggested clarity improvement
-      expect(expected.unsafe && fs.existsSync(path.join(tmp, "local-bin", "openshell"))).toBe(
-        false,
-      );
-      expect(expected.unsafe && /^xzf /m.test(fs.readFileSync(tarLog, "utf8"))).toBe(false);
+      if (expected.unsafe) {
+        expect(fs.existsSync(path.join(tmp, "local-bin", "openshell"))).toBe(false);
+        expect(/^xzf /m.test(fs.readFileSync(tarLog, "utf8"))).toBe(false);
+      }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/install-openshell-version-check.test.ts` around lines 568 - 573, Clarify
the unsafe-branch assertions in the test around the result status checks by
guarding the filesystem and tar-log validations with an explicit expected.unsafe
conditional. Ensure the local OpenShell binary and extraction command are
checked only when the archive is unsafe, while safe cases continue through their
existing download-content assertions without tautological
expect(false).toBe(false) checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@test/install-openshell-version-check.test.ts`:
- Around line 568-573: Clarify the unsafe-branch assertions in the test around
the result status checks by guarding the filesystem and tar-log validations with
an explicit expected.unsafe conditional. Ensure the local OpenShell binary and
extraction command are checked only when the archive is unsafe, while safe cases
continue through their existing download-content assertions without tautological
expect(false).toBe(false) checks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 448ca3c6-2213-497e-9177-c6e6ec847853

📥 Commits

Reviewing files that changed from the base of the PR and between f9e2495 and 546712a.

📒 Files selected for processing (3)
  • scripts/checks/extract-installer-pins.mts
  • test/install-openshell-version-check.test.ts
  • test/installer-hash-check.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/checks/extract-installer-pins.mts

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
@cv
cv merged commit cfffc4b into main Jul 13, 2026
54 checks passed
@cv
cv deleted the chore/installer-hash-multi-release branch July 13, 2026 07:44
cv pushed a commit that referenced this pull request Jul 14, 2026
<!-- markdownlint-disable MD041 -->
## Summary

Addresses the five release/nightly E2E blockers according to the
evidence each failure left behind. The OpenShell version-pin fixture now
supports the installer's archive validation calls, the Shields target
restores lockdown after its intentional failure assertion, MCP patch
failures preserve their real sandbox phase, and the Hermes rebuild
target emits secret-safe phase and host-resource heartbeats.

The Hermes failure was not the fixture's 45-minute rebuild timeout.
GitHub ended the job after about 50 minutes because the hosted runner
lost communication; the rebuild command began later in the scenario and
could not yet have reached its own timeout. No final logs or artifacts
survived, so the underlying memory, disk, CPU, network, or
runner-termination trigger cannot be recovered from that run.

Follow-up to #6744 and #6724.

The source nightly had seven red jobs. A same-SHA rerun cleared the two
accepted flakes, `hermes-shields-config` and `gateway-guard-recovery`,
leaving five release blockers: `openshell-version-pin`,
`shields-config`, `mcp-bridge`, `rebuild-hermes`, and
`channels-stop-start (hermes)`. This PR fixes the two reproducible
fixture defects, repairs the MCP phase evidence, and instruments the
Hermes rebuild runner loss. `mcp-bridge` failed when Docker `stop`
exceeded its 30-second client timeout after a successful image build,
while `channels-stop-start (hermes)` was externally canceled before any
assertion or artifact; neither has evidence for a causal retry or
timeout change, so both still require a fresh green candidate run or a
reproducible product defect.

## Changes

- Model `tar -tzf`, `tar -tvzf`, and extraction in the hermetic
OpenShell version-pin fixture so the installer can validate fake release
archives before extracting them.
- Restore Shields lockdown after the duplicate-down rejection assertion
and register a strict relock cleanup that reports a failed command or
missing lockdown confirmation before continuing destruction.
- Parse modern `NAME CREATED PHASE` OpenShell rows with the shared
sandbox-list parser so MCP Docker-patch diagnostics report
`Provisioning` instead of the creation date, while retaining the
canonical terminal `Evicted` phase.
- Emit a one-minute Hermes rebuild heartbeat from setup through cleanup
with the active phase, child-output age, memory, process RSS, workspace
disk, and load average. The output observer records timestamps only and
never forwards command output or credentials.
- Map the changed OpenShell and Hermes live targets to their fast PR
coverage in the mock/live parity manifest.
- Keep installer recovery behavior, the 30-second destructive
Docker-operation timeout, Shields behavior, sandbox destruction, and
Hermes rebuild behavior unchanged; the 90-minute workflow limit and
45-minute rebuild-command limit are unchanged.
- Keep the underlying `mcp-bridge` Docker timeout and
`channels-stop-start (hermes)` cancellation as explicit green-evidence
blockers instead of masking them with retries.

## Type of Change

- [x] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [ ] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Quality Gates

- [x] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [ ] Tests not applicable — justification:
- [ ] Docs updated for user-facing behavior changes
- [x] Docs not applicable — justification: all changes are internal E2E
fixture, diagnostic, and parity corrections; no user-facing CLI, policy,
lifecycle, installer, or Hermes contract changes.
- [x] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [ ] Sensitive-path review completed or maintainer-approved waiver
recorded — reviewer/approval link/justification: the Shields security
E2E target and secret-safe Hermes diagnostics changed; review is pending
on the current revision and no waiver is requested.
- [ ] Non-success, skipped, or missing CI check accepted by maintainer —
check name, approval link, and follow-up issue: no waiver is requested;
required PR CI is pending.

## Verification

- [x] PR description includes the DCO sign-off declaration and every
commit appears as `Verified` in GitHub
- [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or
`npm run check:diff` passed when hooks were skipped or unavailable
- [x] Targeted behavior tests pass for the current change set, or tests
are marked not applicable above — OpenShell version-pin live fixture
(3/3); focused archive-safety installer tests (9/9); MCP Docker-patch
and shared phase parsing (29/29); Hermes progress and cleanup tests
(15/15); mock/live parity guard; Vitest project membership; source-shape
check; CLI typecheck and build; Shields and Hermes live target
collection.
- [ ] Applicable broad gate passed — `npm test` for broad
runtime/test-harness changes; `npm run check` for repo-wide
validation/coverage changes — command/result: not applicable to narrow
live-target fixture and diagnostic changes. An exploratory local
E2E-support run passed 979 tests and hit 15 environment-specific
failures (macOS Bash behavior, Node 26 warning output, and nested-run
timeouts); required Linux CI remains authoritative.
- [x] Quality Gates section completed with required justifications or
waivers
- [x] No secrets, API keys, or credentials committed
- [ ] `npm run docs` builds without warnings (doc changes only)
- [ ] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

---
Signed-off-by: Charan Jagwani <cjagwani@nvidia.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Bug Fixes**
- Improved sandbox status detection across modern list output formats,
including evicted sandboxes.
- Improved cleanup behavior so shield restoration failures are reported
while subsequent cleanup continues.
  - Strengthened shield-state handling during test teardown.

- **User Experience**
- Added clearer progress and activity reporting during lengthy Hermes
rebuild operations.

- **Tests**
- Expanded coverage for installer version pinning, sandbox lifecycle
states, cleanup ordering, and rebuild progress reporting.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Charan Jagwani <cjagwani@nvidia.com>
@wscurran wscurran added area: ci CI workflows, checks, release automation, or GitHub Actions bug-fix PR fixes a bug or regression labels Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: ci CI workflows, checks, release automation, or GitHub Actions bug-fix PR fixes a bug or regression

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants