# REMOTE-1453: Tech Spec - Resolve service-account global skills at agent driver startup ## Context The product behavior is defined in `specs/REMOTE-1453/PRODUCT.md`. This spec covers the client-side runtime implementation for resolving service-account `User.globalSkills` during Oz agent startup. Relevant existing pieces: - `crates/warp_cli/src/skill.rs`: `SkillSpec`, `SkillSpec::from_str`, `SkillSpec::is_full_path`. - `app/src/ai/skills/file_watchers/skill_watcher.rs`: scans repository trees for `SKILL.md` files. - `app/src/ai/skills/skill_manager.rs`: stores the loaded skills for the active run. - `app/src/ai/agent_sdk/driver/environment.rs`: environment repository clone helper used by environment prep. - `app/src/ai/cloud_environments/mod.rs`: `GithubRepo { owner, repo }` and `AmbientAgentEnvironment.github_repos`. - `app/src/ai/agent_sdk/driver.rs`: agent startup sequence, environment prep, and Oz-only repo skill loading. ## Implemented approach ### 1. Global skill parsing helpers `app/src/ai/skills/global_skills.rs` owns the reusable parsing and filtering helpers and is re-exported through `app/src/ai/skills/mod.rs`. - `resolve_skill_repos(raw_specs: &[String]) -> (Vec, Vec)` parses raw server strings with the existing CLI parser, logs warn-level parse failures, skips invalid entries, and extracts org-qualified `(org, repo)` pairs from the parsed specs while preserving first-seen order. - `filter_skills_by_spec(repo_path, skills, specs)` takes the full scan result for one global-only repo and returns only the parsed skills explicitly requested for that repo. Filtering behavior: - Full-path specs match `repo_path.join(spec.skill_identifier)` exactly. - Simple-name specs match parsed `ParsedSkill::name` values and pick the first match by provider-directory precedence. - Duplicate selected paths are collapsed. ### 2. Driver source classification `app/src/ai/agent_sdk/driver.rs` separates global skill resolution from repository skill loading using two explicit async methods rather than a unified load-request type. - `GlobalSkillResolution` carries parsed global specs and cloneable global skill repos. - `load_environment_skills` handles all repos that are part of the configured environment: it waits for `RepoMetadataModel` indexing to complete, then scans with `SkillWatcher::read_skills_for_repos` and loads all discovered skills. - `load_global_skills` handles global-only repos: it reads directly from the known provider directories on disk via `read_skills_from_directories` (no `RepoMetadataModel` dependency), then applies `filter_skills_by_spec` to keep only the explicitly requested skills. Because global skill repos are cloned before environment prep and are not registered with `DetectedRepositories`, they are not picked up by `SkillWatcher`'s `RepositoryMetadataEvent` path; `load_global_skills` reads them directly from disk instead. If a repo appears in both the environment and the global skill set, `load_environment_skills` runs over it and loads all skills; `load_global_skills` may also process it and add the filtered subset, but `SkillManager` deduplicates by path so the effective result is all skills from that repo. ### 3. Driver startup sequence For Oz harnesses, `AgentDriver::run_internal` now does the following after the session is shared and before environment prep: 1. Read cached `AuthStateProvider::get().global_skills()`. 2. Parse specs and resolve cloneable repos with `resolve_skill_repos`. 3. Clone those repos best-effort with `environment::clone_repo` (which clones the repo but does NOT register it with `DetectedRepositories`, so only the explicitly requested global skills are loaded from it). 4. Prepare the configured environment, if any, preserving the existing file-based MCP discovery and setup-command sequencing. Environment repos are cloned via `ensure_repo_cloned`, which both clones and registers with `DetectedRepositories`. 5. Call `load_environment_skills` with the environment's `github_repos`, then `load_global_skills` with the global specs and repos. Global-skill cloning is performed for all harnesses (before the Oz-only guard) so that skills are on disk, but skill loading into `SkillManager` remains Oz-only because third-party harnesses have separate skill systems. ### 4. Repo skill loading Skill loading is split into two separate functions instead of a single unified `load_skills_from_repos`: - `load_environment_skills(foreground, repos)`: waits for `RepoMetadataModel` indexing on each repo, then scans with `SkillWatcher::read_skills_for_repos` and loads all discovered skills into `SkillManager`. - `load_global_skills(foreground, specs, repos)`: reads skills directly from provider directories via `read_skills_from_directories` (using `SKILL_PROVIDER_DEFINITIONS`), applies `filter_skills_by_spec` to keep only explicitly requested skills, and loads the result into `SkillManager`. This path does not depend on `RepoMetadataModel` because global-skill repos are not registered with `DetectedRepositories`. Both functions call `SkillManager::set_cloud_environment(true)` and `handle_skills_added`, preserving the existing in-scope behavior for repo-loaded skills. ## Behavior guarantees - Environment repos continue to autodiscover all skills. - Global-only repos do not expose unrelated skills from the same repo. - Environment/global overlap is resolved in favor of environment autodiscovery. - Clone failures for global-skill repos are warn-and-continue. - Invalid global skill specs are warn-and-skip. - Non-cloneable specs do not trigger clone attempts. ## Testing and validation Unit coverage added or expected: - `resolve_skill_repos` skips invalid specs and handles empty input, unqualified specs, org-qualified specs, and duplicate repos. - `filter_skills_by_spec` loads only requested simple-name skills, respects provider precedence, and matches full-path specs. Validation commands: - `cargo fmt` - Targeted `cargo nextest` for the `global_skills` and `driver` tests touched by this change. - A targeted `cargo check` or package build if test compilation exposes broader type issues. ## Follow-ups - Add integration coverage around a real service-account run once the test harness can inject `User.globalSkills` and cloned repo contents cheaply. - Consider refreshing `User.globalSkills` at run start if cached user state can become stale for long-lived clients. - Consider global-skill provenance in skill listing UI or logs if users need to debug why a skill was available. - Revisit third-party harness behavior if those harnesses adopt the same runtime skill loading path.