Skip to content

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.

FlowItem typesMasterDirection
Flow 1 — IntakeIssue / Bug / Feature-RequestGitHubfiled in GitHub → mirrors up into ADO for rollup/reporting; ADO status changes flow back down to the same GitHub issue
Flow 2 — Roadmap projectionEpic / Feature / User Story / TaskADOauthored 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-prodtech overlay in work-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 Bug or Feature issue type (see github-issues.md — do not use custom types or priority labels).
  • Triage: needs-triage label until reviewed. A Bug issue mirrors up as an ADO Bug work item 1:1. A Feature issue 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.md work-item-types table and github-issues.md Delivery 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 Stage field — this is the roadmap surface (see github-issues.md). Any web page (docs site, product sites) can read the live roadmap via the Projects v2 GraphQL API — no separate ROADMAP.md file 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:

TypeWhen to useMasterSync
EpicLarge initiative spanning multiple repos or a full quarter. Cross-project by definition.ADONot projected to GitHub
FeatureDeliverable within one sprint or sprint cycle. May span repos but has a single owner.ADOApproved Features project down (Flow 2)
User StorySingle unit of work. Tied to one repo. Has acceptance criteria.ADONot projected to GitHub
BugDefect with repro steps, expected vs. actual behavior.GitHubMirrors up (Flow 1)
Feature-RequestA 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
TaskSub-work inside a Story.ADONot 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 PATs

ADO 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:

  1. 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.
  2. Feature: a Feature candidate is recorded (linked, not yet a tracked ADO Feature) pending human approval.
  3. The GitHub Issue is immediately labeled ado-tracked and a comment is added with the ADO item URL.
  4. The GitHub Issue is left open and visible to the contributor — GitHub remains the original record.
  5. Native fields carry over: GitHub Priority → ADO Priority 1–4; GitHub Effort → ADO Story Points/Effort.

Flow 1: ADO → GitHub (status sync back to the same issue)

When the mirrored ADO item changes state:

ADO StateGitHub Issue action
Active / In ProgressAdd label in-progress
ResolvedAdd label resolved, add comment with resolution summary
ClosedClose the GitHub Issue
Won't FixClose 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):

LabelMeaning
ado-trackedIssue has an ADO work item (Flow 1)
in-progressADO item is actively being worked (Flow 1)
resolvedADO item resolved — pending close (Flow 1)
wont-fixADO item closed as won't fix (Flow 1)
needs-triageIssue received, not yet triaged/mirrored
roadmapApproved Feature projected from ADO — on the public roadmap (Flow 2)
ado-managedThis issue is a locked, bot-owned ADO projection — do not edit (Flow 2)
cross-repoAffects 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:

  1. Identify all issue-related workflows across all org repos (AB#25 discovery task).
  2. Deploy ADO sync automation for the repo.
  3. Disable the existing GitHub workflow (comment out the on: issues: trigger, commit, keep file for reference).
  4. Validate that new Issues route to ADO correctly.
  5. 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):

  1. Export all open GitHub Issues via gh issue list --state open --json number,title,body,labels,assignees,createdAt.
  2. For each issue, create the matching ADO work item — a Bug for issue type Bug, a Feature candidate for issue type Feature (Flow 1) — with the title, body, and a link back to the original GitHub Issue.
  3. Map native GitHub Priority/Effort fields to ADO Priority/Story Points where possible.
  4. After ADO items are confirmed, label the GitHub Issues ado-tracked and add a comment with the ADO link.
  5. 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 on issues: [opened, edited, closed, reopened], filtered to native type Bug/Feature. Uses the Microsoft-documented community Action danhellem/github-actions-issue-to-work-item (pinned to a release commit SHA, never @master — it holds ADO_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 (via Fixes 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 tagged roadmap-approved, creates/updates the locked GitHub issue, and upserts it into the org's GitHub Projects (v2) board via gh 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:

  1. Creation is UI-only. The githubconnections REST API cannot create a connection (POST demands a serviceEndpointId but then fails Failed to get authentication for 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).
  2. 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).
  3. Set the Azure Boards GitHub App to "All repositories" on ado_model: solution orgs (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).
  4. 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.1 with { gitHubRepositoryUrls: [...], operationType: "add" } — this endpoint exists precisely for at-scale repo management on an existing connection.
  5. GitHubReauthorizationRequiredException from that endpoint is self-service: the error body contains an OAuth URL — open it in a browser session logged into GitHub, then retry.
  6. 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_name when 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

StepStatusAB#
Write this standard (original, ADO-master model)doneAB#25
Rewrite for split source of truth (Flow 1 + Flow 2)doneAB#25 (pmo plan, 2026-07-14)
master-registry.db ado_model + org/repo re-tagdoneAB#25 (2026-07-14)
Define ADO project-to-org mapping (ado-project-strategy.md)doneAB#25
Author github-issues.md native-taxonomy standarddoneAB#25 (2026-07-14)
Update/extend Invoke-GitHubIssueMigration.ps1 for split modeldoneAB#25 (2026-07-14)
Author templates/.github/ISSUE_TEMPLATE/* + ado-sync.ymldoneAB#25 (2026-07-14)
Configure org issue types/fields/labels + Roadmap Projects v2 boards (Part E1)doneAB#25 (2026-07-14 — 11 orgs)
Deploy ado-sync workflow + templates + secrets to all in-scope reposdoneAB#25 (2026-07-14 — 90 repos via Invoke-IssueSyncPropagation.ps1, 90/90 OK)
Boards GitHub connections (AB# linking / PR state transitions)doneAB#25 (2026-07-14 — all 13 projects, 90 repos wired, REST-verified)
Roadmap projection pipeline (Flow 2) registereddoneAB#25 ("Roadmap Sync" pipeline ID 4, 6-hourly; first runs to be monitored)
Audit/disable pre-existing GitHub issue workflows across orgspendingAB#25
Migrate pre-existing open GitHub Issues into ADOpendingAB#25

Copyright © Hybrid Cloud Solutions LLC — Kristopher Turner