Skip to content

fix(onboard): preserve tier-default brave/tavily on re-onboard reuse - #6856

Merged
jyaunches merged 5 commits into
mainfrom
fix/6844-brave-preset-reuse-signed
Jul 14, 2026
Merged

fix(onboard): preserve tier-default brave/tavily on re-onboard reuse#6856
jyaunches merged 5 commits into
mainfrom
fix/6844-brave-preset-reuse-signed

Conversation

@yanyunl1991

@yanyunl1991 yanyunl1991 commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Summary

On the re-onboard reuse path, the Balanced-tier default preset brave (Brave Search API access) was silently dropped even when the policy tier (Balanced) and the web-search choice (No web search) were unchanged. The other four Balanced defaults (npm, pypi, huggingface, brew) persisted — only brave was narrowed out of the reapplied set, removing its egress (api.search.brave.com).

Fixes #6844.

Root cause

isStaleBuiltinWebSearchPolicyPreset treats brave/tavily as a stale web-search leftover whenever no matching web-search provider is configured. It did not account for the fact that the same preset is also a default egress preset of the tier being applied. On reuse, preparePolicyPresetResumeSelection pruned brave under this rule, flipping recordedPolicyPresetsNeedReconcile and driving a reapply whose set omitted brave.

Fix

A preset that is a default of the applied tier is a tier egress default, not a stale web-search leftover. The exemption is expressed through the existing classifyPresetProvenance classifier (source: "tier"), so pruning and the policy-list display share a single notion of why a preset is present rather than a second, parallel one. It is threaded through both reuse/resume prune sites (preparePolicyPresetResumeSelection and createUnavailablePolicyPresetPruner).

Scoped to the reuse/resume path. Fresh-onboard suggestion behaviour is intentionally unchanged: a fresh suggested onboard omits brave egress unless web-search is chosen (conservative egress by design — see onboard-policy-suggestions.test.ts), which is not part of this reuse regression.

Design annotation

  • invalidState: on reuse, an already-applied tier-default egress preset (brave/tavily on Balanced/Open) is pruned as a stale web-search leftover and its egress narrowed, on an unchanged tier.
  • sourceBoundary: isStaleBuiltinWebSearchPolicyPreset — the single predicate all reuse/resume prune sites share.
  • whyNotSourceFix: preset provenance is intentionally not persisted (preset-provenance.ts is display-only by design); we reuse its classifier rather than reverse that decision with a persisted-state refactor.
  • regressionTest: policy-resume-selection.test.ts — preserve on Balanced, still-prune on Restricted, keep-with-switched-provider, prune-non-tier-default.
  • removalCondition: if preset provenance ever becomes persisted per-preset, replace the current-tier inference with the stored source.

Behaviour preserved (boundaries)

  • Restricted tier lists no brave/tavily default → a genuinely stale preset there is still pruned.
  • A non-tier-default web-search preset (e.g. tavily on Balanced with no matching provider) is still pruned — the exemption is scoped to real tier defaults, not "any brave/tavily".
  • Provider switch (brave → tavily) still replaces the stale provider and adds the newly active one.
  • Unknown / non-canonical recorded tier fails safe (getTier → null → not "tier" → not exempt → prior behaviour), so the authoritative-rebuild-tier-pending case does not throw.

Testing

  • policy-resume-selection.test.ts covers the full contract above; the broader onboard policy/tier/preset suites pass (213 tests across 15 files); build:cli + typecheck green.
  • End-to-end on both arches, on main, onboarding a local Ollama sandbox (loopback route, so onboarding is not blocked by the endpoint SSRF preflight):
    • x86_64 (our Ubuntu 24.04 no-GPU test host) and aarch64 (our DGX Spark GB10 GPU test host — matching the reporter's DGX Station architecture).
    • Before the fix: reuse reapply = npm, pypi, huggingface, brewRemoved preset: brave → brave inactive.
    • After the fix: reuse reapply = npm, pypi, huggingface, brew, brave → brave stays active ([from balanced tier]), no egress narrowing.

Signed-off-by: Yanyun Liao yanyunl@nvidia.com

Summary by CodeRabbit

  • Bug Fixes
    • Preserved built-in web-search and egress presets based on the recorded tier during policy resume and reconciliation.
    • Prevented tier-default presets from being incorrectly pruned when resuming, including when web search is disabled or uses a different provider.
    • Ensured reconciliation still removes unavailable, non-default presets that don’t match the active tier’s defaults.
  • Tests
    • Added coverage for tier-default preservation across resume selection and reapply behavior.

On the re-onboard reuse path, a Balanced-tier default preset that doubles
as a built-in web-search provider preset (`brave`, `tavily`) was silently
dropped even when the policy tier and web-search choice were unchanged. The
other tier defaults (`npm`, `pypi`, `huggingface`, `brew`) persisted, so
only the search-provider egress (e.g. `api.search.brave.com`) was narrowed
out of the reapplied set.

Root cause: `isStaleBuiltinWebSearchPolicyPreset` treats `brave`/`tavily` as
a stale web-search leftover whenever no matching web-search provider is
configured, without distinguishing the case where the same preset is a
default egress preset of the tier being applied. `preparePolicyPresetResume-
Selection` pruned it, which forced a reconcile and a reapply that dropped it.

Fix: add a `tierDefaultPresetNames` exemption to the staleness predicate and
thread the recorded tier's defaults through the resume-selection prune and
the reuse reapply prune. A preset that is a default of the applied tier is a
tier egress default, not a stale web-search leftover, so it is preserved
regardless of the web-search provider choice. The Restricted tier lists no
such default, so a genuinely stale `brave`/`tavily` there still prunes, and
provider-switch replacement (brave -> tavily) still adds the active provider.

Fixes #6844

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Yanyun Liao <yanyunl@nvidia.com>
@coderabbitai

coderabbitai Bot commented Jul 14, 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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 3ec84c0e-2b40-4c29-b97a-1d71002d7292

📥 Commits

Reviewing files that changed from the base of the PR and between 4e1dac1 and 3aa85e1.

📒 Files selected for processing (1)
  • test/policy-tiers-onboard.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/policy-tiers-onboard.test.ts

📝 Walkthrough

Walkthrough

Onboarding resume now passes tier and agent context into built-in web-search preset reconciliation. Tier-provided defaults are preserved, while unrelated presets remain eligible for pruning. Resume and reapply tests cover balanced, restricted, and alternate-provider scenarios.

Changes

Tier-default preset preservation

Layer / File(s) Summary
Tier-aware preset reconciliation
src/lib/onboard/policy-preset-reconciliation.ts
Staleness checks use tier provenance and agent context to preserve tier-provided built-in web-search presets; unavailable-preset pruning accepts and forwards the tier name.
Tier-aware resume selection
src/lib/onboard/policy-resume-selection.ts, src/lib/onboard/policy-resume-selection.test.ts
Resume selection evaluates web-search presets with tier and agent context, with coverage for balanced defaults, restricted tiers, alternate providers, and unrelated presets.
Recorded-tier pruning
src/lib/onboard/policy-selection.ts, test/policy-tiers-onboard.test.ts
The resume/reapply path passes the recorded tier name to unavailable-preset pruning, with coverage confirming Balanced-tier defaults remain selected.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Onboarding
  participant ResumeSelection
  participant PresetReconciliation
  participant PolicySync
  Onboarding->>ResumeSelection: prepare resume preset selection with tier and agent
  ResumeSelection->>PresetReconciliation: evaluate built-in web-search preset provenance
  PresetReconciliation-->>ResumeSelection: preserve tier defaults or mark stale
  Onboarding->>PolicySync: reapply selected presets with recorded tier
  PolicySync-->>Onboarding: retain tier defaults and prune stale presets
Loading

Possibly related PRs

  • NVIDIA/NemoClaw#6855: Updates the same preset reconciliation and resume paths to preserve tier-default web-search presets.
  • NVIDIA/NemoClaw#1997: Also modifies onboarding resume/reapply handling so previously selected policy presets are retained.

Suggested reviewers: prekshivyas, 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 summarizes the onboard reuse fix and the tier-default preset preservation behavior.
Linked Issues check ✅ Passed The changes preserve tier-default web-search presets on reuse and add tests covering the reported Balanced-tier brave regression in #6844.
Out of Scope Changes check ✅ Passed The added provenance-based pruning logic and tests stay focused on reuse-path preset reconciliation 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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/6844-brave-preset-reuse-signed

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

@github-code-quality

github-code-quality Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage remains at 96%, unchanged from the main branch.

TypeScript / code-coverage/cli

The overall coverage in the fix/6844-brave-prese... branch remains at 79%, unchanged from the main branch.

Show a code coverage summary of the most impacted files.
File main 64140e3 fix/6844-brave-prese... 3aa85e1 +/-
src/lib/hermes-dashboard.ts 95% 63% -32%
src/lib/core/pr...mpt-activity.ts 92% 67% -25%
src/lib/onboard...licy-presets.ts 100% 81% -19%
src/lib/messagi...onfig-parser.ts 100% 90% -10%
src/lib/messagi...l-validation.ts 77% 70% -7%
src/lib/adapters/http/probe.ts 97% 94% -3%
src/lib/adapter.../docker/pull.ts 86% 83% -3%
src/lib/onboard...eway-process.ts 93% 90% -3%
src/lib/actions...e-validation.ts 83% 81% -2%
src/lib/policy/tiers.ts 80% 96% +16%

Updated July 14, 2026 18:06 UTC
Code Coverage is in Public Preview. Learn more and provide us with your feedback.

@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — Informational

Advisor assessment: Informational / high confidence
Next action: No advisor follow-up needed.
Findings: 0 blockers · 0 warnings · 0 suggestions
Status: No actionable findings remain in the canonical review ledger.

Model lanes

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

Nemotron output stays in workflow artifacts and does not change the assessment above.

E2E guidance

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

Recommended E2E: onboard-repair, onboard-resume, cloud-onboard

1 optional E2E recommendation
  • network-policy

Workflow run details

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

yanyunl1991 and others added 2 commits July 14, 2026 19:09
…tiers

resolveTierPresets throws on an unknown tier, and the recorded tier on the
reuse/resume path can be a non-canonical value (e.g. a display label or an
authoritative rebuild tier not yet registered). Guard both new tier-default
lookups on getTier (which returns null for unknown tiers) so a non-tier reuse
path keeps its prior behavior instead of throwing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Yanyun Liao <yanyunl@nvidia.com>
… classifier

Replace the parallel `tierDefaultPresetNames` set threaded through the reuse
prune sites with the existing `classifyPresetProvenance` classifier: a preset
whose provenance is `source: "tier"` (a default of the applied tier) is not a
stale web-search leftover. Pruning and the policy-list display now share one
notion of why a preset is present, instead of a second, parallel one.

- Drops the injected `tiers` dep from preparePolicyPresetResumeSelection and the
  getTier/resolveTierPresets guard in setupPoliciesWithSelection; the classifier
  fails safe on an unknown/non-canonical tier (getTier -> null -> not "tier").
- Exemption stays scoped exactly to the applied tier: Restricted lists no such
  default, so a genuinely stale brave/tavily there is still pruned. Added a
  boundary test that a non-tier-default tavily on Balanced is still pruned.
- Fresh suggested onboard is intentionally unchanged: omitting brave egress
  without web-search is conservative-egress by design, not part of this bug.

Design annotation:
- invalidState: on reuse, an already-applied tier-default egress preset
  (brave/tavily on Balanced/Open) is pruned as a stale web-search leftover and
  its egress narrowed, on an unchanged tier.
- sourceBoundary: isStaleBuiltinWebSearchPolicyPreset, the single predicate all
  reuse/resume prune sites share.
- whyNotSourceFix: preset provenance is intentionally not persisted
  (preset-provenance.ts is display-only by design); reuse its classifier rather
  than reverse that decision.
- regressionTest: policy-resume-selection.test.ts — preserve on Balanced,
  still-prune on Restricted, keep-with-switched-provider, prune-non-tier-default.
- removalCondition: if preset provenance becomes persisted per-preset, replace
  the current-tier inference with the stored source.

Verified end-to-end on our DGX aarch64 test host (GPU) against a local Ollama
sandbox on main: reuse re-onboard preserves brave; the x86_64 path is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Yanyun Liao <yanyunl@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.

Caution

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

⚠️ Outside diff range comments (1)
src/lib/onboard/policy-selection.ts (1)

302-302: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Pass recordedTierName into the remaining stale-pruning checks

src/lib/onboard/policy-selection.ts:83, 138-152, 203-205 still prunes without the active tier. appliedForPreservation runs before recordedTierName is read, computeSetupPresetSuggestions() still omits tierName in its stale-web-search filter, and the non-authoritative pruneUnavailablePresets(chosen, ...) call does too. That can still strip a recorded Balanced brave before selection.

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

In `@src/lib/onboard/policy-selection.ts` at line 302, Update the stale-pruning
flow in policy selection to consistently pass the active recordedTierName: read
it before constructing appliedForPreservation, provide it to
pruneUnavailablePresets(applied), include tierName in
computeSetupPresetSuggestions()’s stale-web-search filtering, and pass it to the
non-authoritative pruneUnavailablePresets(chosen, ...) call. Preserve recorded
Balanced brave entries during selection.

Source: Path instructions

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

Outside diff comments:
In `@src/lib/onboard/policy-selection.ts`:
- Line 302: Update the stale-pruning flow in policy selection to consistently
pass the active recordedTierName: read it before constructing
appliedForPreservation, provide it to pruneUnavailablePresets(applied), include
tierName in computeSetupPresetSuggestions()’s stale-web-search filtering, and
pass it to the non-authoritative pruneUnavailablePresets(chosen, ...) call.
Preserve recorded Balanced brave entries during selection.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 78877dcc-335e-4346-9f0b-f507dddcbc05

📥 Commits

Reviewing files that changed from the base of the PR and between 2c00468 and 135f56b.

📒 Files selected for processing (4)
  • src/lib/onboard/policy-preset-reconciliation.ts
  • src/lib/onboard/policy-resume-selection.test.ts
  • src/lib/onboard/policy-resume-selection.ts
  • src/lib/onboard/policy-selection.ts

@wscurran wscurran added area: onboarding Onboarding FSM, provider setup, sandbox launch, or first-run flow area: policy Network policy, egress rules, presets, or sandbox policy bug-fix PR fixes a bug or regression labels Jul 14, 2026
Signed-off-by: Charan Jagwani <cjagwani@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/policy-tiers-onboard.test.ts`:
- Around line 487-491: Update the runPolicySetup fixture in the affected test to
explicitly set tierName to a valid non-Balanced tier such as "restricted", while
preserving recordedPolicyTier as "balanced".
🪄 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: 4c99d9d4-7ba4-4440-aae2-a984903f12c4

📥 Commits

Reviewing files that changed from the base of the PR and between 135f56b and 4e1dac1.

📒 Files selected for processing (1)
  • test/policy-tiers-onboard.test.ts

Comment thread test/policy-tiers-onboard.test.ts
Signed-off-by: Charan Jagwani <cjagwani@nvidia.com>
@jyaunches jyaunches assigned jyaunches and unassigned cjagwani Jul 14, 2026
@jyaunches
jyaunches merged commit 3461d71 into main Jul 14, 2026
81 checks passed
@jyaunches
jyaunches deleted the fix/6844-brave-preset-reuse-signed branch July 14, 2026 18:55
jyaunches added a commit that referenced this pull request Jul 14, 2026
## Summary

Add v0.0.83 release notes to `docs/about/release-notes.mdx` for pre-tag
release prep.

## Source Summary

- #6773 -> `docs/about/release-notes.mdx`: Shared inference route
changes are explicit and fail-safe; status shows recorded route, live
route, and drift.
- #6875 -> `docs/about/release-notes.mdx`: DGX Station GB300 express
setup restored; vLLM storage preflight narrowed.
- #6770 -> `docs/about/release-notes.mdx`: Risky Spark vLLM server
warning during onboarding.
- #6856 -> `docs/about/release-notes.mdx`: Re-onboard reuse preserves
tier-default brave/tavily presets.
- #6867 -> `docs/about/release-notes.mdx`: Unreachable custom endpoint
routed through transport-recovery path.
- #6860 -> `docs/about/release-notes.mdx`: Rebuild preflight uses
model-aware token field for o-series/GPT-5.
- #6845 -> `docs/about/release-notes.mdx`: Corporate CA anchored for
image build TLS.
- #6833 -> `docs/about/release-notes.mdx`: SSH ControlMaster-delegated
forwards recognized in fallback.
- #6837 -> `docs/about/release-notes.mdx`: Hermes light skin writes via
stdin on macOS.

## Type of Change

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

## Quality Gates
- [ ] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [x] Tests not applicable — justification: doc-only release notes
- [x] Docs updated for user-facing behavior changes
- [ ] Docs not applicable — justification:
- [ ] Sensitive paths changed
- [ ] Non-success, skipped, or missing CI check accepted by maintainer

## 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
- [x] `npm run docs` passes with 0 errors

Signed-off-by: Jessica Yaunches <jyaunches@nvidia.com>

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

Labels

area: onboarding Onboarding FSM, provider setup, sandbox launch, or first-run flow area: policy Network policy, egress rules, presets, or sandbox policy bug-fix PR fixes a bug or regression

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[DGX Station][Onboard] 'brave' Balanced-tier preset dropped on re-onboard reuse while other tier defaults persist

4 participants