Skip to content

feat(skills): own azure.ai.skill service blocks in azure.yaml#9116

Open
glharper wants to merge 2 commits into
mainfrom
glharper/9088-skills-service-ownership
Open

feat(skills): own azure.ai.skill service blocks in azure.yaml#9116
glharper wants to merge 2 commits into
mainfrom
glharper/9088-skills-service-ownership

Conversation

@glharper

Copy link
Copy Markdown
Member

Summary

Gives the azure.ai.skills extension ownership of its host: azure.ai.skill service blocks in azure.yaml: command-driven authoring, idempotent updates, reading, and deploy-time reconciliation.

Developers can now opt a skill into the current azd project directly from the owning extension:

azd ai skill create triage-rules --file ./skills/triage-rules --save-to-azure-yaml
azd ai skill update triage-rules --file ./SKILL.md --save-to-azure-yaml

This has no dependency on the agents extension. An agent that consumes the skill declares the dependency using the existing uses: field.

Fixes #9088

What changed

azure.ai.skills owns authoring and updates

  • Adds --save-to-azure-yaml to skill create and skill update (opt-in; existing remote-only behavior remains the default).
  • New manifest helper uses the azd extension framework APIs:
    • Project.Get to locate/read the current project and existing service.
    • AddService to create a new host: azure.ai.skill block.
    • GetServiceConfigSection + read/merge/SetServiceConfigSection to update an existing block while preserving uses:, project, and unrelated/unknown fields.
  • Detects a same-name service with a different host and returns a structured, actionable conflict instead of overwriting it.
  • Requires ZIP/directory references to live inside the azd project and writes a portable forward-slash relative path.
  • --set-default-version is rejected with --save-to-azure-yaml because a default-version pointer is not a declarative content source that azd deploy can reconcile.

Inline and archive declarations

Inline flags and parsed SKILL.md inputs are saved as service-level content:

services:
  triage-rules:
    host: azure.ai.skill
    description: Rules for triaging incoming issues
    instructions: Classify the issue and recommend next steps.
    tools:
      - web_search

ZIP and directory inputs are saved as an archive reference:

services:
  triage-rules:
    host: azure.ai.skill
    archive: skills/triage-rules

The existing skill service target now reads and reconciles both shapes. Archive reconciliation:

  • Accepts a .zip file or a directory containing root-level SKILL.md.
  • Reuses ArchiveDirectory safety limits for directories (no symlinks/non-regular entries; entry/size limits).
  • Rejects parent traversal and unsupported archive files.
  • Creates a new immutable default skill version, matching existing inline reconcile behavior.

The runtime validation keeps the pre-split legacy config: fallback compatible while enforcing that archive and inline fields are not combined.

Composition/adoption

  • Adds azure.ai.skill to the agents extension's generic Foundry host recognition, so a skill-only or composed unified azure.yaml round-trips through the adoption flow.
  • Documents adding the skill service name to a consuming agent's uses: list.

Tests

  • Add vs idempotent update, including preservation of uses: and unknown fields.
  • Host-name collision and missing-project structured errors.
  • Project-relative archive path conversion and outside-project rejection.
  • Inline/archive config parsing, traversal rejection, ZIP/directory preparation, unsupported file rejection.
  • --save-to-azure-yaml flag registration and --set-default-version conflict.
  • Regression coverage for multi-line inline instructions ending in .md/.txt (must not be misclassified as a file path).
  • Agents adoption recognition for a skill-only manifest.

Validation run:

  • go build ./... and go test ./... in azure.ai.skills
  • golangci-lint run ./... and extension cspell in azure.ai.skills
  • agents internal/cmd test/lint and cspell for the adoption change
  • schema JSON parse and git diff --check

Add an opt-in --save-to-azure-yaml flow to skill create/update, preserving existing uses and unrelated service fields during idempotent updates. Reconcile inline content or project-relative ZIP/directory archive references through the existing service target, and recognize azure.ai.skill in the agents adoption scan.

Fixes #9088

Co-authored-by: Copilot <[email protected]>

Copilot-Session: 3aee4aec-260c-4b09-8748-212aa891d664
Copilot AI review requested due to automatic review settings July 13, 2026 15:20
@github-actions

Copy link
Copy Markdown

📋 Prioritization Note

Thanks for the contribution! The linked issue isn't in the current milestone yet.
Thank you for logging this issue; our team is reviewing it. If you need urgent prioritization, tag @RickWinter and @kristenwomack to let us know.

Copilot AI 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.

Pull request overview

Adds azure.ai.skill service ownership to the skills extension, including manifest authoring and deployment reconciliation.

Changes:

  • Adds opt-in azure.yaml persistence for create/update commands.
  • Supports inline and archive-based skill reconciliation.
  • Recognizes skill services during agents adoption.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
schemas/azure.ai.skill.json Defines archive configuration.
README.md Documents skill composition.
internal/exterrors/codes.go Adds manifest/reconcile error codes.
internal/cmd/skill_update.go Adds manifest persistence to updates.
internal/cmd/skill_update_test.go Tests update flag validation.
internal/cmd/skill_service_config.go Implements service upsert logic.
internal/cmd/skill_service_config_test.go Tests service authoring.
internal/cmd/skill_create.go Adds manifest persistence to creation.
internal/cmd/skill_create_test.go Tests create flag registration.
internal/cmd/service_target.go Reconciles archive declarations.
internal/cmd/service_target_test.go Tests archive parsing/preparation.
azure.ai.agents/internal/cmd/init_adopt.go Recognizes skill hosts.
azure.ai.agents/internal/cmd/init_adopt_test.go Tests skill-only adoption detection.


cfg := declaration.Config
if declaration.ArchiveSource != "" {
archive, err := portableSkillArchiveReference(project.GetPath(), declaration.ArchiveSource)
Comment on lines +261 to +265
baseDir := svc.GetRelativePath()
if baseDir == "" {
baseDir = "."
}
return filepath.Join(baseDir, path), nil
Comment on lines +143 to +147
if err := a.saveService(ctx, skillServiceConfig{
Description: a.flags.description,
Instructions: a.flags.instructions,
}, ""); err != nil {
return err
Comment on lines +132 to +137
if err := a.saveService(ctx, skillServiceConfig{
Description: content.Description,
Instructions: content.Instructions,
Tools: content.AllowedTools,
}, ""); err != nil {
return err

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

Traced the archive vs inline validation, the field-preserving upsert merge (drops stale archive on an inline switch while keeping uses: and unknown fields), and the path handling: portableSkillArchiveReference rejects out-of-project sources and resolveSkillArchivePath/hasParentTraversal block .. escapes. The save-time project-root-relative vs deploy-time service-relative resolution lines up with the existing resolveSkillInstructions convention and the schema doc. The isInstructionFilePath multiline guard is right and covered by a regression test. Tests cover add/update, host conflict, missing project, traversal, outside-project, zip/dir prep, and the --set-default-version conflict.

@github-actions github-actions Bot added ext-agents azure.ai.agents extension ext-skills azure.ai.skills extension labels Jul 13, 2026
Copilot AI review requested due to automatic review settings July 13, 2026 23:53
@glharper glharper removed the request for review from Copilot July 13, 2026 23:53

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

Incremental review of the preflight-persistence commit. Splitting the azure.yaml save into prepare + save runs validation before the Azure delete/create, so a manifest conflict no longer leaves the service mutated with azure.yaml unwritten. Force-delete relocation covers all four create modes, and anchoring archive resolution to the project path (with the traversal guard retained) fixes the nested-working-directory case. Tests, vet, and go fix are clean.

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

I'd like to wait on the "authoring" piece of the corresponding issue until John is back from vacation; there needs to be further discussion about how we introduce those pieces. For now, if we want to make progress on this work item and the others like it, we should focus on provision and deploy surface areas, and leave any authoring pieces that may exist within the agents extension for the time being.

cmd.Flags().StringVar(&flags.file, "file", "",
"Path to SKILL.md (.md), a ZIP package (.zip), or a directory containing SKILL.md at its root")
cmd.Flags().BoolVar(&flags.force, "force", false, "Delete an existing skill of the same name before creating")
cmd.Flags().BoolVar(

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.

I'd like John to weigh in on a parameter like this when he is back from vacation

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

Labels

ext-agents azure.ai.agents extension ext-skills azure.ai.skills extension

Projects

None yet

Development

Successfully merging this pull request may close these issues.

azure.ai.skills: own the azure.ai.skill service block in azure.yaml

5 participants