Skip to content

feat(cli): support profile files in --extends - #1999

Open
Pybsama wants to merge 1 commit into
nolabs-ai:mainfrom
Pybsama:codex/extends-profile-file
Open

Pybsama wants to merge 1 commit into
nolabs-ai:mainfrom
Pybsama:codex/extends-profile-file

Conversation

@Pybsama

@Pybsama Pybsama commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Linked Issue

Closes #1851

Summary

--extends ./overlay.json is currently rejected as an invalid profile name, although --profile accepts the same file. Keep CLI-selected bases separate from JSON-authored inheritance and load CLI file arguments through the existing comment-aware, canonicalizing file parser. Their bases resolve before merging beneath the selected profile, preserving the existing left-to-right order.

Paths remain forbidden in JSON extends, including inside a CLI-selected file. Canonical sibling lookup, draft-directory isolation, pack provenance, defaults, and merge rules remain intact. Help and both authoring/flag references document file support and ./ for unambiguous local paths.

Added name/path parity regressions for the reported multi-base case, including explicit null, empty credentials, and open_urls replacement. They pass without changing merge semantics; this PR does not assert that the older-version discrepancy still exists on current main.

Agent Disclosure

Prepared by OpenAI Codex, an AI coding agent, on behalf of Pybsama. Identity, intent, scope, and risks were disclosed in the issue before editing.

Consulted AGENTS.md, CONTRIBUTING.md, SECURITY.md, GOVERNANCE.md, neps/README.md, the profile loader/resolver and tests, CLI definitions, and authoring/flag documentation. This contribution complies with the applicable repository requirements. No third-party implementation was reused. This is the scoped CLI input addition requested by the issue; no profile-format or policy-merge redesign is introduced.

Test Plan

  • Two initial regressions failed with the expected invalid-base-name error before the implementation; all 7 new regression tests now pass.
  • Coverage includes absolute/relative paths, JSON comments, ordering, name/path parity, symlink referents, draft isolation, registry/pack provenance, missing/malformed files, cycles, and JSON path rejection.
  • make ci passed locally on macOS: all-feature strict Clippy, formatting, cargo test --workspace (3,818 passed / 11 ignored, including 8 doc tests), the separate doc-test step (8 passed), cargo audit, and docs lint.
  • git diff --check passed. Independent agent review found no actionable correctness or security issues. The subsequent upstream CI and SPIRE runs passed on this PR head, including the Linux/macOS test and integration jobs.

Checklist

  • An issue exists and is linked above
  • All commits are signed off using DCO
  • Code follows repository standards and has regression coverage
  • Public-facing changes have help and documentation updates
  • No major feature or security-model change requiring a NEP; JSON format, merge rules, and enforcement remain unchanged

Agent Compliance Check

  • I am not prohibited from contributing under this policy
  • Agent identity, intent, approach, and risks disclosed in the issue
  • Relevant coding and security rules reviewed
  • No reused code requires attribution
  • No forbidden production unwrap/expect patterns introduced
  • Existing NonoError propagation is retained
  • File arguments reuse existing validation and canonicalization
  • This PR matches the disclosed issue scope

Compatibility scope

The file classifier is shared unchanged with --profile; native Windows is not currently supported, and WSL2 uses the Linux build. File paths are accepted only in the top-level CLI base list. Recursive resolution passes an empty CLI list, so JSON-authored paths remain rejected. Repeating a file via separate CLI arguments creates sibling base layers, not a recursive cycle; named-cycle regression coverage remains in place.

Signed-off-by: Pybsama <294532706+Pybsama@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

PR Review Summary

Size

Metric Value
Lines added +351
Lines removed -43
Total changed 394
Classification Large (> 300 lines)

Affected crates

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

Blast radius — Moderate

This PR touches: source code,documentation


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; 1 medium-severity platform divergence concern and 1 low-severity cycle-detection improvement.

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

visited.push(base_name.clone());

let resolved = load_base_profile_raw(base_name, context_dir, source_file)?;
let resolved = if from_cli && is_file_path_ref(base_name) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Warning

🐛 [MEDIUM · bug] The check is_file_path_ref(base_name) only matches slashes / or .json / .jsonc extensions. On Windows, backslashes \ are standard path separators, but are not checked. Consequently, relative paths using backslashes (e.g., --extends .\overlay) will not be recognized as file paths, triggering profile-not-found errors. Update is_file_path_ref to also check for backslash characters \ or use std::path::is_separator to ensure cross-platform compatibility.

.map(|name| (name, true))
.chain(base_names.iter().map(|name| (name, false)))
{
if visited.contains(base_name) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Note

🏗️ [LOW · design] The cycle detector checks visited.contains(base_name) using raw string comparisons on file paths. If a file is extended using different string representations (e.g. ./foo.json vs foo.json or an absolute path), the cycle check will fail to recognize the duplicate. While infinite recursion is ultimately bounded by MAX_INHERITANCE_DEPTH, canonicalizing file paths before inserting/comparing them in visited would provide robust cycle detection.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(cli): let --extends accept a profile file path, like -p does

1 participant