fix(hermes): keep the locked config root writable by the gateway - #7876
Conversation
`shields up` moved /sandbox/.hermes to root:root 0755. That directory is not only configuration: Hermes creates auth.json, the drain request, and the temporary files behind every atomic gateway_state/pid replace directly inside it, so the sandbox identity lost the ability to start a gateway at all. Every restart failed its health check, five exits inside 60s tripped the crash budget, and the supervisor quarantined relaunch until the sandbox was recreated. Because rebuild re-applies the saved lockdown, any sandbox that had ever been shielded reached that state on its next gateway restart. Lock the root the way the code already locks its parent and already documents this root: root-owned in the sandbox group, keeping the set-id and sticky bits, so the gateway manages its own runtime state while the sticky bit stops the sandbox identity from unlinking or renaming the sealed root-owned config. The seal itself is unchanged at 444 root:root, and the locked root loses the world bits 0755 used to grant. Teach every site that encodes the locked posture: the guard's shields target and its locked-root predicate, the boot-time trusted-posture allowlist, the entrypoint's locked-root detection, the host verifier, and the legacy transition. Each keeps accepting the previous root:root 0755 root so an already-shielded sandbox still boots, rolls back, and re-locks; the host verifier reports it as drift so `shields up` repairs it. Skip the compat hash refresh under a locked root, which the sandbox identity can neither perform nor need, and re-apply the recorded root posture when an applied shields transition resumes, so an interruption converges instead of wedging on a drifted root. Fixes #7865 Signed-off-by: Yanyun Liao <yanyunl@nvidia.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughHermes shields locking now establishes a ChangesHermes locked-root posture
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
Suggested labels: Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant CLI
participant RuntimeConfigGuard
participant HermesRoot
participant HermesGateway
CLI->>RuntimeConfigGuard: shields up
RuntimeConfigGuard->>HermesRoot: set root:sandbox 3770 posture
RuntimeConfigGuard->>HermesRoot: seal protected config files
HermesGateway->>HermesRoot: write runtime state
HermesGateway->>HermesRoot: attempt sealed-file mutation
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
Code Coverage OverviewLanguages: TypeScript TypeScript / code-coverage/pluginThe overall coverage in commit e4509a5 in the TypeScript / code-coverage/cliThe overall coverage in commit e4509a5 in the Show a code coverage summary of the most impacted files.
Updated |
|
🌿 Preview your docs: https://nvidia-preview-pr-7876.docs.buildwithfern.com/nemoclaw |
PR Review Advisor — No blocking findings reportedAdvisor assessment: No blocking advisor findings reported Model lanes
Nemotron output stays in workflow artifacts and does not change the assessment above. E2E guidanceAdvisory only. E2E / PR Gate selects and runs jobs independently. Recommended E2E: 1 optional E2E recommendation
1 warning · 1 suggestionWarningsWarnings do not block.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/lib/shields/verify-lock.test.ts (1)
112-112: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMove the issue reference to the required title suffix.
Use a behavior-oriented title ending in
(#7865)instead of embedding#7865inpre-#7865.As per coding guidelines, test titles must put local issue references in a final
(#1234)suffix.Proposed title
- it("reports the pre-#7865 root-owned Hermes config root as drift", async () => { + it("reports the legacy root-owned Hermes config root as drift (`#7865`)", async () => {🤖 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/shields/verify-lock.test.ts` at line 112, Update the test title in the `it` block to describe the reported behavior without embedding the issue number in the wording, and append the required `(`#7865`)` suffix at the end.Source: Coding guidelines
🤖 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 `@docs/reference/troubleshooting.mdx`:
- Line 2699: Replace the hard-coded nemohermes host CLI tokens in the affected
troubleshooting examples with the shared $$nemoclaw placeholder, including both
referenced command lines, so generated pages use the correct agent variant.
- Around line 2694-2695: Update the troubleshooting documentation sentence
describing lockdown to explicitly state that it changes both the directory owner
and mode to 3770, including set-id and sticky-bit protection. Remove the claim
that lockdown “only changes its owner,” while preserving the explanation of
gateway runtime-file management and sandbox unlink/rename protection.
---
Nitpick comments:
In `@src/lib/shields/verify-lock.test.ts`:
- Line 112: Update the test title in the `it` block to describe the reported
behavior without embedding the issue number in the wording, and append the
required `(`#7865`)` suffix at the end.
🪄 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: 7aa9442d-e394-4a1e-acb1-db1557f6cd0e
📒 Files selected for processing (15)
agents/hermes/Dockerfileagents/hermes/runtime-config-guard.pyagents/hermes/start.shagents/hermes/validate-env-secret-boundary.pyci/source-shape-test-budget.jsondocs/reference/troubleshooting.mdxsrc/lib/shields/index.tssrc/lib/shields/legacy-hermes-compat.test.tssrc/lib/shields/legacy-hermes-transition.test.tssrc/lib/shields/verify-lock.test.tssrc/lib/shields/verify-lock.tstest/hermes-restart-config-seal-hostile-input.test.tstest/hermes-restart-config-seal-recovery.test.tstest/hermes-restart-config-seal-transition.test.tstest/hermes-runtime-config-guard-topology.test.ts
The CLI shard and both live E2E checks still pinned the pre-#7865 root:root 0755 config root. Move them to the group-writable, sticky locked root the guard now applies, so the mocked transaction and the real sandbox assertions describe the same posture. Signed-off-by: Yanyun Liao <yanyunl@nvidia.com>
|
I completed the maintainer/security pass on exact head Before I authorize the selected credentialed E2E plan, please address the three still-valid convention findings on this exact head:
This is a plain review comment, not a Changes Requested review. I will re-check the replacement head after its one-hour quiet window and then launch the exact selected E2E plan if it remains clean. |
…epair Review findings on the maintainer/security pass: - The two new host commands in the shared troubleshooting source hard-coded `nemohermes`; use the variant-aware `$$nemoclaw` placeholder so the page renders correctly for every agent variant. - The locked-root description said lockdown only changes the directory's owner. It moves the root to `root:sandbox` mode `3770` and keeps the set-id and sticky bits, so state it that way. - Move the issue reference to the required trailing suffix in the drift test title. Signed-off-by: Yanyun Liao <yanyunl@nvidia.com>
|
Thanks for the pass — all three findings are addressed on head
No behaviour changed, so the earlier evidence still stands on this head. Ready for the |
|
@cjagwani — this PR is parked on Why the gate fired. From the risk plan, three tier-3 families match on the runtime files alone, independent of the two
So this is not incidental to a test edit — the fix is in the shields lockdown path itself, and I do not see a version of it that avoids these files. The security-relevant delta, for review. Hermes lockdown moved The change moves that directory to
Dispatch inputs, if you decide to approve: Risk plan: https://github.com/NVIDIA/NemoClaw/actions/runs/30526949975 (plan hash Everything else on this PR is green. Verified on an Ubuntu 24.04 x86_64 test host: the unit suites alone were not sufficient here — three rounds on real hardware were needed, because two shell-side checks in |
|
Maintainer follow-up at exact head |
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
<!-- markdownlint-disable MD041 --> ## Summary Adds the canonical July 30 release entry for `v0.0.99` before the release tag is captured. The entry covers all 37 merged PRs since `v0.0.98` and bounds experimental or dormant work without presenting it as supported behavior. ## Changes - Adds `docs/changelog/2026-07-30.mdx` with the exact `## v0.0.99` heading, parser-safe MDX SPDX comment, summary, detailed release bullets, and published documentation routes. - Records user-visible recovery, snapshot, shared-route, Hermes, readiness, inference, image, documentation, and release E2E changes. - States that the managed-image selection and startup-profile contracts remain dormant and do not activate buildless onboarding. Source summary: - [#7972](#7972) -> `docs/changelog/2026-07-30.mdx`: Records restored managed OpenClaw configuration modes during recovery. - [#7834](#7834) -> `docs/changelog/2026-07-30.mdx`: Records clone-bound pairing verification after snapshot restore. - [#7975](#7975) -> `docs/changelog/2026-07-30.mdx`: Records managed startup recovery coverage. - [#7960](#7960) -> `docs/changelog/2026-07-30.mdx`: Records dormant startup-profile coordination without activating a supported surface. - [#7856](#7856) -> `docs/changelog/2026-07-30.mdx`: Records persistence of the credential-free OpenClaw startup command. - [#7959](#7959) -> `docs/changelog/2026-07-30.mdx`: Records dormant startup-profile construction without changing onboarding. - [#7946](#7946) -> `docs/changelog/2026-07-30.mdx`: Records the internal startup-profile schema and transport contract. - [#7951](#7951) -> `docs/changelog/2026-07-30.mdx`: Records platform-pull cleanup before managed-image validation. - [#7949](#7949) -> `docs/changelog/2026-07-30.mdx`: Records rejection of retained Hermes `uv` build cache metadata. - [#7597](#7597) -> `docs/changelog/2026-07-30.mdx`: Records separate command and agent first-turn latency evidence. - [#7931](#7931) -> `docs/changelog/2026-07-30.mdx`: Records focused E2E replacement evidence for retired selectors. - [#7950](#7950) -> `docs/changelog/2026-07-30.mdx`: Records exclusion of build-only BuildKit telemetry from the Deep Agents Code probe. - [#7665](#7665) -> `docs/changelog/2026-07-30.mdx`: Records consolidated priority 2 E2E coverage. - [#7911](#7911) -> `docs/changelog/2026-07-30.mdx`: Records the corrected NVIDIA DORI installation pin. - [#7934](#7934) -> `docs/changelog/2026-07-30.mdx`: Records the staging image-family wait before Brev Launchable deployment. - [#7772](#7772) -> `docs/changelog/2026-07-30.mdx`: Records dormant managed-image selection contracts without activating buildless onboarding. - [#7941](#7941) -> `docs/changelog/2026-07-30.mdx`: Records corrected agent-specific provider and policy guidance. - [#7819](#7819) -> `docs/changelog/2026-07-30.mdx`: Records removal of empty Deep Agents Code provider-switch sections. - [#7932](#7932) -> `docs/changelog/2026-07-30.mdx`: Records independent credential-generation E2E execution. - [#7840](#7840) -> `docs/changelog/2026-07-30.mdx`: Records shared-route preservation and pre-delete peer validation during upgrades. - [#7874](#7874) -> `docs/changelog/2026-07-30.mdx`: Records the split between pre-tag release entries and post-tag Announcements. - [#7876](#7876) -> `docs/changelog/2026-07-30.mdx`: Records the writable Hermes runtime root within lockdown. - [#7756](#7756) -> `docs/changelog/2026-07-30.mdx`: Records validated multi-platform managed-image publication. - [#7914](#7914) -> `docs/changelog/2026-07-30.mdx`: Records accepted `uv` version metadata in Hermes image validation. - [#7686](#7686) -> `docs/changelog/2026-07-30.mdx`: Records the explicitly experimental Microsoft Entra runtime identity reference. - [#7869](#7869) -> `docs/changelog/2026-07-30.mdx`: Records classified gateway relaunch quarantine and rebuild guidance. - [#7814](#7814) -> `docs/changelog/2026-07-30.mdx`: Records state restore into replacement sandboxes and SQLite write verification. - [#7839](#7839) -> `docs/changelog/2026-07-30.mdx`: Records quieter onboarding test execution without a user-facing behavior claim. - [#7854](#7854) -> `docs/changelog/2026-07-30.mdx`: Records generalized agent-selection guidance. - [#7845](#7845) -> `docs/changelog/2026-07-30.mdx`: Records isolated CDI test evidence without a user-facing behavior claim. - [#7843](#7843) -> `docs/changelog/2026-07-30.mdx`: Records the corrected Omni sub-agent model ID. - [#7908](#7908) -> `docs/changelog/2026-07-30.mdx`: Records reviewed Hermes and Deep Agents Code dependency pins. - [#7887](#7887) -> `docs/changelog/2026-07-30.mdx`: Records rejection of a symlinked DGX Station release marker. - [#7747](#7747) -> `docs/changelog/2026-07-30.mdx`: Records the internal compute-driver separation without a user-facing behavior claim. - [#7660](#7660) -> `docs/changelog/2026-07-30.mdx`: Records atomic publication of rebuild recovery manifests. - [#7661](#7661) -> `docs/changelog/2026-07-30.mdx`: Records bounded local inference health-response retention. - [#7654](#7654) -> `docs/changelog/2026-07-30.mdx`: Records state preservation across supervisor relaunch recovery. ## 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 - [x] Existing tests cover changed behavior — justification: `test/changelog-docs.test.ts` validates the dated changelog contract, SPDX comment, version heading, and published routes. - [ ] Tests not applicable — justification: - [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: `docs/changelog/2026-07-30.mdx`; the documentation-only diff passed review against `WRITING.md`, the controlled word list, and `docs/CONTRIBUTING.md`. The review covered terminology, structure, active voice, release meaning, product-scope boundaries, and link and code presentation. Changelog tests passed 6/6, and the docs build reported 0 errors with 2 pre-existing warnings. - Agent: Codex CLI <!-- docs-review-head-sha: 200940f --> <!-- docs-review-agents-blob-sha: c052d60 --> ## DGX Station Hardware Evidence - [ ] Tested on DGX Station - Tested commit: - Station profile/scenario: - Result: - Supporting evidence: ## 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 — command/result or justification: `npx vitest run test/changelog-docs.test.ts` passed 6/6 tests. - [ ] 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 this documentation-only release entry. - [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) — result: Build passed with 0 errors and 2 pre-existing warnings. - [x] 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: San Dang <sdang@nvidia.com> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Added release notes for v0.0.99 covering snapshot restoration, sandbox recovery, gateway route upgrades, and Hermes security updates. * Documented experimental Microsoft Entra runtime identity support and enhanced readiness checks. * Added details on managed image validation, trusted CI image promotion, and end-to-end release evidence. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
Summary
shields upleft the Hermes config root root-owned with no group write, which took awaythe directory the gateway needs to create its own top-level runtime state. Every later
gateway restart failed its health check and the in-sandbox supervisor quarantined relaunch
until the sandbox was recreated. This PR locks that root the way the surrounding code
already locks its parent — root-owned in the sandbox group, keeping the set-id and sticky
bits — so the gateway keeps working under lockdown while the seal is unchanged.
Closes #7865.
Reproduction
Run on our Ubuntu 24.04 x86_64 test host (no GPU), against a Hermes sandbox onboarded from
mainin this run:Environment
ollama-localmaineeab81cc5at repro time; this branch includes currentmainat25de83d37Observed on
main(before fix)Step 1 succeeds repeatedly.
shields upthen reports success, and the config integritystill matches — but the config root changed shape:
Step 3, the same command that just succeeded twice:
/tmp/gateway.loginside the sandbox:recovercannot clear it and the sandbox is left without a gateway.Observed on
fix/...(after fix)Zero quarantine, permission-denied, or refusal lines in the boot. The security contract was
re-checked against the live locked tree with the ordinary sandbox identity:
A full
shields down→ mutable restart →shields up→ restart round trip also passes, andthe mutable posture is unchanged (
3770 sandbox:sandbox, config640 sandbox:sandbox).Analysis
_configure_shields_target_metadatainagents/hermes/runtime-config-guard.pyset thelocked config root to
root:root 0755. The Hermes config root is not only configuration:auth.json,.drain_request.json, and the temporary files behind every atomicgateway_state.json/gateway.pidreplace are created directly in/sandbox/.hermes. Anatomic replace must stage its temporary file in the target's own directory, so the existing
-> runtime/symlinks do not help. In the OpenShell-managed topology the gateway runs asthe
sandboxuser, so a root-owned root without group write means no gateway can start.Five failed launches inside 60s trip the crash budget and
recover_hermes_gateway_current_userquarantines relaunch.rebuildre-applies the savedlockdown, so a sandbox that had ever been shielded reached this state on its next restart.
This posture contradicts what the surrounding code already does and already states:
root:<sandbox gid> 01775— root-owned, sticky— commented as keeping
/sandbox"a usable home" while preventing the sandbox identityfrom renaming the lock root out from under the protected files.
src/lib/shields/index.tsdocuments the Hermes root as staying "group-writable + stickyso Hermes can create top-level runtime state while the gateway UID cannot remove
sandbox-owned config files" — implemented for the mutable posture only.
agents/hermes/start.shalready expectsroot:sandbox 1775for the parent inhermes_locked_parent_is_protected, and comments at its locked branch that "thelocked-root posture seals config.yaml/.env, not the dir".
/sandbox/.hermes"sticky group-writable so thegatewayuser can create runtime state without removing sandbox-owned config files".Worth noting for the threat model:
chattr +iis not available on the overlay filesystemthese sandboxes use (
lsattrshows only the extent flag), so the durable protection for thesealed files is directory-level. Before this change that came from the root being
unwritable; now it comes from the sticky bit, which is equally binding and was verified
above against a live tree.
Fix
The locked root becomes
root:<sandbox gid> 03770; only the owner differs between posturesnow, and the locked root additionally loses the world bits
0755granted. The seal itselfis untouched at
444 root:root. Every site that encodes the locked posture is updated, andeach keeps accepting the previous
root:root 0755so an already-shielded sandbox stillboots, rolls back, and re-locks rather than bricking on upgrade:
runtime-config-guard.py_configure_shields_target_metadataruntime-config-guard.py_is_locked_hermes_root(new)root:root 0755validate-env-secret-boundary.pystart.shhermes_config_root_is_lockedstart.shrefresh_hermes_runtime_config_hashessrc/lib/shields/verify-lock.tssrc/lib/shields/index.tsTwo things the live verification forced out, both required for the fix to be complete:
prepare_hermes_nonroot_runtimeunconditionally runsrefresh_hermes_runtime_config_hashes compat, which atomically replaces.config-hashas the sandbox identity. Under lockdown that file is sealed and root-owned, so the sticky
root now refuses the rename and the launch still failed. It is also meaningless work: the
sealed inputs cannot drift, and the lock transaction already wrote a coherent hash. The
guard sits in the function so all three
compatcallers are covered;strict/both(root entrypoint) are untouched, and the
inspect_hermes_mcp_integritythat followsstill validates the sealed hash, so an incoherent locked tree keeps failing closed.
mode. That clamp used to equal the final locked mode, so nothing noticed that the
applied-resume branch never re-applies the recorded root posture. With the two values now
different, an interruption between publishing the applied phase and restoring the seal
would leave the clamp in place and
finishwould refuse the drifted root, wedging thetransaction. Resume now re-applies the recorded posture behind the existing inode pin, so
it converges.
Not changed, deliberately: the OpenClaw locked posture stays
755 root:root(a new testpins it); the sealed file modes, the strict/compat hash contracts, the
/sandboxparentposture, and the unavailable-config root-only clamp are all untouched. No refusal is
relaxed.
Upgrade path. The host verifier reports a pre-#7865
755 root:rootroot as drift, soshields statussurfaces it and the caller's re-lock repairs it; rolling back to lockedre-derives the target rather than replaying the captured original, so a down/up cycle also
heals it. Verified on a sandbox that started from the old posture.
One behavioural note: a
gateway restartissued immediately after ashields uptransition can still report
health timeoutwhile the transition settles; the next restartsucceeds and the sandbox is never quarantined. That is visible in the verification log and
is unrelated to the permission failure this PR fixes.
Alternative if the posture change is not acceptable
This PR widens one capability: under lockdown the sandbox identity can now create new
top-level entries in
/sandbox/.hermes. The reasoning for why that is not a meaningfulweakening is in Analysis above — the sealed entries keep both of their guarantees, and
the rest of the tree under a locked root is already sandbox-writable
(
logs,sessions,cache,runtime,bin,kanban,SOUL.md,gateway.pidare allsandbox-owned and writable while shields are up), so the old root-owned root was not a
containment boundary for the subtree — it only blocked the one operation Hermes needs on
every launch.
If you would rather keep that capability denied, the honest alternative is not a smaller
version of this change. Relocating the writes is not available to us: the failing paths are
Hermes' own (
gateway.run,gateway.drain_control,gateway.platforms.base), and the-> runtime/symlinks NemoClaw already installs cannot cover them, because an atomicreplace must stage its temporary file in the target's own directory.
The alternative is therefore to make
shields uprefuse on a Hermes sandbox whose gatewayruns as the sandbox identity (the OpenShell-managed non-root topology), with an explicit
error naming the reason, instead of producing a sandbox whose next gateway restart
quarantines it. The direct root-entrypoint topology, where the gateway runs as its own
gatewayuid, would keep working with today's posture.Trade-offs, stated plainly:
shields upstops being available on the default onboarding topology.change, no entrypoint changes. Most of this PR would be dropped.
them on the next
shields upinstead.I did not take that route because the code and history read as if the group-writable, sticky
root is the intended posture for this directory and the locked branch simply never adopted
it (the four citations in Analysis). If that reading is wrong and the root-owned root was
deliberate, say so and I will replace this PR with the refusal.
Changes
agents/hermes/runtime-config-guard.py: locked root target posture,_is_locked_hermes_root, applied-resume posture repairagents/hermes/validate-env-secret-boundary.py: boot-time trusted-posture allowlistagents/hermes/start.sh: locked-root detection, compat hash refresh guardagents/hermes/Dockerfile: pinned validator SHA-256 refreshedsrc/lib/shields/verify-lock.ts: agent-aware expected locked dir posturesrc/lib/shields/index.ts: legacy Hermes transition lock branchtest/hermes-runtime-config-guard-topology.test.ts: new root-container capability test; rollback expectationtest/hermes-restart-config-seal-{transition,recovery,hostile-input}.test.ts: locked-posture expectations; legacy-posture regression locksrc/lib/shields/{verify-lock,legacy-hermes-transition,legacy-hermes-compat}.test.ts: locked-posture expectations, drift-on-legacy and OpenClaw-unchanged casesci/source-shape-test-budget.json: security contract exception for the new root-container testdocs/reference/troubleshooting.mdx: symptom, expected posture, and repairType of Change
Quality Gates
Documentation Writer Review
docs-updateddocs/reference/troubleshooting.mdxfor legacy Hermes locked-configuration-root drift, pre-quarantine repair order, acceptance checks, and the relaunch-quarantine rebuild route. Independent review covered all PR text, verified commands and security claims against source and tests, and inspected the OpenClaw, Hermes, and Deep Agents renders.npm run docs,npm run source-shape:check, and targeted hooks passed.DGX Station Hardware Evidence
scripts/prepare-dgx-station-host.shis unchanged.Verification
Signed-off-by:line and every commit appears asVerifiedin GitHubpre-commit,commit-msg, andpre-pushhooks passed, ornpm run validate:prpassed after refreshingorigin/mainwhen hooks were skipped or unavailablenpx vitest run --project cli src/lib/shields/verify-lock.test.ts: 24 passed;npm run source-shape:check: passednpm run docsbuilds without warnings (doc changes only) — 0 errors; two unrelated repository/environment warnings remain for unauthenticated redirect checking and the existing light-mode accent contrastAI Disclosure
Signed-off-by: Yanyun Liao yanyunl@nvidia.com
Summary by CodeRabbit
root:sandboxwith setgid/sticky mode (3770).3770 root:sandboxpermissions.