Work Item Management Standard
Standard scope
Applies to: All HCS-governed repos with GitHub Issues enabled. Superseded 2026-07-14 — the source of truth is now split by work-item type, not "ADO always." See pmo/ado-consolidation-and-github-issues-plan.md for the decision record. Domain: work-item-sync Status: Active
Principle — split source of truth
There are two independent sync flows, each with a different master. Do not treat this as "ADO is the source of truth with a GitHub mirror" — that was the pre-2026-07-14 model.
| Flow | Item types | Master | Direction |
|---|---|---|---|
| Flow 1 — Intake | Issue / Bug / Feature-Request | GitHub | filed in GitHub → mirrors up into ADO for rollup/reporting; ADO status changes flow back down to the same GitHub issue |
| Flow 2 — Roadmap projection | Epic / Feature / User Story / Task | ADO | authored in ADO (planning master) → projected down into GitHub as a new, locked, bot-owned issue (in practice: approved Features only — see work-items.md) |
GitHub is the public, remote-agent-workable surface for bugs/issues/feature-requests. ADO is the planning master for the Epic→Feature→Story→Task hierarchy. Neither system is "the" source of truth overall — the type determines which one is.
This applies to:
- All repos under the AzureLocal GitHub org (already GitHub-Issues-first before this standard, now the model everyone else follows)
- All app-solution and per-repo HCS GitHub orgs (see
docs/standards/ado-project-strategy.md) - The ADO org
dev.azure.com/hybridcloudsolutions - Not TierPoint (GitLab-only, no ADO sync — see the
tierpoint-prodtechoverlay inwork-items.md)
Flow 1 — Intake (GitHub master)
GitHub Issue, type Bug or Feature (filed by anyone, GitHub is master)
│
│ GitHub → ADO sync (on Issue opened, type = Bug|Feature)
â–¼
ADO Bug work item (Bug: mirrors 1:1, ADO copy tracks state)
or ADO Feature CANDIDATE (Feature-request: not a real ADO Feature until approved)
│
│ ADO → GitHub status sync (on state change)
â–¼
Same GitHub Issue, updated with status label + comment- Filing: Anyone files a GitHub Issue using the native
BugorFeatureissue type (seegithub-issues.md— do not use custom types or priority labels). - Triage:
needs-triagelabel until reviewed. ABugissue mirrors up as an ADO Bug work item 1:1. AFeatureissue mirrors up as a Feature candidate — it does not become a real, roadmap-tracked ADO Feature until someone approves it (see Flow 2). - Status flows back down: when the mirrored ADO item changes state, the same GitHub issue (not a new one) gets a status label/comment — this direction is about keeping the reporter informed, not about ADO owning the issue.
- GitHub is never a copy of ADO for these types — the GitHub issue is the original record; the ADO item is the rollup/reporting copy.
Flow 2 — Roadmap projection (ADO master)
ADO Epic / Feature / User Story / Task (authored in ADO, ADO is master)
│ Feature reaches "Approved" Delivery Stage
â–¼
NEW GitHub issue created by the sync bot
- type: Feature
- labels: roadmap, ado-managed
- Delivery Stage field set (Approved / Roadmap / In-Progress / Shipped)
- issue LOCKED (no comments/edits)
│
│ Any out-of-band edit is reverted by the reconcile workflow
â–¼
GitHub issue mirrors ADO Delivery Stage until Shipped- Epics, User Stories, and Tasks stay ADO-only in the common case — only Features that reach "Approved" get projected into GitHub, because that's the roadmap-visible unit (see
work-items.mdwork-item-types table andgithub-issues.mdDelivery Stage). - The projected issue is created and updated only by the sync bot/identity (Part E1 of the pmo plan). It is locked in GitHub; a scheduled reconcile job reverts any manual edit and re-locks.
- The org's GitHub Projects (v2) board is populated from these projected issues + their
Delivery Stagefield — this is the roadmap surface (seegithub-issues.md). Any web page (docs site, product sites) can read the live roadmap via the Projects v2 GraphQL API — no separateROADMAP.mdfile is generated.
ADO project structure
One ADO project per solution (app-solution org) or per repo (per-repo org) — see ado-project-strategy.md for the full ado_model mapping. Work items in ADO always belong to exactly one project. Cross-repo work is modeled by linking items across projects.
Work item types and their master
See work-items.md for full authoring rules. Summary of which system masters each type:
| Type | When to use | Master | Sync |
|---|---|---|---|
| Epic | Large initiative spanning multiple repos or a full quarter. Cross-project by definition. | ADO | Not projected to GitHub |
| Feature | Deliverable within one sprint or sprint cycle. May span repos but has a single owner. | ADO | Approved Features project down (Flow 2) |
| User Story | Single unit of work. Tied to one repo. Has acceptance criteria. | ADO | Not projected to GitHub |
| Bug | Defect with repro steps, expected vs. actual behavior. | GitHub | Mirrors up (Flow 1) |
| Feature-Request | A GitHub issue of native type Feature filed by anyone. | GitHub (until approved) | Mirrors up as a candidate; promotion to a real ADO Feature is a human decision |
| Task | Sub-work inside a Story. | ADO | Not synced to GitHub Issues |
Cross-project work items
Work items that affect more than one repo use the following conventions:
Naming: Include a scope prefix in square brackets before the title:
[cross-repo] Onboard all AzureLocal repos to CLAUDE.md standard
[azurelocal] Standardize MkDocs nav structure across all docs repos
[platform] Rotate all platform-level PATsADO tags: Apply a tag for each affected org or workstream. Tag values match GitHub org names: azurelocal, thisismydemo, hybridcloudsolutions, platform.
Linking: Child User Stories are linked to the parent Feature or Epic. Cross-repo Stories are linked to each other with the "Related" link type in ADO.
Area path: Cross-project items live in the primary area path for their initiating project. Tags carry the secondary scope.
GitHub Issues — sync behavior
Flow 1: GitHub → ADO (intake mirror)
When a GitHub Issue is opened in any synced repo, using native type Bug or Feature:
- Bug: a new ADO Bug work item is created in the mapped ADO project and area path, title prefixed with the GitHub repo name:
[azurelocal-monitoring] Monitor agent not starting after cluster update. - Feature: a Feature candidate is recorded (linked, not yet a tracked ADO Feature) pending human approval.
- The GitHub Issue is immediately labeled
ado-trackedand a comment is added with the ADO item URL. - The GitHub Issue is left open and visible to the contributor — GitHub remains the original record.
- Native fields carry over: GitHub
Priority→ ADO Priority 1–4; GitHubEffort→ ADO Story Points/Effort.
Flow 1: ADO → GitHub (status sync back to the same issue)
When the mirrored ADO item changes state:
| ADO State | GitHub Issue action |
|---|---|
| Active / In Progress | Add label in-progress |
| Resolved | Add label resolved, add comment with resolution summary |
| Closed | Close the GitHub Issue |
| Won't Fix | Close with label wont-fix and comment |
Flow 2: ADO → GitHub (roadmap projection — new, locked issues)
When an ADO Feature reaches Delivery Stage Approved, the sync bot creates (or updates) a new GitHub issue: type Feature, labels roadmap + ado-managed, Delivery Stage field set, locked. See github-issues.md for the full field/label reference.
Labels
All synced repos carry this standard label set (native issue types — Bug/Feature/Task — and native issue fields — Priority/Effort — replace the old type/priority label conventions; see github-issues.md):
| Label | Meaning |
|---|---|
ado-tracked | Issue has an ADO work item (Flow 1) |
in-progress | ADO item is actively being worked (Flow 1) |
resolved | ADO item resolved — pending close (Flow 1) |
wont-fix | ADO item closed as won't fix (Flow 1) |
needs-triage | Issue received, not yet triaged/mirrored |
roadmap | Approved Feature projected from ADO — on the public roadmap (Flow 2) |
ado-managed | This issue is a locked, bot-owned ADO projection — do not edit (Flow 2) |
cross-repo | Affects multiple repos |
Disabling existing GitHub issue automation
The AzureLocal org (and possibly others) has existing GitHub Actions workflows for issue management. These must be replaced or disabled before the ADO sync is active.
Audit step: For each repo, check .github/workflows/ for any workflows triggered by issues: events. Document what they do before disabling.
Cutover order:
- Identify all issue-related workflows across all org repos (AB#25 discovery task).
- Deploy ADO sync automation for the repo.
- Disable the existing GitHub workflow (comment out the
on: issues:trigger, commit, keep file for reference). - Validate that new Issues route to ADO correctly.
- Once validated for all repos in an org, delete the legacy workflow files.
Migration of existing GitHub Issues
Existing open GitHub Issues in all repos must be migrated to ADO before the sync is activated.
Migration steps (per repo):
- Export all open GitHub Issues via
gh issue list --state open --json number,title,body,labels,assignees,createdAt. - For each issue, create the matching ADO work item — a Bug for issue type
Bug, a Feature candidate for issue typeFeature(Flow 1) — with the title, body, and a link back to the original GitHub Issue. - Map native GitHub
Priority/Effortfields to ADO Priority/Story Points where possible. - After ADO items are confirmed, label the GitHub Issues
ado-trackedand add a comment with the ADO link. - Do not close GitHub Issues during migration — the sync will manage their state going forward.
Tooling: scripts/migration/Invoke-GitHubIssueMigration.ps1 (extended 2026-07-14 for the type-partitioned split — was originally a one-way GH→ADO migrator). See AB#25.
Automation implementation
Revised 2026-07-14 — no bespoke Azure Function/Logic App required. The prior draft of this section proposed a custom ADO service hook + Function App to push state changes back to GitHub. That's unnecessary: Microsoft ships two supported building blocks that cover it, documented at Azure Boards-GitHub integration:
- Flow 1, GitHub → ADO (new issue → new work item): GitHub Actions workflow (
templates/.github/workflows/ado-sync.yml), triggered onissues: [opened, edited, closed, reopened], filtered to native typeBug/Feature. Uses the Microsoft-documented community Actiondanhellem/github-actions-issue-to-work-item(pinned to a release commit SHA, never@master— it holdsADO_PAT), which creates/updates/closes the matching ADO work item. - Flow 1, ADO ↔ GitHub linking + PR-driven state transitions: the official Azure Boards app for GitHub (installed from the GitHub Marketplace) + the ADO project's GitHub connection (Project Settings → GitHub connections). This is what makes
AB#<id>mentions in commits/PRs link back to the work item and (viaFixes AB#<id>/Closes AB#<id>in a merged PR) transition its state automatically — no webhook or custom code, just the connection. - Flow 2, ADO → GitHub (roadmap projection):
scripts/Sync-RoadmapToGitHubProjects.ps1, run on a schedule — an ADO pipeline (.ado/roadmap-sync.yml, cron-triggered like the MCP server pipeline) or a scheduled GitHub Actions workflow, not a webhook-triggered Function. It queries ADO Features taggedroadmap-approved, creates/updates the locked GitHub issue, and upserts it into the org's GitHub Projects (v2) board viagh project item-add/item-edit.
The GitHub Actions workflow is deployed from the platform repo template (templates/.github/workflows/ado-sync.yml) and propagated to all repos via scripts/onboarding/Invoke-IssueSyncPropagation.ps1 (registry-driven, stamps templates + workflow + ADO_PAT/ADO_ORG/ADO_PROJECT secrets on every in-scope repo; dry-run by default).
Boards GitHub connection setup (per ADO project, executed 2026-07-14 across all 13 projects)
Field-tested procedure and gotchas:
- Creation is UI-only. The
githubconnectionsREST API cannot create a connection (POSTdemands aserviceEndpointIdbut then failsFailed to get authenticationfor any endpoint created outside the UI's own OAuth handshake). Create via Project Settings → GitHub connections → Connect your GitHub account (GitHub App flavor — authenticates as the Azure Boards app, not a personal identity). - Org access first. Orgs must grant the Azure Boards OAuth app: github.com → Settings → Applications → Authorized OAuth Apps → Azure Boards → Organization access → Grant per org (may require sudo-mode 2FA re-auth).
- Set the Azure Boards GitHub App to "All repositories" on
ado_model: solutionorgs (org Settings → GitHub Apps → Azure Boards → Repository access). "Only select repositories" blocks later API adds of private repos and silently excludes future repos — All-repositories matches the solution-org semantics (new repos join the project automatically). - The create dialog caps at 50 repos. For bigger orgs (CloudSmith = 57): create the connection with one repo in the UI, then batch-add the rest via the documented REST endpoint
POST {project}/_apis/githubconnections/{connectionId}/repos?api-version=7.1-preview.1with{ gitHubRepositoryUrls: [...], operationType: "add" }— this endpoint exists precisely for at-scale repo management on an existing connection. GitHubReauthorizationRequiredExceptionfrom that endpoint is self-service: the error body contains an OAuth URL — open it in a browser session logged into GitHub, then retry.- Beware GitHub repo renames: the API redirects old repo names, so a stale registry name "works" against the renamed repo and can double-count. Verify with
gh api repos/{org}/{old-name} --jq .full_namewhen a repo list doesn't match the registry.
Repos exempt from sync
The following repo types do not need two-way sync:
- Repos that have no GitHub Issues enabled (e.g., docs-only repos using GitHub Discussions)
- Archived repos
- Fork repos where the upstream maintains the issue tracker
These repos should have GitHub Issues disabled in their repository settings.
Implementation status
| Step | Status | AB# |
|---|---|---|
| Write this standard (original, ADO-master model) | done | AB#25 |
| Rewrite for split source of truth (Flow 1 + Flow 2) | done | AB#25 (pmo plan, 2026-07-14) |
master-registry.db ado_model + org/repo re-tag | done | AB#25 (2026-07-14) |
Define ADO project-to-org mapping (ado-project-strategy.md) | done | AB#25 |
Author github-issues.md native-taxonomy standard | done | AB#25 (2026-07-14) |
Update/extend Invoke-GitHubIssueMigration.ps1 for split model | done | AB#25 (2026-07-14) |
Author templates/.github/ISSUE_TEMPLATE/* + ado-sync.yml | done | AB#25 (2026-07-14) |
| Configure org issue types/fields/labels + Roadmap Projects v2 boards (Part E1) | done | AB#25 (2026-07-14 — 11 orgs) |
| Deploy ado-sync workflow + templates + secrets to all in-scope repos | done | AB#25 (2026-07-14 — 90 repos via Invoke-IssueSyncPropagation.ps1, 90/90 OK) |
| Boards GitHub connections (AB# linking / PR state transitions) | done | AB#25 (2026-07-14 — all 13 projects, 90 repos wired, REST-verified) |
| Roadmap projection pipeline (Flow 2) registered | done | AB#25 ("Roadmap Sync" pipeline ID 4, 6-hourly; first runs to be monitored) |
| Audit/disable pre-existing GitHub issue workflows across orgs | pending | AB#25 |
| Migrate pre-existing open GitHub Issues into ADO | pending | AB#25 |