Skip to content

[CFX-4730] feat(plugin): wire CLI version constraint checks into discovery - #815

Draft
ajalon1 wants to merge 1 commit into
aj/cli-version-constraintsfrom
aj/cli-version-constraints-discovery
Draft

[CFX-4730] feat(plugin): wire CLI version constraint checks into discovery#815
ajalon1 wants to merge 1 commit into
aj/cli-version-constraintsfrom
aj/cli-version-constraints-discovery

Conversation

@ajalon1

@ajalon1 ajalon1 commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

What

PR 2 of 2 (stacks on #814). Wires the inert compatibleCLIVersion/cliVersionSkip predicate from #814 into actual plugin discovery behavior.

Why

CFX-4730: plugin manifests can declare minCLIVersion/maxCLIVersion bounds, but until now those bounds were parsed and validated (#814) without being enforced. This PR makes the CLI actually skip incompatible plugins at discovery time.

Changes

  • internal/plugin/discover.go: call cliVersionSkip inside loadManagedPlugin and inside getManifestsParallel's merge/dedup loop, both strictly before the seen[...] name reservation — an incompatible plugin never blocks a compatible, identically-named plugin, and is reported as a version incompatibility, never a name conflict.
  • internal/plugin/discover.go: added ConflictsForReason; LogConflicts now switches on PluginConflict.Reason — WARN for name conflicts (byte-identical wording to before), INFO for version incompatibility (includes the violated bound + running CLI version + upgrade guidance).
  • cmd/plugin/discovery.go: extracted reportDiscoveryConflicts so routine command registration (RegisterPluginCommands, which runs on every dr invocation) only warns about name conflicts and stays silent about version-incompatibility skips. Those surface only via dr plugin list / dr plugin version <name>, which already pass the full, unfiltered conflict set through LogConflicts — verified this needs no code changes there via a real binary harness run (see Testing).
  • Docs: documented maxCLIVersion/minCLIVersion semantics in docs/development/plugins.md and docs/development/remote-plugins.md.

Testing

  • task lint — clean, 0 issues on all 3 GOOS targets.
  • task test — full repo suite green (race + coverage); internal/plugin package coverage 80.9%.
  • New/extended unit and integration tests: managed plugin below minCLIVersion is skipped and not loaded; two PATH dirs sharing a manifest name where the lexicographically-first violates maxCLIVersion — the second still registers under that name, and the skip is reported as SkipReasonVersionIncompatible, never SkipReasonNameConflict; LogConflicts level-switch behavior; ConflictsForReason filtering; reportDiscoveryConflicts silence on routine discovery.
  • Manual runtime harness: built the CLI with -ldflags "-X .../version.Version=1.0.0", created a fake dr-oldwidget PATH plugin declaring maxCLIVersion: 0.9.0. Confirmed dr plugin list excludes it from the table and prints the INFO skip line; dr plugin version oldwidget prints the same line then reports the plugin as not found; plain dr --help prints neither the plugin name nor "version incompatible" (silent on ordinary discovery, per spec).

Refs CFX-4730. Stacks on #814.


Note

Medium Risk
Changes which plugins are loaded and registered on every CLI invocation. Incorrect skip ordering or logging could hide valid plugins or spam every dr command, but this is not auth, secrets, or data-handling.

Overview
Enforces plugin minCLIVersion/maxCLIVersion at discovery so incompatible plugins never load. This is the second slice of CFX-4730: it wires the previously inert cliVersionSkip check into managed and PATH discovery before name reservation, so a version-skipped plugin cannot block a compatible plugin with the same name.

LogConflicts now Warns only for name collisions and Infos version skips (with bound + upgrade guidance). Routine command registration stays silent on version skips; they appear only via dr plugin list / dr plugin version. Docs cover inclusive bounds, the dev/unparseable-CLI bypass, and malformed-bound skips.

Reviewed by Cursor Bugbot for commit 5bbf0ab. Configure here.

…iscovery

Wire the inert compatibleCLIVersion/cliVersionSkip predicate (added in a
prior commit) into actual plugin discovery behavior:

- internal/plugin/discover.go: call cliVersionSkip in loadManagedPlugin and
  in getManifestsParallel's merge/dedup loop, both strictly before the
  seen[...] name reservation, so an incompatible plugin never blocks a
  compatible, identically-named plugin and is reported as an incompatibility
  rather than a name conflict.
- internal/plugin/discover.go: add ConflictsForReason and switch LogConflicts
  on PluginConflict.Reason — WARN for name conflicts (unchanged wording),
  INFO for version incompatibility.
- cmd/plugin/discovery.go: extract reportDiscoveryConflicts so routine
  command registration only warns about name conflicts and stays silent
  about version-incompatibility skips; those surface only via
  `dr plugin list` / `dr plugin version <name>`, which already pass the
  full conflict set through LogConflicts unfiltered.
- docs: document maxCLIVersion/minCLIVersion semantics in
  docs/development/plugins.md and docs/development/remote-plugins.md.

Verified via a real built binary that `dr plugin list`/`dr plugin version`
surface the skip with upgrade guidance while ordinary command discovery
(`dr --help`) stays silent.

Refs: CFX-4730. Stacks on #814.
@datarobot-pr-review-router

Copy link
Copy Markdown

🎫 Jira: CFX-4730 — plugin manifest - CLI version constraints

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

again, will be deleted, just wanted to leave it here for reference

@ajalon1
ajalon1 marked this pull request as ready for review August 21, 2026 08:12
@ajalon1
ajalon1 requested a review from a team as a code owner August 21, 2026 08:12
@ajalon1
ajalon1 marked this pull request as draft August 21, 2026 08:12

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 5bbf0ab. Configure here.

// would otherwise take.
if conflict := cliVersionSkip(&manifest, pluginDir); conflict != nil {
return nil, conflict, nil
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Version skip misclassified as conflict

Medium Severity

cliVersionSkip runs only after the seen name check, so when a higher-priority compatible plugin already claimed the name, an incompatible peer is recorded as SkipReasonNameConflict instead of SkipReasonVersionIncompatible. That turns a routine version skip into a WARN on every dr invocation (via reportDiscoveryConflicts), violating the silence rule for version-incompatible plugins. A common case is a managed compatible install plus an older same-named PATH binary.

Suggested change
}
if conflict := cliVersionSkip(&manifest, pluginDir); conflict != nil {
return nil, conflict, nil
}
if seen[manifest.Name] {
return nil, &PluginConflict{Name: manifest.Name, Path: pluginDir}, nil
}
Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5bbf0ab. Configure here.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant