Skip to content

Commit 91c87a4

Browse files
Antriksh JainCopilot
andcommitted
feat(azure.ai.agents): enrich manual-vars Next: block with run follow-up (P5.1 C3)
Two-part fix to ResolveAfterInit's MissingManualVars branch: 1. Enrich per-var description: was "supply the agent.yaml variable" → now "referenced by agent.yaml but not set in azd env". The command column already shows the var name; the description should explain WHY the var matters so users new to the project understand they aren't just inventing values out of nowhere. 2. Append an `azd ai agent run` follow-up after the env-set lines. Pre-C3 the post-init Next: block stopped at the env-set lines: Next: azd env set MY_API_KEY <value> -- referenced by agent.yaml but not set in azd env azd deploy -- when ready to deploy to Azure Users had to remember the run step themselves. Issue Azure#7975's manual-vars example explicitly ends with "Then run 'azd ai agent run' to start locally" — surface that as a concrete follow-up so the entire "fix env, then run" loop is visible in one Next: block: Next: azd env set MY_API_KEY <value> -- referenced by agent.yaml but not set in azd env azd ai agent run -- start the agent locally once the values above are set azd deploy -- when ready to deploy to Azure Gating: the run follow-up is suppressed when UnresolvedPlaceholders are also present, preserving the existing invariant from ResolveAfterInit's "otherwise" branch — running locally with literal `{{NAME}}` values produces a broken agent, so the placeholder fix-ups must be finished first. The trailing `azd deploy` reminder still applies. Tests: - TestResolveAfterInit (table) — `wantManualVarKeys` cases bump expected length from N+1 to N+2 and assert the run follow-up immediately after the env-set lines. - TestResolveAfterInit_ManualVarsCapAtThree — expected length 4→5, asserts the run follow-up sits at slot 3 (between env-set lines and trailing deploy). - TestResolveAfterInit_ToolboxReproRendersAllCategories — asserts the run follow-up is intentionally absent when placeholders are also present (gating contract). - TestResolveAfterInit_ManualVarsSingleEmitsEnrichedShape (NEW) — locks the single-missing-var canonical case: enriched description text + run follow-up + trailing deploy, all in order. Source of truth: issue Azure#7975 lines 117-127. Phase 5 C3 of the P5.8 commit plan (Tier A — core fixes). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 709e6e8 commit 91c87a4

2 files changed

Lines changed: 84 additions & 7 deletions

File tree

cli/azd/extensions/azure.ai.agents/internal/cmd/nextstep/resolver.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,12 @@ const (
6262
// suggesting `azd ai agent run`. See state.PendingProvisionReasons
6363
// for the env-var contract.
6464
// - MissingManualVars → one `azd env set <KEY> <value>` per missing var
65-
// (up to maxFixupLines)
65+
// (up to maxFixupLines) plus an `azd ai agent run` follow-up so
66+
// the user knows what to do after supplying the values. Matches
67+
// issue #7975's "Then run 'azd ai agent run' to start locally"
68+
// manual-vars example. The run follow-up is suppressed when
69+
// UnresolvedPlaceholders are also present, since literal
70+
// `{{NAME}}` values would still break the local agent.
6671
// - Otherwise → `azd ai agent run`
6772
// Skipped when only UnresolvedPlaceholders are present, because
6873
// running locally with literal `{{NAME}}` values is broken too.
@@ -111,11 +116,28 @@ func ResolveAfterInit(state *State) []Suggestion {
111116
for _, key := range manual[:limit] {
112117
out = append(out, Suggestion{
113118
Command: fmt.Sprintf("azd env set %s <value>", key),
114-
Description: "supply the agent.yaml variable",
119+
Description: "referenced by agent.yaml but not set in azd env",
115120
Priority: priority,
116121
})
117122
priority++
118123
}
124+
// Follow-up: once the user supplies the values above, the next
125+
// productive command is `azd ai agent run`. Without this hint
126+
// the post-init Next: block stops at the env-set lines and the
127+
// user has to remember the run step themselves — that's the
128+
// "Then run 'azd ai agent run' to start locally" line in
129+
// issue #7975's manual-vars example output. Suppressed when
130+
// placeholders are also unresolved — running locally with
131+
// literal `{{NAME}}` values produces a broken agent, so the
132+
// user must finish the placeholder fix-ups first; the
133+
// trailing `azd deploy` reminder still applies.
134+
if !hasPlaceholders {
135+
out = append(out, Suggestion{
136+
Command: "azd ai agent run",
137+
Description: "start the agent locally once the values above are set",
138+
Priority: priority,
139+
})
140+
}
119141
case hasPlaceholders:
120142
// Only unresolved placeholders remain — do not emit
121143
// `azd ai agent run` because running locally with literal

cli/azd/extensions/azure.ai.agents/internal/cmd/nextstep/resolver_test.go

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,22 @@ func TestResolveAfterInit(t *testing.T) {
133133
assert.True(t, last.Trailing, "last suggestion must be flagged Trailing")
134134

135135
if len(tt.wantManualVarKeys) > 0 {
136-
assert.Len(t, out, len(tt.wantManualVarKeys)+1)
136+
// N env-set lines + 1 `azd ai agent run` follow-up + 1
137+
// trailing `azd deploy`.
138+
assert.Len(t, out, len(tt.wantManualVarKeys)+2)
137139
for i, key := range tt.wantManualVarKeys {
138140
assert.True(t,
139141
strings.HasPrefix(out[i].Command, "azd env set "+key+" "),
140142
"got %q", out[i].Command)
141143
}
144+
// The slot immediately after the env-set lines is the
145+
// run follow-up — see ResolveAfterInit's MissingManualVars
146+
// branch (issue #7975 manual-vars example).
147+
followUp := out[len(tt.wantManualVarKeys)]
148+
assert.Equal(t, "azd ai agent run", followUp.Command,
149+
"expected `azd ai agent run` follow-up after env-set lines")
150+
assert.False(t, followUp.Trailing,
151+
"run follow-up must be a primary suggestion, not Trailing")
142152
} else {
143153
assert.Contains(t, out[0].Command, tt.wantPrimaryHas)
144154
}
@@ -154,17 +164,55 @@ func TestResolveAfterInit_ManualVarsCapAtThree(t *testing.T) {
154164
MissingManualVars: []string{"V1", "V2", "V3", "V4", "V5"},
155165
}
156166
out := ResolveAfterInit(state)
157-
// 3 manual + 1 trailing.
158-
require.Len(t, out, 4)
159-
assert.Equal(t, "azd deploy", out[3].Command)
160-
assert.True(t, out[3].Trailing, "deploy footer must be Trailing")
167+
// 3 env-set lines (capped) + 1 `azd ai agent run` follow-up + 1
168+
// trailing `azd deploy`.
169+
require.Len(t, out, 5)
170+
for i := range 3 {
171+
assert.True(t, strings.HasPrefix(out[i].Command, "azd env set "),
172+
"slot %d should be an env-set line, got %q", i, out[i].Command)
173+
}
174+
assert.Equal(t, "azd ai agent run", out[3].Command,
175+
"slot 3 should be the run follow-up")
176+
assert.Equal(t, "azd deploy", out[4].Command)
177+
assert.True(t, out[4].Trailing, "deploy footer must be Trailing")
161178
}
162179

163180
func TestResolveAfterInit_NilState(t *testing.T) {
164181
t.Parallel()
165182
assert.Nil(t, ResolveAfterInit(nil))
166183
}
167184

185+
// TestResolveAfterInit_ManualVarsSingleEmitsEnrichedShape locks the
186+
// single-missing-manual-var case end-to-end. Three asserts: the env-set
187+
// line has the enriched "referenced by agent.yaml but not set in azd
188+
// env" description, the `azd ai agent run` follow-up immediately follows
189+
// the env-set lines, and the trailing `azd deploy` reminder is preserved.
190+
// This is the canonical B2 fix shape from issue #7975's "Example output
191+
// (project ready, but manual config values missing)".
192+
func TestResolveAfterInit_ManualVarsSingleEmitsEnrichedShape(t *testing.T) {
193+
t.Parallel()
194+
195+
state := &State{
196+
HasProjectEndpoint: true,
197+
MissingManualVars: []string{"MY_API_KEY"},
198+
}
199+
out := ResolveAfterInit(state)
200+
// 1 env-set + 1 run follow-up + 1 trailing.
201+
require.Len(t, out, 3)
202+
203+
assert.Equal(t, "azd env set MY_API_KEY <value>", out[0].Command)
204+
assert.Equal(t, "referenced by agent.yaml but not set in azd env", out[0].Description,
205+
"enriched description must explain WHY the env-set is needed")
206+
assert.False(t, out[0].Trailing)
207+
208+
assert.Equal(t, "azd ai agent run", out[1].Command)
209+
assert.Equal(t, "start the agent locally once the values above are set", out[1].Description)
210+
assert.False(t, out[1].Trailing, "run follow-up must be a primary suggestion")
211+
212+
assert.Equal(t, "azd deploy", out[2].Command)
213+
assert.True(t, out[2].Trailing)
214+
}
215+
168216
// TestResolveAfterInit_ToolboxReproRendersAllCategories locks the full
169217
// regression for the toolbox-sample bug end-to-end: the state contains
170218
// BOTH an unresolved manifest placeholder AND a missing manual env var,
@@ -191,6 +239,13 @@ func TestResolveAfterInit_ToolboxReproRendersAllCategories(t *testing.T) {
191239
assert.Contains(t, rendered,
192240
"azd env set TOOLBOX_WEB_SEARCH_TOOLS_MCP_ENDPOINT <value>",
193241
"manual-var fix-up missing — this is the original toolbox-sample regression")
242+
// `azd ai agent run` follow-up is intentionally suppressed when
243+
// UnresolvedPlaceholders are also present: running locally with
244+
// literal `{{NAME}}` values produces a broken agent. The user
245+
// must fix the placeholder first; the trailing `azd deploy`
246+
// still applies.
247+
assert.NotContains(t, rendered, "start the agent locally once the values above are set",
248+
"run follow-up should be suppressed while placeholders are unresolved")
194249
assert.Contains(t, rendered, "azd deploy", "trailing deploy reminder missing")
195250
}
196251

0 commit comments

Comments
 (0)