Skip to content

fix(shields): retire timer authority after lifecycle gates - #9866

Merged
senthilr-nv merged 2 commits into
mainfrom
codex/fix-9750-shields-lock
Aug 21, 2026
Merged

fix(shields): retire timer authority after lifecycle gates#9866
senthilr-nv merged 2 commits into
mainfrom
codex/fix-9750-shields-lock

Conversation

@senthilr-nv

@senthilr-nv senthilr-nv commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Summary

Successful shields auto-restore now retires its exact timer authority only after the lifecycle main and deadline gates are released. This prevents a normal timer exit from leaving stale gates that indefinitely block snapshot operations.

Related Issue

Fixes #9750

Changes

  • Move successful timer-marker cleanup to a synchronous post-release callback after both exact lifecycle gate generations are released.
  • Add the onReleased lifecycle-fence callback because only the fence helper owns both release operations; the lifecycle-lock regression test protects this ordering.
  • Update the timer flow test to prove marker cleanup observes no lifecycle gate, while existing retry and containment tests continue to protect fail-closed outcomes.

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:
  • 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: Independent nine-category review passed with no findings at 0bcf321a50715d533daa4e280cd6ea03985aa72a.
  • Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue:

DGX Station Hardware Evidence

  • Tested on DGX Station
  • Tested commit:
  • Station profile/scenario:
  • Result:
  • Supporting evidence:

Verification

  • PR description includes a Signed-off-by: line and every commit appears as Verified in GitHub
  • Normal pre-commit, commit-msg, and pre-push hooks passed, or npm run validate:pr passed after refreshing origin/main 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: npx --no-install vitest run --project cli src/lib/shields/timer.test.ts src/lib/state/mcp-lifecycle-lock-acquisition.test.ts -t 'retires successful timer authority only after lifecycle gates are released|runs deadline release completion only after exact gates are absent' passed 2/2; npx --no-install vitest run --project integration test/mcp-lifecycle-lock.test.ts passed 44/44 on the host.
  • 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)

Documentation Writer Review

  • Result: docs-not-needed
  • Evidence: docs/manage-sandboxes/runtime-controls.mdx:136-166 and docs/reference/commands.mdx:1668-1688 already document lifecycle-gate retention during restoration, failure containment, and recovery. This fix restores that behavior after successful timed Shields auto-restore.
  • Agent surface: Codex Desktop

Signed-off-by: Senthil Ravichandran senthilr@nvidia.com

Summary by CodeRabbit

  • Bug Fixes

    • Improved automatic policy restoration sequencing to ensure cleanup occurs only after lifecycle safeguards are fully released.
    • Strengthened lifecycle deadline handling so release callbacks run after all related protections are removed.
  • Tests

    • Added coverage validating restoration cleanup and lifecycle release ordering under asynchronous conditions.

Signed-off-by: Charan Jagwani <cjagwani@nvidia.com>
(cherry picked from commit 8e643f8)
Signed-off-by: Charan Jagwani <cjagwani@nvidia.com>
(cherry picked from commit f12e1b3)
@senthilr-nv senthilr-nv self-assigned this Aug 21, 2026
@copy-pr-bot

copy-pr-bot Bot commented Aug 21, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Aug 21, 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: be5d1b3d-54cf-43f6-b911-25bda1a9ac77

📥 Commits

Reviewing files that changed from the base of the PR and between 35903d3 and 0bcf321.

📒 Files selected for processing (4)
  • src/lib/shields/timer.test.ts
  • src/lib/shields/timer.ts
  • src/lib/state/mcp-lifecycle-lock-acquisition.test.ts
  • src/lib/state/mcp-lifecycle-lock-acquisition.ts

Included review availability: Your plan provides up to 12 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

Auto-restore now defers timer-marker cleanup until restoration completes and both lifecycle lock generations are released. The lifecycle deadline fence supports an onReleased callback, with tests covering callback ordering and marker cleanup timing.

Changes

Timer authority cleanup

Layer / File(s) Summary
Lifecycle release callback
src/lib/state/mcp-lifecycle-lock-acquisition.ts, src/lib/state/mcp-lifecycle-lock-acquisition.test.ts
The deadline fence accepts onReleased and invokes it after releasing the main lock and deadline gate. Tests verify single invocation and release ordering.
Deferred marker cleanup
src/lib/shields/timer.ts, src/lib/shields/timer.test.ts
Auto-restore tracks successful completion and removes the owned timer marker from onReleased. Tests verify both lifecycle gates are absent during cleanup.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 0bcf3

The change retires successful timer authority only after lifecycle gates are released, with targeted regression coverage; no actionable merge-blocking risk remains after normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant Timer
  participant DeadlineFence
  participant LifecycleGates
  participant TimerMarker
  Timer->>DeadlineFence: complete auto-restore
  DeadlineFence->>LifecycleGates: release main lock and deadline gate
  DeadlineFence->>Timer: invoke onReleased
  Timer->>TimerMarker: remove owned marker
Loading

Suggested reviewers: jyaunches, ericksoa, hoyalim

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 4 files. 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 summarizes the main change: retiring Shields timer authority after lifecycle gates release.
Linked Issues check ✅ Passed The changes implement issue #9750 by cleaning timer-created lock authority after successful auto-restore and verifying release ordering.
Out of Scope Changes check ✅ Passed All changes support issue #9750 by updating timer cleanup, lifecycle release behavior, and related regression 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 codex/fix-9750-shields-lock

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

@github-code-quality

github-code-quality Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall line coverage in commit 0bcf321 in the codex/fix-9750-shiel... branch remains at 96%, unchanged from commit 5a9fb5a in the main branch.

TypeScript / code-coverage/cli

The overall line coverage in commit 0bcf321 in the codex/fix-9750-shiel... branch remains at 83%, unchanged from commit 35045ae in the main branch.

Show a line coverage summary of the most impacted files.
File main 35045ae codex/fix-9750-shiel... 0bcf321 +/-
src/lib/onboard...ne-authority.ts 96% 85% -11%
src/lib/inferen...al-lifecycle.ts 97% 88% -9%
src/lib/state/g...way-registry.ts 96% 87% -9%
src/lib/messaging/hydration.ts 84% 77% -7%
src/lib/onboard...rtup/profile.ts 94% 89% -5%
src/lib/sandbox...rce-identity.ts 86% 81% -5%
src/lib/onboard...mo-lifecycle.ts 83% 80% -3%
src/lib/state/o...oard-session.ts 85% 82% -3%
src/lib/state/p...l-retirement.ts 81% 80% -1%
src/lib/inferen...pter-forward.ts 73% 79% +6%

Updated August 21, 2026 06:03 UTC

@github-actions

github-actions Bot commented Aug 21, 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 match; severity counts match.
3 terminology differences from the second opinion

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

  • lifecycle gate at src/lib/shields/timer.test.ts:675: selected only by the second-opinion lane as established.
  • lifecycle gates at src/lib/shields/timer.test.ts:675: selected only by the second-opinion lane as established.
  • onReleased at src/lib/state/mcp-lifecycle-lock-acquisition.ts:62: selected only by the second-opinion lane as justified.

Second-opinion terminology and E2E selections are advisory. Live E2E does not run automatically for pull requests.

2 semantic terminology decisions

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

  • established — timer authority at src/lib/shields/timer.test.ts:675: Keep the established term.
  • established — lifecycle generation at src/lib/shields/timer.ts:636: Keep the established term.

E2E guidance

Advisory only. A maintainer can dispatch the default E2E suite for the commit under review.

Recommended E2E: None

Manual-only E2E: security-posture, onboard-repair, onboard-resume, cloud-inference
The manual PR workflow does not run these selectors for the commit under review. Run them from reviewed code on main.

Workflow run details

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

@senthilr-nv
senthilr-nv marked this pull request as ready for review August 21, 2026 06:05
@senthilr-nv
senthilr-nv requested a review from cv August 21, 2026 06:05
@cv

cv commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Maintainer review note: focused lifecycle/security review passes at 0bcf321a50715d533daa4e280cd6ea03985aa72a, all required checks pass, and no review threads remain. The remaining readiness blockers are draft status and review approval. Please mark the PR ready when contributor intent is final; I will not change draft state on the contributor’s behalf.

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

Successful timer cleanup now occurs only after both lifecycle gate generations are released. Exact marker authority is preserved through quarantine and identity checks; release or cleanup failures enter existing retry or containment handling; durable-containment paths retain both gates and marker authority. Focused tests cover release ordering, marker cleanup, retries, and retained containment. Required checks pass and the PR is ready for review.

@senthilr-nv senthilr-nv added bug-fix PR fixes a bug or regression area: policy Network policy, egress rules, presets, or sandbox policy area: sandbox OpenShell sandbox lifecycle, runtime, config, or recovery labels Aug 21, 2026
@senthilr-nv
senthilr-nv merged commit 9dceb32 into main Aug 21, 2026
76 checks passed
@senthilr-nv
senthilr-nv deleted the codex/fix-9750-shields-lock branch August 21, 2026 06:14
senthilr-nv added a commit that referenced this pull request Aug 30, 2026
## Outcome

A completed Shields auto-restore no longer leaves its sandbox
permanently blocked when the detached timer exits after restoring
persisted state but before releasing its lifecycle gates. The next
Shields-aware command retires only the proven orphaned timer generation.
Ordinary commands, interactive `sandbox connect`, and two-sandbox
snapshot restore then proceed normally; live, foreign, replaced, or
otherwise ambiguous process identity remains denied.

## Reason

The timeout path in #10094 can persist `shieldsDown: false` and finish
the restore, then lose the timer process before final cleanup releases
the main/deadline gates and marker. Ordinary acquisition subsequently
commits process-tree containment for the dead owner, but the prior
fail-closed code had no proof-backed path to distinguish this completed
generation from unresolved containment.

### Related issues

Fixes #10094
Relates to #9750
Relates to #9866

## Changes

- Recognize recovery authority only when Shields state is already UP,
the exact 32-hex timer marker is expired and abandoned, and no
transition for that token remains.
- Recover only stale local main/deadline owners bound to the marker PID,
sandbox, host identity, PID namespace, process identity, protected
filesystem generation, and takeover token. Reapers and active, foreign,
replaced, corrupt, or unrelated generations remain denied.
- Require exact structured containment evidence and retire containment,
main, then deadline gates so every partial cleanup failure leaves at
least one admission gate in place. Marker cleanup uses generation-pinned
rename/revalidation, directory durability checks, and restore-or-retain
behavior for retry.
- Reject command admission unless every completed timer artifact is
verifiably removed. A replacement injected after the authority check
remains preserved and blocks the command.
- Run the shared recovery before normal lifecycle locking, before
interactive connect starts, and in stable source/destination order
before snapshot restore locks either sandbox.
- Add process-backed unit/integration regressions for orphan
reproduction, successful recovery, idempotent retry, unrelated
acquisition, interactive connect, snapshot endpoints, marker
replacement, live-owner denial, cleanup failures, and
terminal-containment preservation.
- Document the bounded automatic path and exact fail-closed operator
procedure, including the separate retention rules for invalid or
mismatched artifacts.

## Verification

Latest PR commit: `561845d3b5485813a9b4b4d0708efccdac8262e8`
Current upstream base/workflow:
`d0b120958600e9651001957503c99945248c30d8`

- `npm run validate:pr` — passed pre-commit, commitlint, pre-push,
repository architecture, source-shape, growth, secret, lint, format, and
CLI TypeScript checks on the latest PR commit.
- Focused merged-base lifecycle/security Vitest — 165 tests passed
across regular-file reads, snapshot recovery, completed auto-restore
command admission, launch routing, timer control, timer lifecycle,
OpenClaw transition, and lifecycle identity/authority.
- `npm run docs` — passed with 0 errors and 2 pre-existing unrelated
Fern warnings.
- [CI / Pull
Request](https://github.com/NVIDIA/NemoClaw/actions/runs/33299035713) —
passed on the latest PR commit and current base.
- [PR Review
Advisor](https://github.com/NVIDIA/NemoClaw/actions/runs/33299034668) —
all nine specialists passed; Behavior, Trust, Design, Operations,
Documentation, Test Design, Migration, Dependency, and Code Reduction
reports found no issue.
- [E2E / Pull
Request](https://github.com/NVIDIA/NemoClaw/actions/runs/33299036504) —
the selected sandbox, gateway-isolation, port-override, and non-root
smoke jobs passed; the generic-GPU lane was policy-skipped by selection.
- [CodeQL](https://github.com/NVIDIA/NemoClaw/actions/runs/33299034216)
and [Security Code
Scanning](https://github.com/NVIDIA/NemoClaw/actions/runs/33299035677) —
passed.
- CodeRabbit completed its latest-commit review with no new inline
finding; its check is green. The earlier replacement-authority security
finding is fixed by rejecting every timer-retirement result other than
verified removal.
- [Trusted focused Shields
E2E](https://github.com/NVIDIA/NemoClaw/actions/runs/33290527333) —
passed. [Selected
job](https://github.com/NVIDIA/NemoClaw/actions/runs/33290527333/job/99201448158).
Receipt kind `nemoclaw-e2e-dispatch-v2`; correlation
`bc7af474-d7d9-4918-99c5-694c6d31593e`; target `shields-config`;
candidate/base/workflow SHAs matched that tested revision; risk signal
reported 1 passed, 0 failed/skipped/pending/unhandled; all 14 Shields
assertions passed; cleanup reported no failures. Later commits tighten
proof reads, cleanup failure handling, diagnostic display, and recovery
guidance without expanding the E2E surface.

The first current-base dispatch, [run
33289861067](https://github.com/NVIDIA/NemoClaw/actions/runs/33289861067),
stopped before candidate checkout because its bounded five-minute
prerequisite wait expired while current-main managed images were still
publishing. After [that
publication](https://github.com/NVIDIA/NemoClaw/actions/runs/33289214786)
succeeded, the same tested revision/base selection passed in run
33290527333.

## Review notes

This changes a security-sensitive fail-closed lifecycle path. Final
review covers credentials, input/filesystem validation, exact
authorization identity, dependency use, error/retry behavior,
cryptographic tokens and hashes, configuration boundaries, negative/race
tests, and system/TOCTOU behavior. Recovery requires exact local stale
identity and marker/state authority; ambiguous live identity, foreign
ownership, reaper presence, changed generations, incomplete durable
cleanup, and terminal auto-restore containment remain denied.

PR #10571 was explicitly checked and is now merged at
`b0e242b8bb29993e2bebfd48f00d649cb103f55f`. It owns retained-sandbox
onboarding/destroy cleanup and does not change the Shields timer, MCP
lifecycle-lock, or containment files. This PR is the smallest
independent completed-auto-restore recovery change and does not
duplicate that work.

No new billable infrastructure was provisioned. This validation used the
existing authorized Shields lane in mock inference mode. DGX Spark,
Jetson, and staging dispatch were disabled, so no hardware evidence is
claimed.

---
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

---------

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Signed-off-by: Senthil Ravichandran <senthilr@nvidia.com>
Co-authored-by: Senthil Ravichandran <senthilr@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: policy Network policy, egress rules, presets, or sandbox policy area: sandbox OpenShell sandbox lifecycle, runtime, config, or recovery bug-fix PR fixes a bug or regression

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Linux][Sandbox] snapshot create blocked indefinitely by stale containment lock after shields-down auto-restore timer expires

3 participants