Skip to content

aitools: do not block plugin installs on the latest-release lookup - #7474

Merged
anton-107 merged 1 commit into
mainfrom
aitools/plugin-ref-fallback
Nov 12, 2026
Merged

anton-107 merged 1 commit into
mainfrom
aitools/plugin-ref-fallback

Conversation

@anton-107

@anton-107 anton-107 commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #7473.

Stacked on: aitools: refresh only the Databricks marketplace, and do not fail the install on it

Summary

Plugin installs no longer fail when the lookup of the latest skills release on api.github.com fails (for example HTTP 403 when rate-limited). The agent's own CLI fetches the plugin from its marketplace, so the skills ref only labels the recorded plugin version. On lookup failure the CLI now records the plugin version as unknown and keeps the scope's existing state.Release, the same way a failed marketplace refresh already records "version unknown".

 executePlan (cmd/aitools/install.go)
   if plugin items
     ref = GetSkillsRef(ctx)
-    lookup error -> install fails
+    lookup error -> debug log, ref = "" (plugin version unknown)
     install plugins

 RecordPluginInstalls (libs/aitools/installer/plugin.go)
-  state.Release = ref
+  if ref != "": state.Release = ref

DATABRICKS_SKILLS_REF and a pinned compat version still take precedence. Skills installs are unchanged.

This is the same handling #7162 uses for plugin installs (same code lines, comments and test names), so whichever lands first, the other rebases trivially. The cmd test stubs the lookup through DATABRICKS_CLI_GITHUB_API_URL, which the release lookup uses on this base.

Scope: install only. aitools list and aitools update still fail or skip work on a failed lookup; they are left for a follow-up.

Changelog fragment: .nextchanges/cli/aitools-plugin-ref-fallback.md.

Evidence

New test TestExecutePlanPluginWithUnresolvedSkillsRelease (cmd/aitools): compat says "latest", DATABRICKS_CLI_GITHUB_API_URL points at a stub that returns 403, and installPluginForAgentFn / recordPluginInstallsFn are stubbed to capture the ref. It asserts the command succeeds, the plugin is installed, the lookup ran once, and both functions receive an empty ref.

  • Before (the call site returning the lookup error, i.e. the old behavior):
    --- FAIL: TestExecutePlanPluginWithUnresolvedSkillsRelease
        Received unexpected error: resolve latest skills release: HTTP 403 fetching latest skills release
    
    After (this head): --- PASS: TestExecutePlanPluginWithUnresolvedSkillsRelease

New test TestRecordPluginInstallsUnresolvedRefKeepsRelease (libs/aitools/installer): a plugin record written with an empty ref keeps the state.Release recorded by an earlier install.

Also passing at this head: go build ./..., task test-exp-aitools, task lint-q, task fmt-q, task checks, and the cmd/aitools acceptance tests.

Merge Danger

Door: two-way

A few lines at the plugin call site and in RecordPluginInstalls. Reverting restores the old behavior.

Blast Radius: small

  • Behavior change: when the lookup fails, databricks aitools install with plugin targets exits 0 and shows the plugin version as unknown instead of failing. state.Release is not overwritten.
  • A mixed install (plugin agents plus skills agents) still runs the skills group first, and that group can still fail on the lookup before the plugin step.
  • No extra network calls or waits: the lookup still runs once.
  • Only unit-tested with a stubbed lookup. Not tested against a real rate-limited GitHub API.

This pull request and its description were written by Isaac.

@anton-107
anton-107 requested review from a team and lennartkats-db as code owners November 11, 2026 14:26
@anton-107 anton-107 added the ai-auto PRs made by AI loops & factories, label Nov 11, 2026
@anton-107
anton-107 requested review from a team as code owners November 11, 2026 14:26
@anton-107
anton-107 force-pushed the aitools/databricks-marketplace-refresh branch from 37c16a6 to feb1410 Compare November 11, 2026 14:33
@anton-107
anton-107 requested a review from a team as a code owner November 11, 2026 14:33
@anton-107
anton-107 force-pushed the aitools/plugin-ref-fallback branch from 491114e to 3081662 Compare November 11, 2026 14:33
@eng-dev-ecosystem-bot

eng-dev-ecosystem-bot commented Nov 11, 2026 •

Copy link
Copy Markdown
Collaborator

✅ Integration test report

Commit: 9138ddc

Run: 43219095674

Env 🔄​flaky 💚​RECOVERED ✅​pass 🙈​skip Time
💚​ aws linux-2core-8gb 1 270 27 13:13
🔄​ aws-windows-latest-4core-16gb 1 1 271 25 13:04
🔄​ azure linux-2core-8gb 1 1 268 27 12:17
🔄​ azure-windows-latest-4core-16gb 2 1 269 25 12:17
💚​ gcp linux-2core-8gb 1 270 27 13:53
💚​ gcp-windows-latest-4core-16gb 1 272 25 15:22
Test Name aws linux-2core-8gb aws-windows-latest-4core-16gb azure linux-2core-8gb azure-windows-latest-4core-16gb gcp linux-2core-8gb gcp-windows-latest-4core-16gb
💚​ TestAccept 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
🔄​ TestSyncFullFileSync ✅​p 🔄​f ✅​p ✅​p ✅​p ✅​p
🔄​ TestSyncIncrementalFileSync ✅​p ✅​p ✅​p 🔄​f ✅​p ✅​p
🔄​ TestSyncIncrementalSyncFileToPythonNotebook ✅​p ✅​p ✅​p 🔄​f ✅​p ✅​p
🔄​ TestSyncNestedSpacePlusAndHashAreEscapedSync ✅​p ✅​p 🔄​f ✅​p ✅​p ✅​p
Top 17 slowest tests (at least 2 minutes):
duration env testname
3:57 gcp-windows-latest-4core-16gb TestFsCpFileToFileFileNotOverwritten/dbfs_to_dbfs
3:52 gcp linux-2core-8gb TestAccept
3:50 azure linux-2core-8gb TestAccept
3:39 aws-windows-latest-4core-16gb TestAccept
3:37 azure-windows-latest-4core-16gb TestAccept
3:35 gcp-windows-latest-4core-16gb TestAccept
3:27 aws linux-2core-8gb TestAccept
2:45 azure-windows-latest-4core-16gb TestFilerWorkspaceFilesExtensionsStat
2:38 azure-windows-latest-4core-16gb TestImportDirWithOverwriteFlag
2:35 gcp linux-2core-8gb TestFilerWorkspaceFilesExtensionsReadDir
2:33 gcp linux-2core-8gb TestFilerRecursiveDelete/workspace_files_extensions
2:29 gcp linux-2core-8gb TestFilerWorkspaceNotebook/rNb.r
2:24 gcp-windows-latest-4core-16gb TestFsCpDirToDirFileNotOverwritten/dbfs_to_dbfs
2:12 aws linux-2core-8gb TestFilerWorkspaceFilesExtensionsReadDir
2:10 gcp linux-2core-8gb TestFilerWorkspaceFilesExtensionsDelete
2:09 azure linux-2core-8gb TestImportDirDoesNotOverwrite
2:04 azure linux-2core-8gb TestFilerWorkspaceFilesExtensionsDelete

@anton-107
anton-107 force-pushed the aitools/databricks-marketplace-refresh branch from feb1410 to 4eff713 Compare November 11, 2026 16:52
@anton-107
anton-107 force-pushed the aitools/plugin-ref-fallback branch from 3081662 to 9075cd6 Compare November 11, 2026 16:53
@anton-107
anton-107 force-pushed the aitools/databricks-marketplace-refresh branch from 4eff713 to 056b3e7 Compare November 11, 2026 16:54
@anton-107
anton-107 force-pushed the aitools/plugin-ref-fallback branch from 9075cd6 to 4b0c705 Compare November 11, 2026 16:54
@anton-107
anton-107 force-pushed the aitools/databricks-marketplace-refresh branch from 056b3e7 to 02922d2 Compare November 11, 2026 16:57
@anton-107
anton-107 force-pushed the aitools/plugin-ref-fallback branch from 4b0c705 to 521c720 Compare November 11, 2026 16:57
@anton-107
anton-107 force-pushed the aitools/databricks-marketplace-refresh branch from 02922d2 to d467e3f Compare November 11, 2026 17:28
@anton-107
anton-107 force-pushed the aitools/plugin-ref-fallback branch from 521c720 to dd8dd43 Compare November 11, 2026 17:28
@anton-107
anton-107 force-pushed the aitools/databricks-marketplace-refresh branch from d467e3f to 4cc5453 Compare November 11, 2026 19:28
@anton-107
anton-107 force-pushed the aitools/plugin-ref-fallback branch from dd8dd43 to 270ba1d Compare November 11, 2026 19:28
@anton-107
anton-107 force-pushed the aitools/databricks-marketplace-refresh branch from 4cc5453 to 605b293 Compare November 11, 2026 19:42
@anton-107
anton-107 force-pushed the aitools/plugin-ref-fallback branch from 270ba1d to da3637c Compare November 11, 2026 19:42
@anton-107
anton-107 force-pushed the aitools/databricks-marketplace-refresh branch from 605b293 to eb09c76 Compare November 11, 2026 19:59
@anton-107
anton-107 force-pushed the aitools/plugin-ref-fallback branch from da3637c to f435418 Compare November 11, 2026 19:59

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

🤖 Automated review from simonfaltum's review agent (pr-review skill) at f435418. Each finding was verified against the code; simonfaltum may follow up.

Not approving yet: 1 blocking issue(s), tagged [blocking]; 2 open design questions.

This PR stops databricks aitools install from failing plugin installs when the GitHub lookup of the latest skills release fails, by falling back to the embedded skills version. Unblocking the install is the right goal, and CI is green. The problem is that the fallback version is saved as the plugin's installed version and as the scope's state.Release, so version, list and update --check then show wrong versions. The code and #7069 already handle this case by recording "version unknown", and doing that here, plus a test of the install call site, would resolve the main concerns.

Questions

  1. This overlaps #7162, which handles the same failed lookup during plugin installs by recording the plugin version as unknown and leaving state.Release unchanged. #7162 also covers aitools list. Should this PR take over that piece (and #7162 drop it), or should the plugin-install change land only through #7162?
  2. When the release lookup fails, aitools list still exits with an error (cmd/aitools/list.go:155, covered by acceptance/cmd/aitools/list/latest-lookup-fails), and aitools update skips plugin updates and raw-skills-to-plugin migrations (cmd/aitools/update.go:91-133). Is install-only the intended scope here, with list and update left for a follow-up, or should they get the same handling in this PR?

Comment thread libs/aitools/installer/installer.go Outdated
Comment on lines +109 to +117
// GetPluginSkillsRef returns the skills ref to record for a plugin install,
// falling back to the embedded skills version when the latest release cannot be
// resolved. The agent's own CLI fetches the plugin from its marketplace, so the
// ref is bookkeeping only; an unreachable or rate-limited api.github.com must
// not block the install.
func GetPluginSkillsRef(ctx context.Context) (string, error) {
ref, _, err := getSkillsRef(ctx, true)
return ref, err
}

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.

[blocking] On a failed lookup, this returns the embedded pin (for example v0.2.32) as if it were the plugin's release. InstallPluginForAgent stores it as PluginRecord.Version (plugin.go:309), and RecordPluginInstalls writes it to state.Release (plugin.go:406). But the agent's marketplace installed its own current version, so the ref is not just bookkeeping.

After a rate-limited databricks aitools install --agents claude-code:

  • install, aitools version and aitools list show databricks plugin v0.2.32.
  • aitools update --check reports "update available" for a plugin that is already current.
  • If the scope also has raw skills at a newer release, version and list show those skills at v0.2.32 too.

Mixed installs can hit this as well: the skills group and the plugin path each look up the release. If the first lookup succeeds and the second gets a 403, the older pin overwrites the release the skills install just wrote.

The code already records "version unknown" when the plugin version can't be known (plugin.go:307-311, update.go:471-473), and #7069 says plugin operations treat a failed lookup as unknown.

Suggested change:

  • In executePlan, keep GetSkillsRef. On error, log at debug and continue with ref = "", so the record says "version unknown".
  • In RecordPluginInstalls, set state.Release only when ref != "".

With that, GetPluginSkillsRef is not needed.

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.

Done in 401cca3, following your suggestion. executePlan keeps GetSkillsRef; on error it logs at debug and continues with ref = "", so the plugin record says "version unknown". RecordPluginInstalls now sets state.Release only when ref != "", so a failed lookup no longer overwrites the release a skills install in the same run just wrote. GetPluginSkillsRef is removed.

Comment thread cmd/aitools/install.go Outdated
pluginCount := 0
if len(pluginItems) > 0 {
ref, _, err := installer.GetSkillsRef(ctx)
ref, err := installer.GetPluginSkillsRef(ctx)

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.

[nice to have] No test covers this call site or what gets recorded after the fallback. The cmd tests set DATABRICKS_SKILLS_REF, and the new test calls GetPluginSkillsRef directly, so reverting this line to GetSkillsRef would still pass every test. Please add a test that runs the plugin path while the release lookup fails. For example, point DATABRICKS_CLI_GITHUB_API_URL at a stub that returns 403, as acceptance/cmd/aitools/list/latest-lookup-fails does, and stub installPluginForAgentFn and recordPluginInstallsFn. Assert that the command exits 0, the plugin is installed, and check which ref reaches both functions. That would also catch the recording issue raised on GetPluginSkillsRef.

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.

Added TestExecutePlanPluginWithUnresolvedSkillsRelease in 401cca3. It points DATABRICKS_CLI_GITHUB_API_URL at a stub that returns 403 (compat says "latest"), stubs installPluginForAgentFn and recordPluginInstallsFn, and asserts that executePlan succeeds, the plugin outcome is installed, the lookup ran once, and both functions get an empty ref. Reverting the call site to return the lookup error makes it fail with the 403. TestRecordPluginInstallsUnresolvedRefKeepsRelease covers the state.Release side.

@@ -0,0 +1 @@
* `databricks aitools install` keeps installing plugins when the lookup of the latest skills release on GitHub fails or is rate-limited. ([#7474](https://github.com/databricks/cli/pull/7474))

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.

[nit] This entry says install "keeps installing plugins" when the lookup fails, but a mixed install can still be blocked before the plugin step. executePlan runs the skills group first and returns if it fails (cmd/aitools/install.go:581). The skills group disables its own fallback for a legacy global install (skills on disk, no state file) and for state whose release isn't a semver tag. Example: legacy global skills, Claude Code (plugin) plus Cursor (skills), and GitHub returning 403. The command fails and the plugin is not installed. Either run the plugin items when the skills group fails (still returning the skills error), or narrow this entry to plugin-only installs.

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.

Narrowed the entry to plugin-only installs in 401cca3, and it now says the plugin version is recorded as unknown. The order of the skills group and the plugin step is unchanged in this PR.

Comment on lines +1497 to +1508
setBuildVersion(t, "1.22.0")
t.Setenv("DATABRICKS_SKILLS_REF", "")
t.Setenv("DATABRICKS_FORCE_EMBEDDED_COMPAT", "false")
t.Setenv("DATABRICKS_CACHE_ENABLED", "true")
withCachedCompat(t, skillsLatestSentinel)
lookups := 0
orig := latestSkillsReleaseRefFn
t.Cleanup(func() { latestSkillsReleaseRefFn = orig })
latestSkillsReleaseRefFn = func(context.Context) (string, error) {
lookups++
return "", errors.New("HTTP 403 fetching latest skills release")
}

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.

[nit] This test repeats the setup of setupSkillsFallback and TestGetSkillsRefLatestReleaseLookupFails, and it rewrites the stubLatestSkillsReleaseRef stub just to count calls. .agents/rules/testing.md asks for table-driven tests for near-identical cases. Consider a single table over both entry points: the strict lookup expects the error, the plugin path expects its result. stubLatestSkillsReleaseRef could return a call counter. If GetPluginSkillsRef goes away as suggested above, this test can go too.

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.

GetPluginSkillsRef and this test are removed in 401cca3, so the duplicated setup is gone. The new tests are TestExecutePlanPluginWithUnresolvedSkillsRelease (cmd/aitools) and TestRecordPluginInstallsUnresolvedRefKeepsRelease (installer).

@anton-107
anton-107 force-pushed the aitools/databricks-marketplace-refresh branch from eb09c76 to 7123aae Compare November 11, 2026 22:58
@anton-107
anton-107 force-pushed the aitools/plugin-ref-fallback branch 2 times, most recently from 7d445a5 to 401cca3 Compare November 11, 2026 23:01
@anton-107

anton-107 commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks for the re-review.

  1. Yes, the intent is to land this install-side piece now as groundwork. The PRs stacked above this one build on the empty-ref path, and the state.Release guard in RecordPluginInstalls is what they rely on. Handling for aitools list and aitools update can come with Fix AI tools fallback and embed resolved skills releases #7162, or in a follow-up when "latest" is restored.

On the doc-comment nit outside the changed lines, as of 21231db:

  • RecordPluginInstalls: the doc comment now says that an empty ref (release unresolved) keeps the existing state.Release.

@anton-107
anton-107 force-pushed the aitools/databricks-marketplace-refresh branch from 0d74442 to 40e1da8 Compare November 12, 2026 09:19
@anton-107
anton-107 force-pushed the aitools/plugin-ref-fallback branch from 21231db to f6f7904 Compare November 12, 2026 09:19
@anton-107
anton-107 force-pushed the aitools/databricks-marketplace-refresh branch from 40e1da8 to c2eaa89 Compare November 12, 2026 10:01
@anton-107
anton-107 force-pushed the aitools/plugin-ref-fallback branch from f6f7904 to 9f26165 Compare November 12, 2026 10:01
@anton-107
anton-107 force-pushed the aitools/databricks-marketplace-refresh branch from c2eaa89 to 20e3ef4 Compare November 12, 2026 10:20
@anton-107
anton-107 force-pushed the aitools/plugin-ref-fallback branch from 9f26165 to 8f2ef4e Compare November 12, 2026 10:20
@anton-107
anton-107 force-pushed the aitools/databricks-marketplace-refresh branch from 20e3ef4 to 06fd311 Compare November 12, 2026 10:24
@anton-107
anton-107 force-pushed the aitools/plugin-ref-fallback branch from 8f2ef4e to ae31019 Compare November 12, 2026 10:24

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

🤖 Approval from simonfaltum's review agent at ae31019, on Simon's decision.

The design questions from the earlier automated reviews no longer block this PR: your answers are accepted, and any still-open ones are non-blocking. The changes since the last automated review (f1bf38a → ae31019) address its comments.

@anton-107
anton-107 force-pushed the aitools/databricks-marketplace-refresh branch from 06fd311 to b2ee61c Compare November 12, 2026 12:27
@anton-107
anton-107 force-pushed the aitools/plugin-ref-fallback branch from ae31019 to bb8734f Compare November 12, 2026 12:27
@anton-107
anton-107 force-pushed the aitools/databricks-marketplace-refresh branch from b2ee61c to dab2b7c Compare November 12, 2026 12:30
@anton-107
anton-107 force-pushed the aitools/plugin-ref-fallback branch from bb8734f to 7a75603 Compare November 12, 2026 12:31
@anton-107
anton-107 force-pushed the aitools/databricks-marketplace-refresh branch from dab2b7c to c9feaa5 Compare November 12, 2026 12:32
@anton-107
anton-107 force-pushed the aitools/plugin-ref-fallback branch from 7a75603 to 9138ddc Compare November 12, 2026 12:32
@anton-107
anton-107 force-pushed the aitools/databricks-marketplace-refresh branch from c9feaa5 to af8a5dd Compare November 12, 2026 12:37
@anton-107
anton-107 force-pushed the aitools/plugin-ref-fallback branch from 9138ddc to ae5eede Compare November 12, 2026 12:37
@anton-107
anton-107 force-pushed the aitools/databricks-marketplace-refresh branch from af8a5dd to dfb22a8 Compare November 12, 2026 16:38
@anton-107
anton-107 force-pushed the aitools/plugin-ref-fallback branch from ae5eede to 276f78c Compare November 12, 2026 16:38
@anton-107
anton-107 force-pushed the aitools/databricks-marketplace-refresh branch from dfb22a8 to 4075f1c Compare November 12, 2026 17:13
@anton-107
anton-107 force-pushed the aitools/plugin-ref-fallback branch from 276f78c to e84d3cc Compare November 12, 2026 17:13
@anton-107
anton-107 force-pushed the aitools/databricks-marketplace-refresh branch from 4075f1c to 0134b4b Compare November 12, 2026 20:05
@anton-107
anton-107 changed the base branch from aitools/databricks-marketplace-refresh to main November 12, 2026 20:30
Plugin installs no longer fail when the lookup of the latest skills release on api.github.com fails (for example HTTP 403 when rate-limited). The agent's own CLI fetches the plugin from its marketplace, so the skills ref only labels the recorded plugin version. On lookup failure the CLI now records the plugin version as unknown and keeps the scope's existing `state.Release`.

```diff
 executePlan (cmd/aitools/install.go)
   if plugin items
     ref = GetSkillsRef(ctx)
-    lookup error -> install fails
+    lookup error -> debug log, ref = "" (plugin version unknown)
     install plugins

 RecordPluginInstalls (libs/aitools/installer/plugin.go)
-  state.Release = ref
+  if ref != "": state.Release = ref
```

`DATABRICKS_SKILLS_REF` and a pinned compat version still take precedence. Skills installs are unchanged.

Changelog fragment: `.nextchanges/cli/aitools-plugin-ref-fallback.md`.

Co-authored-by: Isaac <no-reply@databricks.com>
@anton-107
anton-107 force-pushed the aitools/plugin-ref-fallback branch from e84d3cc to 9bfea48 Compare November 12, 2026 20:40
@anton-107
anton-107 added this pull request to the merge queue Nov 12, 2026
Merged via the queue into main with commit b48c9fb Nov 12, 2026
24 checks passed
@anton-107
anton-107 deleted the aitools/plugin-ref-fallback branch November 12, 2026 20:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-auto PRs made by AI loops & factories,

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants