Skip to content

aitools: install skill files when a detected agent's CLI cannot run - #7482

Merged
anton-107 merged 1 commit into
mainfrom
aitools/skills-fallback-for-detected-agents
Nov 13, 2026
Merged

anton-107 merged 1 commit into
mainfrom
aitools/skills-fallback-for-detected-agents

Conversation

@anton-107

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

Copy link
Copy Markdown
Contributor

Stacked on #7481.

Changes

databricks aitools install now gives raw skill files to a detected coding agent whose CLI is not on PATH (for example a Claude Code desktop or IDE install: ~/.claude exists, claude does not), unless the agent already has the plugin. Before, the run printed "Skipped Claude Code" and left the agent with nothing.

The plan decides this, so the picker label and what the run does agree, and the agent joins the run's one skills batch (one ref lookup, one manifest fetch):

 planItemFor (plugin agent, scope supported)
-  plugin
+  if CLI not on PATH, the agent is detected (config dir), and not named via --agents
+    if the agent already has the plugin for this scope
+      keep it (plugin delivery, nothing installed; outcome "kept")
+    else
+      skill files instead (skills delivery)
+  else
+    plugin
  • Already has the plugin: Claude Code's own installed_plugins.json decides, because claude plugin uninstall leaves the CLI's record behind. When that file exists but cannot be read, the CLI's install state for the scope decides, as it does for the other agents. A global install counts only a machine-wide plugin. A project-scope install counts only a project-scope plugin of this project: its skill files are shared through the repository, while a user or local plugin is personal.
  • Known limitation (Codex CLI, GitHub Copilot): the CLI cannot read these agents' own plugin lists, so only a plugin installed by databricks aitools is known. If one of them got the plugin another way (for example through its app or IDE) and its CLI is not on PATH, the fallback adds skill files next to the plugin and every skill shows twice. install --help says so too.
  • Kept plugin: install prints how to refresh it and, before the skills batch, removes the raw skills an earlier run gave the agent, as a plugin install does, when the agent's own manifest shows the plugin. For Codex and Copilot the CLI's record is the only evidence, and it outlives a removal with the agent's own CLI, so their raw skills stay. The picker labels it "plugin installed · CLI not found".
  • Skill files instead: install prints how to switch to the plugin. After the skills install it drops a stale plugin record from the CLI's install state; a failure to do so is a warning. If the skills install fails after a picker selection, the recovery step re-runs the skills agents with --skills-only, since a named agent does not fall back.
  • Project plugin next to a global fallback: a global-scope fallback still runs in a directory where the agent has a project or local databricks plugin, since the global skill files serve every other project, and it warns that this project will see both the plugin and the global skill files.
  • Exit code and telemetry: a kept plugin counts like an installed one. The run exits 0 with outcome SUCCESS, or NOTHING_TO_DO when it did nothing else.
  • Unchanged: agents named via --agents, and agents whose CLI is on PATH but whose plugin command fails, still fail with the exact commands to run.
  • aitools update: no longer tries, and fails, to migrate an agent whose CLI is not on PATH from raw skills to the plugin; it keeps refreshing its raw skills. It also no longer writes raw skills to an agent whose CLI is not on PATH and whose own manifest shows the plugin for the scope, since install keeps such a plugin without a CLI record: it removes them instead, and skips the reconcile when no agent is left that can take file skills in the scope.
  • Helpers: installer.PluginRecorded (now also used for the User-Agent tooling), installer.ForgetPluginInstalls, agents.Agent.HasPluginManifestReader, and an exported agents.Agent.DatabricksPluginActiveIn, which returns an error for an unreadable manifest.

Why

Desktop and IDE installs of Claude Code have the config dir but no claude CLI, so a plain databricks aitools install set nothing up for them. Skill files are what such an agent can use today, and a later run with the CLI on PATH switches it to the plugin. An agent that already has the plugin keeps it, because skill files next to the plugin would list every skill twice.

Tests

  • New acceptance test acceptance/experimental/aitools/skills/install-non-interactive: the detected-agent fallback in one skills batch, a kept plugin that removes the earlier skill files and an aitools update --force that does not write them back, the named-agent failure and its --skills-only recovery, a project-scope fallback that a later user-scope plugin does not stop aitools update --scope=project from refreshing, and a project-scope run that sets up nothing exiting 1.
  • Unit tests (table-driven) for the plan decision and picker label, executePlan for both cases, the funnel outcomes, the scope rules and unreadable manifest in pluginAlreadyInstalled, the project-plugin warning, ForgetPluginInstalls / PluginRecorded, the manifest reader, the project-scope plugin rule (DatabricksProjectPluginIn, excludePluginAgents), the update migration skip, and a project-scope update whose only project-capable agent has the plugin.
  • ./task test-exp-aitools, ./task lint-q, ./task checks pass locally on Linux. Not run on Windows or macOS.

Changelog fragment: .nextchanges/cli/aitools-install-skills-fallback.md.

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:28
@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:28
@anton-107
anton-107 requested a review from misha-db November 11, 2026 14:28
@anton-107
anton-107 force-pushed the aitools/nothing-installed-fails branch from 54c4bc5 to 75d4a60 Compare November 11, 2026 14:34
@anton-107
anton-107 requested a review from a team as a code owner November 11, 2026 14:34
@anton-107
anton-107 requested a review from rauchy November 11, 2026 14:34
@anton-107
anton-107 force-pushed the aitools/skills-fallback-for-detected-agents branch from 5afe64b to d5fb33f Compare November 11, 2026 14:34
@github-actions

github-actions Bot commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

Commit: 007deeb2f | Workflow run

Regression test report

Selected 1 changed tests (limit=20, 0 not selected)

1 tests: regression=1 unreleased=0 coverage=0 failing=0
test branch main (eebf099) latest (v1.26.0)
TestAccept/experimental/aitools/skills/install-non-interactive ✅ ❌ ❌
TestAccept/experimental/aitools/skills/install-non-interactive: branch ✅ | main (eebf099) ❌ | latest (v1.26.0) ❌

main (eebf099):

acceptance_test.go:1242: Substituted DATABRICKS_SKILLS_BASE_URL "$DATABRICKS_HOST" -> "http://127.0.0.1:39921" ("[DATABRICKS_HOST]")
    acceptance_test.go:1374: Diff:
        --- experimental/aitools/skills/install-non-interactive/output.txt
        +++ /tmp/TestAcceptexperimentalaitoolsskillsinstall-non-interactive2442344382/002/output.txt
        @@ -2,27 +2,31 @@
         === no flags, no terminal: no prompts, global scope, every detected agent
         
         >>> [CLI] aitools install
        -Skipped the plugin for Claude Code: 'claude' is not on PATH. Installing skill files instead; to switch to the plugin, install the Claude Code CLI so 'claude' is on PATH and run 'databricks aitools install --scope=global --agents=claude-code'.
        -Installing Databricks skills for Claude Code, Cursor...
        +Installing Databricks skills for Cursor...
         Using skills version test-ref
         Fetching skills manifest...
         Installed 1 skill.
        -Claude Code has skill files
        +Installing databricks plugin for Claude Code...
        +Skipped Claude Code: 'claude' is not on PATH
        +  Install or update the Claude Code CLI so 'claude' is on PATH, then re-run 'databricks aitools install --scope=global --agents=claude-code'. Or install skill files instead: databricks aitools install --scope=global --agents=claude-code --skills-only
        +Claude Code has no skill files
         
         === a plugin installed with Claude Code itself is kept, without skill files next to it
         
         >>> [CLI] aitools install
        -Claude Code already has the databricks plugin; it was not refreshed because 'claude' is not on PATH. To refresh it, install the Claude Code CLI so 'claude' is on PATH and run 'databricks aitools install --scope=global --agents=claude-code'.
         Installing Databricks skills for Cursor...
         Using skills version test-ref
         Fetching skills manifest...
         Installed 1 skill.
        +Installing databricks plugin for Claude Code...
        +Skipped Claude Code: 'claude' is not on PATH
        +  Install or update the Claude Code CLI so 'claude' is on PATH, then re-run 'databricks aitools install --scope=global --agents=claude-code'. Or install skill files instead: databricks aitools install --scope=global --agents=claude-code --skills-only
         Claude Code has no skill files
         
         >>> [CLI] aitools update --force
           updated test-stable v1.0.0 -> v1.0.0
         Updated 1 skill.
        -Claude Code has no skill files
        +Claude Code has skill files
         
         === a named agent does not fall back: the failure says what to run
         
        @@ -43,22 +31,11 @@
         === project scope: a personal plugin does not replace the project's skill files
         
         >>> [CLI] aitools install --scope=project
        -Skipped the plugin for Claude Code: 'claude' is not on PATH. Installing skill files instead; to switch to the plugin, install the Claude Code CLI so 'claude' is on PATH and run 'databricks aitools install --scope=project --agents=claude-code'.
        -Installing Databricks skills for Claude Code...
        -Using skills version test-ref
        -Fetching skills manifest...
        -Installed 1 skill.
        +Installing databricks plugin for Claude Code...
        +Skipped Claude Code: 'claude' is not on PATH
        +  Install or update the Claude Code CLI so 'claude' is on PATH, then re-run 'databricks aitools install --scope=project --agents=claude-code'. Or install skill files instead: databricks aitools install --scope=project --agents=claude-code --skills-only
         Skipped Cursor: does not support project-scoped skills
           To set up Cursor in the global scope, run: databricks aitools install --scope=global --agents=cursor
        -
        ->>> [CLI] aitools update --scope=project
        -Skipped Cursor: does not support project-scoped skills.
        -Already up to date.
        -The project has Claude Code skill files
        -
        -=== nothing can be set up: the run fails instead of exiting 0
        -
        ->>> musterr [CLI] aitools install --scope=project
        -Skipped Cursor: does not support project-scoped skills
        -  To set up Cursor in the global scope, run: databricks aitools install --scope=global --agents=cursor
         Error: nothing was installed; follow the steps above
        +
        +Exit code: 1
        
FAIL

latest (v1.26.0):

acceptance_test.go:1242: Substituted DATABRICKS_SKILLS_BASE_URL "$DATABRICKS_HOST" -> "http://127.0.0.1:37805" ("[DATABRICKS_HOST]")
    acceptance_test.go:1374: Diff:
        --- experimental/aitools/skills/install-non-interactive/output.txt
        +++ /tmp/TestAcceptexperimentalaitoolsskillsinstall-non-interactive2266076862/002/output.txt
        @@ -2,35 +2,36 @@
         === no flags, no terminal: no prompts, global scope, every detected agent
         
         >>> [CLI] aitools install
        -Skipped the plugin for Claude Code: 'claude' is not on PATH. Installing skill files instead; to switch to the plugin, install the Claude Code CLI so 'claude' is on PATH and run 'databricks aitools install --scope=global --agents=claude-code'.
        -Installing Databricks skills for Claude Code, Cursor...
        +Installing Databricks skills for Cursor...
         Using skills version test-ref
         Fetching skills manifest...
         Installed 1 skill.
        -Claude Code has skill files
        +Installing databricks plugin for Claude Code...
        +Skipped Claude Code: claude-code: cli-not-on-path: could not resolve claude on PATH: exec: "claude": executable file not found in $PATH
        +Claude Code has no skill files
         
         === a plugin installed with Claude Code itself is kept, without skill files next to it
         
         >>> [CLI] aitools install
        -Claude Code already has the databricks plugin; it was not refreshed because 'claude' is not on PATH. To refresh it, install the Claude Code CLI so 'claude' is on PATH and run 'databricks aitools install --scope=global --agents=claude-code'.
         Installing Databricks skills for Cursor...
         Using skills version test-ref
         Fetching skills manifest...
         Installed 1 skill.
        +Installing databricks plugin for Claude Code...
        +Skipped Claude Code: claude-code: cli-not-on-path: could not resolve claude on PATH: exec: "claude": executable file not found in $PATH
         Claude Code has no skill files
         
         >>> [CLI] aitools update --force
           updated test-stable v1.0.0 -> v1.0.0
         Updated 1 skill.
        -Claude Code has no skill files
        +Claude Code has skill files
         
         === a named agent does not fall back: the failure says what to run
         
         >>> musterr [CLI] aitools install --agents=claude-code
         Installing databricks plugin for Claude Code...
        -Skipped Claude Code: 'claude' is not on PATH
        -  Install or update the Claude Code CLI so 'claude' is on PATH, then re-run 'databricks aitools install --scope=global --agents=claude-code'. Or install skill files instead: databricks aitools install --scope=global --agents=claude-code --skills-only
        -Error: Claude Code: 'claude' is not on PATH
        +Skipped Claude Code: claude-code: cli-not-on-path: could not resolve claude on PATH: exec: "claude": executable file not found in $PATH
        +Error: claude-code: cli-not-on-path: could not resolve claude on PATH: exec: "claude": executable file not found in $PATH
         
         === the suggested recovery command works
         
        @@ -43,22 +23,19 @@
         === project scope: a personal plugin does not replace the project's skill files
         
         >>> [CLI] aitools install --scope=project
        -Skipped the plugin for Claude Code: 'claude' is not on PATH. Installing skill files instead; to switch to the plugin, install the Claude Code CLI so 'claude' is on PATH and run 'databricks aitools install --scope=project --agents=claude-code'.
        -Installing Databricks skills for Claude Code...
        -Using skills version test-ref
        -Fetching skills manifest...
        -Installed 1 skill.
        +Installing databricks plugin for Claude Code...
        +Skipped Claude Code: claude-code: cli-not-on-path: could not resolve claude on PATH: exec: "claude": executable file not found in $PATH
         Skipped Cursor: does not support project-scoped skills
        -  To set up Cursor in the global scope, run: databricks aitools install --scope=global --agents=cursor
         
         >>> [CLI] aitools update --scope=project
        -Skipped Cursor: does not support project-scoped skills.
        -Already up to date.
        -The project has Claude Code skill files
        +Error: no project-scoped skills found in the current directory.
         
        -=== nothing can be set up: the run fails instead of exiting 0
        +Project-scoped skills are detected based on your working directory.
        +Make sure you are in the project root where you originally ran
        +'databricks aitools install --scope=project'.
         
        ->>> musterr [CLI] aitools install --scope=project
        -Skipped Cursor: does not support project-scoped skills
        -  To set up Cursor in the global scope, run: databricks aitools install --scope=global --agents=cursor
        -Error: nothing was installed; follow the steps above
        +Expected location: [TEST_TMP_DIR]/home/project/.databricks/aitools/skills/
        +
        +Global skills are installed. Run with --scope=global to update those.
        +
        +Exit code: 1
        
FAIL

@eng-dev-ecosystem-bot

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

Copy link
Copy Markdown
Collaborator

✅ Integration test report

Commit: 4e71af1

Run: 43219648803

Env 💚​RECOVERED ✅​pass 🙈​skip Time
💚​ aws linux-2core-8gb 1 270 31 11:38
💚​ aws-windows-latest-4core-16gb 1 272 29 12:06
💚​ azure linux-2core-8gb 1 269 31 12:57
💚​ azure-windows-latest-4core-16gb 1 271 29 11:18
💚​ gcp linux-2core-8gb 1 270 31 12:01
💚​ gcp-windows-latest-4core-16gb 1 272 29 14:44
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
Top 17 slowest tests (at least 2 minutes):
duration env testname
4:57 gcp-windows-latest-4core-16gb TestAccept
3:48 azure linux-2core-8gb TestAccept
3:43 azure-windows-latest-4core-16gb TestAccept
3:40 aws-windows-latest-4core-16gb TestAccept
3:32 azure linux-2core-8gb TestFilerRecursiveDelete/workspace_files
3:26 gcp linux-2core-8gb TestAccept
3:17 aws linux-2core-8gb TestAccept
3:00 aws-windows-latest-4core-16gb TestImportDirWithOverwriteFlag
2:44 azure-windows-latest-4core-16gb TestFilerWorkspaceFilesExtensionsDelete
2:18 gcp-windows-latest-4core-16gb TestFilerReadWrite/workspace_files
2:13 aws linux-2core-8gb TestImportDirDoesNotOverwrite
2:12 azure-windows-latest-4core-16gb TestFilerWorkspaceFilesExtensionsStat
2:12 aws-windows-latest-4core-16gb TestFilerWorkspaceFilesExtensionsRead
2:09 azure linux-2core-8gb TestFilerWorkspaceFilesExtensionsRead
2:07 gcp-windows-latest-4core-16gb TestFsCpFileToDirWithOverwriteFlag/dbfs_to_dbfs
2:02 gcp-windows-latest-4core-16gb TestImportDirDoesNotOverwrite
2:00 aws-windows-latest-4core-16gb TestFilerReadWrite/dbfs

@anton-107
anton-107 force-pushed the aitools/nothing-installed-fails branch from 75d4a60 to 7d9546e Compare November 11, 2026 16:53
@anton-107
anton-107 force-pushed the aitools/skills-fallback-for-detected-agents branch from d5fb33f to 6dfcf53 Compare November 11, 2026 16:54
@anton-107
anton-107 force-pushed the aitools/nothing-installed-fails branch from 7d9546e to a2b82bc Compare November 11, 2026 16:55
@anton-107
anton-107 force-pushed the aitools/skills-fallback-for-detected-agents branch from 6dfcf53 to 0de8029 Compare November 11, 2026 16:55
@anton-107
anton-107 force-pushed the aitools/nothing-installed-fails branch from a2b82bc to 0758e7e Compare November 11, 2026 16:58
@anton-107
anton-107 force-pushed the aitools/skills-fallback-for-detected-agents branch from 0de8029 to 3e2f9ef Compare November 11, 2026 16:58
@anton-107
anton-107 force-pushed the aitools/nothing-installed-fails branch from 0758e7e to 3f574d9 Compare November 11, 2026 17:27

@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 3e2f9ef. Each finding was verified against the code; simonfaltum may follow up.

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

This PR makes databricks aitools install install skill files for a detected agent whose CLI cannot run, keep an existing plugin instead of adding duplicate skills, and drop stale plugin records after a fallback. The approach is reasonable, and CI passes. Two things block merging. First, the branch is built on an older revision of #7481, so it conflicts with its base and the kept-plugin logic has to be redone on top of anyInstalled/errNoAgentSetUp. Second, fallback agents are installed in a separate skills batch, so they keep stale skill copies after an upgrade. The other comments cover edge cases (kept-path cleanup, unreadable manifest, update warnings) and doc, test and description tidy-ups; three questions on the telemetry and fallback design are open.

Questions

  1. When a detected agent keeps its plugin and another agent's install fails, the run exits 0 but the funnel records FAILURE. An installed agent plus the same failure records SUCCESS. The current #7481 removed the exit-0-but-FAILURE branch, and per-agent results for kept or fallback agents carry the same CLI_NOT_ON_PATH entry as a real failure. Should kept be treated like installed (SUCCESS, or NOTHING_TO_DO when nothing else happened), or is the difference intended? If it is intended, could the per-agent telemetry distinguish 'kept' and 'fell back to skills' from 'failed'?
  2. The fallback is decided at run time inside executePlan. That is why the picker label, plan summary and outcomes disagree, and why fallback agents need a second skills install. Would deciding it at plan time in planItemFor work instead, for non-explicit plugin agents whose CLI is missing (StateInstalledCLIMissing)? That puts them in the single skills batch and keeps the summary accurate; the run-time path would only be needed when the binary is present but 'plugin --help' fails. Or is there a reason to keep it at run time?
  3. A global-scope fallback ignores a project or local Claude plugin that applies to the current directory, so that project ends up with both the plugin and global raw skills. Is accepting duplicates in that project the intended trade-off? The alternatives are skipping the fallback, or printing a warning when the cwd has a project-scope plugin.

Comments outside the changed lines

  • [nit] cmd/aitools/install.go:673: If recordPluginInstallsFn fails, executePlan returns before the fallback block. The fallback agents get nothing, even though "Installing skill files instead" was already printed for them. Run the fallback before the early return, or fold it into a single skills batch.
  • [nit] .github/PULL_REQUEST_TEMPLATE.md: The repo's pr-checklist asks for the template sections ## Changes, ## Why and ## Tests, with no invented sections, and an agent disclosure as a single italic line. The description uses ## Summary, ## Evidence and ## Merge Danger. Its claim "No extra network calls beyond the existing skills manifest fetch" is also inaccurate: the fallback fetches the manifest a second time. Please restructure the description and correct that claim.

Comment thread cmd/aitools/install.go Outdated
case runErr == nil:
if category, ok := nothingInstalledCategory(outcomes); ok {
funnel.finishWith(ctx, protos.AitoolsCommandOutcomeFailure, category)
} else if onlyKeptPlugins(outcomes) {

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] This branch sits on an older revision of #7481 (0758e7e). #7481 has since been force-pushed to 3f574d9, the base. GitHub reports this PR as not mergeable, and even a rebase of just this PR's commit conflicts in install.go, telemetry.go and funnel_test.go. The current #7481 removed nothingInstalledCategory, newNothingInstalledError and nothingInstalledError, and runErr == nil now always records SUCCESS. The kept-plugin logic here is written against those removed helpers. If the conflicts are resolved by taking the base's anyInstalled (which only checks outcomeInstalled), a kept-only run returns errNoAgentSetUp and exits 1. If the base's case runErr == nil is taken, the NOTHING_TO_DO outcome is lost. Please rebase onto the current aitools/nothing-installed-fails, count outcomeKept in anyInstalled, and redo the kept-plugin funnel handling on top of the base's runErr == nil branch.

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.

Rebased onto the current aitools/nothing-installed-fails (46180b9) in 7137e09. anyInstalled now counts outcomeKept, so a run whose only agent kept its plugin exits 0. The kept-plugin funnel handling sits in the base's runErr == nil branch through keptPluginsFinish: NOTHING_TO_DO when the run only kept plugins, FAILURE with the failed agent's category when it only kept plugins next to a failed install, SUCCESS when anything was installed. These outcomes are the same as in the previous revision.

Comment thread cmd/aitools/install.go Outdated
if !quiet {
installer.PrintInstallingFor(ctx, fallbackAgents)
}
if err := installSkillsForAgentsFn(ctx, src, fallbackAgents, opts); err != nil {

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] Fallback agents are installed by a second installSkillsForAgentsFn call. At global scope a one-agent batch copies files instead of symlinking (useSymlinks = len(detectedAgents) > 1), so Cursor and Claude Code each get their own copy. On a later install with a newer skill version, the Cursor batch updates the canonical files and sets state.Skills to the new version. The fallback batch then sees a matching version and an existing Claude copy (allAgentsHaveSkill only checks that the copy exists), so it skips Claude. The command succeeds while Claude keeps the old files, on every upgrade, because the skills batch always runs first. Two batches also resolve the ref and fetch the manifest twice and print the install banner twice, as the new acceptance output shows, which contradicts the description's "no extra network calls". Please install the fallback agents in the same InstallSkillsForAgents call as the other skills agents: run the plugin loop first, or decide the fallback at plan time.

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.

Fixed in 7137e09. The fallback is decided in planItemFor, so these agents are deliverySkills items in the run's single InstallSkillsForAgents call: symlinks when there are several agents, one ref lookup, one manifest fetch, one banner (see the acceptance output: "Installing Databricks skills for Claude Code, Cursor..."). The second batch is gone.

Comment thread cmd/aitools/install.go Outdated
cmdio.LogString(ctx, fmt.Sprintf("Installing databricks plugin for %s...", it.agent.DisplayName))
}
rec, err := installPluginForAgentFn(ctx, it.agent, it.scope, ref)
if err != nil && fallsBackToSkills(it, err) && pluginAlreadyInstalledFn(ctx, it.agent, opts.Scope) {

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] The kept branch exists to avoid duplicate skills, but it never calls cleanupLegacyFn, so raw skills written by an earlier fallback stay next to the plugin. Scenario: run 1 has no claude on PATH, falls back, and drops the plugin record. The user then installs the plugin through Claude directly. Run 2, again without claude on PATH, keeps the plugin and leaves ~/.claude/skills alone, so Claude lists every skill twice. With no record, aitools update keeps refreshing the raw files. Please call cleanupLegacyFn(ctx, it.agent, opts.Scope) in the kept branch too, as the plugin-install path does.

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 7137e09: the kept path calls cleanupLegacyFn(ctx, it.agent, opts.Scope) and logs a failure at debug level, as the plugin path does. The acceptance test now runs your scenario: the first run falls back, then a Claude manifest with the plugin appears, and the second run keeps the plugin and removes Claude Code's skill files.

Comment thread cmd/aitools/recovery.go Outdated
project = cwd
}
}
return a.DatabricksPluginActiveIn(ctx, project)

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] claudePluginInstalls returns (nil, false) on any read or parse error, and DatabricksPluginActiveIn discards the ok. So pluginAlreadyInstalled returns false, the run falls back, and ForgetPluginInstalls removes a valid claude-code plugin record. If installed_plugins.json is mid-write or its schema changes, every non-interactive install without claude on PATH writes raw skills next to the installed plugin and drops the record. Please tell "no manifest file" apart from "manifest present but unreadable". In the unreadable case, fall back to the CLI state record, or keep the plugin.

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 7137e09. The manifest reader now returns an error when installed_plugins.json exists but cannot be read or parsed (a missing file is still "not installed"), and DatabricksPluginActiveIn returns it. pluginAlreadyInstalled then uses the CLI's state record for the scope, so an unreadable manifest no longer drops a valid record. DatabricksTooling keeps treating a read error as not installed.

Comment thread cmd/aitools/install.go Outdated
for _, a := range fallbackAgents {
outcomes = append(outcomes, agentOutcome{agent: a, delivery: deliverySkills, status: outcomeInstalled})
}
// These agents have no plugin (pluginAlreadyInstalled said so), so a

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] A fallback agent ends up with raw skills and no plugin record, which makes it a plugin-migration candidate in update (pluginMigrationCandidatesForScope). Every aitools update then calls the plugin install, fails, and prints Skipped Claude Code: claude-code: cli-not-on-path: … (the raw BlockedError). Before this PR only --skills-only users reached that state; now desktop and IDE users reach it by default. Consider skipping the migration attempt when the CLI is not on PATH, or printing the same friendly reason and recovery that install prints.

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 7137e09: pluginMigrationCandidatesForScope skips an agent whose CLI is not on PATH, matching install, so aitools update keeps refreshing its raw skills without a failing plugin install on every run. Covered by TestPluginMigrationCandidatesNeedTheCLI.

Comment thread cmd/aitools/recovery.go Outdated
return false
}
blocked, ok := errors.AsType[*installer.BlockedError](err)
return ok && blocked.Reason == installer.ReasonCLINotOnPath && !blocked.TimedOut

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] probePluginCLI returns ReasonCLINotOnPath both when the binary is missing and when it is on PATH but plugin --help fails, for example an old CLI or a probe false negative. fallsBackToSkills doesn't tell these apart, so an on-PATH CLI that is too old now gets raw skills instead of the "update your CLI" failure. The doc comment and PR description both say "not on PATH". Either check !it.agent.HasBinary(ctx) or update the comment and description.

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.

Fixed in 7137e09: the fallback now checks !a.HasBinary(ctx) when the run is planned, so only a missing binary falls back. A CLI on PATH whose plugin --help fails (too old, or a probe false negative) gets the "Install or update the ... CLI" failure, as before this PR. The description says so.

Comment thread cmd/aitools/funnel_test.go Outdated

// A kept plugin does not hide another detected agent's failed install: the
// funnel reports the failure with that agent's category.
func TestInstallFunnelKeptPluginWithAFailedInstallIsFailure(t *testing.T) {

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] TestInstallFunnelKeptPluginIsNothingToDo and TestInstallFunnelKeptPluginWithAFailedInstallIsFailure repeat the setup of TestInstallFunnelDetectedPluginFailureIsNotSuccess. They differ only in the PATH binaries, the installer stub and the expected funnel shape. The same goes for TestExecutePlanFallsBackToSkillsWhenCLIIsMissing and TestExecutePlanKeepsAnInstalledPluginInsteadOfAddingSkills in install_test.go. .agents/rules/testing.md asks for table-driven tests for similar cases.

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 7137e09: TestInstallFunnelDetectedPluginAgents is one table (the only install failed, skill files instead, plugin kept, plugin kept next to a failed install), and TestExecutePlanWhenTheCLIIsMissing covers the fallback and kept cases in one table.

Comment thread cmd/aitools/install.go Outdated
})
continue
}
if err != nil && fallsBackToSkills(it, 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.

[nit] err != nil && fallsBackToSkills(it, err) is checked at line 609 and again here, followed by a third if err != nil branch for the same decision. One switch, or a single if with an inner already-installed / not-installed branch, would read more clearly (style-guide-go prefers switch for one decision).

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.

No longer applies in 7137e09: with the decision in planItemFor, the plugin loop in executePlan is back to its single if err != nil branch, and planItemFor makes the decision in one switch (scope unsupported, CLI missing, plugin).

// from the state file for the given CLI scope, for agents that no longer have
// the plugin. Agents without a record, or a scope without state, are left as
// they are.
func ForgetPluginInstalls(ctx context.Context, cliScope string, agentNames []string) error {

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] ForgetPluginInstalls has no test in plugin_test.go. It is only exercised indirectly from cmd/aitools, so its no-state and no-record paths are untested. fallsBackToSkills also has no direct test: nothing checks that a TimedOut CLI_NOT_ON_PATH error or an explicit agent does not fall back. The kept notice (pluginKeptText) has no acceptance coverage either. Please add a small table test for fallsBackToSkills in recovery_test.go and a ForgetPluginInstalls test in plugin_test.go.

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 in 7137e09: TestForgetPluginInstalls in plugin_test.go (no state, an agent without a record, a recorded agent; it also checks PluginRecorded per scope). fallsBackToSkills is gone; the plan decision that replaces it is covered by the table test TestPlanItemForAgentWithoutCLI (CLI on PATH, CLI missing, missing with the plugin, named via --agents, scope unsupported). A timeout can no longer lead to a fallback, because planning does not run the CLI. The kept notice is now in the acceptance output.

@@ -0,0 +1 @@
* `databricks aitools install` installs skill files for a detected coding agent whose CLI cannot run (for example a Claude Code desktop or IDE install), and says how to switch to the plugin later. ([#7482](https://github.com/databricks/cli/pull/7482))

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] The pr-checklist says a fragment that leads with the command should use the present tense with "now". Suggest: "databricks aitools install now installs skill files for a detected coding agent whose CLI cannot run …".

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 7137e09: "databricks aitools install now installs skill files for a detected coding agent whose CLI is not on PATH (for example a Claude Code desktop or IDE install), unless it already has the plugin, and says how to switch to the plugin later." The PR link is kept.

@anton-107
anton-107 force-pushed the aitools/nothing-installed-fails branch from 3f574d9 to 46180b9 Compare November 11, 2026 19:33
@anton-107
anton-107 force-pushed the aitools/skills-fallback-for-detected-agents branch from 3e2f9ef to 7137e09 Compare November 11, 2026 19:48
@anton-107

Copy link
Copy Markdown
Contributor Author

Addressed in 7137e09:

  • cmd/aitools/install.go:673 (early return when recordPluginInstallsFn fails): fallback agents are now part of the skills batch, which runs before the plugin loop, so a record failure no longer leaves them without skills.
  • Description: restructured into Changes / Why / Tests, and corrected the network claim: with a single skills batch there is one ref lookup and one manifest fetch. The attribution line keeps its standard wording.
  • Question 2: moved to plan time. planItemFor gives skill files (or keeps an installed plugin) to a detected plugin agent whose binary is not on PATH, which covers StateInstalledCLIMissing. I did not keep a run-time path for a binary on PATH whose plugin --help fails: such an agent fails with the "install or update the CLI" recovery, as before this PR, so there is no second skills batch at all and the picker label, plan summary and outcomes agree.

@anton-107
anton-107 force-pushed the aitools/skills-fallback-for-detected-agents branch from 7137e09 to 4fd042b Compare November 11, 2026 20:02
@anton-107

Copy link
Copy Markdown
Contributor Author

On Question 3 (global-scope fallback next to a project or local Claude plugin), in 4fd042b: the fallback still runs, because the global skill files serve every other project and the run cannot tell which projects the user works in. When the current directory has a project or local databricks plugin for that agent, install now also warns: "Claude Code has the databricks plugin for this project, so this project will see both the plugin and the global skill files." The check reads Claude Code's own manifest for the current directory; an unreadable manifest prints no warning. Covered in TestPluginAlreadyInstalledRespectsTheScope (project and local records of this project warn; a machine-wide record keeps the plugin instead; another project's record does not warn) and TestExecutePlanWhenTheCLIIsMissing.

@anton-107

Copy link
Copy Markdown
Contributor Author

On Question 1, in 4fd042b: a kept plugin now counts like an installed one. A run that sets up an agent by keeping its plugin finishes with SUCCESS, or with NOTHING_TO_DO when keeping plugins was all it did. A kept plugin next to another agent's failed install is now SUCCESS, the same as an installed agent next to that failure (it was FAILURE before). Covered by TestInstallFunnelDetectedPluginAgents and TestAnyInstalledAndFirstFailureCategory.

The per-agent results keep the CLI_NOT_ON_PATH entry for kept and fell-back agents in this PR. Telling them apart from a failed install in the per-agent results can follow in a separate PR.

@anton-107
anton-107 force-pushed the aitools/nothing-installed-fails branch from 46180b9 to 544ae4b Compare November 11, 2026 21:11
@anton-107
anton-107 force-pushed the aitools/skills-fallback-for-detected-agents branch from 4fd042b to 5261133 Compare November 11, 2026 21:31

@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 5261133. Each finding was verified against the code; simonfaltum may follow up.

Not approving yet: 1 open design question.

This PR makes databricks aitools install give skill files to a detected agent whose CLI isn't on PATH, and an agent that already has the plugin keeps it. It also drops stale plugin records and stops update from retrying the plugin migration for such agents. Every point from the earlier review round is addressed at this head, and no CI check has failed. Two non-blocking issues remain. Plugin agents that aren't installed at all now appear in the picker as "skills · CLI not found" and get skill files if picked. And after a run that keeps a plugin, the next aitools update writes the raw skills back next to it. There are a few nits, plus one question about picker preselection.

Questions

  1. In the interactive picker, a detected agent whose CLI isn't on PATH (for example Claude Code desktop) is labeled "skills · CLI not found" but stays unticked, because IsPreselected (libs/aitools/agents/detect.go:82) only pre-checks agents that can complete an install automatically. A non-interactive run of the same command now installs skill files for that agent. Is the difference intended? Or should these agents be preselected now that they get skill files?

Agreeing with existing threads

  • [nit] Telemetry per-agent results report a kept plugin as a CLI_NOT_ON_PATH failure: #7482 (comment)

Comment thread cmd/aitools/install.go Outdated
item.reason = reason
item.skipErrorCategory = protos.AitoolsErrorCategoryUnsupportedScope
} else {
case !explicit && !a.HasBinary(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] The new !explicit && !a.HasBinary(ctx) branch doesn't check that the agent is detected. The interactive picker calls planItemFor for every agent in agents.Registry. So on a machine without Codex or Copilot (no config dir, no CLI), those rows now print "skills · CLI not found" where they used to print "not found". If the user picks one, skill files are written for an agent that isn't installed. That creates its config dir, so every later non-interactive run treats the agent as detected and installs for it. Before this change, that pick failed with recovery steps. Please limit the fallback to detected agents, e.g. case !explicit && !a.HasBinary(ctx) && a.Detected(ctx):, and add a not-detected case to TestPlanItemForAgentWithoutCLI.

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.

Fixed in 09d55e8: the branch is now case !explicit && !a.HasBinary(ctx) && a.Detected(ctx):, so an agent with neither its config dir nor its CLI is labeled "not found" in the picker again and, if picked, gets the plugin attempt and its recovery steps as before. Added a "not installed" case (Copilot) to TestPlanItemForAgentWithoutCLI.

Comment thread cmd/aitools/install.go Outdated
}
// Raw skills an earlier run gave this agent would show every skill
// twice next to the plugin, so remove them as a plugin install does.
if err := cleanupLegacyFn(ctx, it.agent, opts.Scope); err != nil {

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] The kept path removes the agent's raw skills but writes no plugin record to the CLI state. aitools update writes raw skills to every agent from DetectInstalled that excludePluginAgents doesn't filter, and that filter only checks state.Plugins. So the next update that changes a skill (a new release, or --force) writes the raw skills back into ~/.claude/skills next to the plugin. That brings back the duplicate listing the kept path exists to prevent. Example: Claude Code desktop plus Cursor; the user installs the plugin in Claude, runs aitools install (kept, raw skills removed), then aitools update. Please record the kept plugin in the state, or have update skip agents whose own manifest shows the plugin, and extend the acceptance sequence through an update.

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.

Fixed in 09d55e8 on the update side: excludePluginAgents now also drops an agent whose own manifest shows the plugin for the scope, next to the existing check of the CLI's records. I did not record the kept plugin in the state: a record would make update and uninstall run the agent's CLI for it, which is the CLI that is missing. The acceptance sequence now runs aitools update --force after the kept install and checks that Claude Code still has no skill files (it fails without the fix). Unit test: TestExcludePluginAgentsReadsTheAgentsManifest.

Comment thread cmd/aitools/install.go Outdated
project = cwd
}
}
active, err := a.DatabricksPluginActiveIn(ctx, project)

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] DatabricksPluginActiveIn counts a plugin that is disabled in Claude's settings. In the kept path, cleanupLegacyFn then removes the raw skills from an earlier fallback. A user whose plugin is installed but disabled ends up with no active databricks skills, and the run reports success. Consider removing the raw skills only when the plugin is enabled, or mention in the kept notice that the plugin is disabled.

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.

Leaving this one as is. The CLI has no reader for Claude Code's enabled and disabled plugins: that lives in Claude's layered settings files (user, project and local), and reading them reliably is a change of its own. A disabled plugin is also a choice the user made in Claude Code, and the kept notice already says the plugin is there and how to refresh it. Running databricks aitools install --skills-only gives the skill files back if someone wants them while the plugin stays disabled.

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.

Correction to my earlier reply: --skills-only is not a lasting workaround for a disabled plugin. The next default install keeps the plugin and removes those files, and update now does the same. A disabled plugin stays a known limitation of the kept path, because the CLI does not read Claude Code's enabled and disabled settings.

# Claude Code records its plugins in installed_plugins.json. The run keeps the
# plugin and removes the skill files the previous run gave Claude Code.
"$mkdir_bin" -p "${USERPROFILE:-$HOME}/.claude/plugins"
printf '%s\n' '{"plugins":{"databricks@claude-plugins-official":[{"scope":"user","version":"0.2.9"}]}}' > "${USERPROFILE:-$HOME}/.claude/plugins/installed_plugins.json"

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] .agents/rules/testing.md says to commit static test inputs instead of creating them in script at test time. The installed_plugins.json written here with printf is a fixed literal. Please check it in next to the test and cp it into ~/.claude/plugins/.

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 09d55e8: the manifest is checked in as installed_plugins.json next to the test, and the script copies it into ~/.claude/plugins/.

Comment thread libs/aitools/agents/plugins.go Outdated
// HasPluginManifest reports whether the CLI can read the agent's own plugin
// manifest (Claude Code). For such agents the manifest is authoritative: a
// plugin removed with the agent's own CLI leaves the CLI's state record behind.
func (a *Agent) HasPluginManifest() bool {

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] HasPluginManifest() returns true for Claude Code even when installed_plugins.json doesn't exist. It reports that the CLI has a manifest reader for the agent. The comment in pluginAlreadyInstalled ("When the agent has a readable plugin manifest") reads the same way. Consider renaming it to HasPluginManifestReader (or similar) so callers don't take it as a file-existence check.

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.

Renamed to HasPluginManifestReader in 09d55e8, with a doc comment saying it reports a reader, not that the file exists. The comment in pluginAlreadyInstalled now says "When the CLI can read the agent's own plugin manifest", and the scope rules moved into a small manifestPluginActive helper that update uses too.

Comment thread cmd/aitools/recovery.go
// cliMissingReason is why an agent whose CLI is not on PATH does not get the
// plugin.
func cliMissingReason(it agentPlanItem) string {
return fmt.Sprintf("'%s' is not on PATH", it.agent.Binary)

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] cliMissingReason builds '%s' is not on PATH, and pluginFailureText (recovery.go:395) builds the same string separately. Both strings appear in the acceptance output (the fallback notice and the named-agent failure). If one changes, they drift apart. Have pluginFailureText call cliMissingReason for the binary-missing case.

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 09d55e8: pluginFailureText uses cliMissingReason(it) for the binary-missing case, so the fallback notice and the named-agent failure share one string.

@anton-107
anton-107 force-pushed the aitools/skills-fallback-for-detected-agents branch from 5261133 to 09d55e8 Compare November 11, 2026 21:58
@anton-107
anton-107 force-pushed the aitools/nothing-installed-fails branch from 52e34eb to 181bc21 Compare November 12, 2026 09:00
@anton-107
anton-107 force-pushed the aitools/skills-fallback-for-detected-agents branch from a5688b2 to 1c5b9d5 Compare November 12, 2026 09:00
@anton-107
anton-107 force-pushed the aitools/nothing-installed-fails branch from 181bc21 to e8d4f91 Compare November 12, 2026 09:04
@anton-107
anton-107 force-pushed the aitools/skills-fallback-for-detected-agents branch from 1c5b9d5 to 357f7ff Compare November 12, 2026 09:04
@anton-107
anton-107 force-pushed the aitools/nothing-installed-fails branch from e8d4f91 to 2eca9b0 Compare November 12, 2026 09:06
@anton-107
anton-107 force-pushed the aitools/skills-fallback-for-detected-agents branch from 357f7ff to 122bf1b Compare November 12, 2026 09:06
@anton-107
anton-107 force-pushed the aitools/nothing-installed-fails branch from 2eca9b0 to cf6eeb8 Compare November 12, 2026 09:31
@anton-107
anton-107 force-pushed the aitools/skills-fallback-for-detected-agents branch from 122bf1b to 461f2f4 Compare November 12, 2026 09:31
@anton-107
anton-107 force-pushed the aitools/nothing-installed-fails branch from cf6eeb8 to 3289cf7 Compare November 12, 2026 10:05
@anton-107
anton-107 force-pushed the aitools/skills-fallback-for-detected-agents branch from 461f2f4 to eae0662 Compare November 12, 2026 10:05
@anton-107
anton-107 force-pushed the aitools/nothing-installed-fails branch from 3289cf7 to c6b1b5e Compare November 12, 2026 11:33

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

cmd/aitools/install.go:320: [nice to have] After a picker selection, rerun.agents names the selected agents, but rerun.skillsOnly stays false. If the skill download fails for a fallback agent, the recovery step suggests databricks aitools install --agents=claude-code ... without --skills-only. A named agent never falls back ("--agents never changes delivery", line 583), so that re-run tries the plugin and fails on the missing CLI. Add --skills-only to the re-run when the failed agents are fallback agents, or name them in a separate skills-only command.

Comment thread cmd/aitools/install.go Outdated
// This runs before the skills batch: a newer release there replaces the
// recorded checksums, and older copies would then look user-modified
// and stay.
if err := cleanupLegacyFn(ctx, it.agent, opts.Scope); err != nil {

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.

[nit] Codex and Copilot have no manifest reader, so pluginAlreadyInstalled uses only installer.PluginRecorded for them (line 616). That record stays after codex plugin uninstall. If the CLI is then also off PATH, the agent is kept, and this call removes its raw skills. The agent then has neither the plugin nor skills, but the output says that it already has the plugin. Skip the cleanup when the record is the only evidence, or add a comment that accepts this trade-off.

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.

Fixed in 4e71af1. The kept path now removes raw skills only when the agent's own manifest shows the plugin (manifestPluginActive). For Codex and Copilot the CLI's record is the only evidence, and it outlives a removal with the agent's own CLI, so their raw skills stay. The comment there says so. Covered by the new "plugin kept on the CLI's record alone" case in TestExecutePlanWhenTheCLIIsMissing.

@@ -0,0 +1 @@
* `databricks aitools install` now installs skill files for a detected coding agent whose CLI is not on PATH (for example a Claude Code desktop or IDE install), unless it already has the plugin, says how to switch to the plugin later, and warns when the current project also has the plugin. ([#7482](https://github.com/databricks/cli/pull/7482))

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.

[nit] This fragment names only aitools install, but aitools update changes too. It no longer tries the plugin migration for an agent without a CLI. It also skips raw skills for an agent whose manifest shows the plugin, and it removes the raw skills that such an agent still has. Add aitools update to the fragment.

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 4e71af1. The fragment now also says that databricks aitools update no longer tries to install the plugin for such an agent, and no longer writes skill files next to a plugin that such an agent already has. The PR link is kept.

Comment thread cmd/aitools/install.go
cmdio.LogString(ctx, cmdio.Yellow(ctx, projectPluginWarning(it)))
}
}
outcomes = append(outcomes, cliMissingOutcome(it, outcomeFailed))

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.

[Question] An agent that falls back to skill files gets two outcomes: this plugin outcome with status failed and CLI_NOT_ON_PATH, and a skills outcome with status installed. Is that intended? The answer matters because a coding agent that reads --output json can see failed for claude-code and tell the user that the install failed, although the run set it up.

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.

Yes, this is intended, and the --output json case cannot happen. JSON output requires --scope and --agents, and an agent named via --agents never falls back (planItemFor checks !explicit). So the plugin outcome with failed / CLI_NOT_ON_PATH next to the skills outcome with installed only shows up in a run without --agents.

There, the text output prints the fallback notice, and the run counts as a success. The plugin outcome keeps the reason the plugin was not installed in the per-agent telemetry results, and the skills outcome adds the SKILLS_INSTALLED step. Telling a fallback apart from a failed install in the per-agent results can follow in a separate PR.

Comment thread cmd/aitools/update.go
// plugin would show every skill twice, so remove them as
// install does when it keeps the plugin.
if !check {
for _, a := range manifestPlugins {

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.

[nit] This removes the raw skills of every agent whose manifest shows the plugin. But install --skills-only writes raw skills for such an agent (planItemFor, line 560). So a user who runs install --agents=claude-code --skills-only next to a marketplace plugin loses those skills at the next update, and only a debug log says so. Limit this cleanup to agents whose CLI is missing (the case that the kept path creates), or print a line when it removes files.

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.

Fixed in 4e71af1. excludePluginAgents now uses the agent's own manifest only when its CLI is not on PATH, which is the case the kept path creates. With the CLI on PATH, raw skills next to the plugin are the user's choice (install --skills-only). Update refreshes them as before and removes nothing. Covered in TestExcludePluginAgentsReadsTheAgentsManifest.

@anton-107
anton-107 force-pushed the aitools/nothing-installed-fails branch 6 times, most recently from 85990ef to c59988f Compare November 12, 2026 12:33
@anton-107
anton-107 force-pushed the aitools/skills-fallback-for-detected-agents branch from eae0662 to 4e71af1 Compare November 12, 2026 12:36
@anton-107

Copy link
Copy Markdown
Contributor Author

On the review note about rerun.skillsOnly after a picker selection (cmd/aitools/install.go:320): fixed in 4e71af1. When the skills batch fails, includes an agent whose CLI is missing, and the run named its agents through the picker, skillsBatchError attaches a recovery step that re-runs the skills agents with --skills-only. A named agent does not fall back, so the plain re-run would have tried the plugin. Runs without a picker selection keep the plain re-run, because it detects the same agents and they fall back again. Covered by TestExecutePlanSkillsFailureWithAFallbackAgentSuggestsSkillsOnly.

@anton-107
anton-107 force-pushed the aitools/nothing-installed-fails branch from c59988f to 965ae5a Compare November 12, 2026 12:48
@anton-107
anton-107 force-pushed the aitools/skills-fallback-for-detected-agents branch from 4e71af1 to 4baa441 Compare November 12, 2026 12:52

@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 4baa441. Each finding was verified against the code; simonfaltum may follow up.

No blocking issues found, approving. 6 non-blocking comment(s) below.

This PR makes databricks aitools install give skill files to a detected agent whose CLI is not on PATH, or keep the plugin it already has, and changes aitools update to match. The earlier update --scope=project failure is fixed, every later review note has a fix or an answer, and CI passes. Two non-blocking issues remain. After a picker selection, the recovery step for a release with no installable skill still names a fallback agent without --skills-only. And aitools update with no install state now exits 0 silently when the only detected agents are CLI-less agents that have the plugin. The rest are nits: a missing duplicate warning at project scope, the help text, and two style-guide points.

Accepted answers

Comment thread cmd/aitools/install.go Outdated
Comment on lines 936 to 937
if errors.Is(skillsErr, installer.ErrNoSkillsResolved) && (pluginCount > 0 || len(keptItems) > 0) {
recovery := installRecovery(ctx, rerun, skillsErr)

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] After a picker selection, rerun.agents names every picked agent. So the step built here (a release with no skill this CLI can install, next to an installed or kept plugin) says to re-run with --agents=claude-code,... and no --skills-only. In that re-run, Claude Code is a named agent, and a named agent never falls back. It tries the plugin and fails with "'claude' is not on PATH", and it still gets no skill files, even with --experimental added. Example: in the picker, tick Claude Code ("skills · CLI not found") and Codex (CLI on PATH), with a release that has only experimental skills. Codex's plugin installs, and Claude Code's recovery is databricks aitools install --scope=global --agents=claude-code,codex --debug. Build this step from the invocation skillsBatchError uses (the skills agents plus --skills-only) when the batch holds a fallback agent, e.g. through a shared helper. The errors.Join return after a failed recordPluginInstallsFn (line 901) has the same gap.

Comment thread cmd/aitools/update.go Outdated
Comment on lines +214 to +218
if len(manifestPlugins) > 0 && !slices.ContainsFunc(fileAgents, func(a *agents.Agent) bool {
return scope != installer.ScopeProject || a.SupportsProjectScope
}) {
continue
}

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] This skip also runs when the scope has no install state. With no state anywhere, resolveScopeForUpdate sends the run to the global scope, so UpdateSkills can report "no skills or plugins installed" or the legacy-install guidance. The comment above this block says the reconcile keeps running for that reason. Now the loop skips the reconcile when every detected agent's own manifest shows the plugin and its CLI is missing. Example: a Claude Code desktop user installed the plugin from Claude's marketplace and never ran databricks aitools install. databricks aitools update then prints nothing and exits 0 with outcome SUCCESS. Before, it exited 1 with NOTHING_TO_DO and said to run install. With a legacy install (skills on disk, no state file), the legacy guidance is lost too. Only skip when state != nil, and add a no-state case to the tests.

Comment thread cmd/aitools/install.go Outdated
Comment on lines +589 to +591
case scope == installer.ScopeGlobal:
// The global skill files still serve every other project.
item.projectPlugin = projectPluginActive(ctx, a)

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] Suppose Claude Code has a user-scope or local databricks plugin and claude is not on PATH. Then install --scope=project installs the project's skill files, as intended for teammates. This user now loads both the plugin and those skill files, so every skill shows twice, and nothing says so. The global-scope case prints projectPluginWarning for the same situation. Consider setting projectPlugin for the project scope too: a plugin active in cwd that isn't the project's own. Give it a message that names the personal plugin.

Comment thread cmd/aitools/install.go Outdated
Comment on lines +130 to +135
(` + strings.Join(agents.SkillsOnlyNames(), ", ") + `) get raw skill files, and so does a
detected plugin agent whose CLI is not on PATH (for example a desktop or IDE
install), unless it already has the plugin. For Claude Code that is read from
its own plugin list; for Codex and GitHub Copilot only a plugin installed by
'databricks aitools' is known, so one installed another way shows every skill
twice next to the skill files.

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] The help says a detected plugin agent whose CLI is not on PATH gets raw skill files. It doesn't say this only applies when the agent isn't named with --agents. Someone who reads it and runs databricks aitools install --agents=claude-code on a desktop-only install gets "'claude' is not on PATH" instead of skill files. Add "unless it is named with --agents", or point to --skills-only for that case.

Comment thread cmd/aitools/update_test.go Outdated
claude := agents.ByName(agents.NameClaudeCode)
cursor := agents.ByName(agents.NameCursor)
all := []*agents.Agent{claude, cursor}
writeManifest := func(scope, projectPath string) {

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] writeManifest is an inline closure that captures t and home just to name a repeated step. .agents/rules/style-guide-go.md asks for a named function instead. The same "create .claude/plugins and write installed_plugins.json" code is repeated several times in install_test.go and in this file. Extract one named helper, e.g. writeClaudeManifest(t, home, scope, projectPath string), and use it everywhere.

Comment thread libs/aitools/agents/plugins.go Outdated
return false, err
}
for _, install := range installs {
if install.Scope == "project" && install.appliesTo(cwd) {

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] "project" is now used here and in appliesTo's switch (line 44). .agents/rules/style-guide-go.md asks for a package-level constant once a value is used in more than one place. Add constants for Claude's scope names ("project", "local") and use them in both places.

@anton-107
anton-107 force-pushed the aitools/nothing-installed-fails branch 3 times, most recently from e487a01 to d71defa Compare November 12, 2026 17:14
@anton-107
anton-107 force-pushed the aitools/skills-fallback-for-detected-agents branch from 4baa441 to bf467ae Compare November 12, 2026 18:25
@anton-107
anton-107 force-pushed the aitools/nothing-installed-fails branch from d71defa to fd3519d Compare November 13, 2026 06:38
A detected coding agent whose CLI is not on PATH (for example a Claude Code desktop or IDE install: `~/.claude` exists, `claude` does not) used to be skipped, so the run left it with nothing. It now gets raw skill files, unless it already has the plugin.

The plan decides this, so the picker label and what the run does agree, and the agent joins the one skills batch of the run:

```diff
 planItemFor (plugin agent, scope supported)
-  plugin
+  if CLI not on PATH, the agent is detected (config dir), and not named via --agents
+    if the agent already has the plugin for this scope
+      keep it (plugin delivery, nothing installed; outcome "kept")
+    else
+      skill files instead (skills delivery)
+  else
+    plugin
```

- "Already has the plugin": Claude Code's own `installed_plugins.json` decides (a `claude plugin uninstall` leaves the CLI's record behind); when that file exists but cannot be read, the CLI's install state for the scope decides, as it does for other agents. A global install counts only a machine-wide plugin. A project-scope install counts only a project-scope plugin of this project: its skill files are shared through the repository, while a user or local plugin is personal. For Codex and GitHub Copilot only a plugin installed by `databricks aitools` is known, so one installed another way shows every skill twice next to the skill files.
- A kept plugin: install prints how to refresh it and, before the skills batch, removes raw skills an earlier run gave the agent, as a plugin install does, when the agent's own manifest shows the plugin (the CLI's record alone outlives a removal with the agent's own CLI). The picker labels it "plugin installed · CLI not found".
- Skill files instead: install prints how to switch to the plugin, and after the skills install drops a stale plugin record from the CLI's install state (failure to do so is a warning). When the skills install fails after a picker selection, the recovery step re-runs the skills agents with `--skills-only`, since a named agent does not fall back.
- A global-scope fallback in a directory where the agent has a project or local databricks plugin still runs, and warns that this project will see both the plugin and the global skill files.
- A kept plugin counts like an installed one: the run succeeds, with telemetry outcome `NOTHING_TO_DO` when it did nothing else.
- Agents named via `--agents`, and agents whose CLI is on PATH but whose `plugin` command fails, keep failing with the exact commands to run.
- `aitools update` no longer tries to migrate an agent whose CLI is not on PATH from raw skills to the plugin, and no longer writes raw skills to an agent whose CLI is not on PATH and whose own manifest shows the plugin for the scope (a kept plugin has no CLI record): it removes them instead, and skips the reconcile when no agent is left that can take file skills in the scope.
- New: `installer.PluginRecorded` (also used for the User-Agent tooling), `installer.ForgetPluginInstalls`, `agents.Agent.HasPluginManifestReader`, and an exported `agents.Agent.DatabricksPluginActiveIn` that reports an unreadable manifest as an error.
- New acceptance test `acceptance/experimental/aitools/skills/install-non-interactive`.
- Changelog fragment: `.nextchanges/cli/aitools-install-skills-fallback.md`.

Co-authored-by: Isaac <no-reply@databricks.com>
@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

❌ Integration test report

Commit: 8a17f53

Run: 43385350104

Env ❌​FAIL 🟨​KNOWN 💚​RECOVERED ✅​pass 🙈​skip Time
❌​ aws linux-2core-8gb 44 1 11 1507 1290 277:48
❌​ aws-windows-latest-4core-16gb 43 3 9 1427 1317 264:33
❌​ azure linux-2core-8gb 42 1 11 1489 1298 249:46
❌​ azure-windows-latest-4core-16gb 39 4 8 1411 1325 259:46
❌​ gcp linux-2core-8gb 41 3 9 1497 1296 273:14
❌​ gcp-windows-latest-4core-16gb 41 1 11 1416 1323 278:28
67 interesting tests: 55 FAIL, 8 RECOVERED, 4 KNOWN
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 🟨​K 🟨​K 🟨​K 🟨​K 🟨​K 🟨​K
❌​ TestAccept/bundle/config-remote-sync/multiple_resources ✅​p 🙈​s ❌​F 🙈​s ✅​p 🙈​s
❌​ TestAccept/bundle/config-remote-sync/multiple_resources/DMS= ✅​p ❌​F ✅​p
❌​ TestAccept/bundle/config-remote-sync/multiple_resources/DMS=true ✅​p ❌​F ✅​p
❌​ TestAccept/bundle/config-remote-sync/select_basic ✅​p 🙈​s ✅​p 🙈​s ❌​F 🙈​s
❌​ TestAccept/bundle/config-remote-sync/select_basic/DMS= ✅​p ✅​p ❌​F
❌​ TestAccept/bundle/dms/declined-deploy ✅​p ✅​p ✅​p ✅​p ✅​p ❌​F
❌​ TestAccept/bundle/dms/declined-deploy/DMS=true ✅​p ✅​p ✅​p ✅​p ✅​p ❌​F
❌​ TestAccept/bundle/resources/alerts/basic ✅​p ❌​F ✅​p ✅​p ✅​p ✅​p
❌​ TestAccept/bundle/resources/alerts/basic/DMS=true ✅​p ❌​F ✅​p ✅​p ✅​p ✅​p
💚​ TestAccept/bundle/resources/apps/lifecycle-started 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
💚​ TestAccept/bundle/resources/apps/lifecycle-started-omitted 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
💚​ TestAccept/bundle/resources/apps/lifecycle-started-omitted/DMS=true 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
💚​ TestAccept/bundle/resources/apps/lifecycle-started-toggle 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
💚​ TestAccept/bundle/resources/apps/lifecycle-started-toggle/DMS= 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
💚​ TestAccept/bundle/resources/apps/lifecycle-started-toggle/DMS=true 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
💚​ TestAccept/bundle/resources/apps/lifecycle-started/DMS= 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
💚​ TestAccept/bundle/resources/apps/lifecycle-started/DMS=true 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
❌​ TestAccept/bundle/resources/materialization_sets/immutable ❌​F ✅​p ✅​p ✅​p ✅​p ✅​p
❌​ TestAccept/bundle/resources/materialization_sets/immutable/DMS= ❌​F ✅​p ✅​p ✅​p ✅​p ✅​p
❌​ TestAccept/bundle/resources/materialization_sets/immutable/DMS=true ❌​F ✅​p ✅​p ✅​p ✅​p ✅​p
❌​ TestAccept/bundle/resources/permissions/jobs/delete_one ❌​F ✅​p ✅​p ✅​p ✅​p ✅​p
❌​ TestAccept/bundle/resources/permissions/jobs/delete_one/DMS=true ❌​F ✅​p ✅​p ✅​p ✅​p ✅​p
❌​ TestAccept/bundle/resources/postgres_branches/update_protected ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_branches/update_protected/DMS= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_branches/update_protected/DMS=true ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_branches/update_ttl ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_branches/update_ttl/DMS= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_branches/update_ttl/DMS=true ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_databases/update ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_databases/update/DMS= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_databases/update/DMS=true ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_endpoints/remove_suspend_timeout ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_endpoints/remove_suspend_timeout/DMS= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_endpoints/remove_suspend_timeout/DMS=true ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_endpoints/update_autoscaling ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_endpoints/update_autoscaling/DMS= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_endpoints/update_autoscaling/DMS=true ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_endpoints/update_pg_settings ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_endpoints/update_pg_settings/DMS= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_endpoints/update_pg_settings/DMS=true ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_endpoints/update_suspend_timeout ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_endpoints/update_suspend_timeout/DMS= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_endpoints/update_suspend_timeout/DMS=true ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_projects/add_default_endpoint_settings ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_projects/add_default_endpoint_settings/DMS= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_projects/add_default_endpoint_settings/DMS=true ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_projects/remove_history_retention ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_projects/remove_history_retention/DMS= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_projects/remove_history_retention/DMS=true ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_projects/update_default_endpoint_suspend ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_projects/update_default_endpoint_suspend/DMS= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_projects/update_default_endpoint_suspend/DMS=true ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_projects/update_display_name ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_projects/update_display_name/DMS= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_projects/update_display_name/DMS=true ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_projects/update_history_retention ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_projects/update_history_retention/DMS= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_projects/update_history_retention/DMS=true ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_roles/update ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_roles/update/DMS= ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/resources/postgres_roles/update/DMS=true ❌​F ❌​F ❌​F ❌​F ❌​F ❌​F
🟨​ TestAccept/bundle/resources/postgres_synced_tables/recreate 💚​R 🟨​K 💚​R 🟨​K 🟨​K 💚​R
🟨​ TestAccept/bundle/resources/postgres_synced_tables/recreate/DMS= 💚​R 💚​R 💚​R 🟨​K 💚​R 💚​R
🟨​ TestAccept/bundle/resources/postgres_synced_tables/recreate/DMS=true 💚​R 🟨​K 💚​R 🟨​K 🟨​K 💚​R
❌​ TestAccept/bundle/templates/default-python/combinations/serverless ✅​p ❌​F ✅​p ✅​p ✅​p ✅​p
❌​ TestAccept/bundle/templates/default-python/combinations/serverless/DLT=no/DMS=/NBOOK=yes/PY=no/READPLAN= ✅​p ❌​F ✅​p ✅​p ✅​p ✅​p
Top 50 slowest tests (at least 2 minutes):
duration env testname
10:07 aws-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
9:55 aws-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
9:51 azure-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
9:43 aws linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
9:33 azure linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
9:20 azure linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
9:15 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
9:14 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
9:07 azure linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
9:01 aws linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
9:01 azure-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
8:47 aws-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
8:30 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/deploy/local_ssd_count/DMS=
7:55 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
7:44 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
7:42 gcp linux-2core-8gb TestAccept/bundle/resources/clusters/deploy/local_ssd_count/DMS=true
7:35 aws linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
7:09 azure-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
7:04 gcp linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=
7:03 gcp linux-2core-8gb TestAccept/bundle/resources/clusters/deploy/local_ssd_count/DMS=
7:02 gcp linux-2core-8gb TestAccept/bundle/config-remote-sync/multiple_resources/DMS=true
7:00 gcp linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
6:55 gcp linux-2core-8gb TestAccept/bundle/config-remote-sync/multiple_resources/DMS=
6:48 gcp linux-2core-8gb TestAccept/bundle/resources/apps/lifecycle-started/DMS=true
6:46 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DMS=true
6:41 gcp-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/deploy/local_ssd_count/DMS=true
6:37 azure-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
6:36 gcp linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
6:34 azure-windows-latest-4core-16gb TestAccept/bundle/deploy/spark-jar-task/DMS=true
6:28 azure-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/resize-terminated-fallback/DMS=
6:20 aws-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
6:13 aws linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=true
6:06 gcp-windows-latest-4core-16gb TestAccept/bundle/invariant/delete_idempotent/DMS=/INPUT_CONFIG=cluster_apply_policy_default_values.yml.tmpl/READPLAN=1
6:03 gcp linux-2core-8gb TestAccept/bundle/invariant/delete_idempotent/DMS=/INPUT_CONFIG=job_table_update_trigger.yml.tmpl/READPLAN=1
6:00 aws linux-2core-8gb TestAccept/bundle/resources/clusters/deploy/simple/DMS=true
5:56 gcp-windows-latest-4core-16gb TestAccept/bundle/invariant/delete_idempotent/DMS=/INPUT_CONFIG=cluster.yml.tmpl/READPLAN=
5:40 gcp linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DMS=
5:36 aws-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DMS=true
5:26 gcp linux-2core-8gb TestAccept/bundle/invariant/delete_idempotent/DMS=/INPUT_CONFIG=cluster_apply_policy_default_values.yml.tmpl/READPLAN=
5:24 gcp-windows-latest-4core-16gb TestAccept/bundle/deploy/spark-jar-task/DMS=
5:22 aws-windows-latest-4core-16gb TestAccept/bundle/resources/materialization_sets/immutable/DMS=
5:19 gcp linux-2core-8gb TestAccept/bundle/invariant/delete_idempotent/DMS=/INPUT_CONFIG=job_run.yml.tmpl/READPLAN=1
5:19 azure linux-2core-8gb TestAccept/bundle/resources/clusters/lifecycle-started/DMS=
5:14 gcp linux-2core-8gb TestAccept/bundle/deploy/spark-jar-task/DMS=true
5:14 gcp-windows-latest-4core-16gb TestAccept/pipelines/run/refresh-flags
5:12 gcp-windows-latest-4core-16gb TestAccept/bundle/deploy/spark-jar-task/DMS=true
5:05 azure-windows-latest-4core-16gb TestAccept/pipelines/run/refresh-flags
5:04 gcp linux-2core-8gb TestAccept/bundle/invariant/delete_idempotent/DMS=/INPUT_CONFIG=job_cross_resource_ref.yml.tmpl/READPLAN=
5:01 azure-windows-latest-4core-16gb TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DMS=
4:59 aws-windows-latest-4core-16gb TestAccept/bundle/resources/materialization_sets/immutable/DMS=true

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.

4 participants