doc: design spec for azd ai agent init reuse of existing agent.yaml#8325
Open
hund030 wants to merge 4 commits into
Open
doc: design spec for azd ai agent init reuse of existing agent.yaml#8325hund030 wants to merge 4 commits into
hund030 wants to merge 4 commits into
Conversation
During implementation we discovered the manifest case is already handled upstream by detectLocalManifest in init.go, and our reuse helper only owns the bare-definition case. Update the spec to reflect that — narrow the scope, remove the dead reuseLocalManifest sub-section, and update file references to current main.
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a design spec documenting the planned change to azd ai agent init (Azure AI Agents extension) to detect and reuse an existing bare agent.yaml definition in the target directory, avoiding overwrite prompts/fail-fast behavior and only writing the azure.yaml service entry.
Changes:
- Introduces a new design document describing dispatch ordering, reuse flow, and error handling for existing bare
agent.yaml. - Documents key design decisions (where detection happens, non-goals like Foundry project resolution, and why manifest reuse is left to existing logic).
- Provides a decision tree and test plan guidance for the intended behavior.
|
|
||
| This change adds **definition reuse**: when the user has a bare `agent.yaml` (a YAML file without a top-level `template:` wrapper) in the target directory and runs `azd ai agent init`, the command treats that file as the source of truth, skips the from-code prompts, and writes only the `azure.yaml` service entry, matching the issue's wording: *"if it's a definition, then we have less to ask and just setup azure.yaml."* | ||
|
|
||
| Manifest reuse (the other half of the issue) is already handled by upstream `detectLocalManifest` in `init.go`, which intercepts valid local manifests (regardless of filename) and routes them through `runInitFromManifest`. This spec does **not** touch that path; it only fills the bare-definition gap. |
Comment on lines
+56
to
+59
| 1. `detectLocalManifest(srcDir)`: existing helper, runs first. If it returns a valid manifest path, `flags.manifestPointer` is set (with an optional confirmation prompt in interactive mode) and the manifest pipeline runs as today. | ||
| 2. **If `flags.manifestPointer` is still empty**, the new step runs: `findExistingAgentYaml(srcDir)` does a shallow `os.Stat` against the four candidate filenames. Any hit at this point is guaranteed to either be a bare definition (rejected by `detectLocalManifest` for lacking `template:`) or a malformed manifest (rejected for failing manifest validation). | ||
| 3. On a hit, a confirmation prompt ("An existing agent definition was found at ... Use it?") is shown; in `--no-prompt` mode the answer auto-defaults to yes. | ||
| 4. On confirmation, `runReuseDefinition(ctx, flags, azdClient, httpClient, srcDir, existingPath)` is called and the command returns. The init-mode prompt and the from-code scaffolding sequence are both skipped. |
Comment on lines
+40
to
+44
| | Upstream manifest detection | `cmd/init.go` (`detectLocalManifest`) | **Unchanged**. | | ||
| | Manifest pipeline | `cmd/init.go` (`runInitFromManifest`) | **Unchanged**. | | ||
| | Definition reuse helpers | `cmd/init_from_code_reuse.go` (new file) | `findExistingAgentYaml`, `runReuseDefinition`, `loadAgentDefinitionFile`. | | ||
| | `azure.yaml` writer | `cmd/init_from_code.go` (`addToProject`) | **Reused as-is** by `runReuseDefinition`. | | ||
| | Project / environment bootstrap | `cmd/init.go` (`ensureProject`, `getExistingEnvironment`, `createNewEnvironment`) | **Reused as-is**: the reuse path runs the same setup `runInitFromManifest` does. | |
Comment on lines
+120
to
+126
| ## 7. Test Plan | ||
|
|
||
| - **Pre-existing unit tests** in `cli/azd/extensions/azure.ai.agents/internal/cmd/*_test.go` continue to pass. The no-yaml regression is covered by today's `init_from_code_test.go`. | ||
| - **Manual e2e** against the locally-built `azd` + extension. Three scenarios: | ||
| - **Definition reuse**: write a bare `agent.yaml`, run `azd ai agent init --no-prompt`; assert no init-mode prompt, `Detected existing agent definition: ...` printed, `azure.yaml` contains the agent's `name:`, on-disk `agent.yaml` byte-identical to input. | ||
| - **Manifest reuse**: write `agent.manifest.yaml`, run with `AZURE_SUBSCRIPTION_ID` and `AZURE_AI_PROJECT_ID` set; assert the existing upstream manifest path runs unchanged and `azure.yaml` ends up with the manifest's `template.name`. | ||
| - **Invalid yaml**: write broken YAML; assert non-zero exit, error references `agent.yaml`, `azure.yaml` not mutated. |
|
|
||
| 1. `loadAgentDefinitionFile(path)`: reads the file, rejects anything with a top-level `template:` (manifest-shaped but invalid, produces a targeted error), unmarshals to `agent_yaml.ContainerAgent` so `CodeConfiguration` is preserved, and validates the name via `agent_yaml.ValidateAgentName`. | ||
| 2. Prints `Detected existing agent definition: <relative-path> (name: <def.Name>).` | ||
| 3. Bootstraps project + env using the same helpers `runInitFromManifest` uses: `ensureProject`, then `getExistingEnvironment` / `createNewEnvironment` (the env is named `<def.Name>-dev` when none was supplied via `-e`). |
📋 Prioritization NoteThanks for the contribution! The linked issue isn't in the current milestone yet. |
- Tighten manifest-detection wording: detectLocalManifest scans four fixed filenames, not arbitrary ones. - Correct file-location references in the touch-points table (detectLocalManifest lives in init_from_templates_helpers.go; createNewEnvironment lives in init_foundry_resources_helpers.go). - Document the declined-manifest edge case: when detectLocalManifest finds a valid manifest that the user declines, the reuse scan must be skipped so the declined file is not mis-classified as an invalid definition. - Note that the derived env name goes through sanitizeAgentName to match the existing from-code path. - Test plan: remove the inaccurate claim that init_from_code_test.go covers the no-yaml regression; clarify the existing unit tests cover unrelated helpers and that the new paths are exercised by manual e2e instead.
hund030
added a commit
that referenced
this pull request
May 25, 2026
Copilot review of #8325 caught an edge case in the lifted dispatch: when detectLocalManifest finds a valid manifest in srcDir but the user declines the 'Use it?' prompt, flags.manifestPointer stays empty and the new definition reuse scan would re-discover the same on-disk file. The bare definition loader rejects files with a top-level 'template:' key, so the declined manifest would be reported as an invalid definition with CodeInvalidAgentManifest, blocking init and contradicting the user's choice to start fresh. Track the decline explicitly with a manifestDetectedButDeclined flag and gate the definition reuse scan on it being false. Non-interactive mode is unaffected because --no-prompt auto-accepts the manifest.
Collaborator
Author
|
Thanks for the careful review. Summary of how each comment was addressed:
|
hund030
added a commit
that referenced
this pull request
May 25, 2026
Copilot review of #8325 caught an edge case in the lifted dispatch: when detectLocalManifest finds a valid manifest in srcDir but the user declines the 'Use it?' prompt, flags.manifestPointer stays empty and the new definition reuse scan would re-discover the same on-disk file. The bare definition loader rejects files with a top-level 'template:' key, so the declined manifest would be reported as an invalid definition with CodeInvalidAgentManifest, blocking init and contradicting the user's choice to start fresh. Track the decline explicitly with a manifestDetectedButDeclined flag and gate the definition reuse scan on it being false. Non-interactive mode is unaffected because --no-prompt auto-accepts the manifest.
Updates the spec to match what was actually shipped in PR #8326: - §3 / §9 #8: overwrite guard inside InitFromCodeAction.Run is now documented as retained (covering all four candidate filenames), not removed. §4.6 added to describe the decline-reuse fallback path. - §3 / §4.2 / §6 / §9 #7: validation switched from ValidateAgentName to ValidateAgentDefinition so missing/invalid kind, name format, and kind-specific structural checks are caught. - §3: language detection inside addToProject is limited to bare-definition filenames; lookup table updated to reflect that. - §4.2: runReuseDefinition now includes the absolute-src normalization step and a displayPath-aware `Reusing existing <file>` message. - §4.3 / §6: error suggestion text uses displayPath, not hardcoded `agent.yaml`. - §5: new row for the declined-reuse fallback; --src row notes abs->rel. - §7: documents the actual unit tests that now ship in init_from_code_reuse_test.go. - §10: decision tree expanded to cover the manifest-decline branch, the init-mode prompt with the overwrite guard, and the abs-src step.
trangevi
pushed a commit
that referenced
this pull request
May 26, 2026
…8326) * feat(agents): reuse existing agent.yaml on init from existing code Closes #7268. When `azd ai agent init` runs in a directory that already contains a bare `agent.yaml` definition, the from-code action now detects and reuses it instead of either prompting to overwrite (interactive) or failing fast with `CodeInvalidAgentManifest: agent.yaml already exists at ...` (--no-prompt mode, added in #8266). The reuse path: - Validates the file as a bare AgentDefinition (rejects manifest-shaped files that fail upstream detectLocalManifest validation with a targeted error). - Skips the model / instructions / runtime / entry-point prompt sequence in createDefinitionFromLocalAgent. - Ensures an azd environment exists (the prompt sequence normally bootstraps this) and calls the existing addToProject helper. - Never rewrites the on-disk agent.yaml — it is treated as authoritative. Valid local manifest files (top-level `template:`) are unchanged: they continue to be intercepted upstream by detectLocalManifest in init.go and routed through runInitFromManifest before InitFromCodeAction.Run is reached. Design spec: cli/azd/docs/design/azure-ai-agent-init-reuse-agent-yaml.md * fix(agents): skip definition reuse when manifest detected but declined Copilot review of #8325 caught an edge case in the lifted dispatch: when detectLocalManifest finds a valid manifest in srcDir but the user declines the 'Use it?' prompt, flags.manifestPointer stays empty and the new definition reuse scan would re-discover the same on-disk file. The bare definition loader rejects files with a top-level 'template:' key, so the declined manifest would be reported as an invalid definition with CodeInvalidAgentManifest, blocking init and contradicting the user's choice to start fresh. Track the decline explicitly with a manifestDetectedButDeclined flag and gate the definition reuse scan on it being false. Non-interactive mode is unaffected because --no-prompt auto-accepts the manifest. * fix(agents): address copilot review feedback on reuse path Five fixes plus targeted unit tests, in response to inline review on #8326. - Use go.yaml.in/yaml/v3 in init_from_code_reuse.go so custom UnmarshalYAML methods on agent_yaml types (e.g. PropertySchema) are honored. The package previously used gopkg.in/yaml.v3 which has an incompatible Node type and silently bypassed custom decoders. - Run agent_yaml.ValidateAgentDefinition on the loaded bytes so missing or invalid kind, structural shape, etc. fail fast with the same error the manifest pipeline produces, instead of slipping through and failing later. - Walk agentYamlCandidates in addToProject's language-detection block instead of hardcoding 'agent.yaml', so dotnet runtimes declared in agent.yml are detected correctly. - Replace hardcoded 'agent.yaml' in user-facing error/info messages with displayPath, so the reuse path correctly references agent.yml or agent.manifest.yaml when those are the detected file. - Mirror InitFromCodeAction.Run's absolute --src normalization: convert an absolute path to a project-relative path before calling addToProject so azure.yaml's RelativePath stays portable. Unit tests added in init_from_code_reuse_test.go cover findExistingAgentYaml (filename precedence, shallow scan, dir-vs-file), loadAgentDefinitionFile (happy path, missing kind, invalid name, manifest-shaped rejection, broken yaml), and the runReuseDefinition failure paths that surface CodeInvalidAgentManifest without needing the project gRPC mock. * fix(agents): address second-round copilot review and trim comments Five Copilot findings on the new reuse path: - Lang detection in addToProject (init_from_code.go) restricted to agent.yaml / agent.yml so a leftover agent.manifest.* does not short-circuit detection before the actual definition file is read. - agentYamlCandidates reordered to match detectLocalManifest in init_from_templates_helpers.go (manifest.yaml -> yaml -> manifest.yml -> yml). - errors.As replaced with errors.AsType[T] in init_from_code_reuse_test.go per the cli/azd/AGENTS.md Go 1.26 modernization rule. - Overwrite guard restored inside InitFromCodeAction.Run so the from-code scaffold cannot silently overwrite an existing agent.yaml when the user declined the reuse prompt in RunE. Matches the safety invariant from #8266 (--no-prompt fails fast; interactive defaults to no). Also trims verbose doc and inline comments across the touched files.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the design spec for #7268: when
azd ai agent initruns in a directory that already contains a bareagent.yamldefinition, reuse it instead of prompting to overwrite (interactive) or failing fast (--no-prompt, behavior added in #8266).The manifest half of the issue is documented as already handled by upstream
detectLocalManifest; this work fills only the bare-definition gap.Highlights for review
RunEnext todetectLocalManifest, not insideInitFromCodeAction.Run(avoids forcing users through the init-mode prompt before reuse fires).InitFromCodeAction.Runis retained as a safety net for the decline-reuse fallback path (user declines the reuse prompt and then picks "Use the code in the current directory").loadAgentDefinitionFileruns fullValidateAgentDefinition(kind + name + structural checks), not just name validation.Related
azd ai agent initto detect and reuse an existing agent.yaml #7268