fix(shields): refuse a corrupt transition lock without a raw stack trace - #8122
Conversation
An unreadable owner record can never be recovered by waiting, but shields commands polled it for the full wait timeout and then let a plain Error reach the user. Refuse as soon as the record is observed and report it through the shields exit sentinel, so up, down, and read-only status all fail closed with one line plus the recovery hint. Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
📝 WalkthroughWalkthroughThe Shields transition-lock flow now rejects stale malformed lock records immediately. It exposes structured recovery details, prints sanitized deferred-command errors, preserves the lock file, and adds synchronous, asynchronous, and CLI coverage. ChangesShields transition-lock handling
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Code Coverage OverviewLanguages: TypeScript TypeScript / code-coverage/pluginThe overall coverage in commit a986513 in the TypeScript / code-coverage/cliThe overall coverage in commit a986513 in the Show a code coverage summary of the most impacted files.
Updated |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@test/cli/shields-transition-lock.test.ts`:
- Around line 31-69: Add a corrupt-transition-lock test alongside the existing
cases in the “shields commands with a corrupt transition lock” suite, invoking
the public `alpha shields up` command and verifying exit code 1, clean refusal
output via `expectCleanRefusal`, completion within `REFUSAL_BUDGET_MS`, and
unchanged malformed lock contents. Keep the setup consistent with the existing
`shields status` and `shields down` tests.
🪄 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: 23c0e00a-fe78-4cc1-b32c-80c3727c2f6c
📒 Files selected for processing (4)
src/lib/shields/index.tssrc/lib/shields/transition-lock.test.tssrc/lib/shields/transition-lock.tstest/cli/shields-transition-lock.test.ts
| describe("shields commands with a corrupt transition lock", () => { | ||
| it( | ||
| "refuses read-only status without a raw stack trace (#8108)", | ||
| testTimeoutOptions(30_000), | ||
| ({ testHome }) => { | ||
| const { home } = testHome; | ||
| writeSandboxRegistry(home); | ||
| const lockPath = writeCorruptTransitionLock(home, "alpha"); | ||
|
|
||
| const startedAt = Date.now(); | ||
| const status = runWithEnv("alpha shields status 2>&1", testHome.environment()); | ||
| const elapsedMs = Date.now() - startedAt; | ||
|
|
||
| expect(status.code).toBe(1); | ||
| expectCleanRefusal(status.out); | ||
| expect(elapsedMs).toBeLessThan(REFUSAL_BUDGET_MS); | ||
| expect(fs.readFileSync(lockPath, "utf8")).toBe(""); | ||
| }, | ||
| ); | ||
|
|
||
| it( | ||
| "refuses shields down without a raw stack trace (#8108)", | ||
| testTimeoutOptions(30_000), | ||
| ({ testHome }) => { | ||
| const { home } = testHome; | ||
| writeSandboxRegistry(home); | ||
| const lockPath = writeCorruptTransitionLock(home, "alpha"); | ||
|
|
||
| const startedAt = Date.now(); | ||
| const down = runWithEnv("alpha shields down --reason test 2>&1", testHome.environment()); | ||
| const elapsedMs = Date.now() - startedAt; | ||
|
|
||
| expect(down.code).toBe(1); | ||
| expectCleanRefusal(down.out); | ||
| expect(elapsedMs).toBeLessThan(REFUSAL_BUDGET_MS); | ||
| expect(fs.readFileSync(lockPath, "utf8")).toBe(""); | ||
| }, | ||
| ); | ||
| }); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Add corrupt-lock coverage for shields up.
This suite covers shields status and shields down only. Add a shields up case that verifies exit code 1, clean recovery output, prompt refusal, and preservation of the malformed lock file.
As per path instructions, tests must prove that public entrypoints reach the new path. The PR objective includes shields up, shields down, and shields status.
🧰 Tools
🪛 ast-grep (0.45.0)
[warning] 46-46: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(lockPath, "utf8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').
(detect-non-literal-fs-filename-typescript)
[warning] 65-65: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(lockPath, "utf8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').
(detect-non-literal-fs-filename-typescript)
🤖 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/cli/shields-transition-lock.test.ts` around lines 31 - 69, Add a
corrupt-transition-lock test alongside the existing cases in the “shields
commands with a corrupt transition lock” suite, invoking the public `alpha
shields up` command and verifying exit code 1, clean refusal output via
`expectCleanRefusal`, completion within `REFUSAL_BUDGET_MS`, and unchanged
malformed lock contents. Keep the setup consistent with the existing `shields
status` and `shields down` tests.
Source: Path instructions
PR Review Advisor — No blocking findings reportedAdvisor assessment: No blocking advisor findings reported Model lanes
4 terminology differences from the second opinionAdvisory only. These are normalized differences from the primary terminology receipt.
2 additional E2E selections from the second opinionAdvisory only. The primary lane did not select these E2E jobs or targets.
Second-opinion terminology and E2E selections are advisory. They do not change the primary assessment or E2E / PR Gate. E2E guidanceAdvisory only. E2E / PR Gate selects and runs jobs independently. Recommended E2E: This automated review informs maintainers. Warnings and suggestions do not require a response. A maintainer decides whether to merge. |
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
apurvvkumaria
left a comment
There was a problem hiding this comment.
Reviewed current head d1729d9. Stale malformed owner records fail fast while recent partial writes still wait; the corrupt lock remains untouched, and the CLI path exits cleanly after lock cleanup. Sync, async, and public CLI paths are covered, with required CI and E2E passing. I found no blocking defect.
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
apurvvkumaria
left a comment
There was a problem hiding this comment.
Approve — reviewed exact head f070bbe. Old malformed transition locks now fail closed immediately through the typed CLI sentinel, preserve the lock, and render actionable recovery without a raw stack. The current head includes public-entrypoint coverage for status, up, and down, satisfying the still-open automated test thread. Unit coverage passes 47 of 47; the exact CI CLI failure is an unrelated managed-image registry fetch timeout. No blocking correctness, security, compatibility, or regression defect found.
<!-- markdownlint-disable MD041 --> ## Summary Prepares the canonical v0.0.102 release documentation from the current release-labeled scope. The change adds a dated changelog for all 38 user-facing shipping PRs and corrects the OpenClaw agent command reference for the behavior delivered by #8191. ## Changes - Add `docs/changelog/2026-08-04.mdx` with the v0.0.102 release summary, detailed behavior changes, support boundaries, security evidence links, and links to durable documentation. - Update `docs/reference/commands.mdx` to describe non-JSON OpenClaw output capture, its combined limit, marker handling, stream suppression, recovery guidance, and exit behavior. - [#8167](#8167) -> `docs/changelog/2026-08-04.mdx`: Records authenticated attachment of operator-managed llama.cpp servers. - [#8129](#8129) -> `docs/changelog/2026-08-04.mdx`: Records the Experimental managed vLLM profile for two DGX Spark systems. - [#7983](#7983) -> `docs/changelog/2026-08-04.mdx`: Records qualification of the May 2026 GB300WS factory image. - [#8207](#8207) -> `docs/changelog/2026-08-04.mdx`: Records the qualified DGX Station driver transaction. - [#8208](#8208) -> `docs/changelog/2026-08-04.mdx`: Records mode-bound Express resume state. - [#8158](#8158) -> `docs/changelog/2026-08-04.mdx`: Records recovery of host-global dual-Station runtime ownership. - [#8145](#8145) -> `docs/changelog/2026-08-04.mdx`: Records Windows-host Ollama validation from Docker Desktop's network context. - [#8190](#8190) -> `docs/changelog/2026-08-04.mdx`: Records HTTP model pulls when WSL has no local Ollama executable. - [#8195](#8195) -> `docs/changelog/2026-08-04.mdx`: Records reuse of a healthy installer-managed CLI. - [#8053](#8053) -> `docs/changelog/2026-08-04.mdx`: Records early rejection of incompatible OpenShell gateway versions. - [#8098](#8098) -> `docs/changelog/2026-08-04.mdx`: Records the bounded package-service-to-standalone gateway recovery transition. - [#8216](#8216) -> `docs/changelog/2026-08-04.mdx`: Records the final dashboard port selected during multi-sandbox onboarding. - [#8146](#8146) -> `docs/changelog/2026-08-04.mdx`: Records managed startup-state restoration for stopped sandboxes. - [#8092](#8092) -> `docs/changelog/2026-08-04.mdx`: Records gateway watchdog recovery for classified not-serving states. - [#8182](#8182) -> `docs/changelog/2026-08-04.mdx`: Records consistent managed-recovery wait configuration. - [#8040](#8040) -> `docs/changelog/2026-08-04.mdx`: Records Docker sandbox rollback authority through late validation. - [#8130](#8130) -> `docs/changelog/2026-08-04.mdx`: Records bounded Shields deadline recovery and durable containment. - [#8086](#8086) -> `docs/changelog/2026-08-04.mdx`: Records repair of narrowly validated permission-only configuration drift. - [#8122](#8122) -> `docs/changelog/2026-08-04.mdx`: Records prompt failure and guidance for corrupt transition locks. - [#8124](#8124) -> `docs/changelog/2026-08-04.mdx`: Records policy restoration flags, previews, and target revalidation. - [#7886](#7886) -> `docs/changelog/2026-08-04.mdx`: Records explicit destruction after pre-delete Shields hardening failures while preserving recovery authority. - [#7901](#7901) -> `docs/changelog/2026-08-04.mdx`: Records multi-port uninstall behavior and shared-resource preservation. - [#7984](#7984) -> `docs/changelog/2026-08-04.mdx`: Records one classified transient remote MCP startup retry. - [#7954](#7954) -> `docs/changelog/2026-08-04.mdx`: Records bounded hosted-inference probe replies. - [#7574](#7574) -> `docs/changelog/2026-08-04.mdx`: Records preservation of validated reasoning capabilities through onboarding. - [#8089](#8089) -> `docs/changelog/2026-08-04.mdx`: Records proxy routing for Hermes WhatsApp pairing and media traffic. - [#7682](#7682) -> `docs/changelog/2026-08-04.mdx`: Records native Hermes session deletion and identifier validation. - [#8150](#8150) -> `docs/changelog/2026-08-04.mdx`: Records corporate CA trust for LangChain Deep Agents Code image builds. - [#8156](#8156) -> `docs/changelog/2026-08-04.mdx`: Records reviewed managed runtime dependency remediation. - [#8180](#8180) -> `docs/changelog/2026-08-04.mdx`: Records reviewed MCP discovery runtime dependency updates. - [#8196](#8196) -> `docs/changelog/2026-08-04.mdx`: Records private npm dependency remediation across managed images. - [#8203](#8203) -> `docs/changelog/2026-08-04.mdx`: Records reviewed Hermes and LangChain Deep Agents Code Python dependency updates. - [#8125](#8125) -> `docs/changelog/2026-08-04.mdx`: Records bounded diagnostics for invalid enumerated CLI values. - [#8193](#8193) -> `docs/changelog/2026-08-04.mdx`: Records bounded diagnostics for unresolved sandbox base images. - [#8118](#8118) -> `docs/changelog/2026-08-04.mdx`: Records bounded diagnostics for changed gateway authority. - [#8191](#8191) -> `docs/changelog/2026-08-04.mdx`, `docs/reference/commands.mdx`: Records output capture, marker handling, recovery guidance, and exit behavior for non-JSON OpenClaw agent commands. - [#8187](#8187) -> `docs/changelog/2026-08-04.mdx`: Records the aligned interactive-installation start across supported agents. - [#8153](#8153) -> `docs/changelog/2026-08-04.mdx`: Records current product capabilities and support boundaries. ## Type of Change - [ ] Code change (feature, bug fix, or refactor) - [ ] Code change with doc updates - [x] 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: - [x] Tests not applicable — justification: This documentation-only release preparation does not change executable behavior. Existing changelog and published-route tests pass. - [x] 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: - [ ] Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue: ## Documentation Writer Review - [x] Documentation writer subagent reviewed the completed changes - Result: `docs-updated` - Evidence: Independently reviewed `docs/changelog/2026-08-04.mdx` and `docs/reference/commands.mdx` at commit `b89913780`. All 38 user-facing v0.0.102 PRs are represented, #8191 behavior matches the implementation, and the writing rules, documentation style, controlled terminology, route structure, and skip policy pass review. Targeted tests pass 36/36 and the documentation build completes with 0 errors. - Agent: Codex Desktop independent documentation writer <!-- docs-review-head-sha: b899137 --> <!-- docs-review-agents-blob-sha: 3dd7c24 --> ## DGX Station Hardware Evidence - [ ] Tested on DGX Station - Tested commit: Not applicable - Station profile/scenario: Not applicable - Result: Not applicable - Supporting evidence: Not applicable ## Verification - [x] PR description includes a `Signed-off-by:` line and every commit appears as `Verified` in GitHub - [x] 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 - [x] Targeted behavior tests pass for the current change set, or tests are marked not applicable above — `npx vitest run --project integration test/changelog-docs.test.ts test/check-docs-published-routes.test.ts` passed 36/36. - [x] Applicable broad gate passed — not applicable to documentation-only changes; `npm run docs` completed successfully with 0 errors. - [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) — completed with 0 errors and 2 existing Fern warnings. - [x] Doc pages follow the [style guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md) (doc changes only) - [x] New doc pages include SPDX header and frontmatter (new pages only) — the native dated changelog uses the required parser-safe MDX SPDX comment and intentionally has no frontmatter. --- Signed-off-by: Apurv Kumaria <akumaria@nvidia.com> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Documentation** - Added release notes for v0.0.102, covering authentication, hardware setup, WSL, installer recovery, sandbox resilience, policy management, inference reliability, CLI improvements, and unified quickstarts. - Updated command documentation to explain how non-JSON agent output is collected, replayed, and reported. - **Bug Fixes** - Improved command-output recovery guidance when output exceeds limits or contains unsupported fallback markers. - Preserved accurate command exit-status reporting after output processing. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Summary
A shields transition lock whose owner record cannot be parsed is refused on purpose, but the refusal was reached only after the full 30-second wait timeout and then surfaced as a raw Node stack trace. Shields commands now refuse as soon as the unreadable record is observed, and report it through the existing shields exit sentinel, so
shields up,shields down, and the read-onlyshields statusall fail closed with a single line plus the recovery hint.Related Issue
Fixes #8108
Changes
src/lib/shields/transition-lock.ts: split the wait-reason text into a reason and a recovery hint so a caller can present them on separate lines. Every existing message is byte-identical; the error'smessageis still the reason followed by the hint.src/lib/shields/transition-lock.ts: addedShieldsTransitionLockUnavailableErrorfor a lock that cannot be taken, with a name-keyed guard. The guard matches on shape rather thaninstanceofbecausedistandsrccopies of the class can both be loaded, the same reasonisDeferredShieldsExitalready works that way.src/lib/shields/transition-lock.ts: an owner record that is unreadable and already past the malformed-stale threshold is refused immediately instead of polling until the wait timeout. That state is terminal: it cannot become recoverable by waiting, and no automatic recovery applies to it. A record younger than the threshold still waits, because a writer can finish it.src/lib/shields/index.ts:completeDeferredShieldsExitprints the reason and aRecovery:line, then routes throughfailShieldsCommand. BoththrowOnErrorsettings reach the same sentinel, soshields upandshields down(which passthrowOnError: true) andshields status(which does not) all exit 1 without a stack trace.src/lib/shields/transition-lock.test.tscovers the immediate refusal on the synchronous and asynchronous acquisition paths and the reason/hint split;test/cli/shields-transition-lock.test.tsruns publicshields status,shields up, andshields downcommands against a truncated lock file and asserts the exit code, both output lines, the absence of stack frames, preservation of the lock, and completion well inside the old timeout.Type of Change
Quality Gates
docs/security/tcb-boundary.mdxalready states that the transition lock rejects ambiguous owners, anddocs/manage-sandboxes/runtime-controls.mdxanddocs/manage-sandboxes/backup-restore.mdxdescribe only which operations take the lock. No page documents the previous wait duration or the stack trace.a986513e25472c8654c91c8f54d0c847522a3493against49ffca04d0ac3ec3a1715e2fa3455c76db15d803with no findings.Documentation Writer Review
no-docs-needed421952aedc74a60b88238f352644f9f1eeaceacbis identical to the independently reviewed tree. Existingdocs/security/tcb-boundary.mdx,docs/reference/host-files-and-state.mdx, anddocs/changelog/2026-07-15.mdxalready document fail-closed handling and manual recovery for malformed or ambiguous transition-lock owners. This change corrects refusal timing and error presentation without changing command grammar, configuration, supported procedures, or lifecycle behavior. Changed messages and test titles follow the writing rules. GitHub CI provides validation for this commit.DGX Station Hardware Evidence
scripts/prepare-dgx-station-host.sh.Verification
Signed-off-by:line and every commit appears asVerifiedin GitHub.pre-commit,commit-msg, andpre-pushhooks passed, ornpm run validate:prpassed after refreshingorigin/mainwhen hooks were skipped or unavailable — the normal pre-push checks passed for the identical reviewed tree, and GitHub commit lint passed for the contributor's current merge commit.checksgate passed for the current commit.npm run docsbuilds without warnings (doc changes only) — Not applicable; no documentation file changed.GitHub CI is authoritative.
No duplicate local test, documentation, build, or typecheck suite ran after the merge refresh.
Security Review
PASSa986513e25472c8654c91c8f54d0c847522a349349ffca04d0ac3ec3a1715e2fa3455c76db15d803PASSPASSPASSPASSPASSPASSPASSPASSPASSshields status,shields up, andshields down, asserting exit code 1, prompt refusal, bounded recovery output without a raw stack, and an unchanged malformed lock. Synchronous and asynchronous manager paths assert immediate refusal without sleeping.Signed-off-by: Tinson Lai tinsonl@nvidia.com
Summary by CodeRabbit
shields status,shields up, andshields downnow exit cleanly without stack traces when a malformed lock is detected.