diff --git a/.github/skills/_shared/hygiene-rules.md b/.github/skills/_shared/hygiene-rules.md
new file mode 100644
index 00000000000..befc3302d4a
--- /dev/null
+++ b/.github/skills/_shared/hygiene-rules.md
@@ -0,0 +1,65 @@
+# Issue Hygiene Rules
+
+Shared validation rules used by both sprint-check and issue-cleanup skills.
+
+## Required Fields by Milestone Tier
+
+| Milestone tier | Must have |
+|---|---|
+| Future, Backlog, Backlog Candidates | labels (at least one `area/*`), milestone |
+| On Deck, current/upcoming month | labels, milestone, Priority (project), Initiative (project), EPIC parent (if quarterly initiative) |
+| Current Sprint | all above + assigned to someone |
+
+## Field Checks
+
+### 1. Labels
+- **Rule**: every issue must have at least one `area/*` label
+- **Check**: `labels` contains at least one label starting with `area/`
+- **Fix**: present the area label list, let user pick
+- **Note**: type labels (`bug`, `enhancement`, `feature`) are nice-to-have, not required
+
+### 2. Milestone
+- **Rule**: every issue in the current sprint should have the current month's milestone
+- **Current month milestone**: derive from today's date β "May 2026", "June 2026", etc.
+- **Check**: `milestone.title` matches current month
+- **Allowed exceptions**: On Deck, Backlog (for items being tracked but not committed this month)
+- **Fix**: `gh issue edit NUMBER --repo Azure/azure-dev --milestone "May 2026"`
+
+### 3. Priority (Project Field)
+- **Rule**: issues in On Deck or current sprint must have Priority set
+- **Check**: `fieldValueByName(name: "Priority")` is not null
+- **Fix**: present Priority options, let user pick, then mutate via GraphQL
+
+### 4. Initiative (Project Field)
+- **Rule**: issues in On Deck or current sprint must have Initiative set
+- **Check**: `fieldValueByName(name: "Initiative")` is not null
+- **Fix**: present Initiative options, let user pick, then mutate via GraphQL
+- **Special**: π‘οΈ Ongoing initiative allows direct issues (no EPIC parent needed)
+
+### 5. EPIC Parent
+- **Rule**: if initiative is a quarterly initiative (not π‘οΈ Ongoing), the issue should have an EPIC parent
+- **Check**: `parent` field is not null (GitHub sub-issues)
+- **Note**: this is a warning, not a blocker β some standalone items under quarterly initiatives are valid
+- **Fix**: cannot auto-fix, suggest to user which EPICs exist under that initiative
+
+### 6. Assignment
+- **Rule**: issues in the current sprint must be assigned to someone
+- **Check**: `assignees` is not empty
+- **Fix**: `gh issue edit NUMBER --repo Azure/azure-dev --add-assignee "@me"` or ask who
+
+## Customer-Reported Special Rules
+
+Customer-reported issues (`customer-reported` label) get elevated priority in checks:
+- **No milestone** β π΄ Critical (should be triaged into current milestone)
+- **No area label** β π΄ Critical (can't route to the right team)
+- **Has `needs-triage`** β expected, not a problem (pending triage)
+- **Has `needs-team-attention`** β expected after milestone is set
+- **Past-due milestone** β π‘ Warning (work planned but not completed)
+
+## Classification Order (for suggesting placement)
+
+When an issue has no milestone or initiative, suggest placement:
+1. **Planned priorities** β fits a current quarter initiative/EPIC? β parent it there
+2. **Customer-reported / regression** β current milestone + π‘οΈ Ongoing
+3. **One-off items** (engsys, pipeline, test) β current milestone + π‘οΈ Ongoing
+4. **None of the above** β Backlog, Backlog Candidates, or Future
diff --git a/.github/skills/hotfix-release/SKILL.md b/.github/skills/hotfix-release/SKILL.md
new file mode 100644
index 00000000000..fe65a126577
--- /dev/null
+++ b/.github/skills/hotfix-release/SKILL.md
@@ -0,0 +1,281 @@
+---
+name: hotfix-release
+license: MIT
+metadata:
+ version: "1.0"
+ # Bump major on breaking prompt/trigger changes; bump minor on new references or fix strategies.
+description: >-
+ **WORKFLOW SKILL** β Creates a hotfix release branch from an existing release tag,
+ cherry-picks specified PRs, bumps version, updates changelog, and pushes the branch.
+ Interactive β handles cherry-pick conflicts with user guidance.
+
+ INVOKES: git CLI, gh CLI, GitHub MCP tools, ask_user.
+
+ USE FOR: create hotfix, hotfix release, cherry-pick release, patch release,
+ hotfix for azd, emergency release, create hotfix branch, hotfix from tag.
+
+ DO NOT USE FOR: regular releases (use changelog-generation + ADO pipeline),
+ changelog only (use changelog-generation), code review (use code-review),
+ sprint checks (use sprint-check).
+---
+
+# hotfix-release
+
+**WORKFLOW SKILL** β Creates hotfix release branches with cherry-picked fixes.
+
+INVOKES: `git` CLI, `gh` CLI, GitHub MCP tools, `ask_user`.
+
+## Prerequisites
+
+| Tool | Purpose |
+|------|---------|
+| `git` | Git CLI β push access to Azure/azure-dev |
+| `gh` | GitHub CLI β authenticated with repo access |
+| `go` | Go toolchain β to verify build after cherry-pick (optional) |
+
+## Preflight
+
+Verify access:
+
+```bash
+# Check git remote
+git remote -v | grep "Azure/azure-dev"
+
+# Check gh auth
+gh auth status
+
+# Check write access
+gh api repos/Azure/azure-dev --jq .permissions.push
+```
+
+If push is `false`, stop:
+> You need write access to Azure/azure-dev to create hotfix branches.
+
+## Workflow
+
+### Step 1 β Parse Request
+
+The user provides:
+- **Release version** to hotfix (e.g., "1.24.3" or "v1.24.3")
+- **PR numbers** to cherry-pick (e.g., "#8001, #8002" or "8001 8002")
+
+Example prompts:
+- "create hotfix for v1.24.3 with PRs #8001, #8002"
+- "hotfix 1.24.3 cherry-pick 8001 8002"
+- "patch release for 1.24.3"
+
+If either is missing, ask via `ask_user`.
+
+**Version**: ask for the base release version to hotfix:
+> Which release version should I create the hotfix from?
+
+**PRs**: ask which PRs to cherry-pick:
+> Which merged PRs should be included in the hotfix? (comma-separated numbers)
+
+### Step 2 β Validate Inputs
+
+**Validate the release tag exists:**
+
+```bash
+git fetch --tags
+git tag -l "azure-dev-cli_${BASE_VERSION}"
+```
+
+The tag format is `azure-dev-cli_X.Y.Z` (e.g., `azure-dev-cli_1.24.3`).
+If not found, list recent release tags and ask the user to pick:
+
+```bash
+git tag -l "azure-dev-cli_*" --sort=-version:refname | head -10
+```
+
+**Validate each PR is merged:**
+
+```bash
+gh pr view PR_NUMBER --repo Azure/azure-dev --json state,mergeCommit,mergedAt,baseRefName
+```
+
+For each PR:
+- Must be `state: "MERGED"`
+- Must have `baseRefName: "main"` β reject PRs merged into feature branches (they may contain unrelated changes)
+- Record `mergeCommit.oid` β this is what we cherry-pick
+- If PR is not merged, warn and skip it
+- If PR was merged into a non-main branch, warn:
+ > PR #NNNN was merged into `BRANCH`, not `main`. Cherry-picking its merge commit may include unrelated changes. Skip this PR?
+
+**Determine if merge commit:**
+
+After fetching, check the parent count of the merge commit SHA:
+
+```bash
+git fetch origin MERGE_SHA
+git show --no-patch --pretty=%P MERGE_SHA
+```
+
+- If 1 parent β squash merge (cherry-pick directly)
+- If 2+ parents β merge commit (use `git cherry-pick -m 1 MERGE_SHA`)
+
+**Compute hotfix version:**
+- Parse base version X.Y.Z β hotfix version X.Y.(Z+1)
+- e.g., 1.24.3 β 1.24.4
+- Confirm with user:
+ > Hotfix version will be **1.24.4**. Is that correct?
+
+### Step 3 β Create Hotfix Branch
+
+```bash
+# Ensure we're up to date
+git fetch origin
+
+# Create branch from the release tag
+git checkout -b hotfix/azd-HOTFIX_VERSION azure-dev-cli_BASE_VERSION
+```
+
+Example: `git checkout -b hotfix/azd-1.24.4 azure-dev-cli_1.24.3`
+
+### Step 4 β Cherry-Pick PRs
+
+For each PR, in the order specified by the user:
+
+```bash
+# For squash merges (single commit)
+git cherry-pick MERGE_COMMIT_SHA
+
+# For merge commits (multiple commits)
+git cherry-pick -m 1 MERGE_COMMIT_SHA
+```
+
+**If cherry-pick succeeds**: log success and continue.
+
+**If cherry-pick has conflicts**:
+1. Show the conflicting files:
+ ```bash
+ git diff --name-only --diff-filter=U
+ ```
+2. Show the conflict content for each file
+3. Ask the user via `ask_user`:
+ > Cherry-pick of PR #NNNN has conflicts in these files:
+ > - path/to/file1.go
+ > - path/to/file2.go
+ >
+ > How should I proceed?
+
+ Choices:
+ - **Show conflicts** β I'll display the diff and help resolve
+ - **Skip this PR** β abort this cherry-pick, continue with others
+ - **Abort hotfix** β cancel the entire hotfix
+
+4. If resolving: help the user edit the conflicting files, then:
+ ```bash
+ git add .
+ git cherry-pick --continue
+ ```
+
+**After each cherry-pick**, verify the build still compiles:
+```bash
+cd cli/azd && go build ./... 2>&1 | head -20
+```
+
+If build fails, warn the user and offer to continue or stop.
+
+### Step 5 β Bump Version
+
+Update version files per [references/version-files.md](references/version-files.md):
+
+{{ references/version-files.md }}
+
+### Step 6 β Update Changelog
+
+Add a hotfix section to `cli/azd/CHANGELOG.md`:
+
+```markdown
+## X.Y.Z (YYYY-MM-DD) β Hotfix
+
+### Bugs Fixed
+
+- Description from PR #NNNN title [[#NNNN]](https://github.com/Azure/azure-dev/pull/NNNN)
+- Description from PR #MMMM title [[#MMMM]](https://github.com/Azure/azure-dev/pull/MMMM)
+```
+
+- Use PR titles as entry descriptions
+- Place the new section **above** the previous release section
+- Use today's date
+- Mark as "Hotfix" in the header
+
+Present the changelog entry to the user for review before writing:
+> Here's the changelog entry I'll add. Look good?
+
+### Step 7 β Commit & Push
+
+```bash
+git add -A
+git commit -m "Release hotfix azd X.Y.Z
+
+Cherry-picked fixes:
+- PR #NNNN:
+- PR #MMMM:
+
+Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>"
+
+git push origin hotfix/azd-HOTFIX_VERSION
+```
+
+### Step 8 β Summary
+
+Print a summary with next steps:
+
+```
+β
Hotfix branch created and pushed!
+
+ Branch: hotfix/azd-1.24.4
+ Compare: https://github.com/Azure/azure-dev/compare/azure-dev-cli_1.24.3...hotfix/azd-1.24.4
+ Version: 1.24.4
+
+ Cherry-picked PRs:
+ β
#8001 β "Fix auth token refresh"
+ β
#8002 β "Handle nil pointer in deploy"
+
+ Next steps:
+ 1. Review the branch: git log --oneline azure-dev-cli_1.24.3..hotfix/azd-1.24.4
+ 2. Trigger the release pipeline (see "Triggering the Release" below)
+ 3. After release, add the hotfix changelog entry to main's CHANGELOG.md
+```
+
+**Do NOT** create a PR to main. The hotfix branch is released directly via the ADO pipeline.
+
+**Do NOT** create tags manually. The release pipeline creates both `azure-dev-cli_X.Y.Z` and `cli/azd/vX.Y.Z` tags automatically.
+
+---
+
+## Triggering the Release
+
+Open the [azure-dev - cli](https://dev.azure.com/azure-sdk/internal/_build?definitionId=4643) pipeline in ADO:
+
+1. Click **Run pipeline**
+2. Select the `hotfix/*` branch (e.g., `hotfix/azd-1.24.4`)
+3. β
Check **"Check to run a release build"**
+4. Leave Azure Record Mode as **live** (default)
+5. Click **Run**
+
+The pipeline builds, signs, and publishes the release β including GitHub release, tags, storage upload, Chocolatey, WinGet, and Homebrew.
+
+> **Tip**: Set the pipeline variable `Skip.IncrementVersion = true` before running. The default increment job creates a version-bump PR targeting the source branch, which is unnecessary for a hotfix branch.
+
+---
+
+## Post-Release
+
+1. Add the hotfix changelog section (e.g., `## 1.24.4`) to `main`'s CHANGELOG.md so the release history is complete
+2. Do **not** merge version file changes back β main's version is already ahead
+3. The hotfix branch can be kept for reference or deleted per team preference
+
+---
+
+## Error Handling
+
+- Tag not found β list recent tags, let user pick
+- PR not merged β warn, skip, continue with others
+- Cherry-pick conflict β interactive resolution (Step 4)
+- Build failure after cherry-pick β warn, offer to continue or abort
+- Push rejected β first try `git pull --rebase origin BRANCH` to incorporate any remote commits, then retry `git push`. Only suggest `git push --force-with-lease` as a last resort if rebase also fails, and warn the user that force-pushing may overwrite others' work on the branch
+- `gh` not authenticated β stop, tell user to run `gh auth login`
+- No write access β stop, explain requirement
diff --git a/.github/skills/hotfix-release/references/version-files.md b/.github/skills/hotfix-release/references/version-files.md
new file mode 100644
index 00000000000..abc18303b91
--- /dev/null
+++ b/.github/skills/hotfix-release/references/version-files.md
@@ -0,0 +1,52 @@
+# Version Files β Hotfix Update
+
+When creating a hotfix release, two version files must be updated.
+
+## 1. `cli/version.txt`
+
+Contains the bare version string (no `v` prefix):
+
+```
+1.24.4
+```
+
+Update:
+```bash
+echo "HOTFIX_VERSION" > cli/version.txt
+```
+
+## 2. `cli/azd/pkg/azdext/version.go`
+
+Contains the `Version` constant. Find and update the line:
+
+```go
+const Version = "1.24.3"
+```
+
+Change to:
+
+```go
+const Version = "1.24.4"
+```
+
+Use `sed` or direct file edit. Verify:
+```bash
+grep 'const Version' cli/azd/pkg/azdext/version.go
+```
+
+## Validation
+
+After updating both files, verify consistency:
+
+```bash
+VERSION_TXT=$(cat cli/version.txt | tr -d '\n')
+VERSION_GO=$(grep 'const Version' cli/azd/pkg/azdext/version.go | sed 's/.*"\(.*\)".*/\1/')
+echo "version.txt: $VERSION_TXT"
+echo "version.go: $VERSION_GO"
+
+if [ "$VERSION_TXT" != "$VERSION_GO" ]; then
+ echo "β Version mismatch!"
+else
+ echo "β
Versions match: $VERSION_TXT"
+fi
+```
diff --git a/.github/skills/issue-cleanup/SKILL.md b/.github/skills/issue-cleanup/SKILL.md
new file mode 100644
index 00000000000..9c22474cdfa
--- /dev/null
+++ b/.github/skills/issue-cleanup/SKILL.md
@@ -0,0 +1,224 @@
+---
+name: issue-cleanup
+license: MIT
+metadata:
+ version: "1.0"
+ # Bump major on breaking prompt/trigger changes; bump minor on new references or fix strategies.
+description: >-
+ **WORKFLOW SKILL** β Scans the issue backlog for hygiene problems: missing labels,
+ missing milestones, customer-reported issues without triage, stale milestone assignments.
+ Reports findings grouped by severity and offers interactive fixes.
+
+ INVOKES: gh CLI, GitHub MCP tools, ask_user.
+
+ USE FOR: cleanup check, issue cleanup, backlog cleanup, check issue hygiene,
+ backlog scan, find unlabeled issues, find issues without milestones, customer-reported check,
+ triage check, orphan issues, stale milestone check.
+
+ DO NOT USE FOR: sprint readiness (use sprint-check), changelog (use changelog-generation),
+ code quality (use azd-preflight), hotfix (use hotfix-release).
+---
+
+# issue-cleanup
+
+**WORKFLOW SKILL** β Scans the issue backlog for hygiene problems and offers interactive fixes.
+
+INVOKES: `gh` CLI, GitHub MCP tools, `ask_user`.
+
+## Prerequisites
+
+| Tool | Purpose |
+|------|---------|
+| `gh` | GitHub CLI β authenticated with repo access |
+
+## Preflight
+
+Verify `gh` is authenticated and has access to the repo:
+
+```bash
+gh auth status
+gh repo view Azure/azure-dev --json name -q .name
+```
+
+If project field checks are needed (e.g., sprint-check crossover), also verify `project` scope per sprint-check preflight.
+
+## Workflow
+
+### Step 1 β Determine Scope
+
+Parse the user's request:
+
+| User says | Scope | What to query |
+|-----------|-------|---------------|
+| "cleanup check" | **Full backlog** | All open issues |
+| "cleanup check for May 2026" | **Milestone** | Issues in that milestone |
+| "cleanup check for customer-reported" | **Label** | Issues with `customer-reported` label |
+| "cleanup check for area/provisioning" | **Area** | Issues with that area label |
+| "find issues without milestones" | **Specific** | Open issues with no milestone |
+| "find unlabeled issues" | **Specific** | Open issues with no area/* label |
+
+If scope is unclear, ask via `ask_user`:
+> What would you like me to scan?
+
+Choices:
+- **Full backlog** (all open issues)
+- **Customer-reported issues** (Recommended β highest priority)
+- **Specific milestone** (I'll ask which one)
+- **Specific area label** (I'll ask which one)
+
+### Step 2 β Query Issues
+
+Use `gh` CLI to fetch issues based on scope.
+
+**Full backlog:**
+```bash
+gh issue list --repo Azure/azure-dev --state open --limit 500 \
+ --json number,title,labels,milestone,assignees,createdAt,updatedAt
+```
+
+**Customer-reported:**
+```bash
+gh issue list --repo Azure/azure-dev --state open --label "customer-reported" --limit 200 \
+ --json number,title,labels,milestone,assignees,createdAt,updatedAt
+```
+
+**Specific milestone:**
+```bash
+gh issue list --repo Azure/azure-dev --state open --milestone "May 2026" --limit 200 \
+ --json number,title,labels,milestone,assignees,createdAt,updatedAt
+```
+
+**No milestone:**
+```bash
+gh issue list --repo Azure/azure-dev --state open --search "no:milestone" --limit 500 \
+ --json number,title,labels,milestone,assignees,createdAt,updatedAt
+```
+
+For large result sets (500+), paginate and warn the user about volume.
+
+### Step 3 β Analyze Issues
+
+Apply hygiene rules from [references/hygiene-rules.md](references/hygiene-rules.md) (shared with sprint-check).
+
+**Note**: issue-cleanup uses the **full hygiene rules** but applies tier-appropriate checks. An issue in "Future" only needs labels + milestone, while one in "On Deck" needs everything.
+
+For each issue, produce a finding if ANY rule is violated.
+
+### Step 4 β Special: Customer-Reported Triage Check
+
+Customer-reported issues get special treatment:
+
+**π΄ CRITICAL β Customer-reported with no milestone:**
+These are customer issues that landed but were never triaged into the planning process.
+- Flag as critical
+- Offer to set milestone to current month (treat as triage inbox)
+- Offer to add `needs-triage` label if not present
+
+**π΄ CRITICAL β Customer-reported with no area label:**
+These can't be routed to the right team member.
+- Flag as critical
+- Present area label list, let user pick
+
+**π‘ WARNING β Customer-reported in past milestone:**
+Issue had a milestone assigned but the milestone has passed and issue is still open.
+- The work was planned but not completed
+- Offer to move to current milestone or On Deck
+
+**Flow for pulling customer-reported into current sprint:**
+```
+Customer opens issue
+ β fabricbot: +customer-reported +question (instant)
+ β cleanup-check finds it: "no milestone, no area label"
+ β user confirms: set milestone to "May 2026"
+ β user picks: area/provisioning
+ β team sees it in current milestone during sprint planning
+ β team triages: keep in sprint, move to On Deck, or move to Backlog
+```
+
+### Step 5 β Report Findings
+
+Group by severity, then by finding type:
+
+```
+π Issue Cleanup Report β Customer-Reported Issues
+βββββββββββββββββββββββββββββββββββββββββββββββββββ
+
+π΄ Critical (5 issues β need immediate attention):
+
+ No milestone + no area label:
+ #7926 "Feedback after creating Go extension" (created Apr 25)
+ #7894 "Product exited" (created Apr 23)
+
+ No milestone (has area label):
+ #7507 "Bug: Remote invoke for invocations-protocol..." (created Apr 5)
+ #7339 "Azure Extension Crashing" (created Mar 26, area/vscode)
+ #7244 "[Issue] AZD should support custom clouds" (created Mar 23)
+
+π‘ Warning (3 issues):
+
+ Past milestone (still open):
+ #7564 "agent.yaml not in docs" (April 2026 milestone, due May 6)
+
+ Missing area label:
+ #6612 "[Feature request] Add deployment targets" (On Deck)
+
+β
Clean (12 issues)
+
+Summary: 5 critical, 3 warnings, 12 clean out of 20 issues
+```
+
+### Step 6 β Fix Interactively
+
+After the report, offer to fix:
+
+> Found 5 critical issues. Would you like me to fix them?
+
+Choices:
+- **Fix all critical issues** (I'll ask for each field choice)
+- **Fix one at a time** (I'll go through each issue)
+- **Just the report** (no changes)
+
+For each issue being fixed:
+
+1. **Set milestone**: default to current month milestone, confirm via `ask_user`
+ ```bash
+ gh issue edit NUMBER --repo Azure/azure-dev --milestone "May 2026"
+ ```
+
+2. **Add area label**: present area label list, let user pick
+ ```bash
+ gh issue edit NUMBER --repo Azure/azure-dev --add-label "area/core-cli"
+ ```
+
+3. **Add needs-triage**: if not already present
+ ```bash
+ gh issue edit NUMBER --repo Azure/azure-dev --add-label "needs-triage"
+ ```
+
+4. **Assign**: offer to assign to the user or someone else
+ ```bash
+ gh issue edit NUMBER --repo Azure/azure-dev --add-assignee "@me"
+ ```
+
+### Step 7 β Stale Milestone Check
+
+Find issues in past milestones that are still open:
+
+```bash
+# Get all milestones with due dates
+gh api repos/Azure/azure-dev/milestones --jq '.[] | select(.due_on != null and .open_issues > 0) | "\(.number) \(.title) \(.due_on) \(.open_issues)"'
+```
+
+For each past-due milestone with open issues:
+- List the issues
+- Offer to move them to current milestone, On Deck, or Backlog
+
+---
+
+## Error Handling
+
+- `gh` not authenticated β stop, tell user to run `gh auth login`
+- Too many issues (>500) β warn, suggest narrowing scope
+- Rate limiting β wait and retry with backoff
+- Issue edit fails β log error, continue with next issue
+- Milestone not found β list available milestones, let user pick
diff --git a/.github/skills/issue-cleanup/references/hygiene-rules.md b/.github/skills/issue-cleanup/references/hygiene-rules.md
new file mode 120000
index 00000000000..b43e4060b33
--- /dev/null
+++ b/.github/skills/issue-cleanup/references/hygiene-rules.md
@@ -0,0 +1 @@
+../../_shared/hygiene-rules.md
\ No newline at end of file
diff --git a/.github/skills/sprint-check/SKILL.md b/.github/skills/sprint-check/SKILL.md
new file mode 100644
index 00000000000..dfd951ba632
--- /dev/null
+++ b/.github/skills/sprint-check/SKILL.md
@@ -0,0 +1,339 @@
+---
+name: sprint-check
+license: MIT
+metadata:
+ version: "1.0"
+ # Bump major on breaking prompt/trigger changes; bump minor on new references or fix strategies.
+description: >-
+ **WORKFLOW SKILL** β Validates sprint readiness for issues.
+ Checks labels, milestones, project fields (Sprint, Priority, Initiative),
+ EPIC parents, and assignments. Can fix fields interactively with user confirmation.
+
+ INVOKES: gh CLI, GitHub MCP tools, ask_user.
+
+ USE FOR: sprint check, sprint-check, check sprint readiness, check my sprint,
+ sprint check for the team, sprint planning, mid-sprint check, sprint close check,
+ prepare issue for sprint, make issue sprint ready, pull issue into sprint.
+
+ DO NOT USE FOR: backlog cleanup (use issue-cleanup), changelog (use changelog-generation),
+ code quality checks (use azd-preflight), release (use hotfix-release).
+---
+
+# sprint-check
+
+**WORKFLOW SKILL** β Validates sprint readiness for issues in the current sprint.
+
+INVOKES: `gh` CLI, GitHub MCP tools, `ask_user`.
+
+## Prerequisites
+
+| Tool | Purpose |
+|------|---------|
+| `gh` | GitHub CLI β authenticated with `project` scope |
+
+## Preflight
+
+Before any operation, verify access:
+
+```bash
+gh auth status
+```
+
+**Check for `project` scope.** If missing, tell the user:
+> Your `gh` token needs the `project` scope to read/write project fields.
+> Run: `gh auth refresh --scopes project`
+
+Then verify project access:
+```bash
+gh api graphql -f query='{
+ organization(login: "Azure") {
+ projectV2(number: 182) { title }
+ }
+}'
+```
+
+If this fails, stop and report the error.
+
+## Workflow
+
+### Step 1 β Determine Scope
+
+Parse the user's request to determine scope:
+
+| User says | Scope | What to query |
+|-----------|-------|---------------|
+| "sprint-check" / "check my sprint" | **Personal** | Issues assigned to the current `gh` user in the current sprint |
+| "sprint-check for the team" | **Team** | All issues in the current sprint |
+| "sprint-check for @username" | **User** | Issues assigned to `@username` in the current sprint |
+| "check issue #1234 for sprint" | **Single issue** | Validate one issue for sprint readiness |
+| "pull #1234 into this sprint" | **Single issue + fix** | Validate + fix all fields |
+
+If ambiguous, ask via `ask_user`.
+
+### Step 2 β Get Current Sprint
+
+Query the current sprint iteration from Project #182:
+
+```bash
+gh api graphql -f query='{
+ organization(login: "Azure") {
+ projectV2(number: 182) {
+ field(name: "Sprint") {
+ ... on ProjectV2IterationField {
+ id
+ configuration {
+ iterations { id title startDate duration }
+ }
+ }
+ }
+ }
+ }
+}'
+```
+
+Find the iteration where `today >= startDate && today < startDate + duration`.
+Store the sprint `id`, `title`, and the field `id` for later mutations.
+
+### Step 3 β Query Sprint Issues
+
+**For team/personal scope** β query all project items in the current sprint.
+
+**Note**: `--paginate` does not work reliably for this query. Use manual cursor-based pagination β fetch 100 items at a time, pass the `endCursor` to the next query until `hasNextPage` is false.
+
+```bash
+gh api graphql -f query='
+query($cursor: String) {
+ organization(login: "Azure") {
+ projectV2(number: 182) {
+ items(first: 100, after: $cursor) {
+ pageInfo { hasNextPage endCursor }
+ nodes {
+ content {
+ ... on Issue {
+ number
+ title
+ state
+ assignees(first: 10) { nodes { login } }
+ labels(first: 20) { nodes { name } }
+ milestone { title }
+ parent { number title }
+ }
+ }
+ fieldValueByName(name: "Sprint") {
+ ... on ProjectV2ItemFieldIterationValue { title iterationId }
+ }
+ fieldValueByName(name: "Priority") {
+ ... on ProjectV2ItemFieldSingleSelectValue { name optionId }
+ }
+ fieldValueByName(name: "Initiative") {
+ ... on ProjectV2ItemFieldSingleSelectValue { name optionId }
+ }
+ }
+ }
+ }
+ }
+}'
+```
+
+Filter items where Sprint title matches the current sprint.
+For personal scope, further filter by assignee matching the `gh` user.
+
+**For single-issue scope** β query the specific issue:
+
+```bash
+gh api graphql -f query='{
+ repository(owner: "Azure", name: "azure-dev") {
+ issue(number: ISSUE_NUMBER) {
+ number title state
+ assignees(first: 10) { nodes { login } }
+ labels(first: 20) { nodes { name } }
+ milestone { title number }
+ parent { number title }
+ projectItems(first: 100) {
+ nodes {
+ project { number }
+ id
+ fieldValueByName(name: "Sprint") {
+ ... on ProjectV2ItemFieldIterationValue { title iterationId }
+ }
+ fieldValueByName(name: "Priority") {
+ ... on ProjectV2ItemFieldSingleSelectValue { name optionId }
+ }
+ fieldValueByName(name: "Initiative") {
+ ... on ProjectV2ItemFieldSingleSelectValue { name optionId }
+ }
+ }
+ }
+ }
+ }
+}'
+```
+
+### Step 4 β Validate Each Issue
+
+Apply the hygiene rules from [references/hygiene-rules.md](references/hygiene-rules.md).
+
+For each issue, check:
+
+{{ references/hygiene-rules.md }}
+
+### Step 5 β Report Findings
+
+Group findings by severity:
+
+**π΄ Critical** (blocks sprint work):
+- No assignee
+- No milestone
+- No area/* label
+
+**π‘ Warning** (should fix):
+- No Priority set (project field)
+- No Initiative set (project field)
+- Missing EPIC parent (if initiative is quarterly, not π‘οΈ Ongoing)
+- Milestone doesn't match current month
+- Customer-reported without `needs-triage` removed (still pending triage)
+
+**π’ Info**:
+- Multiple area labels (fine, just note it)
+- Has `needs-team-attention` (expected for customer-reported)
+
+Present as a table:
+
+```
+Sprint: May 2026 - Sprint 2 (May 5 β May 16)
+
+π΄ Critical (3 issues):
+ #7926 "Feedback after creating Go extension" β no assignee, no milestone, no area label
+ #7894 "Product exited" β no assignee, no milestone, no area label
+ #8033 "Create skills..." β no labels, no milestone
+
+π‘ Warning (2 issues):
+ #7248 "Handle DeploymentActive..." β no Priority, no Initiative
+ #7712 "Emergency hotfix release..." β no Initiative
+
+β
Clean (4 issues):
+ #8026, #7680, #7317, #435
+
+Summary: 3 critical, 2 warnings, 4 clean out of 9 issues
+```
+
+### Step 6 β Fix Interactively
+
+After reporting, offer to fix issues:
+
+> Found 3 issues with critical problems. Would you like me to fix them?
+
+For each fixable field, apply the rules:
+
+**Deterministic fixes** (apply with confirmation):
+- **Add to project**: if issue not in project #182, add it first via `addProjectV2ItemById`
+- **Set Sprint**: set to current sprint iteration
+- **Set Milestone**: set to current month milestone
+- **Assign**: assign to the user running the skill (or ask who)
+
+**Require user choice** (present options via `ask_user`):
+- **Area label**: present the list of `area/*` labels, let user pick
+- **Priority**: present Priority options from the project field
+- **Initiative**: present Initiative options from the project field
+
+For project field mutations, see [references/project-mutations.md](references/project-mutations.md).
+
+For milestone/label fixes via `gh` CLI:
+
+```bash
+# Set milestone
+gh issue edit ISSUE_NUMBER --repo Azure/azure-dev --milestone "May 2026"
+
+# Add label
+gh issue edit ISSUE_NUMBER --repo Azure/azure-dev --add-label "area/core-cli"
+
+# Assign
+gh issue edit ISSUE_NUMBER --repo Azure/azure-dev --add-assignee "@me"
+```
+
+### Step 7 β Pull Issue Into Sprint (single-issue mode)
+
+When the user says "pull #1234 into this sprint" or "make #1234 sprint ready":
+
+1. Query the issue (Step 3, single-issue)
+2. Validate (Step 4)
+3. For each missing field, ask the user what value to set
+4. Apply all fixes
+5. Confirm: "Issue #1234 is now sprint-ready β
"
+
+This is the "given an issue being pulled into this sprint, make it ready" flow.
+
+### Step 8 β Sprint Close / Rollover
+
+**Trigger**: This step activates when:
+- The user says "sprint close check" or similar
+- OR today is within 1 day of the current sprint's end date (auto-detected)
+
+**Workflow**:
+
+1. **Detect sprint end proximity** β from Step 2, calculate `sprint_end = startDate + duration`. If `today >= sprint_end - 1 day`, this step activates automatically after the normal report.
+
+2. **Identify the next sprint** β from the iterations list (Step 2), find the iteration whose `startDate` is immediately after the current sprint's end date. Store its `id` and `title`.
+
+3. **Classify open issues** β for each open issue still in the current sprint, present:
+
+```
+Sprint closing: May 04 - May 10 β Next: May 11 - May 17
+
+Open issues to resolve (5 issues):
+ #8033 β Create skills for weekly reports...
+ #8026 β RBAC propagation race causes 403...
+ #7712 β Emergency hotfix release process...
+ #7680 β Add extension telemetry...
+ #7317 β Improve error messages...
+```
+
+4. **Ask user how to handle** via `ask_user`:
+
+Choices:
+ - **Move all to next sprint** (Recommended) β bulk move everything
+ - **Decide per issue** β go through each one individually
+ - **Skip** β leave everything as-is
+
+If "Decide per issue", for each issue ask:
+ - **Move to next sprint** (default) β update Sprint field to next iteration
+ - **Keep in current sprint** β leave as-is (will show as overdue)
+ - **Remove from sprint** β clear Sprint field (back to backlog)
+
+5. **Apply moves** β for issues the user confirms, update the Sprint iteration field:
+
+```bash
+gh api graphql -f query='
+mutation {
+ updateProjectV2ItemFieldValue(input: {
+ projectId: "PROJECT_ID"
+ itemId: "ITEM_ID"
+ fieldId: "SPRINT_FIELD_ID"
+ value: { iterationId: "NEXT_SPRINT_ITERATION_ID" }
+ }) {
+ projectV2Item { id }
+ }
+}'
+```
+
+See [references/project-mutations.md](references/project-mutations.md) for full mutation details.
+
+6. **Summary** β after all moves:
+
+```
+Sprint rollover complete:
+ β
3 issues moved to May 11 - May 17
+ βΈοΈ 1 issue kept in May 04 - May 10
+ π 0 issues removed from sprint
+```
+
+---
+
+## Error Handling
+
+- `gh` not authenticated β stop, tell user to run `gh auth login`
+- Project access denied β stop, explain `project` scope requirement
+- Issue not in project β offer to add it via `addProjectV2ItemById`
+- Sprint field not found β warn, skip sprint-related checks
+- Rate limiting β wait and retry with backoff
+- Issue is closed β skip, note in report
diff --git a/.github/skills/sprint-check/references/hygiene-rules.md b/.github/skills/sprint-check/references/hygiene-rules.md
new file mode 120000
index 00000000000..b43e4060b33
--- /dev/null
+++ b/.github/skills/sprint-check/references/hygiene-rules.md
@@ -0,0 +1 @@
+../../_shared/hygiene-rules.md
\ No newline at end of file
diff --git a/.github/skills/sprint-check/references/project-mutations.md b/.github/skills/sprint-check/references/project-mutations.md
new file mode 100644
index 00000000000..0e6e813e35a
--- /dev/null
+++ b/.github/skills/sprint-check/references/project-mutations.md
@@ -0,0 +1,138 @@
+# Project V2 GraphQL Mutations
+
+Reference for reading and writing GitHub Projects V2 fields.
+
+## Project Info
+
+- **Organization**: Azure
+- **Project number**: 182
+- **Key fields**: Sprint (iteration), Priority (single select), Initiative (single select)
+
+## Prerequisites
+
+The `gh` CLI must be authenticated with `project` scope:
+```bash
+gh auth refresh --scopes project
+```
+
+## Reading Field Options
+
+Before setting a field value, you need the field ID and option IDs.
+
+### Get all field IDs and options
+
+```bash
+gh api graphql -f query='{
+ organization(login: "Azure") {
+ projectV2(number: 182) {
+ id
+ field(name: "Priority") {
+ ... on ProjectV2FieldCommon { id name }
+ ... on ProjectV2SingleSelectField {
+ id name
+ options { id name }
+ }
+ }
+ }
+ }
+}'
+```
+
+Repeat for "Initiative" and "Sprint" fields.
+
+### Get Sprint iterations
+
+```bash
+gh api graphql -f query='{
+ organization(login: "Azure") {
+ projectV2(number: 182) {
+ field(name: "Sprint") {
+ ... on ProjectV2IterationField {
+ id
+ configuration {
+ iterations { id title startDate duration }
+ completedIterations { id title startDate duration }
+ }
+ }
+ }
+ }
+ }
+}'
+```
+
+## Adding an Issue to the Project
+
+Before setting fields, the issue must be a project item.
+
+```bash
+# Get the issue's node ID
+gh api graphql -f query='{
+ repository(owner: "Azure", name: "azure-dev") {
+ issue(number: ISSUE_NUMBER) { id }
+ }
+}'
+```
+
+```bash
+# Add to project (returns the project item ID)
+gh api graphql -f query='
+mutation {
+ addProjectV2ItemById(input: {
+ projectId: "PROJECT_ID"
+ contentId: "ISSUE_NODE_ID"
+ }) {
+ item { id }
+ }
+}'
+```
+
+Store the returned `item.id` β you need it for field mutations.
+
+## Setting Field Values
+
+### Set Priority (single select)
+
+```bash
+gh api graphql -f query='
+mutation {
+ updateProjectV2ItemFieldValue(input: {
+ projectId: "PROJECT_ID"
+ itemId: "ITEM_ID"
+ fieldId: "PRIORITY_FIELD_ID"
+ value: { singleSelectOptionId: "OPTION_ID" }
+ }) {
+ projectV2Item { id }
+ }
+}'
+```
+
+### Set Initiative (single select)
+
+Same mutation shape as Priority, with Initiative field ID and option ID.
+
+### Set Sprint (iteration)
+
+```bash
+gh api graphql -f query='
+mutation {
+ updateProjectV2ItemFieldValue(input: {
+ projectId: "PROJECT_ID"
+ itemId: "ITEM_ID"
+ fieldId: "SPRINT_FIELD_ID"
+ value: { iterationId: "ITERATION_ID" }
+ }) {
+ projectV2Item { id }
+ }
+}'
+```
+
+## Common Workflow
+
+1. **Get project ID**: from the initial project query
+2. **Get field IDs**: Priority field ID, Initiative field ID, Sprint field ID
+3. **Get option IDs**: for Priority β list options, for Initiative β list options, for Sprint β list iterations
+4. **Check if issue is in project**: query issue's `projectItems`
+5. **If not in project**: add via `addProjectV2ItemById`
+6. **Set fields**: use `updateProjectV2ItemFieldValue` for each field
+
+Cache the project ID and field IDs across operations β they don't change within a session.
diff --git a/.vscode/cspell.misc.yaml b/.vscode/cspell.misc.yaml
index 043e51e8def..0e07a8ed1e7 100644
--- a/.vscode/cspell.misc.yaml
+++ b/.vscode/cspell.misc.yaml
@@ -103,3 +103,13 @@ overrides:
- Buildpacks
- containerapp
- staticwebapp
+ - filename: .github/skills/hotfix-release/**/*.md
+ words:
+ - azdext
+ - filename: .github/skills/issue-cleanup/**/*.md
+ words:
+ - engsys
+ - fabricbot
+ - filename: .github/skills/sprint-check/**/*.md
+ words:
+ - engsys