Skip to content

fix: bootstrap cli pairing when device list is gated - #6330

Merged
cv merged 17 commits into
mainfrom
fix/6113_cli-auto-pair
Jul 7, 2026
Merged

cv merged 17 commits into
mainfrom
fix/6113_cli-auto-pair

Conversation

@chengjiew

@chengjiew chengjiew commented Jul 6, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes #6113

Test Plan

  • npx vitest run --project integration test/nemoclaw-start-auto-pair-bootstrap.test.ts test/nemoclaw-start.test.ts -t "seeds an initial CLI pairing request|approves only known client identities|approves concurrent late CLI"
  • npx vitest run --project cli src/lib/actions/sandbox/auto-pair-approval.test.ts -t "approves allowlisted upgrades"
  • npx vitest run --project integration test/openclaw-device-approval-policy.test.ts
  • npm run test-size:check
  • git diff --check
  • npm run check:diff
  • Linux live A/B on aits-log-worker-6 / ncq2621-latest-main: before the new watcher, openclaw devices list --json returned pairing required; after running the new watcher against the same pending state, devices list returned pending: [] and a paired cli device with operator.pairing only

Summary by CodeRabbit

  • New Features
    • Added deterministic “initial CLI pairing” recovery when device listing indicates pairing is required.
    • Extended auto-pairing to support an additional allowlisted CLI client identity.
  • Bug Fixes
    • Improved auto-pair approval failure logging with concise, formatted last-error output (and safer logging conditions).
  • Tests
    • Added/expanded coverage for initial CLI auto-pair bootstrap, including multiple validation and recovery scenarios.
    • Updated auto-pairing expectations and sandbox approval-pass assertions for the new CLI client.
  • Chores
    • Adjusted CI test file size budget for a specific test file.

Signed-off-by: Chengjie Wang chengjiew@nvidia.com

@copy-pr-bot

copy-pr-bot Bot commented Jul 6, 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.

@github-code-quality

github-code-quality Bot commented Jul 6, 2026 •

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in the fix/6113_cli-auto-pa... branch is 96%. Coverage data for the main branch is not yet available.

Show a code coverage summary of the most covered files.
File main fix/6113_cli-auto-pa... cda99e2 +/-
nemoclaw/src/se...cret-scanner.ts — 100% —
nemoclaw/src/commands/slash.ts — 100% —
nemoclaw/src/li...bprocess-env.ts — 100% —
nemoclaw/src/bl...eprint/state.ts — 98% —
nemoclaw/src/onboard/config.ts — 98% —
nemoclaw/src/bl...int/snapshot.ts — 97% —
nemoclaw/src/bl...print/runner.ts — 95% —
nemoclaw/src/co...ration-state.ts — 94% —
nemoclaw/src/bl...ate-networks.ts — 94% —
nemoclaw/src/index.ts — 94% —

TypeScript / code-coverage/cli

The overall coverage in the fix/6113_cli-auto-pa... branch is 76%. Coverage data for the main branch is not yet available.

Show a code coverage summary of the most covered files.
File main fix/6113_cli-auto-pa... cda99e2 +/-
src/lib/onboard/preflight.ts — 82% —
src/lib/actions...all/run-plan.ts — 81% —
src/lib/state/o...oard-session.ts — 81% —
src/lib/actions...licy-channel.ts — 79% —
src/lib/state/sandbox.ts — 75% —
src/lib/actions...dbox/connect.ts — 72% —
src/lib/onboard...er-gpu-patch.ts — 69% —
src/lib/policy/index.ts — 66% —
src/lib/shields/index.ts — 61% —
src/lib/onboard.ts — 28% —

Updated July 07, 2026 22:02 UTC
Code Coverage is in Public Preview. Learn more and provide us with your feedback.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds an initial CLI auto-pair recovery path in the watcher, then extends unit and integration coverage for allowlisted CLI pairing, validation failures, retries, and related test-budget updates.

Changes

Initial CLI auto-pair seed

Layer / File(s) Summary
Auto-pair watcher seeding pipeline
scripts/nemoclaw-start.sh
Adds pairing-failure parsing, initial CLI request validation, requestId extraction, immediate approve-once recovery, and concise child-error logging in the watcher loop.
Unit test coverage for CLI pairing approval
src/lib/actions/sandbox/auto-pair-approval.test.ts
Extends the pending-fixture set with an allowlisted CLI request and updates approval/output assertions.
Bootstrap integration test for seeded pairing
test/nemoclaw-start-auto-pair-bootstrap.test.ts
Adds helpers plus positive and negative integration tests for seeded pairing against a fake openclaw CLI and temp state.
Existing whitelisting and keepalive test updates
test/nemoclaw-start.test.ts, ci/test-file-size-budget.json
Updates CLI identity fixtures and expected approvals in whitelisting and keepalive tests, applies minor line-structure changes, and lowers the line-size budget by one.

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

Sequence Diagram(s)

sequenceDiagram
  participant Watcher as start_auto_pair watcher
  participant CLI as openclaw devices list
  participant Approve as openclaw devices approve
  participant State as pending.json / paired.json / device-auth.json

  Watcher->>CLI: devices list --json
  CLI-->>Watcher: pairing required / empty output
  Watcher->>Watcher: parse requestId and validate initial CLI pairing
  Watcher->>Approve: approve <requestId> --json
  Approve-->>Watcher: approval result
  Watcher->>State: update handled approvals and fast re-entry state
Loading

Suggested labels: bug-fix

Suggested reviewers: jyaunches, ericksoa

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: bootstrapping CLI pairing when the device list is gated.
Linked Issues check ✅ Passed The changes address #6113 by allowing initial CLI auto-pairing to complete when pairing is required and adding regression coverage.
Out of Scope Changes check ✅ Passed The test-budget tweak and expanded tests support the auto-pairing fix and do not introduce unrelated scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/6113_cli-auto-pair

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

@github-actions

github-actions Bot commented Jul 6, 2026 •

Copy link
Copy Markdown
Contributor

PR Review Advisor (Nemotron Ultra) — Changes requested

Merge posture: Do not merge yet
Primary next action: Fix PRA-2: Gated-list bootstrap clientId allowlist asymmetry with shared policy; then add or justify PRA-T1.
Open items: 1 required · 14 warnings · 4 suggestions · 8 test follow-ups
Since last review: 2 prior items resolved · 12 still apply · 6 new items found

Action checklist

  • PRA-2 Fix: Gated-list bootstrap clientId allowlist asymmetry with shared policy in scripts/nemoclaw-start.sh:2595
  • PRA-1 Resolve or justify: Source-of-truth review needed: scripts/nemoclaw-start.sh: _identity_public_key (Ed25519 key extraction)
  • PRA-3 Resolve or justify: SHA256 comparison constant-time rationale undocumented in scripts/nemoclaw-start.sh:2588
  • PRA-4 Resolve or justify: _identity_public_key silently returns empty string on PEM parse failure in scripts/nemoclaw-start.sh:2545
  • PRA-5 Resolve or justify: Exception type not logged in initial_cli_request_is_allowlisted except block in scripts/nemoclaw-start.sh:2625
  • PRA-6 Resolve or justify: autoPairPythonScript helper duplicated across test files with divergent signatures in test/nemoclaw-start-auto-pair-bootstrap.test.ts:26
  • PRA-7 Resolve or justify: Non-pairing error test doesn't verify continued polling behavior in test/nemoclaw-start-auto-pair-bootstrap.test.ts:762
  • PRA-10 Resolve or justify: _identity_public_key assumes Ed25519 without validation or documentation in scripts/nemoclaw-start.sh:2552
  • PRA-11 Resolve or justify: RequestId parsing from untrusted gateway error output lacks security acknowledgment in scripts/nemoclaw-start.sh:2670
  • PRA-12 Resolve or justify: binascii.Error not caught locally in _identity_public_key in scripts/nemoclaw-start.sh:2558
  • PRA-14 Resolve or justify: Complex regex in pairing_required_request_id ReDoS exposure in scripts/nemoclaw-start.sh:2670
  • PRA-T1 Add or justify test follow-up: Runtime validation
  • PRA-T2 Add or justify test follow-up: Runtime validation
  • PRA-T3 Add or justify test follow-up: Runtime validation
  • PRA-T4 Add or justify test follow-up: Runtime validation
  • PRA-T5 Add or justify test follow-up: Runtime validation
  • PRA-T6 Add or justify test follow-up: autoPairPythonScript helper duplicated across test files with divergent signatures
  • PRA-T7 Add or justify test follow-up: Non-pairing error test doesn't verify continued polling behavior
  • PRA-T8 Add or justify test follow-up: Missing gated-list test coverage for clientId='openclaw-cli' and fast-reentry bump
  • PRA-8 In-scope improvement: Embedded Python watcher monolith growth in shell heredoc in scripts/nemoclaw-start.sh:2300
  • PRA-9 In-scope improvement: Test file size budget unchanged; helper duplication persists in ci/test-file-size-budget.json:9
  • PRA-13 In-scope improvement: Missing gated-list test coverage for clientId='openclaw-cli' and fast-reentry bump in test/nemoclaw-start-auto-pair-bootstrap.test.ts:1
  • PRA-19 In-scope improvement: SOURCE_OF_TRUTH_REVIEW references PR fix: bootstrap cli pairing when device list is gated #6330 (this PR) circularly in scripts/nemoclaw-start.sh:2570

Findings index

ID Severity Category Location Required action
PRA-1 Resolve/justify architecture — Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
PRA-2 Required acceptance scripts/nemoclaw-start.sh:2595 Either: (a) Align gated-list validation with shared policy by accepting all ALLOWED_CLIENTS, or (b) Strengthen comment to cite OpenClaw source (e.g., 'verified against OpenClaw CLI source at openclaw/cmd/devices/pair.go: clientId=cli for initial pairing') and add test case with clientId='openclaw-cli' in gated-list flow to confirm rejection is intentional. If asymmetry is unintentional, fix the allowlist.
PRA-3 Resolve/justify security scripts/nemoclaw-start.sh:2588 Add a brief comment: 'deviceId is SHA256 of public key; comparison is not secret-dependent, constant-time not required.'
PRA-4 Resolve/justify correctness scripts/nemoclaw-start.sh:2545 Add a debug log line inside _identity_public_key for each failure path (e.g., print(f'[auto-pair] PEM parse failed: {err}') before returning ''), or refine the caller's except block to log the exception type alongside the message.
PRA-5 Resolve/justify correctness scripts/nemoclaw-start.sh:2625 Log exception type alongside message: `print(f'[auto-pair] initial CLI pairing validation skipped request={request_id}: {type(err).__name__}: {err}')`
PRA-6 Resolve/justify tests test/nemoclaw-start-auto-pair-bootstrap.test.ts:26 Extract common test helpers into a shared module (e.g., test/helpers/auto-pair-test-helpers.ts) in this PR since both test files are modified. Harmonize the signatures.
PRA-7 Resolve/justify tests test/nemoclaw-start-auto-pair-bootstrap.test.ts:762 Add assertion that watcher logs show continued polling (or at least doesn't log 'watcher deadline reached' with approvals=0 immediately after the non-pairing error). Verify the watcher stays alive for the configured deadline.
PRA-8 Improvement architecture scripts/nemoclaw-start.sh:2300 Track as technical debt. Consider extracting the auto-pair watcher to a separate Python module (e.g., scripts/lib/auto_pair_watcher.py) in a future refactor. This would improve testability, allow proper unit testing, and reduce nemoclaw-start.sh size.
PRA-9 Improvement architecture ci/test-file-size-budget.json:9 Address PRA-5 (extract shared helper) and PRA-7 (extract watcher module) which would naturally reduce test file sizes and eliminate the need for legacy budget entries.
PRA-10 Resolve/justify correctness scripts/nemoclaw-start.sh:2552 Add a comment documenting the Ed25519 assumption, or parse the DER to verify the algorithm identifier (OID 1.3.101.112 for Ed25519) before extracting the raw key bytes.
PRA-11 Resolve/justify security scripts/nemoclaw-start.sh:2670 Add a SECURITY NOTE comment in pairing_required_request_id acknowledging that requestId is parsed from untrusted gateway error output, but the strict validation (single requestId, regex whitelist, length bound) and fail-closed behavior mitigate the risk. Note the removal condition (OpenClaw structured bootstrap API) will eliminate this parsing entirely.
PRA-12 Resolve/justify security scripts/nemoclaw-start.sh:2558 Catch binascii.Error locally in _identity_public_key and return empty string with a debug log, or let it propagate with a more specific exception type. Current state is acceptable but not ideal.
PRA-13 Improvement tests test/nemoclaw-start-auto-pair-bootstrap.test.ts:1 Add test cases: (1) gated-list bootstrap with clientId='openclaw-cli' to confirm rejection, (2) transient approve failure retry on gated-list path with fast-reentry bump verification.
PRA-14 Resolve/justify security scripts/nemoclaw-start.sh:2670 Add a comment noting the regex is bounded by REQUEST_ID_RE (max 128 chars) and the single-requestId check, which limits ReDoS exposure. Consider using a simpler parsing approach if this becomes a concern.
PRA-15 Resolve/justify correctness scripts/nemoclaw-start.sh:2615 Document why initial CLI pairing must be exactly {'operator.pairing'} and not a subset of ALLOWED_SCOPES. If this is intentional (bounded initial pairing), add a clear comment. If not, align with the shared policy's allowed scopes.
PRA-16 Resolve/justify tests test/nemoclaw-start-auto-pair-bootstrap.test.ts:45 Use a more targeted replacement (e.g., regex with word boundaries `\btime\.time\(` and `\btime\.sleep\(`) or verify that 'time.time()' and 'time.sleep(' only appear as function calls in the source.
PRA-17 Resolve/justify security scripts/nemoclaw-start.sh:2545 Parse the DER to verify the algorithm OID is 1.3.101.112 (Ed25519) before extracting raw key bytes. Use cryptography.hazmat.primitives.serialization.load_der_public_key if available, or manually parse the ASN.1 structure.
PRA-18 Resolve/justify correctness test/nemoclaw-start.test.ts:1500 Verify what clientId OpenClaw actually sends for CLI pairing requests. If it's 'openclaw-cli', revert the test change and update the gated-list path to accept it. If it's 'cli', update the shared policy to only allow 'cli' (or document why both are allowed).
PRA-19 Improvement docs scripts/nemoclaw-start.sh:2570 Either add a brief summary of what 'cv item 3' refers to, or replace with a link to the GitHub PR/comment.

🚨 Required before merge

Address these before merging unless a maintainer explicitly overrides the advisor with rationale.

PRA-2 Required — Gated-list bootstrap clientId allowlist asymmetry with shared policy

  • Location: scripts/nemoclaw-start.sh:2595
  • Category: acceptance
  • Problem: initial_cli_request_is_allowlisted() only accepts clientId='cli' and clientMode='cli', but the shared approval policy ALLOWED_CLIENTS in scripts/lib/openclaw_device_approval_policy.py includes 'cli', 'openclaw-cli', 'openclaw-control-ui'. The comment cites 'observed DGX Spark/Station repros' without referencing OpenClaw source code. This asymmetry means legitimate openclaw-cli initial pairing requests would be rejected in the gated-list path, while the main pending-list branch would allow them.
  • Impact: Legitimate openclaw-cli initial pairing requests rejected in gated-list path but accepted in main path — inconsistent policy enforcement. Future maintainers may incorrectly modify allowlist, allowing spoofed clients or blocking legitimate ones. Violates single source of truth for client identity policy.
  • Required action: Either: (a) Align gated-list validation with shared policy by accepting all ALLOWED_CLIENTS, or (b) Strengthen comment to cite OpenClaw source (e.g., 'verified against OpenClaw CLI source at openclaw/cmd/devices/pair.go: clientId=cli for initial pairing') and add test case with clientId='openclaw-cli' in gated-list flow to confirm rejection is intentional. If asymmetry is unintentional, fix the allowlist.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Search for 'clientId' in scripts/nemoclaw-start.sh line ~2595 and scripts/lib/openclaw_device_approval_policy.py line 24 to compare allowlisted values. Run the gated-list bootstrap test with clientId='openclaw-cli' in the pending record.
  • Missing regression test: Add a test case in test/nemoclaw-start-auto-pair-bootstrap.test.ts with clientId='openclaw-cli' in the gated-list pending record to confirm it is rejected (or accepted, if aligned) by initial_cli_request_is_allowlisted.
  • Done when: The required change is committed and verification passes: Search for 'clientId' in scripts/nemoclaw-start.sh line ~2595 and scripts/lib/openclaw_device_approval_policy.py line 24 to compare allowlisted values. Run the gated-list bootstrap test with clientId='openclaw-cli' in the pending record.
  • Evidence: Line ~2595 in scripts/nemoclaw-start.sh: if str(request.get('clientId', '')).strip() != 'cli': return False. Line 24 in openclaw_device_approval_policy.py: ALLOWED_CLIENTS = {'cli', 'openclaw-cli', 'openclaw-control-ui'}.
Review findings by urgency: 1 required fix, 14 items to resolve/justify, 4 in-scope improvements

⚠️ Resolve or justify before merge

Investigate these in the current review; either fix them, explain why they are not applicable, or document the accepted risk.

PRA-1 Resolve/justify — Source-of-truth review needed: scripts/nemoclaw-start.sh: _identity_public_key (Ed25519 key extraction)

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Missing — no test with non-Ed25519 key (NEW-1)
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: Line 2552: return base64.urlsafe_b64encode(der[-32:]).decode('ascii').rstrip('=') — no algorithm check. No SOURCE_OF_TRUTH_REVIEW comment on this function.

PRA-3 Resolve/justify — SHA256 comparison constant-time rationale undocumented

  • Location: scripts/nemoclaw-start.sh:2588
  • Category: security
  • Problem: SHA256 comparison `hashlib.sha256(public_key_raw).hexdigest() != device_id` is not constant-time. The device_id is a public SHA256 hash of the public key (not a secret), so timing attacks are not a practical concern, but the rationale is undocumented.
  • Impact: Future maintainer may incorrectly flag this as a timing vulnerability or add unnecessary constant-time comparison. Lack of documentation creates confusion about threat model.
  • Recommended action: Add a brief comment: 'deviceId is SHA256 of public key; comparison is not secret-dependent, constant-time not required.'
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read scripts/nemoclaw-start.sh line ~2588 and confirm a comment explains why constant-time comparison is not needed.
  • Missing regression test: N/A - documentation only
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read scripts/nemoclaw-start.sh line ~2588 and confirm a comment explains why constant-time comparison is not needed.
  • Evidence: Line 2588: if len(public_key_raw) != 32 or hashlib.sha256(public_key_raw).hexdigest() != device_id: return False

PRA-4 Resolve/justify — _identity_public_key silently returns empty string on PEM parse failure

  • Location: scripts/nemoclaw-start.sh:2545
  • Category: correctness
  • Problem: _identity_public_key() silently returns empty string on PEM parse failure (base64 decode error, short DER, etc.) without any debug logging. The caller initial_cli_request_is_allowlisted() catches binascii.Error but the failure path inside _identity_public_key itself (empty body, short DER) returns '' without logging.
  • Impact: Debugging PEM parse failures is difficult — no visibility into why public key extraction failed. Silent failures may mask configuration issues or malformed identity files.
  • Recommended action: Add a debug log line inside _identity_public_key for each failure path (e.g., print(f'[auto-pair] PEM parse failed: {err}') before returning ''), or refine the caller's except block to log the exception type alongside the message.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read _identity_public_key function and verify debug logging on failure paths (empty body, short DER, base64 decode error).
  • Missing regression test: Add a test that triggers a PEM parse failure (e.g., malformed base64 in publicKeyPem) and asserts a debug log line is emitted.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read _identity_public_key function and verify debug logging on failure paths (empty body, short DER, base64 decode error).
  • Evidence: Lines 2545-2558: function returns '' on empty body, short DER, base64 decode error without logging.

PRA-5 Resolve/justify — Exception type not logged in initial_cli_request_is_allowlisted except block

  • Location: scripts/nemoclaw-start.sh:2625
  • Category: correctness
  • Problem: Exception type not logged in initial_cli_request_is_allowlisted except block. The log prints only the error message: `print(f'[auto-pair] initial CLI pairing validation skipped request={request_id}: {brief_child_error("", str(err))}')`. Exception type (e.g., OSError vs ValueError vs binascii.Error) is lost.
  • Impact: Operational debugging harder — cannot distinguish between file I/O errors, JSON parse errors, and crypto errors from logs alone.
  • Recommended action: Log exception type alongside message: `print(f'[auto-pair] initial CLI pairing validation skipped request={request_id}: {type(err).__name__}: {err}')`
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read the except block at line ~2625 and confirm exception type is included in the log.
  • Missing regression test: N/A - logging improvement
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read the except block at line ~2625 and confirm exception type is included in the log.
  • Evidence: Line 2625: except (OSError, ValueError, RuntimeError, binascii.Error) as err: print(f'[auto-pair] initial CLI pairing validation skipped request={request_id}: {brief_child_error("", str(err))}')

PRA-6 Resolve/justify — autoPairPythonScript helper duplicated across test files with divergent signatures

  • Location: test/nemoclaw-start-auto-pair-bootstrap.test.ts:26
  • Category: tests
  • Problem: autoPairPythonScript helper duplicated across test files with divergent signatures. test/nemoclaw-start.test.ts has autoPairPythonScript(src: string) while the new test/nemoclaw-start-auto-pair-bootstrap.test.ts has autoPairPythonScript(src: string, tmpDir: string). Both do nearly identical transformations.
  • Impact: Maintenance burden — changes to time mocking or policy file substitution must be applied in two places. Divergent signatures increase cognitive load and risk of inconsistency.
  • Recommended action: Extract common test helpers into a shared module (e.g., test/helpers/auto-pair-test-helpers.ts) in this PR since both test files are modified. Harmonize the signatures.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Compare the two autoPairPythonScript functions in test/nemoclaw-start.test.ts (line ~170) and test/nemoclaw-start-auto-pair-bootstrap.test.ts (line 26).
  • Missing regression test: N/A - refactor to shared helper
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Compare the two autoPairPythonScript functions in test/nemoclaw-start.test.ts (line ~170) and test/nemoclaw-start-auto-pair-bootstrap.test.ts (line 26).
  • Evidence: test/nemoclaw-start.test.ts:170 has autoPairPythonScript(src: string). test/nemoclaw-start-auto-pair-bootstrap.test.ts:26 has autoPairPythonScript(src: string, tmpDir: string).

PRA-7 Resolve/justify — Non-pairing error test doesn't verify continued polling behavior

  • Location: test/nemoclaw-start-auto-pair-bootstrap.test.ts:762
  • Category: tests
  • Problem: Non-pairing error test ('does not seed when device list fails for a non-pairing error') doesn't verify continued polling behavior. It only asserts no approval happened and no paired/auth files created, but doesn't check that the watcher stays alive and continues polling for the configured deadline.
  • Impact: A regression where the watcher exits early on non-pairing errors would not be caught. The watcher should continue polling for the full DEADLINE (8h default) after transient gateway errors.
  • Recommended action: Add assertion that watcher logs show continued polling (or at least doesn't log 'watcher deadline reached' with approvals=0 immediately after the non-pairing error). Verify the watcher stays alive for the configured deadline.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Run the test and check stdout for 'watcher deadline reached approvals=0' appearing too quickly (before the 2s deadline).
  • Missing regression test: Add assertion that the watcher process runs for the full NEMOCLAW_AUTO_PAIR_DEADLINE_SECS (2s in test) before exiting, not exiting early on first non-pairing error.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Run the test and check stdout for 'watcher deadline reached approvals=0' appearing too quickly (before the 2s deadline).
  • Evidence: Test at line 762 only asserts no approval, no paired/auth files. No check for continued polling logs or deadline adherence.

PRA-10 Resolve/justify — _identity_public_key assumes Ed25519 without validation or documentation

  • Location: scripts/nemoclaw-start.sh:2552
  • Category: correctness
  • Problem: _identity_public_key assumes Ed25519 without validation or documentation. It takes the last 32 bytes of the DER-encoded SubjectPublicKeyInfo (der[-32:]) which is only correct for Ed25519 keys (where the raw public key is the last 32 bytes). For other key types (RSA, P-256, etc.), this extracts incorrect bytes.
  • Impact: If a non-Ed25519 key is present (RSA, P-256), the function extracts bogus bytes. This could pass the SHA256 device_id check if an attacker crafts a key where the last 32 bytes hash to the expected device_id (NEW-1).
  • Recommended action: Add a comment documenting the Ed25519 assumption, or parse the DER to verify the algorithm identifier (OID 1.3.101.112 for Ed25519) before extracting the raw key bytes.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read _identity_public_key function and confirm Ed25519 assumption is documented or validated.
  • Missing regression test: Add a test with a non-Ed25519 public key PEM (e.g., RSA) to verify the function fails closed (returns '') rather than extracting wrong bytes.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read _identity_public_key function and confirm Ed25519 assumption is documented or validated.
  • Evidence: Line 2552: return base64.urlsafe_b64encode(der[-32:]).decode('ascii').rstrip('=') — no algorithm check.

PRA-11 Resolve/justify — RequestId parsing from untrusted gateway error output lacks security acknowledgment

  • Location: scripts/nemoclaw-start.sh:2670
  • Category: security
  • Problem: RequestId parsing from untrusted gateway error output lacks explicit security acknowledgment. The pairing_required_request_id() function parses requestId from gateway error text (untrusted input) using complex regex. While SOURCE_OF_TRUTH_REVIEW comments exist, there's no SECURITY NOTE acknowledging the untrusted source and the mitigations (bounded regex, single requestId check, fail-closed).
  • Impact: Security reviewers may miss that requestId comes from untrusted gateway output. Without explicit acknowledgment, future changes could weaken validation.
  • Recommended action: Add a SECURITY NOTE comment in pairing_required_request_id acknowledging that requestId is parsed from untrusted gateway error output, but the strict validation (single requestId, regex whitelist, length bound) and fail-closed behavior mitigate the risk. Note the removal condition (OpenClaw structured bootstrap API) will eliminate this parsing entirely.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read pairing_required_request_id function and confirm SECURITY NOTE comment exists.
  • Missing regression test: N/A - documentation improvement
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read pairing_required_request_id function and confirm SECURITY NOTE comment exists.
  • Evidence: Function at line ~2670 has SOURCE_OF_TRUTH_REVIEW but no SECURITY NOTE about untrusted input source.

PRA-12 Resolve/justify — binascii.Error not caught locally in _identity_public_key

  • Location: scripts/nemoclaw-start.sh:2558
  • Category: security
  • Problem: binascii.Error is now caught in initial_cli_request_is_allowlisted except block (good), but _identity_public_key itself doesn't catch it locally - the exception propagates from base64.b64decode(). This is acceptable since the caller catches it, but local handling would be cleaner.
  • Impact: Exception propagates through multiple stack frames before being caught. Local handling would provide better context and allow specific debug logging for base64 decode failures.
  • Recommended action: Catch binascii.Error locally in _identity_public_key and return empty string with a debug log, or let it propagate with a more specific exception type. Current state is acceptable but not ideal.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read _identity_public_key and confirm binascii.Error handling approach.
  • Missing regression test: N/A - code style improvement
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read _identity_public_key and confirm binascii.Error handling approach.
  • Evidence: Line 2558: der = base64.b64decode(body) — no try/except; exception propagates to caller.

PRA-14 Resolve/justify — Complex regex in pairing_required_request_id ReDoS exposure

  • Location: scripts/nemoclaw-start.sh:2670
  • Category: security
  • Problem: Complex regex in pairing_required_request_id for requestId extraction from untrusted error output. While REQUEST_ID_RE bounds length to 128 chars, the extraction regex uses alternation with lookaheads which could theoretically have ReDoS exposure on crafted input. No comment acknowledging the bound exists.
  • Impact: Theoretical ReDoS if gateway returns crafted error text with many alternation matches. Bounded by 128-char limit and single-requestId check, but not documented.
  • Recommended action: Add a comment noting the regex is bounded by REQUEST_ID_RE (max 128 chars) and the single-requestId check, which limits ReDoS exposure. Consider using a simpler parsing approach if this becomes a concern.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read pairing_required_request_id function and confirm ReDoS acknowledgment comment.
  • Missing regression test: N/A - documentation improvement
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read pairing_required_request_id function and confirm ReDoS acknowledgment comment.
  • Evidence: Line ~2695: complex regex with alternation and lookaheads for requestId extraction.

PRA-15 Resolve/justify — Initial CLI pairing restricted to exactly {'operator.pairing'} without rationale

  • Location: scripts/nemoclaw-start.sh:2615
  • Category: correctness
  • Problem: Initial CLI pairing must be exactly {'operator.pairing'} and not a subset of ALLOWED_SCOPES. The code enforces `if scopes != {'operator.pairing'}: return False` but the comment only says 'The broader policy still handles normal openclaw-cli scope upgrades through the main pending-list branch' without explaining WHY initial pairing is restricted to exactly operator.pairing.
  • Impact: Future maintainer may not understand the security rationale for this restriction. If unintentional, it could prevent legitimate initial pairings that need additional scopes.
  • Recommended action: Document why initial CLI pairing must be exactly {'operator.pairing'} and not a subset of ALLOWED_SCOPES. If this is intentional (bounded initial pairing), add a clear comment. If not, align with the shared policy's allowed scopes.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read the scopes validation in initial_cli_request_is_allowlisted and confirm rationale is documented.
  • Missing regression test: N/A - documentation improvement
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read the scopes validation in initial_cli_request_is_allowlisted and confirm rationale is documented.
  • Evidence: Line 2615: if scopes != {'operator.pairing'}: return False. Comment above mentions broader policy but not why this restriction exists.

PRA-16 Resolve/justify — autoPairPythonScript uses broad replaceAll for time mocking

  • Location: test/nemoclaw-start-auto-pair-bootstrap.test.ts:45
  • Category: tests
  • Problem: autoPairPythonScript uses broad replaceAll for 'time.time()' and 'time.sleep(' which could affect non-function-call occurrences (e.g., in comments, strings, or variable names). Should use more targeted replacement (e.g., regex with word boundaries) or verify these only appear as function calls in the source.
  • Impact: Potential test flakiness if source contains 'time.time()' or 'time.sleep(' in comments/strings. Broad replacement is fragile.
  • Recommended action: Use a more targeted replacement (e.g., regex with word boundaries `\btime\.time\(` and `\btime\.sleep\(`) or verify that 'time.time()' and 'time.sleep(' only appear as function calls in the source.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Check the PYAUTOPAIR heredoc source for any non-call occurrences of 'time.time' or 'time.sleep'.
  • Missing regression test: N/A - test robustness improvement
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Check the PYAUTOPAIR heredoc source for any non-call occurrences of 'time.time' or 'time.sleep'.
  • Evidence: Line 45: .replaceAll("time.time()", "_nemoclaw_test_time()").replaceAll("time.sleep(", "_nemoclaw_test_sleep(")

PRA-17 Resolve/justify — _identity_public_key extracts raw key bytes without validating algorithm OID

  • Location: scripts/nemoclaw-start.sh:2545
  • Category: security
  • Problem: _identity_public_key extracts raw key bytes without validating the algorithm OID in the SubjectPublicKeyInfo. If a malicious or misconfigured identity file contains a non-Ed25519 key (RSA, P-256, etc.), the function will incorrectly extract the last 32 bytes of the DER, producing a bogus 'public key' that could pass the SHA256 device_id check if the attacker can craft a key where the last 32 bytes hash to the expected device_id.
  • Impact: Algorithm confusion vulnerability: non-Ed25519 keys produce bogus public key bytes that could hash to the expected device_id, bypassing identity validation. This is a latent correctness/security issue.
  • Recommended action: Parse the DER to verify the algorithm OID is 1.3.101.112 (Ed25519) before extracting raw key bytes. Use cryptography.hazmat.primitives.serialization.load_der_public_key if available, or manually parse the ASN.1 structure.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read _identity_public_key and confirm algorithm validation is added.
  • Missing regression test: Add a test with a valid RSA public key PEM that has a different device_id - verify the function returns '' (fails closed) rather than extracting bogus bytes.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read _identity_public_key and confirm algorithm validation is added.
  • Evidence: Line 2552: takes der[-32:] without checking algorithm OID. No cryptography library used for parsing.

PRA-18 Resolve/justify — Test fixture changed clientId from 'openclaw-cli' to 'cli' for late CLI scope upgrades

  • Location: test/nemoclaw-start.test.ts:1500
  • Category: correctness
  • Problem: Test fixture changed clientId from 'openclaw-cli' to 'cli' for late CLI scope upgrades. This changes the expected behavior but the shared policy ALLOWED_CLIENTS still includes both. If OpenClaw actually sends 'openclaw-cli' for CLI clients, this test would pass but the real watcher would reject the request in the main pending-list path (which uses the shared policy).
  • Impact: Test passes but real watcher would reject 'openclaw-cli' late CLI upgrades in main pending-list path. Creates false confidence in test coverage.
  • Recommended action: Verify what clientId OpenClaw actually sends for CLI pairing requests. If it's 'openclaw-cli', revert the test change and update the gated-list path to accept it. If it's 'cli', update the shared policy to only allow 'cli' (or document why both are allowed).
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Check OpenClaw source or device pairing logs to confirm actual clientId values sent by CLI.
  • Missing regression test: Integration test with real OpenClaw CLI to verify clientId value.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Check OpenClaw source or device pairing logs to confirm actual clientId values sent by CLI.
  • Evidence: test/nemoclaw-start.test.ts line ~1500: cliClient changed from { clientId: 'openclaw-cli', clientMode: 'cli' } to { clientId: 'cli', clientMode: 'cli' }.

💡 In-scope improvements

These are lower-risk, not throwaway. Prefer fixing them in this PR when they are local to changed code; defer only with rationale or a linked follow-up.

PRA-8 Improvement — Embedded Python watcher monolith growth in shell heredoc

  • Location: scripts/nemoclaw-start.sh:2300
  • Category: architecture
  • Problem: Embedded Python watcher monolith growth in shell heredoc. The PYAUTOPAIR heredoc grew from ~399 lines to ~627 lines (+228 lines, 57% increase) in this PR alone. This reduces testability, makes unit testing difficult, and increases nemoclaw-start.sh size.
  • Impact: Reduced auditability, harder unit testing, larger shell script. Technical debt that will compound with future changes.
  • Suggested action: Track as technical debt. Consider extracting the auto-pair watcher to a separate Python module (e.g., scripts/lib/auto_pair_watcher.py) in a future refactor. This would improve testability, allow proper unit testing, and reduce nemoclaw-start.sh size.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Count lines in the PYAUTOPAIR heredoc (between <<'PYAUTOPAIR' and PYAUTOPAIR).
  • Missing regression test: N/A - architecture suggestion
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: PYAUTOPAIR heredoc spans lines ~2329-2955 in scripts/nemoclaw-start.sh (~627 lines). Previous review noted ~399 lines.

PRA-9 Improvement — Test file size budget unchanged; helper duplication persists

  • Location: ci/test-file-size-budget.json:9
  • Category: architecture
  • Problem: Test file size budget unchanged (nemoclaw-start.test.ts: 4827→4826 lines, -1 line); helper duplication (PRA-5) persists. The budget entry for this legacy large file remains instead of being reduced by extracting shared helpers.
  • Impact: Legacy budget entry masks technical debt. Extracting shared helpers (PRA-5) and watcher module (PRA-7) would naturally reduce test file sizes.
  • Suggested action: Address PRA-5 (extract shared helper) and PRA-7 (extract watcher module) which would naturally reduce test file sizes and eliminate the need for legacy budget entries.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Check ci/test-file-size-budget.json legacyMaxLines entry for test/nemoclaw-start.test.ts.
  • Missing regression test: N/A - architecture suggestion
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: ci/test-file-size-budget.json line 9: "test/nemoclaw-start.test.ts": 4826 (was 4827).

PRA-13 Improvement — Missing gated-list test coverage for clientId='openclaw-cli' and fast-reentry bump

  • Location: test/nemoclaw-start-auto-pair-bootstrap.test.ts:1
  • Category: tests
  • Problem: Missing gated-list test coverage for clientId='openclaw-cli' and fast-reentry bump verification. The new test file comprehensively tests rejection cases but doesn't test: (1) gated-list bootstrap with clientId='openclaw-cli' to confirm rejection (or acceptance if aligned), (2) transient approve failure retry on gated-list path with fast-reentry bump verification.
  • Impact: Cannot verify PRA-1 asymmetry behavior. Cannot verify fast-reentry mechanism works on gated-list path (only tested on main pending-list path).
  • Suggested action: Add test cases: (1) gated-list bootstrap with clientId='openclaw-cli' to confirm rejection, (2) transient approve failure retry on gated-list path with fast-reentry bump verification.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Search test/nemoclaw-start-auto-pair-bootstrap.test.ts for 'openclaw-cli' - no matches found. Search for 'fast-reentry' in gated-list test context.
  • Missing regression test: Two new test cases: (1) gated-list bootstrap with clientId='openclaw-cli' in pending record, (2) transient gated-list approve failure with fast-reentry bump assertion.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: No test uses clientId='openclaw-cli' in gated-list path. No test asserts FAST_REENTRY_REMAINING bump on gated-list approve retry.

PRA-19 Improvement — SOURCE_OF_TRUTH_REVIEW references PR #6330 (this PR) circularly

Simplification opportunities: 1 possible cut, net -550 lines possible

These are safe simplification checks only. Do not remove validation, security controls, data-loss prevention, or required tests.

  • PRA-8 shrink (scripts/nemoclaw-start.sh:2300): Entire PYAUTOPAIR heredoc (~627 lines) from scripts/nemoclaw-start.sh
    • Replacement: Import from scripts/lib/auto_pair_watcher.py module; shell launches python3 -m auto_pair_watcher
    • Net: -550 lines
    • Safety boundary: Must preserve: sandbox user execution, env var configuration, policy file permission checks, gateway env stripping for approve calls, fail-closed behavior
Test follow-ups to resolve or justify

If these cover changed behavior, prefer adding them in this PR; otherwise state why existing coverage is enough or link the follow-up.

  • PRA-T1 Runtime validation — gated-list bootstrap with clientId='openclaw-cli' → confirms rejection (or acceptance if aligned). scripts/nemoclaw-start.sh and ci/test-file-size-budget.json are runtime/sandbox/infrastructure paths. Current tests are unit/integration with mocked openclaw — no live sandbox E2E in this PR. Issue comment confirms manual A/B on aits-log-worker-6 / ncq2621-latest-main — should be captured as regression test or documented as manual verification.
  • PRA-T2 Runtime validation — non-pairing error → watcher polls for full DEADLINE (2s) before exiting, not immediate. scripts/nemoclaw-start.sh and ci/test-file-size-budget.json are runtime/sandbox/infrastructure paths. Current tests are unit/integration with mocked openclaw — no live sandbox E2E in this PR. Issue comment confirms manual A/B on aits-log-worker-6 / ncq2621-latest-main — should be captured as regression test or documented as manual verification.
  • PRA-T3 Runtime validation — transient gated-list approve failure → fast-reentry bump fires (FAST_REENTRY_REMAINING set). scripts/nemoclaw-start.sh and ci/test-file-size-budget.json are runtime/sandbox/infrastructure paths. Current tests are unit/integration with mocked openclaw — no live sandbox E2E in this PR. Issue comment confirms manual A/B on aits-log-worker-6 / ncq2621-latest-main — should be captured as regression test or documented as manual verification.
  • PRA-T4 Runtime validation — _identity_public_key with RSA public key PEM → returns '' (fails closed), doesn't extract bogus bytes. scripts/nemoclaw-start.sh and ci/test-file-size-budget.json are runtime/sandbox/infrastructure paths. Current tests are unit/integration with mocked openclaw — no live sandbox E2E in this PR. Issue comment confirms manual A/B on aits-log-worker-6 / ncq2621-latest-main — should be captured as regression test or documented as manual verification.
  • PRA-T5 Runtime validation — late CLI scope upgrade with real OpenClaw CLI → verify actual clientId sent. scripts/nemoclaw-start.sh and ci/test-file-size-budget.json are runtime/sandbox/infrastructure paths. Current tests are unit/integration with mocked openclaw — no live sandbox E2E in this PR. Issue comment confirms manual A/B on aits-log-worker-6 / ncq2621-latest-main — should be captured as regression test or documented as manual verification.
  • PRA-T6 autoPairPythonScript helper duplicated across test files with divergent signatures — Extract common test helpers into a shared module (e.g., test/helpers/auto-pair-test-helpers.ts) in this PR since both test files are modified. Harmonize the signatures.
  • PRA-T7 Non-pairing error test doesn't verify continued polling behavior — Add assertion that watcher logs show continued polling (or at least doesn't log 'watcher deadline reached' with approvals=0 immediately after the non-pairing error). Verify the watcher stays alive for the configured deadline.
  • PRA-T8 Missing gated-list test coverage for clientId='openclaw-cli' and fast-reentry bump — Add test cases: (1) gated-list bootstrap with clientId='openclaw-cli' to confirm rejection, (2) transient approve failure retry on gated-list path with fast-reentry bump verification.
Since last review details

Current findings, using the urgency labels above:

PRA-1 Resolve/justify — Source-of-truth review needed: scripts/nemoclaw-start.sh: _identity_public_key (Ed25519 key extraction)

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Missing — no test with non-Ed25519 key (NEW-1)
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: Line 2552: return base64.urlsafe_b64encode(der[-32:]).decode('ascii').rstrip('=') — no algorithm check. No SOURCE_OF_TRUTH_REVIEW comment on this function.

PRA-2 Required — Gated-list bootstrap clientId allowlist asymmetry with shared policy

  • Location: scripts/nemoclaw-start.sh:2595
  • Category: acceptance
  • Problem: initial_cli_request_is_allowlisted() only accepts clientId='cli' and clientMode='cli', but the shared approval policy ALLOWED_CLIENTS in scripts/lib/openclaw_device_approval_policy.py includes 'cli', 'openclaw-cli', 'openclaw-control-ui'. The comment cites 'observed DGX Spark/Station repros' without referencing OpenClaw source code. This asymmetry means legitimate openclaw-cli initial pairing requests would be rejected in the gated-list path, while the main pending-list branch would allow them.
  • Impact: Legitimate openclaw-cli initial pairing requests rejected in gated-list path but accepted in main path — inconsistent policy enforcement. Future maintainers may incorrectly modify allowlist, allowing spoofed clients or blocking legitimate ones. Violates single source of truth for client identity policy.
  • Required action: Either: (a) Align gated-list validation with shared policy by accepting all ALLOWED_CLIENTS, or (b) Strengthen comment to cite OpenClaw source (e.g., 'verified against OpenClaw CLI source at openclaw/cmd/devices/pair.go: clientId=cli for initial pairing') and add test case with clientId='openclaw-cli' in gated-list flow to confirm rejection is intentional. If asymmetry is unintentional, fix the allowlist.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Search for 'clientId' in scripts/nemoclaw-start.sh line ~2595 and scripts/lib/openclaw_device_approval_policy.py line 24 to compare allowlisted values. Run the gated-list bootstrap test with clientId='openclaw-cli' in the pending record.
  • Missing regression test: Add a test case in test/nemoclaw-start-auto-pair-bootstrap.test.ts with clientId='openclaw-cli' in the gated-list pending record to confirm it is rejected (or accepted, if aligned) by initial_cli_request_is_allowlisted.
  • Done when: The required change is committed and verification passes: Search for 'clientId' in scripts/nemoclaw-start.sh line ~2595 and scripts/lib/openclaw_device_approval_policy.py line 24 to compare allowlisted values. Run the gated-list bootstrap test with clientId='openclaw-cli' in the pending record.
  • Evidence: Line ~2595 in scripts/nemoclaw-start.sh: if str(request.get('clientId', '')).strip() != 'cli': return False. Line 24 in openclaw_device_approval_policy.py: ALLOWED_CLIENTS = {'cli', 'openclaw-cli', 'openclaw-control-ui'}.

PRA-3 Resolve/justify — SHA256 comparison constant-time rationale undocumented

  • Location: scripts/nemoclaw-start.sh:2588
  • Category: security
  • Problem: SHA256 comparison `hashlib.sha256(public_key_raw).hexdigest() != device_id` is not constant-time. The device_id is a public SHA256 hash of the public key (not a secret), so timing attacks are not a practical concern, but the rationale is undocumented.
  • Impact: Future maintainer may incorrectly flag this as a timing vulnerability or add unnecessary constant-time comparison. Lack of documentation creates confusion about threat model.
  • Recommended action: Add a brief comment: 'deviceId is SHA256 of public key; comparison is not secret-dependent, constant-time not required.'
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read scripts/nemoclaw-start.sh line ~2588 and confirm a comment explains why constant-time comparison is not needed.
  • Missing regression test: N/A - documentation only
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read scripts/nemoclaw-start.sh line ~2588 and confirm a comment explains why constant-time comparison is not needed.
  • Evidence: Line 2588: if len(public_key_raw) != 32 or hashlib.sha256(public_key_raw).hexdigest() != device_id: return False

PRA-4 Resolve/justify — _identity_public_key silently returns empty string on PEM parse failure

  • Location: scripts/nemoclaw-start.sh:2545
  • Category: correctness
  • Problem: _identity_public_key() silently returns empty string on PEM parse failure (base64 decode error, short DER, etc.) without any debug logging. The caller initial_cli_request_is_allowlisted() catches binascii.Error but the failure path inside _identity_public_key itself (empty body, short DER) returns '' without logging.
  • Impact: Debugging PEM parse failures is difficult — no visibility into why public key extraction failed. Silent failures may mask configuration issues or malformed identity files.
  • Recommended action: Add a debug log line inside _identity_public_key for each failure path (e.g., print(f'[auto-pair] PEM parse failed: {err}') before returning ''), or refine the caller's except block to log the exception type alongside the message.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read _identity_public_key function and verify debug logging on failure paths (empty body, short DER, base64 decode error).
  • Missing regression test: Add a test that triggers a PEM parse failure (e.g., malformed base64 in publicKeyPem) and asserts a debug log line is emitted.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read _identity_public_key function and verify debug logging on failure paths (empty body, short DER, base64 decode error).
  • Evidence: Lines 2545-2558: function returns '' on empty body, short DER, base64 decode error without logging.

PRA-5 Resolve/justify — Exception type not logged in initial_cli_request_is_allowlisted except block

  • Location: scripts/nemoclaw-start.sh:2625
  • Category: correctness
  • Problem: Exception type not logged in initial_cli_request_is_allowlisted except block. The log prints only the error message: `print(f'[auto-pair] initial CLI pairing validation skipped request={request_id}: {brief_child_error("", str(err))}')`. Exception type (e.g., OSError vs ValueError vs binascii.Error) is lost.
  • Impact: Operational debugging harder — cannot distinguish between file I/O errors, JSON parse errors, and crypto errors from logs alone.
  • Recommended action: Log exception type alongside message: `print(f'[auto-pair] initial CLI pairing validation skipped request={request_id}: {type(err).__name__}: {err}')`
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read the except block at line ~2625 and confirm exception type is included in the log.
  • Missing regression test: N/A - logging improvement
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read the except block at line ~2625 and confirm exception type is included in the log.
  • Evidence: Line 2625: except (OSError, ValueError, RuntimeError, binascii.Error) as err: print(f'[auto-pair] initial CLI pairing validation skipped request={request_id}: {brief_child_error("", str(err))}')

PRA-6 Resolve/justify — autoPairPythonScript helper duplicated across test files with divergent signatures

  • Location: test/nemoclaw-start-auto-pair-bootstrap.test.ts:26
  • Category: tests
  • Problem: autoPairPythonScript helper duplicated across test files with divergent signatures. test/nemoclaw-start.test.ts has autoPairPythonScript(src: string) while the new test/nemoclaw-start-auto-pair-bootstrap.test.ts has autoPairPythonScript(src: string, tmpDir: string). Both do nearly identical transformations.
  • Impact: Maintenance burden — changes to time mocking or policy file substitution must be applied in two places. Divergent signatures increase cognitive load and risk of inconsistency.
  • Recommended action: Extract common test helpers into a shared module (e.g., test/helpers/auto-pair-test-helpers.ts) in this PR since both test files are modified. Harmonize the signatures.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Compare the two autoPairPythonScript functions in test/nemoclaw-start.test.ts (line ~170) and test/nemoclaw-start-auto-pair-bootstrap.test.ts (line 26).
  • Missing regression test: N/A - refactor to shared helper
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Compare the two autoPairPythonScript functions in test/nemoclaw-start.test.ts (line ~170) and test/nemoclaw-start-auto-pair-bootstrap.test.ts (line 26).
  • Evidence: test/nemoclaw-start.test.ts:170 has autoPairPythonScript(src: string). test/nemoclaw-start-auto-pair-bootstrap.test.ts:26 has autoPairPythonScript(src: string, tmpDir: string).

PRA-7 Resolve/justify — Non-pairing error test doesn't verify continued polling behavior

  • Location: test/nemoclaw-start-auto-pair-bootstrap.test.ts:762
  • Category: tests
  • Problem: Non-pairing error test ('does not seed when device list fails for a non-pairing error') doesn't verify continued polling behavior. It only asserts no approval happened and no paired/auth files created, but doesn't check that the watcher stays alive and continues polling for the configured deadline.
  • Impact: A regression where the watcher exits early on non-pairing errors would not be caught. The watcher should continue polling for the full DEADLINE (8h default) after transient gateway errors.
  • Recommended action: Add assertion that watcher logs show continued polling (or at least doesn't log 'watcher deadline reached' with approvals=0 immediately after the non-pairing error). Verify the watcher stays alive for the configured deadline.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Run the test and check stdout for 'watcher deadline reached approvals=0' appearing too quickly (before the 2s deadline).
  • Missing regression test: Add assertion that the watcher process runs for the full NEMOCLAW_AUTO_PAIR_DEADLINE_SECS (2s in test) before exiting, not exiting early on first non-pairing error.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Run the test and check stdout for 'watcher deadline reached approvals=0' appearing too quickly (before the 2s deadline).
  • Evidence: Test at line 762 only asserts no approval, no paired/auth files. No check for continued polling logs or deadline adherence.

PRA-8 Improvement — Embedded Python watcher monolith growth in shell heredoc

  • Location: scripts/nemoclaw-start.sh:2300
  • Category: architecture
  • Problem: Embedded Python watcher monolith growth in shell heredoc. The PYAUTOPAIR heredoc grew from ~399 lines to ~627 lines (+228 lines, 57% increase) in this PR alone. This reduces testability, makes unit testing difficult, and increases nemoclaw-start.sh size.
  • Impact: Reduced auditability, harder unit testing, larger shell script. Technical debt that will compound with future changes.
  • Suggested action: Track as technical debt. Consider extracting the auto-pair watcher to a separate Python module (e.g., scripts/lib/auto_pair_watcher.py) in a future refactor. This would improve testability, allow proper unit testing, and reduce nemoclaw-start.sh size.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Count lines in the PYAUTOPAIR heredoc (between <<'PYAUTOPAIR' and PYAUTOPAIR).
  • Missing regression test: N/A - architecture suggestion
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: PYAUTOPAIR heredoc spans lines ~2329-2955 in scripts/nemoclaw-start.sh (~627 lines). Previous review noted ~399 lines.

PRA-9 Improvement — Test file size budget unchanged; helper duplication persists

  • Location: ci/test-file-size-budget.json:9
  • Category: architecture
  • Problem: Test file size budget unchanged (nemoclaw-start.test.ts: 4827→4826 lines, -1 line); helper duplication (PRA-5) persists. The budget entry for this legacy large file remains instead of being reduced by extracting shared helpers.
  • Impact: Legacy budget entry masks technical debt. Extracting shared helpers (PRA-5) and watcher module (PRA-7) would naturally reduce test file sizes.
  • Suggested action: Address PRA-5 (extract shared helper) and PRA-7 (extract watcher module) which would naturally reduce test file sizes and eliminate the need for legacy budget entries.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Check ci/test-file-size-budget.json legacyMaxLines entry for test/nemoclaw-start.test.ts.
  • Missing regression test: N/A - architecture suggestion
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: ci/test-file-size-budget.json line 9: "test/nemoclaw-start.test.ts": 4826 (was 4827).

PRA-10 Resolve/justify — _identity_public_key assumes Ed25519 without validation or documentation

  • Location: scripts/nemoclaw-start.sh:2552
  • Category: correctness
  • Problem: _identity_public_key assumes Ed25519 without validation or documentation. It takes the last 32 bytes of the DER-encoded SubjectPublicKeyInfo (der[-32:]) which is only correct for Ed25519 keys (where the raw public key is the last 32 bytes). For other key types (RSA, P-256, etc.), this extracts incorrect bytes.
  • Impact: If a non-Ed25519 key is present (RSA, P-256), the function extracts bogus bytes. This could pass the SHA256 device_id check if an attacker crafts a key where the last 32 bytes hash to the expected device_id (NEW-1).
  • Recommended action: Add a comment documenting the Ed25519 assumption, or parse the DER to verify the algorithm identifier (OID 1.3.101.112 for Ed25519) before extracting the raw key bytes.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read _identity_public_key function and confirm Ed25519 assumption is documented or validated.
  • Missing regression test: Add a test with a non-Ed25519 public key PEM (e.g., RSA) to verify the function fails closed (returns '') rather than extracting wrong bytes.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read _identity_public_key function and confirm Ed25519 assumption is documented or validated.
  • Evidence: Line 2552: return base64.urlsafe_b64encode(der[-32:]).decode('ascii').rstrip('=') — no algorithm check.

PRA-11 Resolve/justify — RequestId parsing from untrusted gateway error output lacks security acknowledgment

  • Location: scripts/nemoclaw-start.sh:2670
  • Category: security
  • Problem: RequestId parsing from untrusted gateway error output lacks explicit security acknowledgment. The pairing_required_request_id() function parses requestId from gateway error text (untrusted input) using complex regex. While SOURCE_OF_TRUTH_REVIEW comments exist, there's no SECURITY NOTE acknowledging the untrusted source and the mitigations (bounded regex, single requestId check, fail-closed).
  • Impact: Security reviewers may miss that requestId comes from untrusted gateway output. Without explicit acknowledgment, future changes could weaken validation.
  • Recommended action: Add a SECURITY NOTE comment in pairing_required_request_id acknowledging that requestId is parsed from untrusted gateway error output, but the strict validation (single requestId, regex whitelist, length bound) and fail-closed behavior mitigate the risk. Note the removal condition (OpenClaw structured bootstrap API) will eliminate this parsing entirely.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read pairing_required_request_id function and confirm SECURITY NOTE comment exists.
  • Missing regression test: N/A - documentation improvement
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read pairing_required_request_id function and confirm SECURITY NOTE comment exists.
  • Evidence: Function at line ~2670 has SOURCE_OF_TRUTH_REVIEW but no SECURITY NOTE about untrusted input source.

PRA-12 Resolve/justify — binascii.Error not caught locally in _identity_public_key

  • Location: scripts/nemoclaw-start.sh:2558
  • Category: security
  • Problem: binascii.Error is now caught in initial_cli_request_is_allowlisted except block (good), but _identity_public_key itself doesn't catch it locally - the exception propagates from base64.b64decode(). This is acceptable since the caller catches it, but local handling would be cleaner.
  • Impact: Exception propagates through multiple stack frames before being caught. Local handling would provide better context and allow specific debug logging for base64 decode failures.
  • Recommended action: Catch binascii.Error locally in _identity_public_key and return empty string with a debug log, or let it propagate with a more specific exception type. Current state is acceptable but not ideal.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read _identity_public_key and confirm binascii.Error handling approach.
  • Missing regression test: N/A - code style improvement
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read _identity_public_key and confirm binascii.Error handling approach.
  • Evidence: Line 2558: der = base64.b64decode(body) — no try/except; exception propagates to caller.

PRA-13 Improvement — Missing gated-list test coverage for clientId='openclaw-cli' and fast-reentry bump

  • Location: test/nemoclaw-start-auto-pair-bootstrap.test.ts:1
  • Category: tests
  • Problem: Missing gated-list test coverage for clientId='openclaw-cli' and fast-reentry bump verification. The new test file comprehensively tests rejection cases but doesn't test: (1) gated-list bootstrap with clientId='openclaw-cli' to confirm rejection (or acceptance if aligned), (2) transient approve failure retry on gated-list path with fast-reentry bump verification.
  • Impact: Cannot verify PRA-1 asymmetry behavior. Cannot verify fast-reentry mechanism works on gated-list path (only tested on main pending-list path).
  • Suggested action: Add test cases: (1) gated-list bootstrap with clientId='openclaw-cli' to confirm rejection, (2) transient approve failure retry on gated-list path with fast-reentry bump verification.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Search test/nemoclaw-start-auto-pair-bootstrap.test.ts for 'openclaw-cli' - no matches found. Search for 'fast-reentry' in gated-list test context.
  • Missing regression test: Two new test cases: (1) gated-list bootstrap with clientId='openclaw-cli' in pending record, (2) transient gated-list approve failure with fast-reentry bump assertion.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: No test uses clientId='openclaw-cli' in gated-list path. No test asserts FAST_REENTRY_REMAINING bump on gated-list approve retry.

PRA-14 Resolve/justify — Complex regex in pairing_required_request_id ReDoS exposure

  • Location: scripts/nemoclaw-start.sh:2670
  • Category: security
  • Problem: Complex regex in pairing_required_request_id for requestId extraction from untrusted error output. While REQUEST_ID_RE bounds length to 128 chars, the extraction regex uses alternation with lookaheads which could theoretically have ReDoS exposure on crafted input. No comment acknowledging the bound exists.
  • Impact: Theoretical ReDoS if gateway returns crafted error text with many alternation matches. Bounded by 128-char limit and single-requestId check, but not documented.
  • Recommended action: Add a comment noting the regex is bounded by REQUEST_ID_RE (max 128 chars) and the single-requestId check, which limits ReDoS exposure. Consider using a simpler parsing approach if this becomes a concern.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read pairing_required_request_id function and confirm ReDoS acknowledgment comment.
  • Missing regression test: N/A - documentation improvement
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read pairing_required_request_id function and confirm ReDoS acknowledgment comment.
  • Evidence: Line ~2695: complex regex with alternation and lookaheads for requestId extraction.

PRA-15 Resolve/justify — Initial CLI pairing restricted to exactly {'operator.pairing'} without rationale

  • Location: scripts/nemoclaw-start.sh:2615
  • Category: correctness
  • Problem: Initial CLI pairing must be exactly {'operator.pairing'} and not a subset of ALLOWED_SCOPES. The code enforces `if scopes != {'operator.pairing'}: return False` but the comment only says 'The broader policy still handles normal openclaw-cli scope upgrades through the main pending-list branch' without explaining WHY initial pairing is restricted to exactly operator.pairing.
  • Impact: Future maintainer may not understand the security rationale for this restriction. If unintentional, it could prevent legitimate initial pairings that need additional scopes.
  • Recommended action: Document why initial CLI pairing must be exactly {'operator.pairing'} and not a subset of ALLOWED_SCOPES. If this is intentional (bounded initial pairing), add a clear comment. If not, align with the shared policy's allowed scopes.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read the scopes validation in initial_cli_request_is_allowlisted and confirm rationale is documented.
  • Missing regression test: N/A - documentation improvement
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read the scopes validation in initial_cli_request_is_allowlisted and confirm rationale is documented.
  • Evidence: Line 2615: if scopes != {'operator.pairing'}: return False. Comment above mentions broader policy but not why this restriction exists.

PRA-16 Resolve/justify — autoPairPythonScript uses broad replaceAll for time mocking

  • Location: test/nemoclaw-start-auto-pair-bootstrap.test.ts:45
  • Category: tests
  • Problem: autoPairPythonScript uses broad replaceAll for 'time.time()' and 'time.sleep(' which could affect non-function-call occurrences (e.g., in comments, strings, or variable names). Should use more targeted replacement (e.g., regex with word boundaries) or verify these only appear as function calls in the source.
  • Impact: Potential test flakiness if source contains 'time.time()' or 'time.sleep(' in comments/strings. Broad replacement is fragile.
  • Recommended action: Use a more targeted replacement (e.g., regex with word boundaries `\btime\.time\(` and `\btime\.sleep\(`) or verify that 'time.time()' and 'time.sleep(' only appear as function calls in the source.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Check the PYAUTOPAIR heredoc source for any non-call occurrences of 'time.time' or 'time.sleep'.
  • Missing regression test: N/A - test robustness improvement
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Check the PYAUTOPAIR heredoc source for any non-call occurrences of 'time.time' or 'time.sleep'.
  • Evidence: Line 45: .replaceAll("time.time()", "_nemoclaw_test_time()").replaceAll("time.sleep(", "_nemoclaw_test_sleep(")

PRA-17 Resolve/justify — _identity_public_key extracts raw key bytes without validating algorithm OID

  • Location: scripts/nemoclaw-start.sh:2545
  • Category: security
  • Problem: _identity_public_key extracts raw key bytes without validating the algorithm OID in the SubjectPublicKeyInfo. If a malicious or misconfigured identity file contains a non-Ed25519 key (RSA, P-256, etc.), the function will incorrectly extract the last 32 bytes of the DER, producing a bogus 'public key' that could pass the SHA256 device_id check if the attacker can craft a key where the last 32 bytes hash to the expected device_id.
  • Impact: Algorithm confusion vulnerability: non-Ed25519 keys produce bogus public key bytes that could hash to the expected device_id, bypassing identity validation. This is a latent correctness/security issue.
  • Recommended action: Parse the DER to verify the algorithm OID is 1.3.101.112 (Ed25519) before extracting raw key bytes. Use cryptography.hazmat.primitives.serialization.load_der_public_key if available, or manually parse the ASN.1 structure.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read _identity_public_key and confirm algorithm validation is added.
  • Missing regression test: Add a test with a valid RSA public key PEM that has a different device_id - verify the function returns '' (fails closed) rather than extracting bogus bytes.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read _identity_public_key and confirm algorithm validation is added.
  • Evidence: Line 2552: takes der[-32:] without checking algorithm OID. No cryptography library used for parsing.

PRA-18 Resolve/justify — Test fixture changed clientId from 'openclaw-cli' to 'cli' for late CLI scope upgrades

  • Location: test/nemoclaw-start.test.ts:1500
  • Category: correctness
  • Problem: Test fixture changed clientId from 'openclaw-cli' to 'cli' for late CLI scope upgrades. This changes the expected behavior but the shared policy ALLOWED_CLIENTS still includes both. If OpenClaw actually sends 'openclaw-cli' for CLI clients, this test would pass but the real watcher would reject the request in the main pending-list path (which uses the shared policy).
  • Impact: Test passes but real watcher would reject 'openclaw-cli' late CLI upgrades in main pending-list path. Creates false confidence in test coverage.
  • Recommended action: Verify what clientId OpenClaw actually sends for CLI pairing requests. If it's 'openclaw-cli', revert the test change and update the gated-list path to accept it. If it's 'cli', update the shared policy to only allow 'cli' (or document why both are allowed).
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Check OpenClaw source or device pairing logs to confirm actual clientId values sent by CLI.
  • Missing regression test: Integration test with real OpenClaw CLI to verify clientId value.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Check OpenClaw source or device pairing logs to confirm actual clientId values sent by CLI.
  • Evidence: test/nemoclaw-start.test.ts line ~1500: cliClient changed from { clientId: 'openclaw-cli', clientMode: 'cli' } to { clientId: 'cli', clientMode: 'cli' }.

PRA-19 Improvement — SOURCE_OF_TRUTH_REVIEW references PR #6330 (this PR) circularly

Workflow run details

This is an automated, non-binding review; it still expects maintainers and agents to respond to each required or warning item. Treat suggestions as current-PR improvements when they touch changed code; defer only with maintainer rationale or a linked follow-up. A human maintainer must make the final merge decision.

@github-actions

github-actions Bot commented Jul 6, 2026 •

Copy link
Copy Markdown
Contributor

E2E Advisor Recommendation

Required E2E: issue-4462-scope-upgrade-approval, sessions-agents-cli, full-e2e
Optional E2E: device-auth-health, openclaw-tui-chat-correlation

Dispatch hint: issue-4462-scope-upgrade-approval,sessions-agents-cli,full-e2e

Workflow run

Full advisor summary

E2E Recommendation Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E

  • issue-4462-scope-upgrade-approval (high): Most directly exercises the live OpenClaw device pairing/scope-upgrade approval path, gateway env stripping, and agent no-embedded-fallback behavior affected by the auto-pair watcher changes.
  • sessions-agents-cli (medium): Covers real sessions/agents CLI dispatch through the OpenClaw gateway, including pending pairing/scope approval and JSON envelope behavior; this is a user-visible flow that can regress if initial CLI pairing bootstrap fails.
  • full-e2e (high): The startup script is the sandbox entrypoint. Run the full live install/onboard/sandbox/assistant journey to validate that the changed watcher still works in the normal hosted onboarding path.

Optional E2E

  • device-auth-health (medium): Useful adjacent confidence for device-auth-enabled sandboxes and gateway health semantics, since the PR changes device identity validation and approval bootstrap behavior.
  • openclaw-tui-chat-correlation (medium): Optional coverage for another real OpenClaw CLI/TUI client path that depends on gateway connectivity and approved device scopes after sandbox startup.

New E2E recommendations

  • initial CLI auto-pair bootstrap (high): This PR adds strong local tests for the gated openclaw devices list --json compatibility path, but there does not appear to be an existing live E2E that forces a real sandbox into the initial gated-list state and proves the watcher approves the CLI pairing request without editing OpenClaw state directly.
    • Suggested test: Add a live E2E target for initial CLI gated-list bootstrap that starts a sandbox with device list gated, verifies auto-pair approves the parsed requestId through openclaw devices approve, and then runs an OpenClaw agent/CLI command without embedded fallback.

Dispatch hint

  • Workflow: .github/workflows/e2e.yaml
  • jobs input: issue-4462-scope-upgrade-approval,sessions-agents-cli,full-e2e

@github-actions

github-actions Bot commented Jul 6, 2026 •

Copy link
Copy Markdown
Contributor

E2E Target Recommendation

Required E2E targets: issue-4462-scope-upgrade-approval
Optional E2E targets: ubuntu-repo-cloud-openclaw

Dispatch required E2E targets:

  • gh workflow run e2e.yaml --ref <pr-head-ref> --field jobs=issue-4462-scope-upgrade-approval

Workflow run

Full E2E target advisor summary

E2E Target Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E targets

  • issue-4462-scope-upgrade-approval: scripts/nemoclaw-start.sh changes the in-sandbox OpenClaw auto-pair watcher, including initial CLI pairing bootstrap, devices list/approve handling, and gateway-backed agent scope-upgrade behavior. The issue-4462-scope-upgrade-approval free-standing live job is the smallest wired E2E path that exercises this approval boundary and asserts OpenClaw agent turns stay on the gateway path instead of falling back or remaining pending.
    • Dispatch: gh workflow run e2e.yaml --ref <pr-head-ref> --field jobs=issue-4462-scope-upgrade-approval

Optional E2E targets

  • ubuntu-repo-cloud-openclaw: Optional broader baseline registry target for the normal Ubuntu Docker OpenClaw onboarding path through nemoclaw-start.sh. It is adjacent coverage for startup regressions, while the required issue-4462 job is the focused auto-pair/agent approval proof.
    • Dispatch: gh workflow run e2e.yaml --ref <pr-head-ref> --field targets=ubuntu-repo-cloud-openclaw

Relevant changed files

  • scripts/nemoclaw-start.sh

@github-actions

github-actions Bot commented Jul 6, 2026 •

Copy link
Copy Markdown
Contributor

PR Review Advisor — No blocking findings

Merge posture: No blocking advisor findings
Primary next action: Resolve or justify PRA-1: Source-of-truth review needed: scripts/nemoclaw-start.sh pairing-required request-id extraction.
Open items: 0 required · 1 warning · 1 suggestion · 8 test follow-ups
Since last review: 0 prior items resolved · 1 still applies · 0 new items found

Action checklist

  • PRA-1 Resolve or justify: Source-of-truth review needed: scripts/nemoclaw-start.sh pairing-required request-id extraction
  • PRA-T1 Add or justify test follow-up: Runtime validation
  • PRA-T2 Add or justify test follow-up: Runtime validation
  • PRA-T3 Add or justify test follow-up: Runtime validation
  • PRA-T4 Add or justify test follow-up: Runtime validation
  • PRA-T5 Add or justify test follow-up: Clarify the snake_case request-id parser contract
  • PRA-T6 Add or justify test follow-up: Acceptance clause
  • PRA-T7 Add or justify test follow-up: Acceptance clause
  • PRA-T8 Add or justify test follow-up: Acceptance clause
  • PRA-2 In-scope improvement: Clarify the snake_case request-id parser contract in scripts/nemoclaw-start.sh:2665

Findings index

ID Severity Category Location Required action
PRA-1 Resolve/justify architecture — Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
PRA-2 Improvement tests scripts/nemoclaw-start.sh:2665 Either add focused fake-runtime coverage for a pairing-required JSON denial containing exactly one `request_id` approving the matching local `cli` request, or remove `request_id` from `_structured_request_ids()` so the parser surface matches the tested camelCase-only contract.
Review findings by urgency: 0 required fixes, 1 item to resolve/justify, 1 in-scope improvement

⚠️ Resolve or justify before merge

Investigate these in the current review; either fix them, explain why they are not applicable, or document the accepted risk.

PRA-1 Resolve/justify — Source-of-truth review needed: scripts/nemoclaw-start.sh pairing-required request-id extraction

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Malformed, overlong, whitespace, and multiple camelCase request-id cases are covered; a pure `request_id` structured denial is not covered.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: The source comments claim structured JSON support, and `_structured_request_ids()` collects `request_id`, but the outer `re.findall(r'\brequestId\b', message)` gate rejects pure snake_case payloads. The finding `Clarify the snake_case request-id parser contract` covers this follow-up.

💡 In-scope improvements

These are lower-risk, not throwaway. Prefer fixing them in this PR when they are local to changed code; defer only with rationale or a linked follow-up.

PRA-2 Improvement — Clarify the snake_case request-id parser contract

  • Location: scripts/nemoclaw-start.sh:2665
  • Category: tests
  • Problem: `_structured_request_ids()` still collects both `requestId` and `request_id`, but `pairing_required_request_id()` first requires exactly one literal camelCase `requestId` occurrence in the combined output. A pure snake_case structured OpenClaw denial therefore fails closed, and the changed tests do not prove whether that is intentional.
  • Impact: Future maintainers may assume snake_case structured errors are supported when they are not; if OpenClaw changes this bootstrap denial to `request_id`, the initial CLI pairing deadlock can return without a targeted regression signal.
  • Suggested action: Either add focused fake-runtime coverage for a pairing-required JSON denial containing exactly one `request_id` approving the matching local `cli` request, or remove `request_id` from `_structured_request_ids()` so the parser surface matches the tested camelCase-only contract.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Read `scripts/nemoclaw-start.sh` around `_structured_request_ids()` and `pairing_required_request_id()`, then search `test/nemoclaw-start-auto-pair-bootstrap.test.ts` for `request_id`; current coverage exercises malformed and ambiguous camelCase request-id forms but not a pure snake_case structured denial.
  • Missing regression test: Add a behavior-specific test named like `approves a pairing-required JSON denial containing exactly one request_id for the matching local cli request`; if snake_case is unsupported, remove the parser branch and keep camelCase-only tests as the contract.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: `_structured_request_ids()` accepts `key in {'requestId', 'request_id'}`, while `pairing_required_request_id()` rejects unless `len(re.findall(r'\brequestId\b', message)) == 1`.
Simplification opportunities: 1 possible cut, net -1 lines possible

These are safe simplification checks only. Do not remove validation, security controls, data-loss prevention, or required tests.

  • PRA-2 shrink (scripts/nemoclaw-start.sh:2665): Remove the untested `request_id` key from `_structured_request_ids()` if snake_case denials are not intentionally supported.
    • Replacement: Keep the parser camelCase-only, matching the outer `requestId` occurrence gate and existing tests.
    • Net: -1 lines
    • Safety boundary: Do not weaken request-id length/charset validation, ambiguity rejection, local pending/identity checks, exact `operator.pairing` scope validation, or gateway-env stripping.
Test follow-ups to resolve or justify

If these cover changed behavior, prefer adding them in this PR; otherwise state why existing coverage is enough or link the follow-up.

  • PRA-T1 Runtime validation — Validate a fresh packaged sandbox with no prior OpenClaw device state where the first `openclaw devices list --json` is pairing-gated, and confirm the watcher approves only `operator.pairing` for the initial `cli` request.. The changed behavior is sandbox startup authentication/bootstrap code. The PR adds strong fake-runtime subprocess coverage and negative tests, but the linked issue also calls for packaged runtime validation of fresh onboarding, gated list approval, subsequent device listing, and a gateway-backed tool call without embedded fallback.
  • PRA-T2 Runtime validation — Validate that a subsequent packaged `openclaw devices list --json` succeeds after the watcher approval and reports no extra scopes on the paired `cli` device.. The changed behavior is sandbox startup authentication/bootstrap code. The PR adds strong fake-runtime subprocess coverage and negative tests, but the linked issue also calls for packaged runtime validation of fresh onboarding, gated list approval, subsequent device listing, and a gateway-backed tool call without embedded fallback.
  • PRA-T3 Runtime validation — Validate that a packaged Gateway/Nodes or equivalent tool call completes through the gateway after bootstrap and does not print or take the `EMBEDDED FALLBACK` path.. The changed behavior is sandbox startup authentication/bootstrap code. The PR adds strong fake-runtime subprocess coverage and negative tests, but the linked issue also calls for packaged runtime validation of fresh onboarding, gated list approval, subsequent device listing, and a gateway-backed tool call without embedded fallback.
  • PRA-T4 Runtime validation — Add or remove support for `request_id`: either test `approves a pairing-required JSON denial containing exactly one request_id for the matching local cli request`, or remove `request_id` from `_structured_request_ids()` so the parser contract is camelCase-only.. The changed behavior is sandbox startup authentication/bootstrap code. The PR adds strong fake-runtime subprocess coverage and negative tests, but the linked issue also calls for packaged runtime validation of fresh onboarding, gated list approval, subsequent device listing, and a gateway-backed tool call without embedded fallback.
  • PRA-T5 Clarify the snake_case request-id parser contract — Either add focused fake-runtime coverage for a pairing-required JSON denial containing exactly one `request_id` approving the matching local `cli` request, or remove `request_id` from `_structured_request_ids()` so the parser surface matches the tested camelCase-only contract.
  • PRA-T6 Acceptance clause — gateway-backed tools execute normally, and the agent does not fall back to embedded execution because of pairing. — add test evidence or identify existing coverage. The new fake-runtime test runs a fake `openclaw agent` after bootstrap, asserts the output does not contain `EMBEDDED FALLBACK`, and verifies `tool:file-write-ok`. This proves the scripted behavior under the local harness; real packaged runtime validation remains a test-depth follow-up rather than trusted diff evidence.
  • PRA-T7 Acceptance clause — Fresh onboarding with no prior device state. — add test evidence or identify existing coverage. The new test creates fresh temporary OpenClaw state with no prior paired device or device-auth file. It is fake-runtime coverage, not a packaged onboarding run in the diff.
  • PRA-T8 Acceptance clause — A real Gateway/Nodes or equivalent tool call completes through the gateway without embedded fallback. — add test evidence or identify existing coverage. The diff includes a fake `openclaw agent` no-embedded-fallback assertion. The linked issue asks for real packaged runtime validation; the PR body claims manual live validation, but PR text is untrusted evidence and no real packaged automated test is added in this diff.
Since last review details

Current findings, using the urgency labels above:

PRA-1 Resolve/justify — Source-of-truth review needed: scripts/nemoclaw-start.sh pairing-required request-id extraction

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Malformed, overlong, whitespace, and multiple camelCase request-id cases are covered; a pure `request_id` structured denial is not covered.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: The source comments claim structured JSON support, and `_structured_request_ids()` collects `request_id`, but the outer `re.findall(r'\brequestId\b', message)` gate rejects pure snake_case payloads. The finding `Clarify the snake_case request-id parser contract` covers this follow-up.

PRA-2 Improvement — Clarify the snake_case request-id parser contract

  • Location: scripts/nemoclaw-start.sh:2665
  • Category: tests
  • Problem: `_structured_request_ids()` still collects both `requestId` and `request_id`, but `pairing_required_request_id()` first requires exactly one literal camelCase `requestId` occurrence in the combined output. A pure snake_case structured OpenClaw denial therefore fails closed, and the changed tests do not prove whether that is intentional.
  • Impact: Future maintainers may assume snake_case structured errors are supported when they are not; if OpenClaw changes this bootstrap denial to `request_id`, the initial CLI pairing deadlock can return without a targeted regression signal.
  • Suggested action: Either add focused fake-runtime coverage for a pairing-required JSON denial containing exactly one `request_id` approving the matching local `cli` request, or remove `request_id` from `_structured_request_ids()` so the parser surface matches the tested camelCase-only contract.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Read `scripts/nemoclaw-start.sh` around `_structured_request_ids()` and `pairing_required_request_id()`, then search `test/nemoclaw-start-auto-pair-bootstrap.test.ts` for `request_id`; current coverage exercises malformed and ambiguous camelCase request-id forms but not a pure snake_case structured denial.
  • Missing regression test: Add a behavior-specific test named like `approves a pairing-required JSON denial containing exactly one request_id for the matching local cli request`; if snake_case is unsupported, remove the parser branch and keep camelCase-only tests as the contract.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: `_structured_request_ids()` accepts `key in {'requestId', 'request_id'}`, while `pairing_required_request_id()` rejects unless `len(re.findall(r'\brequestId\b', message)) == 1`.

Workflow run details

This is an automated, non-binding review; it still expects maintainers and agents to respond to each required or warning item. Treat suggestions as current-PR improvements when they touch changed code; defer only with maintainer rationale or a linked follow-up. A human maintainer must make the final merge decision.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
scripts/nemoclaw-start.sh (1)

2497-2498: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate scope allowlist source of truth.

INITIAL_PAIRING_ALLOWED_SCOPES re-declares the same set as ALLOWED_SCOPES in scripts/lib/openclaw_device_approval_policy.py (already loaded here via load_approval_policy). If the policy's allowlist changes later, this copy can silently drift and either over- or under-restrict the bootstrap seed.

♻️ Suggested fix: reuse the already-loaded policy constant
-approval_request_decision, gateway_approval_env = load_approval_policy(APPROVAL_POLICY_FILE)
+approval_request_decision, gateway_approval_env, policy_allowed_scopes = load_approval_policy(APPROVAL_POLICY_FILE)
...
-INITIAL_PAIRING_ALLOWED_SCOPES = {'operator.pairing', 'operator.read', 'operator.write'}
+INITIAL_PAIRING_ALLOWED_SCOPES = policy_allowed_scopes

(requires load_approval_policy to also return module.ALLOWED_SCOPES)

Also applies to: 2573-2585

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

In `@scripts/nemoclaw-start.sh` around lines 2497 - 2498,
`INITIAL_PAIRING_ALLOWED_SCOPES` is duplicating the allowlist already defined by
the loaded approval policy, so update the bootstrap logic in
`scripts/nemoclaw-start.sh` to reuse the policy’s `ALLOWED_SCOPES` instead of
hardcoding a second set. Extend `load_approval_policy` to return or expose
`module.ALLOWED_SCOPES`, then use that value where `INITIAL_PAIRING_SCOPES` and
`INITIAL_PAIRING_ALLOWED_SCOPES` are initialized so the bootstrap seed stays in
sync with `openclaw_device_approval_policy`.
test/nemoclaw-start-auto-pair-bootstrap.test.ts (1)

27-43: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated clock-mocking heredoc-rewrite logic.

This helper (including the exact _nemoclaw_test_clock/_nemoclaw_test_sleep injection block) duplicates the equivalent logic in test/nemoclaw-start.test.ts (autoPairPythonScript, lines 177-189). Consider extracting a shared test utility (e.g. under test/ helpers) that both files import, so the mocking behavior can't silently drift between the two suites.

🤖 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/nemoclaw-start-auto-pair-bootstrap.test.ts` around lines 27 - 43, The
auto-pair script rewrite logic in autoPairPythonScript duplicates the same
clock-mocking heredoc injection used in the other nemoclaw test suite, so
extract the shared replacement behavior into a common test helper and have both
autoPairPythonScript implementations call it. Keep the shared utility
responsible for the _nemoclaw_test_clock, _nemoclaw_test_time, and
_nemoclaw_test_sleep injection so the behavior stays consistent across both test
files.
🤖 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 `@scripts/nemoclaw-start.sh`:
- Around line 2590-2663: The initial CLI pairing flow in
seed_initial_cli_pairing_from_state updates pending.json, paired.json, and
device-auth.json independently, so the state can become inconsistent if another
writer races or the process crashes mid-write. Add a single guard around the
whole read/modify/write sequence in seed_initial_cli_pairing_from_state, or
re-check freshness before any write and abort if the files changed. Keep the
existing _write_json_atomic calls, but make them part of one protected
transaction so all three files stay in sync.
- Around line 2590-2663: Add bootstrap negative-path coverage for
seed_initial_cli_pairing_from_state by testing the failure branches that
currently return None or skip seeding: a persisted identity/public-key mismatch
in the device.json path, an already-paired device present in paired.json, and
the OPENCLAW_GATEWAY_TOKEN collision check after token generation. Use the
seed_initial_cli_pairing_from_state helper and the related
_identity_public_key/_same_initial_cli_requests flow so the tests directly
exercise the bootstrap logic rather than only the happy path.

In `@test/nemoclaw-start-auto-pair-bootstrap.test.ts`:
- Around line 14-18: The helper startScriptHeredoc currently introduces a new
conditional branch that triggers the guardrail; refactor it to avoid any if
statement while keeping the same behavior. Update the regex match handling in
startScriptHeredoc so it returns the captured heredoc content when present and
throws the same error when absent, but do so with a branchless expression
instead of control flow.

---

Nitpick comments:
In `@scripts/nemoclaw-start.sh`:
- Around line 2497-2498: `INITIAL_PAIRING_ALLOWED_SCOPES` is duplicating the
allowlist already defined by the loaded approval policy, so update the bootstrap
logic in `scripts/nemoclaw-start.sh` to reuse the policy’s `ALLOWED_SCOPES`
instead of hardcoding a second set. Extend `load_approval_policy` to return or
expose `module.ALLOWED_SCOPES`, then use that value where
`INITIAL_PAIRING_SCOPES` and `INITIAL_PAIRING_ALLOWED_SCOPES` are initialized so
the bootstrap seed stays in sync with `openclaw_device_approval_policy`.

In `@test/nemoclaw-start-auto-pair-bootstrap.test.ts`:
- Around line 27-43: The auto-pair script rewrite logic in autoPairPythonScript
duplicates the same clock-mocking heredoc injection used in the other nemoclaw
test suite, so extract the shared replacement behavior into a common test helper
and have both autoPairPythonScript implementations call it. Keep the shared
utility responsible for the _nemoclaw_test_clock, _nemoclaw_test_time, and
_nemoclaw_test_sleep injection so the behavior stays consistent across both test
files.
🪄 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: 0cf9e583-e3aa-4855-87be-9de2728ed236

📥 Commits

Reviewing files that changed from the base of the PR and between f0d2549 and 8b9c228.

📒 Files selected for processing (5)
  • ci/test-file-size-budget.json
  • scripts/nemoclaw-start.sh
  • src/lib/actions/sandbox/auto-pair-approval.test.ts
  • test/nemoclaw-start-auto-pair-bootstrap.test.ts
  • test/nemoclaw-start.test.ts

Comment thread scripts/nemoclaw-start.sh Resolved
Signed-off-by: Chengjie Wang <chengjiew@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

🤖 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/nemoclaw-start-auto-pair-bootstrap.test.ts`:
- Line 146: The test title in the root-level integration test should include the
local issue reference suffix to match the test naming guideline. Update the
`it(...)` description in `nemoclaw-start-auto-pair-bootstrap.test.ts` so it
remains behavior-oriented and ends with the `(`#6113`)` suffix, keeping the change
localized to the affected test case.
🪄 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: 20273553-bdcd-44ab-b3bf-8bb8a2501fb9

📥 Commits

Reviewing files that changed from the base of the PR and between 8b9c228 and 2c164d0.

📒 Files selected for processing (2)
  • scripts/nemoclaw-start.sh
  • test/nemoclaw-start-auto-pair-bootstrap.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/nemoclaw-start.sh

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
test/nemoclaw-start-auto-pair-bootstrap.test.ts (1)

171-261: 📐 Maintainability & Code Quality | 🔵 Trivial

Extract shared scaffolding to reduce duplication across the negative-path suites.

Each of these five blocks (rejected-pending fixtures, gated-list requestId errors, identity validation, malformed public key, and retry) repeats near-identical boilerplate: mkdtempSync, writing device.json/pending.json, generating the fake openclaw bash script, spawnSync, and the finally { fs.rmSync(...) } cleanup. Factoring this into one or two helpers (e.g. setupAutoPairFixture({ identity, pending, openclawScript }) and runAutoPairWatcher(src, tmpDir, env)) would cut this file's size substantially and make future fixture variants cheaper to add without copy/paste drift.

Also applies to: 263-355, 357-442, 444-512, 514-599

🤖 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/nemoclaw-start-auto-pair-bootstrap.test.ts` around lines 171 - 261, The
negative-path auto-pair tests duplicate the same fixture setup, fake openclaw
script creation, spawnSync invocation, and temp-dir cleanup across multiple
blocks. Extract the repeated scaffolding into shared helpers such as
setupAutoPairFixture and runAutoPairWatcher, then update the affected test cases
to call those helpers while keeping each scenario’s specific identity/pending
overrides and script behavior intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@test/nemoclaw-start-auto-pair-bootstrap.test.ts`:
- Around line 171-261: The negative-path auto-pair tests duplicate the same
fixture setup, fake openclaw script creation, spawnSync invocation, and temp-dir
cleanup across multiple blocks. Extract the repeated scaffolding into shared
helpers such as setupAutoPairFixture and runAutoPairWatcher, then update the
affected test cases to call those helpers while keeping each scenario’s specific
identity/pending overrides and script behavior intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 38807fd0-2190-4319-ad34-c87cc0d35503

📥 Commits

Reviewing files that changed from the base of the PR and between 8461b0b and 1465087.

📒 Files selected for processing (2)
  • scripts/nemoclaw-start.sh
  • test/nemoclaw-start-auto-pair-bootstrap.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/nemoclaw-start.sh

# Conflicts:
#	scripts/nemoclaw-start.sh
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

E2E Target Results — ✅ All requested jobs passed

Run: 28884729073
Workflow ref: fix/6113_cli-auto-pair
Requested targets: (default — all supported)
Requested jobs: issue-4462-scope-upgrade-approval
Summary: 1 passed, 0 failed, 0 cancelled, 0 skipped

Job Result
issue-4462-scope-upgrade-approval ✅ success

@wscurran wscurran added the area: cli Command line interface, flags, terminal UX, or output label Jul 7, 2026
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

E2E Target Results — ❌ Some jobs failed

Run: 28896008881
Workflow ref: fix/6113_cli-auto-pair
Requested targets: (default — all supported)
Requested jobs: full-e2e
Summary: 0 passed, 1 failed, 0 cancelled, 0 skipped

Job Result
full-e2e ❌ failure

Failed jobs: full-e2e. Check run artifacts for logs.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

E2E Target Results — ✅ All requested jobs passed

Run: 28896000850
Workflow ref: fix/6113_cli-auto-pair
Requested targets: (default — all supported)
Requested jobs: sessions-agents-cli,issue-4462-scope-upgrade-approval
Summary: 2 passed, 0 failed, 0 cancelled, 0 skipped

Job Result
issue-4462-scope-upgrade-approval ✅ success
sessions-agents-cli ✅ success

@prekshivyas
prekshivyas requested a review from cv July 7, 2026 20:33

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

Re-review of exact head 818b7c1: the fast-to-slow polling transition and embedded requestId validation are fixed, and standard attached CI is green.

Changes are still required for the remaining hard gates:

  • 10 of 12 commits are unsigned/unverified. chengjiew is not covered by the maintainer waiver. Please rebuild this as compliant Verified history; if the published branch cannot be force-pushed, use a fresh branch and PR.
  • The exact packaged first-run gated-list bootstrap is still not proven with real OpenClaw: the deterministic positive test uses a fake openclaw shell fixture. The required cloud-onboard job was not dispatched.
  • Exact-head full-e2e failed twice at 188s and 183s against the 180s cold-onboard budget. Please provide a passing run or an explicit maintainer disposition of that repeated failure.

The ubuntu-repo-cloud-openclaw, sessions-agents-cli, and issue-4462-scope-upgrade-approval runs passed; CodeRabbit threads are resolved.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

E2E Target Results — ✅ All requested jobs passed

Run: 28898782251
Workflow ref: fix/6113_cli-auto-pair
Requested targets: (default — all supported)
Requested jobs: full-e2e
Summary: 1 passed, 0 failed, 0 cancelled, 0 skipped

Job Result
full-e2e ✅ success

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

E2E Target Results — ✅ All requested jobs passed

Run: 28898779642
Workflow ref: fix/6113_cli-auto-pair
Requested targets: (default — all supported)
Requested jobs: cloud-onboard
Summary: 1 passed, 0 failed, 0 cancelled, 0 skipped

Job Result
cloud-onboard ✅ success

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

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

E2E Target Results — ❌ Some jobs failed

Run: 28899404186
Workflow ref: fix/6113_cli-auto-pair
Requested targets: (default — all supported)
Requested jobs: full-e2e
Summary: 0 passed, 1 failed, 0 cancelled, 0 skipped

Job Result
full-e2e ❌ failure

Failed jobs: full-e2e. Check run artifacts for logs.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

E2E Target Results — ❌ Some jobs failed

Run: 28899402431
Workflow ref: fix/6113_cli-auto-pair
Requested targets: (default — all supported)
Requested jobs: cloud-onboard
Summary: 0 passed, 1 failed, 0 cancelled, 0 skipped

Job Result
cloud-onboard ❌ failure

Failed jobs: cloud-onboard. Check run artifacts for logs.

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

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

E2E Target Results — ✅ All requested jobs passed

Run: 28899786080
Workflow ref: fix/6113_cli-auto-pair
Requested targets: (default — all supported)
Requested jobs: full-e2e
Summary: 1 passed, 0 failed, 0 cancelled, 0 skipped

Job Result
full-e2e ✅ success

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

E2E Target Results — ❌ Some jobs failed

Run: 28899784129
Workflow ref: fix/6113_cli-auto-pair
Requested targets: (default — all supported)
Requested jobs: cloud-onboard
Summary: 0 passed, 1 failed, 0 cancelled, 0 skipped

Job Result
cloud-onboard ❌ failure

Failed jobs: cloud-onboard. Check run artifacts for logs.

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

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

E2E Target Results — ❌ Some jobs failed

Run: 28900218936
Workflow ref: fix/6113_cli-auto-pair
Requested targets: (default — all supported)
Requested jobs: full-e2e
Summary: 0 passed, 1 failed, 0 cancelled, 0 skipped

Job Result
full-e2e ❌ failure

Failed jobs: full-e2e. Check run artifacts for logs.

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

Re-reviewing exact head 08b575c99e0ea2b0e65d7eb490442e13030df1b7.

The production issues from my prior review are resolved: the fast-to-slow transition runs before failure continuations, and embedded request IDs fail closed unless they match the map key. The three commits since 818b7c18 are Verified and test-only; the packaged cloud-onboard proof now forces a fresh gated rebootstrap, exact operator.pairing-only scopes, a successful device list, and a real gateway agent turn without fallback.

The hard contributor-compliance blocker remains: 10 of the 15 commits are GitHub unsigned/unverified. They are the original chengjiew commits, and that account is not covered by the maintainer waiver (which applies only to ericksoa and jyaunches). Please replace them with a clean Verified history; if force-push is unavailable, use a fresh branch and PR.

After the history is compliant, exact-head gates still need to settle. The E2E advisor requires cloud-onboard, issue-4462-scope-upgrade-approval, and device-auth-health. cloud-onboard and optional full-e2e are running, but the other two required exact-head targets have not been dispatched.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

E2E Target Results — ❌ Some jobs failed

Run: 28900217228
Workflow ref: fix/6113_cli-auto-pair
Requested targets: (default — all supported)
Requested jobs: cloud-onboard
Summary: 0 passed, 1 failed, 0 cancelled, 0 skipped

Job Result
cloud-onboard ❌ failure

Failed jobs: cloud-onboard. Check run artifacts for logs.

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

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

E2E Target Results — ❌ Some jobs failed

Run: 28900735091
Workflow ref: fix/6113_cli-auto-pair
Requested targets: (default — all supported)
Requested jobs: cloud-onboard
Summary: 0 passed, 1 failed, 0 cancelled, 0 skipped

Job Result
cloud-onboard ❌ failure

Failed jobs: cloud-onboard. Check run artifacts for logs.

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

Re-reviewing exact head 016cde3 with the updated maintainer policy.

Contributor compliance now passes: chengjiew is a team member explicitly exempted from commit signing, all six non-waived commits are GitHub Verified, the PR-body DCO declaration is present, and exact-head DCO CI passes. All attached CI is green and the branch is conflict-free.

I also adjudicated Nemotron PRA-1 as nonblocking. The gated first-pair path is intentionally narrower than the shared normal pending-list/scope-upgrade policy: it validates the same local identity and accepts only exact clientId/clientMode cli, operator role, and operator.pairing scope. The dependency review and inline source-boundary comment document that split, and the parameterized rejection test exercises the non-cli guard. Broadening this first-pair exception to every shared-policy client would weaken the fail-closed boundary. The primary exact-head advisor recommends merge_as_is.

The remaining blockers are live evidence:

  • Required cloud-onboard failed on this exact head: https://github.com/NVIDIA/NemoClaw/actions/runs/28900735091/job/85736663178
    It exited 22 with INITIAL_CLI_PAIRING_MARKER_MISSING. The watcher log showed only the normal approval/convergence path, so the new test still did not prove the gated-list fallback this PR adds.
  • The exact-head E2E advisor also requires issue-4462-scope-upgrade-approval; no exact-head run exists yet.

Please make the packaged proof reliably enter and observe the gated bootstrap, then pass both required exact-head targets. Commit signing is no longer a blocker.

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

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

E2E Target Results — ❌ Some jobs failed

Run: 28901316811
Workflow ref: fix/6113_cli-auto-pair
Requested targets: (default — all supported)
Requested jobs: full-e2e
Summary: 0 passed, 1 failed, 0 cancelled, 0 skipped

Job Result
full-e2e ❌ failure

Failed jobs: full-e2e. Check run artifacts for logs.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

E2E Target Results — ✅ All requested jobs passed

Run: 28901315087
Workflow ref: fix/6113_cli-auto-pair
Requested targets: (default — all supported)
Requested jobs: cloud-onboard
Summary: 1 passed, 0 failed, 0 cancelled, 0 skipped

Job Result
cloud-onboard ✅ success

@prekshivyas

Copy link
Copy Markdown
Collaborator

Exact-head follow-up for the remaining validation gates on cda99e2c42a6:

  • ✅ cloud-onboard 28901315087 passed on the exact head. It exercised the public installer at this SHA with packaged OpenClaw 2026.6.10 and completed the existing cloud checks.
  • I also instrumented three cloud runs to try to prove the specific [All Platforms][Agent&Skills] gateway auto-pair fails — agent tools rejected with 'device is not approved yet' #6113 gated-list branch. The real packaged watcher consistently logged the normal path (approved request=... client=cli mode=cli), including after canonical device removal and local credential reset; it never logged approved initial CLI pairing request=.... I reverted that nonrepresentative probe rather than weakening it to accept the wrong path. The final tree is byte-for-byte identical to reviewed head 818b7c18a93f (tree 3aa1def389683075fe0be4502988f1eaf3cf0c75). This runner can prove packaged health, but cannot reproduce the platform-dependent gated-list branch; that exact proof still needs an affected DGX/Brev environment or maintainer disposition.
  • ✅ full-e2e 28898782251 passed on that identical tree at 154144 ms total (155s), with real OpenClaw first turn, no BuildKit fallback, and 30s max silence.
  • ❌ exact-head full-e2e 28901316811 completed the functional first-turn path but measured 183390 ms (184s), 3390 ms over the 180s budget. This is the same narrow runner-timing pattern as the earlier 188s/183s/182s attempts, alongside the 155s pass. Please disposition whether the tree-identical passing run is sufficient or whether the budget needs separate performance ownership.

All commits I authored during this investigation are DCO-signed and GitHub Verified. The previously reported legacy-history verification gate remains separate.

@cv
cv merged commit c33a686 into main Jul 7, 2026
187 of 188 checks passed
@cv
cv deleted the fix/6113_cli-auto-pair branch July 7, 2026 22:03
@apurvvkumaria apurvvkumaria mentioned this pull request Jul 8, 2026
9 of 21 tasks
apurvvkumaria added a commit that referenced this pull request Jul 8, 2026
<!-- markdownlint-disable MD041 -->
## Summary

Prepares the user-facing documentation for NemoClaw v0.0.76 and closes
the release-prep documentation gate. It adds the release highlights,
documents the arm64 Local NIM warning and expanded image cleanup
behavior, and fixes agent-specific command headings in generated guides.

## Changes

- Add the v0.0.76 release-notes section and move the shared-gateway
route containment entry out of the v0.0.74 history where it was
incorrectly placed.
- Document the advisory Linux arm64 Local NIM manifest warning in the
canonical platform matrix and local-inference guidance.
- Document that `gc` scans both gateway-built and locally prebuilt
sandbox image repositories.
- Keep OpenClaw and Hermes session headings out of the generated Deep
Agents command guide.
- Add a focused variant regression test for the agent-specific session
headings.

### Source summary

| Merged sources | Documentation coverage |
| --- | --- |
| [#6414](#6414),
[#6418](#6418),
[#6416](#6416),
[#6344](#6344) | v0.0.76 release
notes and the Deep Agents quickstart/inference routes |
| [#6340](#6340) | v0.0.76
release notes and existing Deep Agents observability guidance |
| [#6338](#6338),
[#6378](#6378),
[#6297](#6297) | v0.0.76 release
notes and existing inference/troubleshooting guidance |
| [#6362](#6362) | v0.0.76
release notes and existing lifecycle, command, and credential guidance |
| [#6330](#6330),
[#6307](#6307),
[#6008](#6008) | v0.0.76 release
notes and existing security, troubleshooting, and command guidance |
| [#6382](#6382) | v0.0.76
release notes and existing MCP/command guidance |
| [#6326](#6326),
[#5868](#5868),
[#5539](#5539) | v0.0.76 release
notes, platform matrix, inference options, and local-inference guidance
|
| [#6396](#6396),
[#6390](#6390),
[#6007](#6007) | v0.0.76 release
notes and existing messaging guidance |
| [#5388](#5388),
[#6249](#6249),
[#6303](#6303),
[#6306](#6306) | v0.0.76 release
notes and command/lifecycle guidance |

## Type of Change

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

## Quality Gates

- [x] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [ ] Tests not applicable — justification:
- [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:

## Verification

- [x] PR description includes the DCO sign-off declaration and every
commit appears as `Verified` in GitHub
- [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or
`npm run check:diff` passed when hooks were skipped or unavailable
- [x] Targeted behavior tests pass for the current change set, or tests
are marked not applicable above — `npx vitest run --project integration
test/generate-platform-docs.test.ts test/agent-variant-docs.test.ts
test/sync-agent-variant-docs.test.ts` (3 files, 29 tests passed)
- [ ] Applicable broad gate passed — `npm test` for broad
runtime/test-harness changes; `npm run check` for repo-wide
validation/coverage changes — command/result:
- [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 pre-existing Fern 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: Apurv Kumaria <akumaria@nvidia.com>


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

## Summary by CodeRabbit

* **Documentation**
* Added v0.0.76 release notes content, and removed an older conflicting
bullet from the surrounding release history.
* Expanded Local NVIDIA NIM guidance across inference/provider docs,
including an advisory for Linux arm64 DGX Spark/DGX Station hosts when a
matching `linux/arm64` image manifest is unavailable.
* Updated the command reference for correct session-section rendering
and clarified `gc` image cleanup sources.
* **Tests**
* Added coverage ensuring Deep Agents omits sessions headings while
Hermes includes them.
* **CI**
* Refreshed Local NVIDIA NIM provider notes used in the platform matrix.

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

---------

Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Hadar301 pushed a commit to Hadar301/NemoClaw-OpenShift that referenced this pull request Jul 12, 2026
## Summary
- bootstrap the initial local CLI pairing state when `openclaw devices
list --json` is itself blocked by gateway pairing
- keep the bootstrap narrowly scoped to the persisted same-device `cli`
request and grant only `operator.pairing`
- add NVIDIA#6113 regression coverage in a split test file and update existing
auto-pair expectations for the `cli` client id

Fixes NVIDIA#6113

## Test Plan
- [x] `npx vitest run --project integration
test/nemoclaw-start-auto-pair-bootstrap.test.ts
test/nemoclaw-start.test.ts -t "seeds an initial CLI pairing
request|approves only known client identities|approves concurrent late
CLI"`
- [x] `npx vitest run --project cli
src/lib/actions/sandbox/auto-pair-approval.test.ts -t "approves
allowlisted upgrades"`
- [x] `npx vitest run --project integration
test/openclaw-device-approval-policy.test.ts`
- [x] `npm run test-size:check`
- [x] `git diff --check`
- [x] `npm run check:diff`
- [x] Linux live A/B on `aits-log-worker-6` / `ncq2621-latest-main`:
before the new watcher, `openclaw devices list --json` returned `pairing
required`; after running the new watcher against the same pending state,
`devices list` returned `pending: []` and a paired `cli` device with
`operator.pairing` only

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

* **New Features**
* Added deterministic “initial CLI pairing” recovery when device listing
indicates pairing is required.
* Extended auto-pairing to support an additional allowlisted CLI client
identity.
* **Bug Fixes**
* Improved auto-pair approval failure logging with concise, formatted
last-error output (and safer logging conditions).
* **Tests**
* Added/expanded coverage for initial CLI auto-pair bootstrap, including
multiple validation and recovery scenarios.
* Updated auto-pairing expectations and sandbox approval-pass assertions
for the new CLI client.
* **Chores**
  * Adjusted CI test file size budget for a specific test file.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: Chengjie Wang <chengjiew@nvidia.com>

---------

Signed-off-by: Chengjie Wang <chengjiew@nvidia.com>
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Co-authored-by: Prekshi Vyas <prekshiv@nvidia.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Hadar301 pushed a commit to Hadar301/NemoClaw-OpenShift that referenced this pull request Jul 12, 2026
<!-- markdownlint-disable MD041 -->
## Summary

Prepares the user-facing documentation for NemoClaw v0.0.76 and closes
the release-prep documentation gate. It adds the release highlights,
documents the arm64 Local NIM warning and expanded image cleanup
behavior, and fixes agent-specific command headings in generated guides.

## Changes

- Add the v0.0.76 release-notes section and move the shared-gateway
route containment entry out of the v0.0.74 history where it was
incorrectly placed.
- Document the advisory Linux arm64 Local NIM manifest warning in the
canonical platform matrix and local-inference guidance.
- Document that `gc` scans both gateway-built and locally prebuilt
sandbox image repositories.
- Keep OpenClaw and Hermes session headings out of the generated Deep
Agents command guide.
- Add a focused variant regression test for the agent-specific session
headings.

### Source summary

| Merged sources | Documentation coverage |
| --- | --- |
| [NVIDIA#6414](NVIDIA#6414),
[NVIDIA#6418](NVIDIA#6418),
[NVIDIA#6416](NVIDIA#6416),
[NVIDIA#6344](NVIDIA#6344) | v0.0.76 release
notes and the Deep Agents quickstart/inference routes |
| [NVIDIA#6340](NVIDIA#6340) | v0.0.76
release notes and existing Deep Agents observability guidance |
| [NVIDIA#6338](NVIDIA#6338),
[NVIDIA#6378](NVIDIA#6378),
[NVIDIA#6297](NVIDIA#6297) | v0.0.76 release
notes and existing inference/troubleshooting guidance |
| [NVIDIA#6362](NVIDIA#6362) | v0.0.76
release notes and existing lifecycle, command, and credential guidance |
| [NVIDIA#6330](NVIDIA#6330),
[NVIDIA#6307](NVIDIA#6307),
[NVIDIA#6008](NVIDIA#6008) | v0.0.76 release
notes and existing security, troubleshooting, and command guidance |
| [NVIDIA#6382](NVIDIA#6382) | v0.0.76
release notes and existing MCP/command guidance |
| [NVIDIA#6326](NVIDIA#6326),
[NVIDIA#5868](NVIDIA#5868),
[NVIDIA#5539](NVIDIA#5539) | v0.0.76 release
notes, platform matrix, inference options, and local-inference guidance
|
| [NVIDIA#6396](NVIDIA#6396),
[NVIDIA#6390](NVIDIA#6390),
[NVIDIA#6007](NVIDIA#6007) | v0.0.76 release
notes and existing messaging guidance |
| [NVIDIA#5388](NVIDIA#5388),
[NVIDIA#6249](NVIDIA#6249),
[NVIDIA#6303](NVIDIA#6303),
[NVIDIA#6306](NVIDIA#6306) | v0.0.76 release
notes and command/lifecycle guidance |

## Type of Change

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

## Quality Gates

- [x] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [ ] Tests not applicable — justification:
- [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:

## Verification

- [x] PR description includes the DCO sign-off declaration and every
commit appears as `Verified` in GitHub
- [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or
`npm run check:diff` passed when hooks were skipped or unavailable
- [x] Targeted behavior tests pass for the current change set, or tests
are marked not applicable above — `npx vitest run --project integration
test/generate-platform-docs.test.ts test/agent-variant-docs.test.ts
test/sync-agent-variant-docs.test.ts` (3 files, 29 tests passed)
- [ ] Applicable broad gate passed — `npm test` for broad
runtime/test-harness changes; `npm run check` for repo-wide
validation/coverage changes — command/result:
- [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 pre-existing Fern 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: Apurv Kumaria <akumaria@nvidia.com>


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

## Summary by CodeRabbit

* **Documentation**
* Added v0.0.76 release notes content, and removed an older conflicting
bullet from the surrounding release history.
* Expanded Local NVIDIA NIM guidance across inference/provider docs,
including an advisory for Linux arm64 DGX Spark/DGX Station hosts when a
matching `linux/arm64` image manifest is unavailable.
* Updated the command reference for correct session-section rendering
and clarified `gc` image cleanup sources.
* **Tests**
* Added coverage ensuring Deep Agents omits sessions headings while
Hermes includes them.
* **CI**
* Refreshed Local NVIDIA NIM provider notes used in the platform matrix.

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

---------

Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: cli Command line interface, flags, terminal UX, or output bug-fix PR fixes a bug or regression integration: openclaw OpenClaw integration behavior platform: dgx-station Affects DGX Station hardware or workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[All Platforms][Agent&Skills] gateway auto-pair fails — agent tools rejected with 'device is not approved yet'

4 participants