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: <title> + +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