Visitar URL original
fix(profile): child overrides base in env_credentials by destination env var by dselivanov · Pull Request #1533 · nolabs-ai/nono · GitHub
Skip to content

fix(profile): child overrides base in env_credentials by destination env var - #1533

Merged
SequeI merged 2 commits into
nolabs-ai:mainfrom
dselivanov:fix/env-credentials-child-wins-by-destination
Jul 30, 2026
Merged

SequeI merged 2 commits into
nolabs-ai:mainfrom
dselivanov:fix/env-credentials-child-wins-by-destination

Conversation

@dselivanov

Copy link
Copy Markdown
Contributor

Fixes the env_credentials race reported in #1532: when a base profile and a child that extends it map different keystore accounts to the same destination env var, both entries survived the merge and the winning value was chosen non-deterministically per launch.

Root cause

SecretsConfig.mappings is a HashMap<account, env_var> — keyed by the keystore account (source), with the env var as the value (destination). The extends merge used HashMap::extend:

let mut merged = base.env_credentials.mappings;
merged.extend(child.env_credentials.mappings);

extend only replaces an entry when the key (account) matches. Two different accounts targeting the same env var (e.g. a base's GITHUB_TOKEN -> GITHUB_TOKEN and a child's GH_ENTERPRISE_TOKEN -> GITHUB_TOKEN) therefore both survived. At load time keystore::load_secrets iterates the map in random per-process order, and the environment is assembled last-write-wins (execution_runtime.rs), so the final value of that env var flipped between launches — silent and intermittent, with no collision warning.

Fix

Drop any base entry whose destination env var a child overrides before extending, so the child wins by destination — matching set_vars, whose merge key already is the destination env var (so it has always been child-wins-by-destination):

let child_destinations: std::collections::HashSet<String> =
    child.env_credentials.mappings.values().cloned().collect();
let mut merged = base.env_credentials.mappings;
merged.retain(|_account, dest| !child_destinations.contains(dest));
merged.extend(child.env_credentials.mappings);

merge_profiles is the single merge path used by extends (single + array) and platform_overrides, so this covers all inheritance routes.

Tests

Two new unit tests in profile::tests:

  • env_credentials_child_overrides_base_by_destination_env_var — base ACCOUNT_A -> MY_TOKEN + child ACCOUNT_B -> MY_TOKEN => child wins, base entry dropped, unrelated base entry preserved.
  • env_credentials_child_extends_base_without_collision — no destination collision => both kept.

(The existing test_merge_profiles_env_credentials_child_wins covers same-account override, which already worked; these cover the different-account-same-destination case that did not.)

Full nono-cli binary suite: 1816 passed, 0 failed.

Scope / not in this PR

This fully fixes the cross-profile case (base + extends child). A rarer, same-profile variant — two accounts mapping to one env var within a single profile's env_credentials ({ "A": "T", "B": "T" }) — still races and is not resolved by this merge change; it would need a validation warning (e.g. via collect_profile_warnings). Left as a follow-up noted in #1532 to keep this PR focused.

Refs #1532.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@github-actions github-actions Bot added bug Something isn't working nono-cli size/medium labels Jul 30, 2026
@github-actions

github-actions Bot commented Jul 30, 2026 •

Copy link
Copy Markdown
Contributor

PR Review Summary

Size

Metric Value
Lines added +85
Lines removed -0
Total changed 85
Classification Medium (50–300 lines)

Affected crates

  • crates/nono-cli — CLI changes. Verify argument parsing, flag documentation, and UX behaviour across supported platforms.

Blast radius — Contained

This PR touches: source code


Updated automatically on each push to this PR.

@nogent-nolabs-ai nogent-nolabs-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nogent code review

No blocking issues; 0 suggestions.

Findings: none flagged in scope.

Automated code + security review. CI already covers clippy, rustfmt, tests, cargo-audit and commit-lint.

…env var

Two env_credentials entries mapping different keystore accounts to the
same env var (e.g. a base's GITHUB_TOKEN mapping and a child's GHE
account -> GITHUB_TOKEN) both survived the extends merge: HashMap::extend
only replaces an entry when the account name (the map key) matches, so
distinct accounts targeting one env var coexisted. At load time
keystore::load_secrets iterates the map in random per-process order and
the environment is assembled last-write-wins, so the winning value
flipped between launches -- a silent, intermittent misconfiguration with
no collision warning.

Drop any base entry whose destination env var a child overrides before
extending, matching set_vars semantics (whose key already is the
destination env var, so it already merges child-wins-by-destination).
merge_profiles is the single merge path used by extends (single + array)
and platform_overrides, so the fix covers all inheritance routes.

Refs nolabs-ai#1532.
@dselivanov
dselivanov force-pushed the fix/env-credentials-child-wins-by-destination branch from e09c0ea to 3b1fe0c Compare July 30, 2026 05:21

@SequeI SequeI left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This makes sense, thank you for the fix!

@dselivanov

Copy link
Copy Markdown
Contributor Author

it;s and amazing how smooth contributions happen nowadays with the correct ai coding agent setup and right engineering culture. My second PR here based on my experience with nono. Like it so far!
PS never written a single line in Rust myself.

@SequeI
SequeI merged commit 02ceea4 into nolabs-ai:main Jul 30, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working nono-cli size/medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants