Skip to content

fix(state): restore Hermes cron scripts before enabling restored jobs - #7880

Open
laitingsheng wants to merge 25 commits into
mainfrom
fix/hermes-cron-script-state-restore
Open

fix(state): restore Hermes cron scripts before enabling restored jobs#7880
laitingsheng wants to merge 25 commits into
mainfrom
fix/hermes-cron-script-state-restore

Conversation

@laitingsheng

@laitingsheng laitingsheng commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Hermes confines the scripts that no_agent cron jobs run to HERMES_HOME/scripts, but that directory was absent from the Hermes state contract. A rebuild could therefore restore enabled cron definitions without restoring the scripts they call. This PR includes scripts in Hermes state, publishes restored directories as complete units with cron after scripts, and rolls live state back if staged publication fails instead of leaving a partial restore or deleting the prior state.

Before either live scheduled-work directory moves, NemoClaw now asks the running Hermes gateway to enter its native external-drain state and waits for messaging, API, and cron activity to reach zero. It validates every enabled restored script reference before releasing only the drain marker NemoClaw created. Operator-owned drains remain in place, and rollback failure preserves both the recovery tree and the drain.

Related Issue

Fixes #7806

Changes

  • Add scripts to the Hermes state contract and the high-risk state-directory lock inventory.
  • Stage whole state directories before publication and apply scheduled-work definitions last.
  • Preserve each live directory in a rollback tree before replacing or removing it. Restore the prior directories when publication fails.
  • Preserve .nemoclaw-restore-rollback and refuse a later restore if rollback or post-commit cleanup cannot finish, so a retry cannot overwrite the recovery copy.
  • Quiesce Hermes through the pinned native drain marker before the first scripts/cron transition; wait for gateway_state=draining and zero active messaging, API, and cron work.
  • Validate enabled job records and require their script references to resolve to readable regular files inside HERMES_HOME/scripts before resuming dispatch. Hermes invokes those files through an explicit Bash/Python interpreter, so readability is the pinned runtime permission contract.
  • Preserve a drain already owned by an operator. Release only a correctly shaped NemoClaw ownership token, and fail closed with the drain engaged if rollback cannot restore the prior state.
  • Install the restore guard root-owned and immutable in the Hermes image and bind it to the reviewed source SHA-256.
  • Keep the image-managed OpenClaw extension merge path unchanged because it must combine fresh image content with backed-up user content.
  • Document Hermes script capture, drain/validation behavior, and recovery-tree handling.

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:
  • 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: Codex Desktop performed a serialized nine-category review of the exact current-main diff at 88f6b0cfa9c69976ba0df1b0fd0b0fad56c84b35. Secrets, input/path validation, authorization, dependencies, logging, cryptography, configuration scope, rollback/concurrency integrity, and regression coverage pass. The root-owned image helper is hash-bound; every dynamic shell path is quoted; enabled scripts must remain under the resolved scripts root; operator drains are not intentionally released; and failed rollback preserves the drain and recovery tree. The follow-up CI repair only adds the helper to Docker replay fixtures and classifies its SHA-256 build argument as an integrity pin. Exact diff fingerprint: d93aa2010392d6b11ba46b87da530791f32e03b997cd6743a92ff25718a272ea (git diff --binary origin/main...HEAD | shasum -a 256). Fresh exact-head CI/E2E and automated review remain required.
  • Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue:

Documentation Writer Review

  • Documentation writer reviewed the completed changes
  • Result: docs-updated
  • Evidence: docs/manage-sandboxes/backup-restore.mdx, docs/reference/commands.mdx, and docs/security/best-practices.mdx; the exact-head CI repair is test/contract-only and needs no additional docs. npm run docs completed with 0 errors and 2 pre-existing warnings.
  • Agent: Codex Desktop (serialized main task)

DGX Station Hardware Evidence

  • Tested on DGX Station
  • Tested commit: Not applicable; scripts/prepare-dgx-station-host.sh is unchanged.
  • Station profile/scenario: Not applicable.
  • Result: Not applicable.
  • Supporting evidence: Not applicable.

Verification

  • PR description includes a Signed-off-by: line and every new commit appears as Verified in GitHub
  • Normal pre-commit, commit-msg, and pre-push hooks passed, and npm run validate:pr passed against current origin/main
  • Targeted behavior tests pass for the current change set — staged Hermes restore: 15/15; restore guard/image/watch contracts: 22/22; adjacent CLI restore suites: 21/21; broader snapshot/recreation suites: 102 passed, 2 skipped; state permission/guard suites: 37 passed, 1 skipped. Exact CI repair: affected Docker replay assertions pass, managed startup profile 102/102, Biome, npm run checks, and full npm run validate:pr pass. The config-hash path also passes 3/3 in a clean Linux container with isolated PyYAML; the macOS-only permission result is an environment limitation, not a product failure.
  • Applicable broad gate passed — npm test for broad runtime/test-harness changes; npm run check for repo-wide validation/coverage changes — command/result: Fresh exact-head GitHub CI, E2E, CodeRabbit, and Advisor gates are running.
  • 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) — 0 errors and 2 pre-existing Fern warnings
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Signed-off-by: Tinson Lai tinsonl@nvidia.com

Summary by CodeRabbit

  • New Features

    • Hermes scheduled-work backups and restores now include associated scripts and cron definitions.
    • Restore operations safely drain active gateway activity and validate script references before completion.
  • Bug Fixes

    • Improved rollback and cleanup when publication or validation fails.
    • Added safeguards against incomplete restores, unsafe script paths, and unauthorized state-directory changes.
    • Failed recoveries retain rollback information to prevent unsafe subsequent restores.
  • Documentation

    • Updated backup, restore, command-reference, and security guidance to reflect the enhanced safeguards.

Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
@coderabbitai

coderabbitai Bot commented Jul 30, 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: febc3b1d-bda0-4388-b9c5-bd6947b5100c

📥 Commits

Reviewing files that changed from the base of the PR and between 2056cc6 and 2cd6d4e.

📒 Files selected for processing (6)
  • agents/hermes/Dockerfile
  • src/lib/onboard/managed-startup/profile.ts
  • test/hermes-doctor-config-hash.test.ts
  • test/hermes-final-image-layout.test.ts
  • test/sandbox-provisioning.test.ts
  • test/sandbox-rlimit-hooks.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • agents/hermes/Dockerfile

📝 Walkthrough

Walkthrough

Hermes now persists .hermes/scripts with cron state. Restore drains gateway work, validates enabled scripts, stages directory replacement, publishes scripts before cron, and protects rollback and cleanup state.

Changes

Sandbox restore flow

Layer / File(s) Summary
Hermes state and runtime contract
agents/hermes/manifest.yaml, agents/hermes/Dockerfile, agents/hermes/restore-cron-guard.py, scripts/state-dir-guard.py, src/lib/onboard/managed-startup/profile.ts, docs/security/best-practices.mdx, test/hermes-doctor-config-hash.test.ts, test/hermes-final-image-layout.test.ts, test/sandbox-provisioning.test.ts, test/sandbox-rlimit-hooks.test.ts
Hermes declares scripts as state. The runtime packages and verifies the restore guard. Protected-file, ownership, mode, image-layout, and high-risk state checks include the new guard and state directory.
Gateway drain and script validation
agents/hermes/restore-cron-guard.py, docs/manage-sandboxes/backup-restore.mdx, docs/reference/commands.mdx, docs/security/best-practices.mdx, test/hermes-restore-cron-guard.test.ts
Restore drains gateway work, validates enabled cron script references, preserves operator-owned drains, and fails closed when validation or recovery fails.
Staged state publication
src/lib/state/sandbox.ts, src/lib/state/sandbox-staged-restore.test.ts, docs/manage-sandboxes/backup-restore.mdx
Restore stages directory replacements, publishes scripts before cron, rolls back failed transitions, and tests successful and failed publication paths.

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

Possibly related issues

Possibly related PRs

Suggested labels: area: security

Suggested reviewers: cv, cjagwani, prekshivyas

Sequence Diagram(s)

sequenceDiagram
  participant SandboxRestore
  participant RestoreCronGuard
  participant HermesGateway
  participant HermesState
  SandboxRestore->>RestoreCronGuard: Begin drain
  RestoreCronGuard->>HermesGateway: Stop dispatch and wait for active work
  SandboxRestore->>HermesState: Stage scripts and cron state
  SandboxRestore->>RestoreCronGuard: Validate enabled script references
  RestoreCronGuard-->>SandboxRestore: Allow publication or fail closed
  SandboxRestore->>HermesState: Publish scripts before cron
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 clearly identifies the primary change: restoring Hermes cron scripts before enabling restored jobs.
Linked Issues check ✅ Passed The implementation restores scripts and cron definitions transactionally, drains Hermes, validates enabled scripts, and resumes only after commit or rollback [#7806].
Out of Scope Changes check ✅ Passed The changes support the linked issue through transactional restore, draining, validation, integrity controls, documentation, and tests.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/hermes-cron-script-state-restore

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

@github-actions

Copy link
Copy Markdown
Contributor

@github-code-quality

github-code-quality Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in commit 2cd6d4e in the fix/hermes-cron-scri... branch remains at 96%, unchanged from commit f504948 in the main branch.


Updated August 04, 2026 08:48 UTC

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — No blocking findings reported

Advisor assessment: No blocking advisor findings reported
Next action: No advisor follow-up needed.
Findings: 0 blockers · 0 warnings · 0 suggestions

Model lanes

  • GPT-5.6 Terra (primary): Completed · high confidence · 0 blockers · 0 warnings · 0 suggestions
  • Nemotron 3 Ultra (second opinion): Completed · high confidence · 0 blockers · 0 warnings · 0 suggestions
  • Model comparison: normalized findings match; normalized terminology decisions differ; normalized E2E selections differ; severity counts match.
7 terminology differences from the second opinion

Advisory only. These are normalized differences from the primary terminology receipt.

  • staging directory at src/lib/state/sandbox-staged-restore.test.ts:388: selected only by the second-opinion lane as established.
  • restore guard at agents/hermes/restore-cron-guard.py:29: selected only by the second-opinion lane as define.
  • operator-owned drain at test/hermes-restore-cron-guard.test.ts:149: selected only by the second-opinion lane as define.
  • drain marker at agents/hermes/restore-cron-guard.py:59: selected only by the second-opinion lane as define.
  • scheduled-work state at agents/hermes/restore-cron-guard.py:4: selected only by the second-opinion lane as define.
  • staged restore at src/lib/state/sandbox-staged-restore.test.ts:65: selected only by the second-opinion lane as define.
  • recovery tree at src/lib/state/sandbox-staged-restore.test.ts:461: selected only by the second-opinion lane as define.

Second-opinion terminology and E2E selections are advisory. They do not change the primary assessment or E2E / PR Gate.

3 semantic terminology decisions

Terminology decisions are advisory. They affect the assessment only when a separate finding identifies concrete semantic impact.

  • justified — scheduled work at docs/manage-sandboxes/backup-restore.mdx:56: Keep “scheduled work” where the text refers to Hermes cron-dispatched work; retain the stated contrast to scheduled workflows.
  • established — drain at docs/manage-sandboxes/backup-restore.mdx:54: Keep “drain” and identify the Hermes gateway when the surrounding text does not already establish it.
  • replace — recovery tree at src/lib/state/sandbox.ts:1785: Replace “recovery tree” with “rollback directory” when referring to .nemoclaw-restore-rollback.

E2E guidance

Advisory only. E2E / PR Gate selects and runs jobs independently.

Recommended E2E: cloud-inference, cloud-onboard, full-e2e, hermes-e2e, hermes-inference-switch, security-posture, device-auth-health, issue-4462-scope-upgrade-approval, onboard-repair, onboard-resume, openclaw-inference-switch

3 optional E2E recommendations
  • rebuild-hermes
  • state-backup-restore
  • snapshot-commands

Workflow run details

This automated review informs maintainers. Warnings and suggestions do not require a response. A maintainer decides whether to merge.

Signed-off-by: Tinson Lai <tinsonl@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.

Actionable comments posted: 2

🤖 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.

Inline comments:
In `@src/lib/state/sandbox-staged-restore.test.ts`:
- Around line 46-48: Update the fixture setup in the sandbox staged-restore test
to create the scripts directory if necessary and always write digest.sh,
removing the options.stateDirs.includes("scripts") conditional. Leave archive
selection controlled by the manifest.

In `@src/lib/state/sandbox.ts`:
- Around line 1377-1388: Update the restore command construction around the
commands array so staging cleanup runs when extraction or any state-directory
move fails, not only on successful completion. Add failure-safe shell cleanup,
such as an exit trap, for the staging path while preserving the existing
successful restore flow, and add coverage for failed extraction or move cleanup.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 00a0a7d7-3a7b-414a-96ef-f29fbed89c70

📥 Commits

Reviewing files that changed from the base of the PR and between 4dcb89e and 6462e22.

📒 Files selected for processing (5)
  • agents/hermes/manifest.yaml
  • docs/manage-sandboxes/backup-restore.mdx
  • docs/reference/commands.mdx
  • src/lib/state/sandbox-staged-restore.test.ts
  • src/lib/state/sandbox.ts

Comment thread src/lib/state/sandbox-staged-restore.test.ts Outdated
Comment thread src/lib/state/sandbox.ts Outdated

@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.

♻️ Duplicate comments (1)
src/lib/state/sandbox-staged-restore.test.ts (1)

42-46: ⚠️ Potential issue | 🟠 Major

Still create backup/scripts before the unconditional write.

Lines 42-44 create only configured state directories, so Line 46 throws ENOENT whenever stateDirs omits "scripts". Create the fixture directory independently; let the manifest control whether it is restored.

Proposed fix
     for (const stateDir of options.stateDirs) {
       fs.mkdirSync(path.join(backupPath, stateDir), { recursive: true });
     }
+    fs.mkdirSync(path.join(backupPath, "scripts"), { recursive: true });
     fs.writeFileSync(path.join(backupPath, "cron", "jobs.json"), '{"jobs":[{"enabled":true}]}\n');
     fs.writeFileSync(path.join(backupPath, "scripts", "digest.sh"), "#!/bin/bash\necho ok\n");

This is the same unresolved fixture-setup issue reported previously.

🤖 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 `@src/lib/state/sandbox-staged-restore.test.ts` around lines 42 - 46, Update
the fixture setup around the stateDirs loop and scripts/digest.sh write to
always create the backup/scripts directory independently before writing the
file. Keep stateDirs controlling only which directories are configured for
restoration, while ensuring the unconditional scripts fixture write cannot fail
when scripts is omitted.
🤖 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.

Duplicate comments:
In `@src/lib/state/sandbox-staged-restore.test.ts`:
- Around line 42-46: Update the fixture setup around the stateDirs loop and
scripts/digest.sh write to always create the backup/scripts directory
independently before writing the file. Keep stateDirs controlling only which
directories are configured for restoration, while ensuring the unconditional
scripts fixture write cannot fail when scripts is omitted.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ead92611-e2b1-4770-b6af-cfc925049245

📥 Commits

Reviewing files that changed from the base of the PR and between 6462e22 and 7da56a0.

📒 Files selected for processing (1)
  • src/lib/state/sandbox-staged-restore.test.ts

Signed-off-by: Tinson Lai <tinsonl@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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lib/state/sandbox.ts (1)

1377-1390: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Make failed publication rollback-safe, not only staging-clean.

The command still removes each live state directory before moving its staged replacement. If a mv fails, the new EXIT trap removes staging but cannot restore the live directory already deleted; earlier directories may also have been published. A transient publication failure can therefore leave the sandbox partially restored or missing state. Use a rollback/transactional swap and extend the failure test to seed existing state and assert it survives.

As per path instructions, destructive lifecycle operations must validate before mutation, preserve state/backup invariants, and cover failure/recovery/rebuild/resume behavior.

🤖 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 `@src/lib/state/sandbox.ts` around lines 1377 - 1390, Update the restore
command construction around orderStateDirsForRestore so publication is
rollback-safe: preserve each existing live state directory in a backup, publish
staged replacements transactionally, and restore the original state if any
removal or move fails before cleanup. Keep staging cleanup via the EXIT trap,
and extend the failure test to seed existing state and verify it remains intact
after a failed publication.

Source: Path instructions

🤖 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.

Inline comments:
In `@src/lib/state/sandbox-staged-restore.test.ts`:
- Around line 80-83: The movesFail shim in the staged-restore test must record
that publication was attempted before exiting. Update the injected script in the
movesFail branch to write a failure marker, then assert that marker together
with success === false and staging cleanup in the affected test cases.

---

Outside diff comments:
In `@src/lib/state/sandbox.ts`:
- Around line 1377-1390: Update the restore command construction around
orderStateDirsForRestore so publication is rollback-safe: preserve each existing
live state directory in a backup, publish staged replacements transactionally,
and restore the original state if any removal or move fails before cleanup. Keep
staging cleanup via the EXIT trap, and extend the failure test to seed existing
state and verify it remains intact after a failed publication.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 9b7dc759-dc37-43fb-99aa-9c06ac5ca2bb

📥 Commits

Reviewing files that changed from the base of the PR and between 7da56a0 and 617ea5d.

📒 Files selected for processing (2)
  • src/lib/state/sandbox-staged-restore.test.ts
  • src/lib/state/sandbox.ts

Comment thread src/lib/state/sandbox-staged-restore.test.ts Outdated
@laitingsheng laitingsheng added NV QA Bugs found by the NVIDIA QA Team integration: hermes Hermes integration behavior area: sandbox OpenShell sandbox lifecycle, runtime, config, or recovery bug-fix PR fixes a bug or regression labels Jul 30, 2026
@cjagwani

Copy link
Copy Markdown
Collaborator

Babysitting review for exact head dfde003a364d9ecf8ffd1f68097dea852d9c8e38 (plain comment; no Changes Requested review): approval is held on one code issue plus the exact-head protected failure.

  • buildStagedRestoreCommand removes each live state directory before moving its staged replacement. If any mv fails, the EXIT trap removes staging but cannot restore the live directory already deleted, and any earlier directories remain replaced. The new failure test confirms this partial-publication behavior (scripts is attempted, cron remains absent) but does not seed/assert preservation of pre-restore state. Because this is a destructive lifecycle path, publication needs a rollback-safe swap: retain each old directory until all new directories are publishable, and on failure restore every prior live directory. Add a failure test with existing scripts/cron state that proves the original state survives and the gateway never sees restored cron before its script.

  • The trusted exact-head E2E gate is terminal red: selected run 30524999509 failed hermes-e2e while building the Hermes image because /sandbox/.hermes/runtime/cron-executions.db was absent at Dockerfile step 137. The gate policy does not permit retrying a product failure on the same SHA. Please determine whether the head caused/exposed it and fix it; if it is independently proven infrastructure/base behavior, record a narrowly scoped maintainer waiver with that evidence.

No base refresh is requested.

Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
@apurvvkumaria apurvvkumaria self-assigned this Jul 30, 2026

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lib/state/sandbox.ts (1)

1352-1393: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Make staged publication rollback-safe.

Removing a live directory before its staged mv succeeds turns a failed restore into data loss; the EXIT trap only removes staging. Preserve every replaced live directory in a rollback location, restore all prior directories if any publish step fails, and delete rollback data only after the full ordered publication succeeds. Add failed-mv coverage proving existing scripts and cron survive and restored cron is never exposed without its scripts.

As per path instructions, destructive lifecycle operations must preserve state/backup invariants and cover failure, recovery, rebuild, and resume behavior without bypassing the public action boundary.

🤖 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 `@src/lib/state/sandbox.ts` around lines 1352 - 1393, Update
buildStagedRestoreCommand and its restore tests to make publication
rollback-safe: move each existing live state directory into a rollback location
before replacing it, and on any failed publish restore all previously replaced
directories in the original order. Remove rollback data only after every ordered
directory publish succeeds, while retaining staging cleanup. Add failed-mv
coverage proving existing scripts and cron are preserved and cron is never
visible without its restored scripts.

Source: Path instructions

🤖 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.

Outside diff comments:
In `@src/lib/state/sandbox.ts`:
- Around line 1352-1393: Update buildStagedRestoreCommand and its restore tests
to make publication rollback-safe: move each existing live state directory into
a rollback location before replacing it, and on any failed publish restore all
previously replaced directories in the original order. Remove rollback data only
after every ordered directory publish succeeds, while retaining staging cleanup.
Add failed-mv coverage proving existing scripts and cron are preserved and cron
is never visible without its restored scripts.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 67464f1b-80a9-46ba-bb13-4d77aee86011

📥 Commits

Reviewing files that changed from the base of the PR and between dfde003 and a5a1de4.

📒 Files selected for processing (3)
  • docs/manage-sandboxes/backup-restore.mdx
  • docs/reference/commands.mdx
  • src/lib/state/sandbox.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/manage-sandboxes/backup-restore.mdx
  • docs/reference/commands.mdx

Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
…edger-e2e

Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
apurvvkumaria and others added 5 commits July 31, 2026 07:55
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
@cjagwani

cjagwani commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Exact-head babysitting update for b52ec4f2bb4b90af397efd1865c052bf44cf4d01 (plain comment; no Changes Requested review): the new drain/validate/release transaction resolves the previously reported accepted-scope blocker. It drains before replacing scripts/cron, verifies enabled script references before resuming, preserves operator-owned drains, rolls back on validation failure, and keeps its own drain when rollback cannot recover. My nine-category security re-review of that delta is clean: no new secret, injection, authorization, dependency, logging, crypto, configuration, testing, or concurrency blocker found. The selected E2E run 30663129129 failed full-e2e on attempt 1, then all selected jobs passed on attempt 2; however, the trusted coordination check remains terminal red against attempt 1, and no maintainer waiver reconciles that retry. The captured base is also no longer current. I therefore cannot accept the gate or approve this SHA, did not rerun it again, and am not asking for a base refresh solely for currency.

@copy-pr-bot

copy-pr-bot Bot commented Aug 2, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@apurvvkumaria apurvvkumaria left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Blocking: current head 7824b3f is syntactically invalid. The merge-resolution change removed the closing brace of buildStagedRestoreCommand in src/lib/state/sandbox.ts after its return and before snapshotManifestAuthority. Parsing the exact blob reaches EOF with TypeScript diagnostic TS1005 at 2615:1: closing brace expected, so the CLI cannot type-check or build and the feature cannot serve its stated purpose.\n\nPlease restore the missing closing brace and run npm run typecheck:cli plus the staged-restore tests and required CI. This is a direct, reproducible current-revision build break.

@apurvvkumaria apurvvkumaria left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Re-reviewed exact head a5a698f. The blocking syntax defect remains in src/lib/state/sandbox.ts: buildStagedRestoreCommand closes its return expression with ); but never closes the function body before function snapshotManifestAuthority begins. TypeScript therefore parses the following declarations inside a function and the supported CLI build/typecheck cannot succeed. Please add the missing closing brace immediately after the return expression and add or run a focused syntax/typecheck regression on this exact revision. No other blocking finding is asserted here.

Signed-off-by: Tinson Lai <tinsonl@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.

Actionable comments posted: 3

🤖 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.

Inline comments:
In `@agents/hermes/restore-cron-guard.py`:
- Around line 108-139: Update validate_enabled_scripts to validate each enabled
script’s readability using the gateway user’s permissions rather than os.access
under the restore process; perform the open/readability check through the
existing privilege-switching mechanism and retain the current missing/unreadable
ValueError behavior. Add a negative test covering a sandbox-owned 0600 script
that the restore process can access but gateway cannot.
- Around line 50-84: Update begin_drain and _release_owned_marker to use atomic
create-if-absent and clear-if-token-matches operations. Always acquire an owned
drain marker before checking or waiting on the gateway PID, including when it is
initially inactive, and return “inactive” only after safely releasing that owned
marker. Ensure the owned token is released on successful completion as well as
timeout, commit, or rollback, while never clearing a replacement marker.

In `@docs/security/best-practices.mdx`:
- Line 347: In the Hermes rebuild restore description, split the two sentences
onto separate source lines while preserving their wording and paragraph
formatting.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: bdce40c8-3a99-4ed7-aab7-38e057a478e2

📥 Commits

Reviewing files that changed from the base of the PR and between dfde003 and a5a698f.

📒 Files selected for processing (7)
  • agents/hermes/Dockerfile
  • agents/hermes/manifest.yaml
  • agents/hermes/restore-cron-guard.py
  • docs/manage-sandboxes/backup-restore.mdx
  • docs/reference/commands.mdx
  • docs/security/best-practices.mdx
  • scripts/state-dir-guard.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • agents/hermes/manifest.yaml
  • docs/reference/commands.mdx

Comment thread agents/hermes/restore-cron-guard.py
Comment thread agents/hermes/restore-cron-guard.py
Comment thread docs/security/best-practices.mdx Outdated
laitingsheng and others added 4 commits August 4, 2026 06:26
Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
Claim an exclusive restore drain marker before the gateway PID check so a
gateway that starts during publication still sees the drain, release only a
marker the restore owns, and validate enabled cron scripts against the gateway
account rather than the restore process.

Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
@cjagwani

cjagwani commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Exact-head CI follow-up for 2cd6d4ed8: the Hermes managed-image job fails deterministically before startup because agents/hermes/restore-cron-guard.py no longer matches NEMOCLAW_HERMES_RESTORE_CRON_GUARD_SHA256 in the image build. Buildx stops at the sha256sum -c - guard with the explicit hash-mismatch error.

Please update the committed Hermes restore-guard digest to the exact current script bytes (and its contract assertion, if applicable), then let the new head rerun the managed-image and protected E2E gates. I have not edited the author branch or rerun the failing SHA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: sandbox OpenShell sandbox lifecycle, runtime, config, or recovery bug-fix PR fixes a bug or regression integration: hermes Hermes integration behavior v0.0.102 Release target

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Ubuntu 24.04][Upgrade] rebuild enables restored cron jobs before their scripts are available

6 participants