From e4d39ba71e2088910bd9bf51e6484cc1b2731483 Mon Sep 17 00:00:00 2001 From: huimiu Date: Fri, 10 Jul 2026 21:54:13 +0800 Subject: [PATCH 1/5] feat: add unified azure yaml design --- docs/specs/unified-azure-yaml/spec.md | 334 ++++++++++++++++++++++++++ 1 file changed, 334 insertions(+) create mode 100644 docs/specs/unified-azure-yaml/spec.md diff --git a/docs/specs/unified-azure-yaml/spec.md b/docs/specs/unified-azure-yaml/spec.md new file mode 100644 index 00000000000..d2f5cd0612d --- /dev/null +++ b/docs/specs/unified-azure-yaml/spec.md @@ -0,0 +1,334 @@ + + +# Unified azure.yaml configuration and core next-step guidance + +## Context and scope + +This design implements the configuration migration described by +[issue #8710](https://github.com/Azure/azure-dev/issues/8710). It also +addresses the missing guidance between `azd provision` and `azd deploy` +described by [issue #8804](https://github.com/Azure/azure-dev/issues/8804). + +The design covers the Azure AI Agents extension's configuration readers, +state-aware guidance, and doctor checks. It does not redesign Foundry +provisioning, agent deployment, or the content of individual agent manifests. + +## Part 1: End-to-end experience + +### New project initialization + +`azd ai agent init` writes the complete deployable agent and Foundry resource +configuration into `azure.yaml`. It does not require a generated `agent.yaml` +or a retained `agent.manifest.yaml` for later guidance or doctor checks. + +```yaml +services: + travel-agent: + host: azure.ai.agent + project: src/travel-agent + uses: + - travel-project + - search-connection + - travel-tools + kind: hosted + name: travel-agent + protocols: + - protocol: responses + environmentVariables: + - name: SEARCH_KEY + value: ${SEARCH_KEY} + container: + resources: + cpu: "0.5" + memory: 1Gi + + travel-project: + host: azure.ai.project + deployments: + - name: chat + model: + name: gpt-4o + format: OpenAI + version: "2024-11-20" + sku: + name: GlobalStandard + capacity: 1 + + search-connection: + host: azure.ai.connection + name: search + category: AzureAISearch + target: https://example.search.windows.net + authType: ApiKey + + travel-tools: + host: azure.ai.toolbox + name: travel-tools + tools: [] +``` + +The agent's `uses` entries preserve the resource ordering. The project service +is provisioned before connections and toolboxes, and those resources are ready +before the agent deploys. Existing Foundry projects use `endpoint` on the +`azure.ai.project` service instead of creating a second project. + +### First provision + +After a successful interactive `azd provision`, core azd prints one +consolidated terminal block after its command summary. The extension contributes +the suggestions, but core owns rendering and ordering. + +```text +Next: + azd deploy + deploy the agent to Azure +``` + +If state still needs user input, the block names that prerequisite rather than +suggesting an action that will fail. For example, an unresolved environment +value remains actionable after a partial configuration: + +```text +Next: + azd env set SEARCH_KEY + referenced by azure.yaml but not set in azd env + + azd deploy + deploy the agent to Azure +``` + +The block is omitted for JSON output, redirected output, and noninteractive +output. Scripts retain their existing stable machine-readable output. + +### Deploy and inner loop + +After `azd deploy`, core prints one top-level block for all deployed agent +services. It replaces the current behavior where guidance is visible only in a +single endpoint artifact note. + +```text +Next: + azd ai agent show travel-agent + verify it is running + + azd ai agent invoke travel-agent '' + test the deployment +``` + +`azd ai agent init`, `run`, `invoke`, `show`, and a successful `doctor` +continue to use the same resolver. They derive agent protocol, +environment-variable references, unresolved placeholders, model deployments, +connections, and toolboxes from the parsed `azure.yaml` service graph. A +sample payload from OpenAPI or a sibling README remains optional enrichment, +not configuration input. + +### Existing projects and migration + +Projects that already have unified service entries continue without a warning. +The readers support both flat service properties and the deprecated +config-nested service properties through `project.ServiceConfigProps`. + +Projects whose guidance or doctor result still depends on an on-disk +`agent.yaml` or `agent.manifest.yaml` receive one migration warning with the +existing migration guide. The warning directs the user to rerun +`azd ai agent init`. During the compatibility window, deployment can retain +the existing `project.LoadAgentDefinition` fallback, but next-step and doctor +state do not treat legacy files as authoritative. + +### Reuse, teardown, and failure + +An `azure.ai.project` service with `endpoint` represents a reused Foundry +project. Guidance and doctor checks inspect its declared sibling resources but +do not infer ownership from the endpoint or recommend deletion. `azd down` +continues to follow the resource lifecycle ownership already defined by the +project, connection, toolbox, and agent service targets. + +If provision succeeds but the extension cannot assemble its optional state, +core still reports provision success and omits the contributed block. If the +extension returns a structured contribution error, core logs it only in debug +diagnostics and does not change the completed command's exit status. A later +`azd ai agent doctor` reconstructs state from `azure.yaml`, so a rerun is safe +and does not depend on a partially written manifest file. + +## Part 2: Technical design + +### Configuration source and migration boundary + +`cli/azd/extensions/azure.ai.agents/internal/cmd/init.go` already converts the +agent template with `project.AgentDefinitionToServiceProperties` and writes it +as `ServiceConfig.AdditionalProperties`. It then calls `emitResourceServices` +in `internal/cmd/resource_services.go` to create: + +- one `azure.ai.project` service that owns `deployments` +- one `azure.ai.connection` service for each connection +- one `azure.ai.toolbox` service for each toolbox +- `uses` edges from the agent service to those siblings + +The migration completes this model by making all state consumers read the same +parsed service graph. `project.AgentDefinitionFromService` and +`project.LoadServiceTargetAgentConfig` in +`internal/project/agent_definition.go` are the canonical readers for agent +definition fields and agent-specific settings. `ServiceConfigProps` preserves +the config-nested compatibility path without adding another reader. + +### State assembly + +Replace the file-oriented portions of `nextstep.AssembleState` in +`internal/cmd/nextstep/state.go` with service-graph collectors: + +1. Keep `collectServices` as the filtered and sorted list of + `host: azure.ai.agent` services. +2. Replace `loadServiceProtocol` with a reader that obtains `Protocols` from + `project.AgentDefinitionFromService`. +3. Replace `detectMissingVars` with a reader of the inline + `EnvironmentVariables` in `AgentDefinitionInline`. It preserves the + existing `${VAR}` and `${VAR:-default}` semantics and detects literal + `{{NAME}}` placeholders in the same values. +4. Replace `populateManifestResources` in + `internal/cmd/nextstep/manifest.go` with a collector over + `azure.ai.project`, `azure.ai.connection`, and `azure.ai.toolbox` services. + It must use `project.UnmarshalStruct` with the corresponding + `ServiceTargetAgentConfig`, `Connection`, and `Toolbox` types. +5. Preserve deterministic ordering and the `(ServiceName, Name)` identity + semantics of `ResourceRef`. For a resource sibling, `ServiceName` is the + owning agent reached through its `uses` edge. A shared resource is reported + for every consuming agent, not silently attributed to one of them. + +The collector must reject malformed service properties as a state-assembly +diagnostic rather than silently interpreting a malformed unified entry as no +resources. Optional OpenAPI and README lookups remain best effort because they +only improve command examples. + +### Guidance resolution and rendering + +`nextstep.ResolveAfterInit` and `nextstep.ResolveAfterDeploy` remain pure +functions over `nextstep.State`. Their decision trees do not depend on file +names after this migration. `nextstep.PrintNext`, `PrintAllNext`, and +`FormatNextForNote` remain formatters, with one behavioral change: endpoint +artifact notes are no longer the primary post-deploy surface. + +Create a core extension result contract for project lifecycle events. Today, +`azdext.ProjectEventHandler` in `cli/azd/pkg/azdext/event_manager.go` returns +only `error`, and `eventService.createProjectEventHandler` in +`cli/azd/internal/grpcserver/event_service.go` converts it only to completion +or failure status. The extension must not write directly to stdout from +`postprovisionHandler`, because that output can interleave with the core +progress UI. + +The core contract needs a structured, ordered post-command contribution that: + +- is returned by a successful project or service event handler +- is collected for the encompassing command +- is rendered by core after the final command summary +- is suppressed for JSON and noninteractive output +- carries a source identifier for debug diagnostics +- treats rendering failures as diagnostic, not command failures + +The agents extension's `postprovisionHandler` in +`internal/cmd/listen.go` first clears `AI_AGENT_PENDING_PROVISION` after a +successful provision. It then assembles the refreshed state and contributes +the post-provision resolver output through the new contract. No direct +terminal write is added to the handler. + +For deploy, `AgentServiceTargetProvider.Deploy` in +`internal/project/service_target_agent.go` currently calls +`augmentDeployNote`, which scopes `ResolveAfterDeploy` to one service and +embeds the result in `Artifact.Metadata["note"]`. Replace that embedding with +a structured contribution collected across deployed services. The final +resolver invocation receives the full deployed service set once, so multi-agent +projects produce one ordered block rather than repeated per-service blocks. +The static endpoint documentation note remains an endpoint artifact detail and +does not carry the navigation workflow. + +### Doctor + +`azd ai agent doctor` retains `nextstep.AssembleState` as its shared snapshot +source. Update `local.agent-yaml-valid` in +`internal/cmd/doctor/checks_project.go` to validate an inline agent definition +with `project.AgentDefinitionFromService` and the existing +`agent_yaml.ValidateAgentDefinition` rules. It no longer requires an +`agent.yaml` file per service. + +Update `local.toolboxes` and `remote.connections` in +`internal/cmd/doctor/checks_toolboxes.go` and +`internal/cmd/doctor/checks_connections.go` to use the unified resource +references. Their status names, skip conditions, details payloads, and +remediation text must refer to declared `azure.yaml` services, not manifest +resources. `resolveDoctorTrailing` in `internal/cmd/doctor.go` continues to +select deploy guidance for deployed services and init guidance otherwise. + +### Errors, compatibility, and observability + +Malformed inline configuration returns the existing structured validation error +shape, including the service name and a suggestion to regenerate or fix its +`azure.yaml` entry. Missing legacy files are not an error for unified projects. +When compatibility fallback is used, `project.WarnLegacyAgentShape` remains the +single warning path. + +No telemetry event is required for the first implementation. The core result +contract should record extension source and rendering suppression in debug +logs. A later telemetry proposal must follow the event and field requirements +in `cli/azd/AGENTS.md`. + +## Part 3: Dependencies that need PM confirmation + +1. **Core lifecycle output contract:** Confirm that the core framework change + needed for extension-contributed terminal guidance is in scope for this + milestone. Issue #8804 cannot deliver a clean post-provision block without + it. +2. **Post-deploy presentation:** Confirm that the top-level post-deploy block + replaces the workflow content in endpoint artifact notes, while retaining + endpoint-specific documentation. This resolves the choice identified in + issue #8804. +3. **Legacy-file support window:** Confirm the release and support policy for + `agent.yaml` and `agent.manifest.yaml` after unified initialization. The + extension needs a dated removal target before compatibility readers can be + deleted. +4. **Ownership of shared resources:** Confirm whether a toolbox or connection + referenced through multiple `uses` edges should appear once per consumer or + once per resource in doctor remediation. This design proposes one result per + consumer so each agent remains actionable. + +## Part 4: Open questions + +1. What is the supported core result shape for aggregating contributions from + both project and service lifecycle events without duplicating a block during + `azd up`? +2. Should a successful `azd provision` include `azd ai agent run` as a + secondary local-development suggestion, or should it show only the required + `azd deploy` progression? +3. When an agent service has no direct `uses` edge to a resource because a user + authored an indirect graph, should doctor attribute the resource to every + reachable agent or display the resource service key without an agent owner? +4. Should unified state classification continue to inspect Bicep outputs for + infrastructure variables, or should the project service expose that + classification directly? + +## Summary of required changes + +- `cli/azd/extensions/azure.ai.agents/internal/cmd/nextstep/state.go`: assemble + agent protocols, variable references, placeholders, and resource references + from parsed `azure.yaml` service properties. +- `cli/azd/extensions/azure.ai.agents/internal/cmd/nextstep/manifest.go`: + replace manifest walking with deterministic unified service-graph collection. +- `cli/azd/extensions/azure.ai.agents/internal/cmd/nextstep/types.go`: update + resource-reference documentation and any fields needed for `uses` ownership. +- `cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_project.go`: + validate inline agent definitions rather than on-disk `agent.yaml` files. +- `cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_toolboxes.go`: + inspect unified toolbox services and update user-facing results. +- `cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_connections.go`: + inspect unified connection services and update user-facing results. +- `cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go`: contribute + post-provision suggestions after clearing pending-provision state. +- `cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go`: + replace per-artifact deploy guidance with an aggregated contribution. +- `cli/azd/pkg/azdext/event_manager.go`: add a structured lifecycle result that + can carry post-command contributions. +- `cli/azd/internal/grpcserver/event_service.go`: transport lifecycle + contributions from extensions to core. +- Core command output pipeline: collect and render contributions after command + summaries while preserving JSON and noninteractive behavior. +- Unit and integration tests: cover unified state collection, compatibility + warnings, doctor output, multi-agent aggregation, output suppression, and + core rendering order. From a0820553b5a229a879437bbe798e88c1ab9d75a5 Mon Sep 17 00:00:00 2001 From: huimiu Date: Fri, 10 Jul 2026 22:30:33 +0800 Subject: [PATCH 2/5] fix: unwrap unified azure yaml spec --- docs/specs/unified-azure-yaml/spec.md | 244 ++++++-------------------- 1 file changed, 54 insertions(+), 190 deletions(-) diff --git a/docs/specs/unified-azure-yaml/spec.md b/docs/specs/unified-azure-yaml/spec.md index d2f5cd0612d..d3da31c7568 100644 --- a/docs/specs/unified-azure-yaml/spec.md +++ b/docs/specs/unified-azure-yaml/spec.md @@ -4,22 +4,15 @@ ## Context and scope -This design implements the configuration migration described by -[issue #8710](https://github.com/Azure/azure-dev/issues/8710). It also -addresses the missing guidance between `azd provision` and `azd deploy` -described by [issue #8804](https://github.com/Azure/azure-dev/issues/8804). +This design implements the configuration migration described by [issue #8710](https://github.com/Azure/azure-dev/issues/8710). It also addresses the missing guidance between `azd provision` and `azd deploy` described by [issue #8804](https://github.com/Azure/azure-dev/issues/8804). -The design covers the Azure AI Agents extension's configuration readers, -state-aware guidance, and doctor checks. It does not redesign Foundry -provisioning, agent deployment, or the content of individual agent manifests. +The design covers the Azure AI Agents extension's configuration readers, state-aware guidance, and doctor checks. It does not redesign Foundry provisioning, agent deployment, or the content of individual agent manifests. ## Part 1: End-to-end experience ### New project initialization -`azd ai agent init` writes the complete deployable agent and Foundry resource -configuration into `azure.yaml`. It does not require a generated `agent.yaml` -or a retained `agent.manifest.yaml` for later guidance or doctor checks. +`azd ai agent init` writes the complete deployable agent and Foundry resource configuration into `azure.yaml`. It does not require a generated `agent.yaml` or a retained `agent.manifest.yaml` for later guidance or doctor checks. ```yaml services: @@ -67,16 +60,11 @@ services: tools: [] ``` -The agent's `uses` entries preserve the resource ordering. The project service -is provisioned before connections and toolboxes, and those resources are ready -before the agent deploys. Existing Foundry projects use `endpoint` on the -`azure.ai.project` service instead of creating a second project. +The agent's `uses` entries preserve the resource ordering. The project service is provisioned before connections and toolboxes, and those resources are ready before the agent deploys. Existing Foundry projects use `endpoint` on the `azure.ai.project` service instead of creating a second project. ### First provision -After a successful interactive `azd provision`, core azd prints one -consolidated terminal block after its command summary. The extension contributes -the suggestions, but core owns rendering and ordering. +After a successful interactive `azd provision`, core azd prints one consolidated terminal block after its command summary. The extension contributes the suggestions, but core owns rendering and ordering. ```text Next: @@ -84,9 +72,7 @@ Next: deploy the agent to Azure ``` -If state still needs user input, the block names that prerequisite rather than -suggesting an action that will fail. For example, an unresolved environment -value remains actionable after a partial configuration: +If state still needs user input, the block names that prerequisite rather than suggesting an action that will fail. For example, an unresolved environment value remains actionable after a partial configuration: ```text Next: @@ -97,14 +83,11 @@ Next: deploy the agent to Azure ``` -The block is omitted for JSON output, redirected output, and noninteractive -output. Scripts retain their existing stable machine-readable output. +The block is omitted for JSON output, redirected output, and noninteractive output. Scripts retain their existing stable machine-readable output. ### Deploy and inner loop -After `azd deploy`, core prints one top-level block for all deployed agent -services. It replaces the current behavior where guidance is visible only in a -single endpoint artifact note. +After `azd deploy`, core prints one top-level block for all deployed agent services. It replaces the current behavior where guidance is visible only in a single endpoint artifact note. ```text Next: @@ -115,105 +98,50 @@ Next: test the deployment ``` -`azd ai agent init`, `run`, `invoke`, `show`, and a successful `doctor` -continue to use the same resolver. They derive agent protocol, -environment-variable references, unresolved placeholders, model deployments, -connections, and toolboxes from the parsed `azure.yaml` service graph. A -sample payload from OpenAPI or a sibling README remains optional enrichment, -not configuration input. +`azd ai agent init`, `run`, `invoke`, `show`, and a successful `doctor` continue to use the same resolver. They derive agent protocol, environment-variable references, unresolved placeholders, model deployments, connections, and toolboxes from the parsed `azure.yaml` service graph. A sample payload from OpenAPI or a sibling README remains optional enrichment, not configuration input. ### Existing projects and migration -Projects that already have unified service entries continue without a warning. -The readers support both flat service properties and the deprecated -config-nested service properties through `project.ServiceConfigProps`. +Projects that already have unified service entries continue without a warning. The readers support both flat service properties and the deprecated config-nested service properties through `project.ServiceConfigProps`. -Projects whose guidance or doctor result still depends on an on-disk -`agent.yaml` or `agent.manifest.yaml` receive one migration warning with the -existing migration guide. The warning directs the user to rerun -`azd ai agent init`. During the compatibility window, deployment can retain -the existing `project.LoadAgentDefinition` fallback, but next-step and doctor -state do not treat legacy files as authoritative. +Projects whose guidance or doctor result still depends on an on-disk `agent.yaml` or `agent.manifest.yaml` receive one migration warning with the existing migration guide. The warning directs the user to rerun `azd ai agent init`. During the compatibility window, deployment can retain the existing `project.LoadAgentDefinition` fallback, but next-step and doctor state do not treat legacy files as authoritative. ### Reuse, teardown, and failure -An `azure.ai.project` service with `endpoint` represents a reused Foundry -project. Guidance and doctor checks inspect its declared sibling resources but -do not infer ownership from the endpoint or recommend deletion. `azd down` -continues to follow the resource lifecycle ownership already defined by the -project, connection, toolbox, and agent service targets. +An `azure.ai.project` service with `endpoint` represents a reused Foundry project. Guidance and doctor checks inspect its declared sibling resources but do not infer ownership from the endpoint or recommend deletion. `azd down` continues to follow the resource lifecycle ownership already defined by the project, connection, toolbox, and agent service targets. -If provision succeeds but the extension cannot assemble its optional state, -core still reports provision success and omits the contributed block. If the -extension returns a structured contribution error, core logs it only in debug -diagnostics and does not change the completed command's exit status. A later -`azd ai agent doctor` reconstructs state from `azure.yaml`, so a rerun is safe -and does not depend on a partially written manifest file. +If provision succeeds but the extension cannot assemble its optional state, core still reports provision success and omits the contributed block. If the extension returns a structured contribution error, core logs it only in debug diagnostics and does not change the completed command's exit status. A later `azd ai agent doctor` reconstructs state from `azure.yaml`, so a rerun is safe and does not depend on a partially written manifest file. ## Part 2: Technical design ### Configuration source and migration boundary -`cli/azd/extensions/azure.ai.agents/internal/cmd/init.go` already converts the -agent template with `project.AgentDefinitionToServiceProperties` and writes it -as `ServiceConfig.AdditionalProperties`. It then calls `emitResourceServices` -in `internal/cmd/resource_services.go` to create: +`cli/azd/extensions/azure.ai.agents/internal/cmd/init.go` already converts the agent template with `project.AgentDefinitionToServiceProperties` and writes it as `ServiceConfig.AdditionalProperties`. It then calls `emitResourceServices` in `internal/cmd/resource_services.go` to create: - one `azure.ai.project` service that owns `deployments` - one `azure.ai.connection` service for each connection - one `azure.ai.toolbox` service for each toolbox - `uses` edges from the agent service to those siblings -The migration completes this model by making all state consumers read the same -parsed service graph. `project.AgentDefinitionFromService` and -`project.LoadServiceTargetAgentConfig` in -`internal/project/agent_definition.go` are the canonical readers for agent -definition fields and agent-specific settings. `ServiceConfigProps` preserves -the config-nested compatibility path without adding another reader. +The migration completes this model by making all state consumers read the same parsed service graph. `project.AgentDefinitionFromService` and `project.LoadServiceTargetAgentConfig` in `internal/project/agent_definition.go` are the canonical readers for agent definition fields and agent-specific settings. `ServiceConfigProps` preserves the config-nested compatibility path without adding another reader. ### State assembly -Replace the file-oriented portions of `nextstep.AssembleState` in -`internal/cmd/nextstep/state.go` with service-graph collectors: - -1. Keep `collectServices` as the filtered and sorted list of - `host: azure.ai.agent` services. -2. Replace `loadServiceProtocol` with a reader that obtains `Protocols` from - `project.AgentDefinitionFromService`. -3. Replace `detectMissingVars` with a reader of the inline - `EnvironmentVariables` in `AgentDefinitionInline`. It preserves the - existing `${VAR}` and `${VAR:-default}` semantics and detects literal - `{{NAME}}` placeholders in the same values. -4. Replace `populateManifestResources` in - `internal/cmd/nextstep/manifest.go` with a collector over - `azure.ai.project`, `azure.ai.connection`, and `azure.ai.toolbox` services. - It must use `project.UnmarshalStruct` with the corresponding - `ServiceTargetAgentConfig`, `Connection`, and `Toolbox` types. -5. Preserve deterministic ordering and the `(ServiceName, Name)` identity - semantics of `ResourceRef`. For a resource sibling, `ServiceName` is the - owning agent reached through its `uses` edge. A shared resource is reported - for every consuming agent, not silently attributed to one of them. - -The collector must reject malformed service properties as a state-assembly -diagnostic rather than silently interpreting a malformed unified entry as no -resources. Optional OpenAPI and README lookups remain best effort because they -only improve command examples. +Replace the file-oriented portions of `nextstep.AssembleState` in `internal/cmd/nextstep/state.go` with service-graph collectors: + +1. Keep `collectServices` as the filtered and sorted list of `host: azure.ai.agent` services. +2. Replace `loadServiceProtocol` with a reader that obtains `Protocols` from `project.AgentDefinitionFromService`. +3. Replace `detectMissingVars` with a reader of the inline `EnvironmentVariables` in `AgentDefinitionInline`. It preserves the existing `${VAR}` and `${VAR:-default}` semantics and detects literal `{{NAME}}` placeholders in the same values. +4. Replace `populateManifestResources` in `internal/cmd/nextstep/manifest.go` with a collector over `azure.ai.project`, `azure.ai.connection`, and `azure.ai.toolbox` services. It must use `project.UnmarshalStruct` with the corresponding `ServiceTargetAgentConfig`, `Connection`, and `Toolbox` types. +5. Preserve deterministic ordering and the `(ServiceName, Name)` identity semantics of `ResourceRef`. For a resource sibling, `ServiceName` is the owning agent reached through its `uses` edge. A shared resource is reported for every consuming agent, not silently attributed to one of them. + +The collector must reject malformed service properties as a state-assembly diagnostic rather than silently interpreting a malformed unified entry as no resources. Optional OpenAPI and README lookups remain best effort because they only improve command examples. ### Guidance resolution and rendering -`nextstep.ResolveAfterInit` and `nextstep.ResolveAfterDeploy` remain pure -functions over `nextstep.State`. Their decision trees do not depend on file -names after this migration. `nextstep.PrintNext`, `PrintAllNext`, and -`FormatNextForNote` remain formatters, with one behavioral change: endpoint -artifact notes are no longer the primary post-deploy surface. +`nextstep.ResolveAfterInit` and `nextstep.ResolveAfterDeploy` remain pure functions over `nextstep.State`. Their decision trees do not depend on file names after this migration. `nextstep.PrintNext`, `PrintAllNext`, and `FormatNextForNote` remain formatters, with one behavioral change: endpoint artifact notes are no longer the primary post-deploy surface. -Create a core extension result contract for project lifecycle events. Today, -`azdext.ProjectEventHandler` in `cli/azd/pkg/azdext/event_manager.go` returns -only `error`, and `eventService.createProjectEventHandler` in -`cli/azd/internal/grpcserver/event_service.go` converts it only to completion -or failure status. The extension must not write directly to stdout from -`postprovisionHandler`, because that output can interleave with the core -progress UI. +Create a core extension result contract for project lifecycle events. Today, `azdext.ProjectEventHandler` in `cli/azd/pkg/azdext/event_manager.go` returns only `error`, and `eventService.createProjectEventHandler` in `cli/azd/internal/grpcserver/event_service.go` converts it only to completion or failure status. The extension must not write directly to stdout from `postprovisionHandler`, because that output can interleave with the core progress UI. The core contract needs a structured, ordered post-command contribution that: @@ -224,111 +152,47 @@ The core contract needs a structured, ordered post-command contribution that: - carries a source identifier for debug diagnostics - treats rendering failures as diagnostic, not command failures -The agents extension's `postprovisionHandler` in -`internal/cmd/listen.go` first clears `AI_AGENT_PENDING_PROVISION` after a -successful provision. It then assembles the refreshed state and contributes -the post-provision resolver output through the new contract. No direct -terminal write is added to the handler. - -For deploy, `AgentServiceTargetProvider.Deploy` in -`internal/project/service_target_agent.go` currently calls -`augmentDeployNote`, which scopes `ResolveAfterDeploy` to one service and -embeds the result in `Artifact.Metadata["note"]`. Replace that embedding with -a structured contribution collected across deployed services. The final -resolver invocation receives the full deployed service set once, so multi-agent -projects produce one ordered block rather than repeated per-service blocks. -The static endpoint documentation note remains an endpoint artifact detail and -does not carry the navigation workflow. +The agents extension's `postprovisionHandler` in `internal/cmd/listen.go` first clears `AI_AGENT_PENDING_PROVISION` after a successful provision. It then assembles the refreshed state and contributes the post-provision resolver output through the new contract. No direct terminal write is added to the handler. + +For deploy, `AgentServiceTargetProvider.Deploy` in `internal/project/service_target_agent.go` currently calls `augmentDeployNote`, which scopes `ResolveAfterDeploy` to one service and embeds the result in `Artifact.Metadata["note"]`. Replace that embedding with a structured contribution collected across deployed services. The final resolver invocation receives the full deployed service set once, so multi-agent projects produce one ordered block rather than repeated per-service blocks. The static endpoint documentation note remains an endpoint artifact detail and does not carry the navigation workflow. ### Doctor -`azd ai agent doctor` retains `nextstep.AssembleState` as its shared snapshot -source. Update `local.agent-yaml-valid` in -`internal/cmd/doctor/checks_project.go` to validate an inline agent definition -with `project.AgentDefinitionFromService` and the existing -`agent_yaml.ValidateAgentDefinition` rules. It no longer requires an -`agent.yaml` file per service. - -Update `local.toolboxes` and `remote.connections` in -`internal/cmd/doctor/checks_toolboxes.go` and -`internal/cmd/doctor/checks_connections.go` to use the unified resource -references. Their status names, skip conditions, details payloads, and -remediation text must refer to declared `azure.yaml` services, not manifest -resources. `resolveDoctorTrailing` in `internal/cmd/doctor.go` continues to -select deploy guidance for deployed services and init guidance otherwise. +`azd ai agent doctor` retains `nextstep.AssembleState` as its shared snapshot source. Update `local.agent-yaml-valid` in `internal/cmd/doctor/checks_project.go` to validate an inline agent definition with `project.AgentDefinitionFromService` and the existing `agent_yaml.ValidateAgentDefinition` rules. It no longer requires an `agent.yaml` file per service. + +Update `local.toolboxes` and `remote.connections` in `internal/cmd/doctor/checks_toolboxes.go` and `internal/cmd/doctor/checks_connections.go` to use the unified resource references. Their status names, skip conditions, details payloads, and remediation text must refer to declared `azure.yaml` services, not manifest resources. `resolveDoctorTrailing` in `internal/cmd/doctor.go` continues to select deploy guidance for deployed services and init guidance otherwise. ### Errors, compatibility, and observability -Malformed inline configuration returns the existing structured validation error -shape, including the service name and a suggestion to regenerate or fix its -`azure.yaml` entry. Missing legacy files are not an error for unified projects. -When compatibility fallback is used, `project.WarnLegacyAgentShape` remains the -single warning path. +Malformed inline configuration returns the existing structured validation error shape, including the service name and a suggestion to regenerate or fix its `azure.yaml` entry. Missing legacy files are not an error for unified projects. When compatibility fallback is used, `project.WarnLegacyAgentShape` remains the single warning path. -No telemetry event is required for the first implementation. The core result -contract should record extension source and rendering suppression in debug -logs. A later telemetry proposal must follow the event and field requirements -in `cli/azd/AGENTS.md`. +No telemetry event is required for the first implementation. The core result contract should record extension source and rendering suppression in debug logs. A later telemetry proposal must follow the event and field requirements in `cli/azd/AGENTS.md`. ## Part 3: Dependencies that need PM confirmation -1. **Core lifecycle output contract:** Confirm that the core framework change - needed for extension-contributed terminal guidance is in scope for this - milestone. Issue #8804 cannot deliver a clean post-provision block without - it. -2. **Post-deploy presentation:** Confirm that the top-level post-deploy block - replaces the workflow content in endpoint artifact notes, while retaining - endpoint-specific documentation. This resolves the choice identified in - issue #8804. -3. **Legacy-file support window:** Confirm the release and support policy for - `agent.yaml` and `agent.manifest.yaml` after unified initialization. The - extension needs a dated removal target before compatibility readers can be - deleted. -4. **Ownership of shared resources:** Confirm whether a toolbox or connection - referenced through multiple `uses` edges should appear once per consumer or - once per resource in doctor remediation. This design proposes one result per - consumer so each agent remains actionable. +1. **Core lifecycle output contract:** Confirm that the core framework change needed for extension-contributed terminal guidance is in scope for this milestone. Issue #8804 cannot deliver a clean post-provision block without it. +2. **Post-deploy presentation:** Confirm that the top-level post-deploy block replaces the workflow content in endpoint artifact notes, while retaining endpoint-specific documentation. This resolves the choice identified in issue #8804. +3. **Legacy-file support window:** Confirm the release and support policy for `agent.yaml` and `agent.manifest.yaml` after unified initialization. The extension needs a dated removal target before compatibility readers can be deleted. +4. **Ownership of shared resources:** Confirm whether a toolbox or connection referenced through multiple `uses` edges should appear once per consumer or once per resource in doctor remediation. This design proposes one result per consumer so each agent remains actionable. ## Part 4: Open questions -1. What is the supported core result shape for aggregating contributions from - both project and service lifecycle events without duplicating a block during - `azd up`? -2. Should a successful `azd provision` include `azd ai agent run` as a - secondary local-development suggestion, or should it show only the required - `azd deploy` progression? -3. When an agent service has no direct `uses` edge to a resource because a user - authored an indirect graph, should doctor attribute the resource to every - reachable agent or display the resource service key without an agent owner? -4. Should unified state classification continue to inspect Bicep outputs for - infrastructure variables, or should the project service expose that - classification directly? +1. What is the supported core result shape for aggregating contributions from both project and service lifecycle events without duplicating a block during `azd up`? +2. Should a successful `azd provision` include `azd ai agent run` as a secondary local-development suggestion, or should it show only the required `azd deploy` progression? +3. When an agent service has no direct `uses` edge to a resource because a user authored an indirect graph, should doctor attribute the resource to every reachable agent or display the resource service key without an agent owner? +4. Should unified state classification continue to inspect Bicep outputs for infrastructure variables, or should the project service expose that classification directly? ## Summary of required changes -- `cli/azd/extensions/azure.ai.agents/internal/cmd/nextstep/state.go`: assemble - agent protocols, variable references, placeholders, and resource references - from parsed `azure.yaml` service properties. -- `cli/azd/extensions/azure.ai.agents/internal/cmd/nextstep/manifest.go`: - replace manifest walking with deterministic unified service-graph collection. -- `cli/azd/extensions/azure.ai.agents/internal/cmd/nextstep/types.go`: update - resource-reference documentation and any fields needed for `uses` ownership. -- `cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_project.go`: - validate inline agent definitions rather than on-disk `agent.yaml` files. -- `cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_toolboxes.go`: - inspect unified toolbox services and update user-facing results. -- `cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_connections.go`: - inspect unified connection services and update user-facing results. -- `cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go`: contribute - post-provision suggestions after clearing pending-provision state. -- `cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go`: - replace per-artifact deploy guidance with an aggregated contribution. -- `cli/azd/pkg/azdext/event_manager.go`: add a structured lifecycle result that - can carry post-command contributions. -- `cli/azd/internal/grpcserver/event_service.go`: transport lifecycle - contributions from extensions to core. -- Core command output pipeline: collect and render contributions after command - summaries while preserving JSON and noninteractive behavior. -- Unit and integration tests: cover unified state collection, compatibility - warnings, doctor output, multi-agent aggregation, output suppression, and - core rendering order. +- `cli/azd/extensions/azure.ai.agents/internal/cmd/nextstep/state.go`: assemble agent protocols, variable references, placeholders, and resource references from parsed `azure.yaml` service properties. +- `cli/azd/extensions/azure.ai.agents/internal/cmd/nextstep/manifest.go`: replace manifest walking with deterministic unified service-graph collection. +- `cli/azd/extensions/azure.ai.agents/internal/cmd/nextstep/types.go`: update resource-reference documentation and any fields needed for `uses` ownership. +- `cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_project.go`: validate inline agent definitions rather than on-disk `agent.yaml` files. +- `cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_toolboxes.go`: inspect unified toolbox services and update user-facing results. +- `cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_connections.go`: inspect unified connection services and update user-facing results. +- `cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go`: contribute post-provision suggestions after clearing pending-provision state. +- `cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go`: replace per-artifact deploy guidance with an aggregated contribution. +- `cli/azd/pkg/azdext/event_manager.go`: add a structured lifecycle result that can carry post-command contributions. +- `cli/azd/internal/grpcserver/event_service.go`: transport lifecycle contributions from extensions to core. +- Core command output pipeline: collect and render contributions after command summaries while preserving JSON and noninteractive behavior. +- Unit and integration tests: cover unified state collection, compatibility warnings, doctor output, multi-agent aggregation, output suppression, and core rendering order. From 15eead071866f71414189f93dad482fe16c887f2 Mon Sep 17 00:00:00 2001 From: huimiu Date: Fri, 10 Jul 2026 22:38:31 +0800 Subject: [PATCH 3/5] fix: shorten unified azure yaml spec --- docs/specs/unified-azure-yaml/spec.md | 193 ++++++-------------------- 1 file changed, 39 insertions(+), 154 deletions(-) diff --git a/docs/specs/unified-azure-yaml/spec.md b/docs/specs/unified-azure-yaml/spec.md index d3da31c7568..f1c4575862f 100644 --- a/docs/specs/unified-azure-yaml/spec.md +++ b/docs/specs/unified-azure-yaml/spec.md @@ -1,198 +1,83 @@ - + # Unified azure.yaml configuration and core next-step guidance ## Context and scope -This design implements the configuration migration described by [issue #8710](https://github.com/Azure/azure-dev/issues/8710). It also addresses the missing guidance between `azd provision` and `azd deploy` described by [issue #8804](https://github.com/Azure/azure-dev/issues/8804). +This design implements the configuration migration in [issue #8710](https://github.com/Azure/azure-dev/issues/8710) and restores the missing guidance after `azd provision` described in [issue #8804](https://github.com/Azure/azure-dev/issues/8804). -The design covers the Azure AI Agents extension's configuration readers, state-aware guidance, and doctor checks. It does not redesign Foundry provisioning, agent deployment, or the content of individual agent manifests. +The scope is limited to the Azure AI Agents extension's configuration readers, next-step guidance, and doctor checks, plus the core contract needed to render extension guidance. Foundry provisioning, agent deployment, and manifest content are unchanged. ## Part 1: End-to-end experience -### New project initialization - -`azd ai agent init` writes the complete deployable agent and Foundry resource configuration into `azure.yaml`. It does not require a generated `agent.yaml` or a retained `agent.manifest.yaml` for later guidance or doctor checks. - -```yaml -services: - travel-agent: - host: azure.ai.agent - project: src/travel-agent - uses: - - travel-project - - search-connection - - travel-tools - kind: hosted - name: travel-agent - protocols: - - protocol: responses - environmentVariables: - - name: SEARCH_KEY - value: ${SEARCH_KEY} - container: - resources: - cpu: "0.5" - memory: 1Gi - - travel-project: - host: azure.ai.project - deployments: - - name: chat - model: - name: gpt-4o - format: OpenAI - version: "2024-11-20" - sku: - name: GlobalStandard - capacity: 1 - - search-connection: - host: azure.ai.connection - name: search - category: AzureAISearch - target: https://example.search.windows.net - authType: ApiKey - - travel-tools: - host: azure.ai.toolbox - name: travel-tools - tools: [] -``` - -The agent's `uses` entries preserve the resource ordering. The project service is provisioned before connections and toolboxes, and those resources are ready before the agent deploys. Existing Foundry projects use `endpoint` on the `azure.ai.project` service instead of creating a second project. +### One configuration source -### First provision +`azd ai agent init` writes the agent definition and related `azure.ai.project`, `azure.ai.connection`, and `azure.ai.toolbox` services into `azure.yaml`. The agent's `uses` entries preserve dependencies between these services. Generated `agent.yaml` and retained `agent.manifest.yaml` files are no longer required for guidance or doctor checks. -After a successful interactive `azd provision`, core azd prints one consolidated terminal block after its command summary. The extension contributes the suggestions, but core owns rendering and ordering. +An `azure.ai.project` service with `endpoint` continues to represent an existing Foundry project. Reusing a project does not transfer ownership or change `azd down` behavior. -```text -Next: - azd deploy - deploy the agent to Azure -``` +### Terminal experience -If state still needs user input, the block names that prerequisite rather than suggesting an action that will fail. For example, an unresolved environment value remains actionable after a partial configuration: +After a successful interactive `azd provision`, core azd renders the extension contribution after its existing command result. Colors are omitted below, and values in angle brackets are dynamic. ```text -Next: - azd env set SEARCH_KEY - referenced by azure.yaml but not set in azd env +SUCCESS: Your application was provisioned in Azure in . +You can view the resources created under the resource group in Azure Portal: + +Next: azd deploy deploy the agent to Azure ``` -The block is omitted for JSON output, redirected output, and noninteractive output. Scripts retain their existing stable machine-readable output. - -### Deploy and inner loop - -After `azd deploy`, core prints one top-level block for all deployed agent services. It replaces the current behavior where guidance is visible only in a single endpoint artifact note. - -```text -Next: - azd ai agent show travel-agent - verify it is running - - azd ai agent invoke travel-agent '' - test the deployment -``` +If required state is missing, the block shows the fix first, such as `azd env set SEARCH_KEY `, and keeps `azd deploy` as the final action. After deploy, the same top-level block suggests `azd ai agent show` and `azd ai agent invoke` for the deployed services. -`azd ai agent init`, `run`, `invoke`, `show`, and a successful `doctor` continue to use the same resolver. They derive agent protocol, environment-variable references, unresolved placeholders, model deployments, connections, and toolboxes from the parsed `azure.yaml` service graph. A sample payload from OpenAPI or a sibling README remains optional enrichment, not configuration input. +The block is suppressed for JSON, redirected, and noninteractive output. A failed command never prints success guidance. If optional guidance assembly fails after a successful command, azd keeps the successful exit status and records the failure only in debug output. -### Existing projects and migration +### Existing projects -Projects that already have unified service entries continue without a warning. The readers support both flat service properties and the deprecated config-nested service properties through `project.ServiceConfigProps`. - -Projects whose guidance or doctor result still depends on an on-disk `agent.yaml` or `agent.manifest.yaml` receive one migration warning with the existing migration guide. The warning directs the user to rerun `azd ai agent init`. During the compatibility window, deployment can retain the existing `project.LoadAgentDefinition` fallback, but next-step and doctor state do not treat legacy files as authoritative. - -### Reuse, teardown, and failure - -An `azure.ai.project` service with `endpoint` represents a reused Foundry project. Guidance and doctor checks inspect its declared sibling resources but do not infer ownership from the endpoint or recommend deletion. `azd down` continues to follow the resource lifecycle ownership already defined by the project, connection, toolbox, and agent service targets. - -If provision succeeds but the extension cannot assemble its optional state, core still reports provision success and omits the contributed block. If the extension returns a structured contribution error, core logs it only in debug diagnostics and does not change the completed command's exit status. A later `azd ai agent doctor` reconstructs state from `azure.yaml`, so a rerun is safe and does not depend on a partially written manifest file. +Unified projects continue without warnings. During the compatibility window, deployment may still read the legacy files through `project.LoadAgentDefinition`, but next-step guidance and doctor use `azure.yaml` as the source of truth. Legacy fallback emits the existing migration warning and directs the user to rerun `azd ai agent init`. ## Part 2: Technical design -### Configuration source and migration boundary - -`cli/azd/extensions/azure.ai.agents/internal/cmd/init.go` already converts the agent template with `project.AgentDefinitionToServiceProperties` and writes it as `ServiceConfig.AdditionalProperties`. It then calls `emitResourceServices` in `internal/cmd/resource_services.go` to create: - -- one `azure.ai.project` service that owns `deployments` -- one `azure.ai.connection` service for each connection -- one `azure.ai.toolbox` service for each toolbox -- `uses` edges from the agent service to those siblings - -The migration completes this model by making all state consumers read the same parsed service graph. `project.AgentDefinitionFromService` and `project.LoadServiceTargetAgentConfig` in `internal/project/agent_definition.go` are the canonical readers for agent definition fields and agent-specific settings. `ServiceConfigProps` preserves the config-nested compatibility path without adding another reader. - -### State assembly - -Replace the file-oriented portions of `nextstep.AssembleState` in `internal/cmd/nextstep/state.go` with service-graph collectors: - -1. Keep `collectServices` as the filtered and sorted list of `host: azure.ai.agent` services. -2. Replace `loadServiceProtocol` with a reader that obtains `Protocols` from `project.AgentDefinitionFromService`. -3. Replace `detectMissingVars` with a reader of the inline `EnvironmentVariables` in `AgentDefinitionInline`. It preserves the existing `${VAR}` and `${VAR:-default}` semantics and detects literal `{{NAME}}` placeholders in the same values. -4. Replace `populateManifestResources` in `internal/cmd/nextstep/manifest.go` with a collector over `azure.ai.project`, `azure.ai.connection`, and `azure.ai.toolbox` services. It must use `project.UnmarshalStruct` with the corresponding `ServiceTargetAgentConfig`, `Connection`, and `Toolbox` types. -5. Preserve deterministic ordering and the `(ServiceName, Name)` identity semantics of `ResourceRef`. For a resource sibling, `ServiceName` is the owning agent reached through its `uses` edge. A shared resource is reported for every consuming agent, not silently attributed to one of them. - -The collector must reject malformed service properties as a state-assembly diagnostic rather than silently interpreting a malformed unified entry as no resources. Optional OpenAPI and README lookups remain best effort because they only improve command examples. - -### Guidance resolution and rendering - -`nextstep.ResolveAfterInit` and `nextstep.ResolveAfterDeploy` remain pure functions over `nextstep.State`. Their decision trees do not depend on file names after this migration. `nextstep.PrintNext`, `PrintAllNext`, and `FormatNextForNote` remain formatters, with one behavioral change: endpoint artifact notes are no longer the primary post-deploy surface. - -Create a core extension result contract for project lifecycle events. Today, `azdext.ProjectEventHandler` in `cli/azd/pkg/azdext/event_manager.go` returns only `error`, and `eventService.createProjectEventHandler` in `cli/azd/internal/grpcserver/event_service.go` converts it only to completion or failure status. The extension must not write directly to stdout from `postprovisionHandler`, because that output can interleave with the core progress UI. - -The core contract needs a structured, ordered post-command contribution that: +### Authoritative service graph -- is returned by a successful project or service event handler -- is collected for the encompassing command -- is rendered by core after the final command summary -- is suppressed for JSON and noninteractive output -- carries a source identifier for debug diagnostics -- treats rendering failures as diagnostic, not command failures +Initialization already writes inline agent properties through `project.AgentDefinitionToServiceProperties` in `internal/project/agent_definition.go` and sibling resources through `emitResourceServices` in `internal/cmd/resource_services.go`. `project.AgentDefinitionFromService`, `project.LoadServiceTargetAgentConfig`, and `project.ServiceConfigProps` remain the canonical readers, including compatibility with config-nested service properties. -The agents extension's `postprovisionHandler` in `internal/cmd/listen.go` first clears `AI_AGENT_PENDING_PROVISION` after a successful provision. It then assembles the refreshed state and contributes the post-provision resolver output through the new contract. No direct terminal write is added to the handler. +### State and doctor -For deploy, `AgentServiceTargetProvider.Deploy` in `internal/project/service_target_agent.go` currently calls `augmentDeployNote`, which scopes `ResolveAfterDeploy` to one service and embeds the result in `Artifact.Metadata["note"]`. Replace that embedding with a structured contribution collected across deployed services. The final resolver invocation receives the full deployed service set once, so multi-agent projects produce one ordered block rather than repeated per-service blocks. The static endpoint documentation note remains an endpoint artifact detail and does not carry the navigation workflow. +Refactor `nextstep.AssembleState` in `internal/cmd/nextstep/state.go` to read protocols, environment references, placeholders, model deployments, connections, and toolboxes from the parsed service graph. Resource ownership follows `uses` edges and remains deterministic for shared resources. This replaces the file readers in `state.go` and `manifest.go`. -### Doctor +Doctor continues to consume the same state snapshot. Its project, toolbox, and connection checks validate inline agent properties and sibling resource services rather than requiring `agent.yaml` or `agent.manifest.yaml`. -`azd ai agent doctor` retains `nextstep.AssembleState` as its shared snapshot source. Update `local.agent-yaml-valid` in `internal/cmd/doctor/checks_project.go` to validate an inline agent definition with `project.AgentDefinitionFromService` and the existing `agent_yaml.ValidateAgentDefinition` rules. It no longer requires an `agent.yaml` file per service. +### Core-rendered guidance -Update `local.toolboxes` and `remote.connections` in `internal/cmd/doctor/checks_toolboxes.go` and `internal/cmd/doctor/checks_connections.go` to use the unified resource references. Their status names, skip conditions, details payloads, and remediation text must refer to declared `azure.yaml` services, not manifest resources. `resolveDoctorTrailing` in `internal/cmd/doctor.go` continues to select deploy guidance for deployed services and init guidance otherwise. +`azdext.ProjectEventHandler` currently returns only `error`, and `event_service.go` transports only completion or failure. Add a structured successful result that can carry ordered post-command contributions. Core collects these contributions, renders them once after the command result, and applies output-mode suppression. -### Errors, compatibility, and observability +The agents extension's `postprovisionHandler` clears `AI_AGENT_PENDING_PROVISION`, assembles refreshed state, and returns the resolved guidance. It does not write directly to stdout. Deploy contributes guidance for each deployed agent, and core aggregates it into one block. `augmentDeployNote` no longer embeds workflow guidance in an endpoint artifact note. -Malformed inline configuration returns the existing structured validation error shape, including the service name and a suggestion to regenerate or fix its `azure.yaml` entry. Missing legacy files are not an error for unified projects. When compatibility fallback is used, `project.WarnLegacyAgentShape` remains the single warning path. +### Compatibility and failures -No telemetry event is required for the first implementation. The core result contract should record extension source and rendering suppression in debug logs. A later telemetry proposal must follow the event and field requirements in `cli/azd/AGENTS.md`. +Malformed unified configuration produces a service-scoped validation or doctor error instead of being treated as an empty configuration. Legacy fallback keeps the single warning path in `project.WarnLegacyAgentShape`. Contribution failures remain diagnostic and do not change a completed command's result. No new telemetry is required for the first implementation. ## Part 3: Dependencies that need PM confirmation -1. **Core lifecycle output contract:** Confirm that the core framework change needed for extension-contributed terminal guidance is in scope for this milestone. Issue #8804 cannot deliver a clean post-provision block without it. -2. **Post-deploy presentation:** Confirm that the top-level post-deploy block replaces the workflow content in endpoint artifact notes, while retaining endpoint-specific documentation. This resolves the choice identified in issue #8804. -3. **Legacy-file support window:** Confirm the release and support policy for `agent.yaml` and `agent.manifest.yaml` after unified initialization. The extension needs a dated removal target before compatibility readers can be deleted. -4. **Ownership of shared resources:** Confirm whether a toolbox or connection referenced through multiple `uses` edges should appear once per consumer or once per resource in doctor remediation. This design proposes one result per consumer so each agent remains actionable. +1. **Core output contract:** Confirm that extension-contributed post-command output is in scope for this milestone. A clean post-provision block for issue #8804 depends on it. +2. **Post-deploy presentation:** Confirm that the top-level block replaces workflow guidance in endpoint artifact notes while endpoint-specific documentation remains. +3. **Legacy support window:** Confirm the release when `agent.yaml` and `agent.manifest.yaml` compatibility can be removed. ## Part 4: Open questions -1. What is the supported core result shape for aggregating contributions from both project and service lifecycle events without duplicating a block during `azd up`? -2. Should a successful `azd provision` include `azd ai agent run` as a secondary local-development suggestion, or should it show only the required `azd deploy` progression? -3. When an agent service has no direct `uses` edge to a resource because a user authored an indirect graph, should doctor attribute the resource to every reachable agent or display the resource service key without an agent owner? -4. Should unified state classification continue to inspect Bicep outputs for infrastructure variables, or should the project service expose that classification directly? +1. What contribution shape prevents duplicate guidance when both project and service events run during `azd up`? +2. Should post-provision guidance include `azd ai agent run` as a secondary local-development action, or only `azd deploy`? +3. How should doctor attribute a shared or indirectly referenced resource to agent services? +4. Should infrastructure-variable classification continue to inspect Bicep outputs, or move into the project service contract? ## Summary of required changes -- `cli/azd/extensions/azure.ai.agents/internal/cmd/nextstep/state.go`: assemble agent protocols, variable references, placeholders, and resource references from parsed `azure.yaml` service properties. -- `cli/azd/extensions/azure.ai.agents/internal/cmd/nextstep/manifest.go`: replace manifest walking with deterministic unified service-graph collection. -- `cli/azd/extensions/azure.ai.agents/internal/cmd/nextstep/types.go`: update resource-reference documentation and any fields needed for `uses` ownership. -- `cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_project.go`: validate inline agent definitions rather than on-disk `agent.yaml` files. -- `cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_toolboxes.go`: inspect unified toolbox services and update user-facing results. -- `cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_connections.go`: inspect unified connection services and update user-facing results. -- `cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go`: contribute post-provision suggestions after clearing pending-provision state. -- `cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go`: replace per-artifact deploy guidance with an aggregated contribution. -- `cli/azd/pkg/azdext/event_manager.go`: add a structured lifecycle result that can carry post-command contributions. -- `cli/azd/internal/grpcserver/event_service.go`: transport lifecycle contributions from extensions to core. -- Core command output pipeline: collect and render contributions after command summaries while preserving JSON and noninteractive behavior. -- Unit and integration tests: cover unified state collection, compatibility warnings, doctor output, multi-agent aggregation, output suppression, and core rendering order. +- **Unified state:** Replace next-step manifest and agent-file readers with parsed `azure.yaml` service-graph readers. +- **Doctor:** Validate inline agent definitions and sibling project, connection, and toolbox services. +- **Core events:** Transport and collect structured post-command contributions from extension lifecycle handlers. +- **Core output:** Render one contribution block after the command result and suppress it for machine-oriented output. +- **Extension lifecycle:** Return post-provision and aggregated post-deploy guidance without direct terminal writes or artifact-note workflow content. +- **Tests:** Cover unified state, legacy fallback, doctor results, terminal ordering, multi-agent aggregation, and output suppression. From 8ffdb1305784a6e651da2cf5e5eda77fcf2d9b21 Mon Sep 17 00:00:00 2001 From: huimiu Date: Mon, 13 Jul 2026 14:50:47 +0800 Subject: [PATCH 4/5] fix: align unified yaml guidance decisions --- docs/specs/unified-azure-yaml/spec.md | 67 ++++++++------------------- 1 file changed, 19 insertions(+), 48 deletions(-) diff --git a/docs/specs/unified-azure-yaml/spec.md b/docs/specs/unified-azure-yaml/spec.md index f1c4575862f..6adead309e2 100644 --- a/docs/specs/unified-azure-yaml/spec.md +++ b/docs/specs/unified-azure-yaml/spec.md @@ -1,83 +1,54 @@ - + # Unified azure.yaml configuration and core next-step guidance ## Context and scope -This design implements the configuration migration in [issue #8710](https://github.com/Azure/azure-dev/issues/8710) and restores the missing guidance after `azd provision` described in [issue #8804](https://github.com/Azure/azure-dev/issues/8804). - -The scope is limited to the Azure AI Agents extension's configuration readers, next-step guidance, and doctor checks, plus the core contract needed to render extension guidance. Foundry provisioning, agent deployment, and manifest content are unchanged. +This design aligns the end-to-end CLI experience for the configuration migration in [issue #8710](https://github.com/Azure/azure-dev/issues/8710) and the local-first guidance from [issue #7975](https://github.com/Azure/azure-dev/issues/7975). It covers the Azure AI Agents extension's configuration readers, next-step guidance, doctor checks, and the core contribution contract. Foundry provisioning, agent deployment, and manifest content are unchanged. ## Part 1: End-to-end experience ### One configuration source -`azd ai agent init` writes the agent definition and related `azure.ai.project`, `azure.ai.connection`, and `azure.ai.toolbox` services into `azure.yaml`. The agent's `uses` entries preserve dependencies between these services. Generated `agent.yaml` and retained `agent.manifest.yaml` files are no longer required for guidance or doctor checks. +`azd ai agent init` writes the agent definition and related `azure.ai.project`, `azure.ai.connection`, and `azure.ai.toolbox` services into `azure.yaml`. An `azure.ai.project` service with `endpoint` still represents an existing Foundry project without changing ownership or `azd down` behavior. -An `azure.ai.project` service with `endpoint` continues to represent an existing Foundry project. Reusing a project does not transfer ownership or change `azd down` behavior. +Next-step and doctor prefer the unified `azure.yaml` service graph. During the compatibility window, they retain the existing `agent.yaml` and `agent.manifest.yaml` fallback and migration warning. Other legacy readers are not removed by this design. ### Terminal experience -After a successful interactive `azd provision`, core azd renders the extension contribution after its existing command result. Colors are omitted below, and values in angle brackets are dynamic. +After successful provisioning and any required fixes, the primary action starts the local agent. Deployment remains the trailing action for when the user is ready. ```text SUCCESS: Your application was provisioned in Azure in . -You can view the resources created under the resource group in Azure Portal: - Next: + azd ai agent run + run the agent locally + +When ready: azd deploy deploy the agent to Azure ``` -If required state is missing, the block shows the fix first, such as `azd env set SEARCH_KEY `, and keeps `azd deploy` as the final action. After deploy, the same top-level block suggests `azd ai agent show` and `azd ai agent invoke` for the deployed services. - -The block is suppressed for JSON, redirected, and noninteractive output. A failed command never prints success guidance. If optional guidance assembly fails after a successful command, azd keeps the successful exit status and records the failure only in debug output. - -### Existing projects - -Unified projects continue without warnings. During the compatibility window, deployment may still read the legacy files through `project.LoadAgentDefinition`, but next-step guidance and doctor use `azure.yaml` as the source of truth. Legacy fallback emits the existing migration warning and directs the user to rerun `azd ai agent init`. +Core renders guidance after the command result for human-facing terminal output. JSON, non-TTY, and other machine-oriented output suppress it. `--no-prompt` alone does not. Failed commands do not print success guidance. ## Part 2: Technical design -### Authoritative service graph - -Initialization already writes inline agent properties through `project.AgentDefinitionToServiceProperties` in `internal/project/agent_definition.go` and sibling resources through `emitResourceServices` in `internal/cmd/resource_services.go`. `project.AgentDefinitionFromService`, `project.LoadServiceTargetAgentConfig`, and `project.ServiceConfigProps` remain the canonical readers, including compatibility with config-nested service properties. +### Unified state and attribution -### State and doctor +`nextstep.AssembleState` reads the parsed service graph first and falls back to legacy agent files only when unified configuration is unavailable. Doctor consumes the same state snapshot. Both continue classifying unresolved infrastructure variables by matching Bicep outputs, consistent with [issue #7975](https://github.com/Azure/azure-dev/issues/7975) and [PR #9081](https://github.com/Azure/azure-dev/pull/9081). -Refactor `nextstep.AssembleState` in `internal/cmd/nextstep/state.go` to read protocols, environment references, placeholders, model deployments, connections, and toolboxes from the parsed service graph. Resource ownership follows `uses` edges and remains deterministic for shared resources. This replaces the file readers in `state.go` and `manifest.go`. - -Doctor continues to consume the same state snapshot. Its project, toolbox, and connection checks validate inline agent properties and sibling resource services rather than requiring `agent.yaml` or `agent.manifest.yaml`. +Each resource service is attributed to its own `azure.yaml` service key and checked once, including shared resources. `uses` defines dependency ordering, not ownership. Inline and legacy resources remain attributed to the agent service key. ### Core-rendered guidance -`azdext.ProjectEventHandler` currently returns only `error`, and `event_service.go` transports only completion or failure. Add a structured successful result that can carry ordered post-command contributions. Core collects these contributions, renders them once after the command result, and applies output-mode suppression. - -The agents extension's `postprovisionHandler` clears `AI_AGENT_PENDING_PROVISION`, assembles refreshed state, and returns the resolved guidance. It does not write directly to stdout. Deploy contributes guidance for each deployed agent, and core aggregates it into one block. `augmentDeployNote` no longer embeds workflow guidance in an endpoint artifact note. - -### Compatibility and failures - -Malformed unified configuration produces a service-scoped validation or doctor error instead of being treated as an empty configuration. Legacy fallback keeps the single warning path in `project.WarnLegacyAgentShape`. Contribution failures remain diagnostic and do not change a completed command's result. No new telemetry is required for the first implementation. - -## Part 3: Dependencies that need PM confirmation - -1. **Core output contract:** Confirm that extension-contributed post-command output is in scope for this milestone. A clean post-provision block for issue #8804 depends on it. -2. **Post-deploy presentation:** Confirm that the top-level block replaces workflow guidance in endpoint artifact notes while endpoint-specific documentation remains. -3. **Legacy support window:** Confirm the release when `agent.yaml` and `agent.manifest.yaml` compatibility can be removed. +Core accepts one project-scoped post-command contribution with a stable identity. Post-provision writes or updates that contribution after refreshing state. Project post-deploy recomputes and replaces it, so `azd up` renders only the final post-deploy guidance. Service postdeploy handlers do not contribute user guidance. -## Part 4: Open questions +The contribution is structured rather than direct stdout. Core renders it once after the command result and applies output-mode suppression. Contribution assembly failures remain diagnostic and do not change a successful command result. -1. What contribution shape prevents duplicate guidance when both project and service events run during `azd up`? -2. Should post-provision guidance include `azd ai agent run` as a secondary local-development action, or only `azd deploy`? -3. How should doctor attribute a shared or indirectly referenced resource to agent services? -4. Should infrastructure-variable classification continue to inspect Bicep outputs, or move into the project service contract? +Malformed unified configuration produces a service-scoped validation or doctor error instead of an empty state. Legacy fallback keeps the single warning path in `project.WarnLegacyAgentShape`. -## Summary of required changes +## Decisions for John -- **Unified state:** Replace next-step manifest and agent-file readers with parsed `azure.yaml` service-graph readers. -- **Doctor:** Validate inline agent definitions and sibling project, connection, and toolbox services. -- **Core events:** Transport and collect structured post-command contributions from extension lifecycle handlers. -- **Core output:** Render one contribution block after the command result and suppress it for machine-oriented output. -- **Extension lifecycle:** Return post-provision and aggregated post-deploy guidance without direct terminal writes or artifact-note workflow content. -- **Tests:** Cover unified state, legacy fallback, doctor results, terminal ordering, multi-agent aggregation, and output suppression. +1. **Core output contract:** Decide whether the project-scoped post-command contribution contract is in this milestone. +2. **Post-deploy presentation:** Decide whether top-level post-deploy guidance replaces workflow guidance in endpoint artifact notes while endpoint-specific documentation remains. From 0853ad7e5fb5ebf9b851b6ab111bb80c45973162 Mon Sep 17 00:00:00 2001 From: huimiu Date: Mon, 13 Jul 2026 17:57:35 +0800 Subject: [PATCH 5/5] fix: clarify unified yaml guidance failure and contract scope --- docs/specs/unified-azure-yaml/spec.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/specs/unified-azure-yaml/spec.md b/docs/specs/unified-azure-yaml/spec.md index 6adead309e2..d2e883c9a76 100644 --- a/docs/specs/unified-azure-yaml/spec.md +++ b/docs/specs/unified-azure-yaml/spec.md @@ -42,11 +42,11 @@ Each resource service is attributed to its own `azure.yaml` service key and chec ### Core-rendered guidance -Core accepts one project-scoped post-command contribution with a stable identity. Post-provision writes or updates that contribution after refreshing state. Project post-deploy recomputes and replaces it, so `azd up` renders only the final post-deploy guidance. Service postdeploy handlers do not contribute user guidance. +Core accepts one project-scoped post-command contribution with a stable identity. Post-provision writes or updates that contribution after refreshing state. Project post-deploy recomputes and replaces it, so `azd up` renders only the final post-deploy guidance. Service postdeploy handlers do not contribute user guidance. The concrete SDK/proto shape, identity namespace, and multi-extension conflict rules are deferred to the implementation design once Decision 1 confirms the contract is in scope. -The contribution is structured rather than direct stdout. Core renders it once after the command result and applies output-mode suppression. Contribution assembly failures remain diagnostic and do not change a successful command result. +The contribution is structured rather than direct stdout. Core renders it once after the command result and applies output-mode suppression. -Malformed unified configuration produces a service-scoped validation or doctor error instead of an empty state. Legacy fallback keeps the single warning path in `project.WarnLegacyAgentShape`. +Malformed configuration is handled per caller: `provision` and `deploy` fail with a service-scoped validation error, `doctor` reports it as a failed check, and next-step guidance degrades to best-effort so a broken snapshot never blocks a successful command. Guidance assembly failures stay diagnostic only. Legacy fallback keeps the single warning path in `project.WarnLegacyAgentShape`. ## Decisions for John