Skip to main content

A. ADO Foundation & Repo Standards

Wasteology already runs every active repo inside a single Azure DevOps organization (wasteology, project Wasteology). This section codifies that structure into a company standard, extends it with templating and branching rules so every new repo starts consistent, and documents how Boards, commits, and PRs connect so the development work is visible to the whole organization — not just the engineer who wrote the code.


A.1 — ADO Org / Project Structure

Current state

One ADO organization (wasteology), one ADO project (Wasteology), all repos inside it. This is the live configuration; it has not been formally declared a standard or documented anywhere a new hire would find it.

Naming gotcha for new developers: The az CLI was originally configured against a stale org named kgwasteology. Git remotes correctly use wasteology. When running az commands always pass --org https://dev.azure.com/wasteology explicitly, or set AZURE_DEVOPS_EXT_PAT and the org via env var. Never rely on the az devops configure default — it points at the wrong org.

Single ADO project, single org. Do not create additional ADO projects.

Rationale: All repos share the same ACR registry, the same Prefect Cloud workspace, the same n8n instance, and the same ADO PAT. Splitting into multiple ADO projects would require per-project service connections, separate PAT scopes, and separate pipeline agent pools — overhead that delivers no benefit at Wasteology's current scale.

Use Area Paths within the single Wasteology project to group work by team or domain instead of splitting into separate projects.

StructureRecommendationWhy
ADO organizations1 (wasteology)Shared billing, shared service connections
ADO projects1 (Wasteology)Shared Boards, shared pipelines, shared PAT
Work item groupingArea Paths per team/domainLightweight — no separate auth, no separate settings

Suggested area path hierarchy (starting point):

Wasteology
├── Data Platform ← wg-orchestration, wdp-palantiri, tirion
├── ETL & Import ← wdp-import-pipeline, prefect-azure-infra
├── CRM & Portals ← portal-reconciliation, cietrade flows
├── Data Warehouse ← dbt_wasteology_warehouse
├── Infrastructure ← Bicep IaC, wg-agent-infra
└── Docs & Tooling ← wasteology-docs, support projects

Area paths can be set on work items individually or inherited from a team's default.

PO decision point

Accept the single-project structure (recommended) or request one additional ADO project for a team/partner that needs strict permission isolation (e.g., a vendor who should not see internal repos). Creating a second project is a one-way door — work item IDs, pipelines, and service connections do not move between projects.


A.2 — Repo Creation & Template Strategy

Current state

Repos are created manually via the ADO UI or the curl REST recipe in .claude/skills/ado/SKILL.md. Each project bootstraps its Claude infrastructure (CLAUDE.md, .claude/, lat.md/) by hand, copying files from wg-orchestration or another existing repo. There is no canonical starting point, so new repos inevitably miss steps.

Every new repo is seeded from a template repo named wg-template. The template is maintained in ADO, lives at dev.azure.com/wasteology/Wasteology/_git/wg-template, and contains all Tier 0 + Tier 1 scaffolding pre-filled. Creating a repo from the template takes one curl call; bootstrapping from scratch takes 14 manual steps and is error-prone.

What wg-template contains

wg-template/
├── .claude/
│ ├── settings.json # Tier 0 permissions + hooks (edit for tier)
│ ├── commands/
│ │ ├── prime.md # Load context at session start
│ │ ├── doctor.md # Health check: CLIs, .env keys, lat check
│ │ ├── session-wrap.md # End-of-session audit + lat gap detection
│ │ ├── git-update.md # Stage → commit → PAT push → PR
│ │ └── pull_request.md # Push branch + create PR with auto-complete
│ ├── hooks/
│ │ └── session_start.py # Fast env check (CLIs, .env, lat availability)
│ └── skills/
│ ├── lat-md/SKILL.md # lat.md authoring rules (copy verbatim)
│ └── ado/SKILL.md # ADO REST recipes (UPDATE ADO_REPO variable)
├── lat.md/
│ ├── lat.md # Index file (must list all child files)
│ └── architecture.md # Minimal architecture section (fill in)
├── .env.example # Tier 0 + Tier 1 keys; add tier-specific below
├── .gitignore # Excludes .env, __pycache__, .venv, dist/
├── pyproject.toml # uv + ruff defaults (Python projects only)
└── CLAUDE.md # All 6 required sections, placeholders filled

.env.example required keys (Tier 0 + 1)

# ADO — same PAT works across all wasteology repos
ADO_PAT="<personal access token — Code R/W + Work Items R/W scopes>"

# Add tier-specific keys below:
# Tier 2a (Python Service): POSTGRES_DEV_HOST, POSTGRES_DEV_PORT, etc.
# Tier 3a (Docker/ACI): ACR_NAME, ACR_PASSWORD, AZURE_CONTAINER_APP_NAME
# Tier 4a (Prefect ETL): PREFECT_API_KEY, PREFECT_API_URL

PAT scope requirement: The PAT must have Code (Read & Write) + Work Items (Read & Write). A read-only PAT makes PR creation and ADW dispatch fail with an empty-body 401 — a silent, misleading error.

Creating a repo from the template

# 1. Load PAT
export ADO_PAT=$(grep ADO_PAT .env | cut -d= -f2 | tr -d '"')

# 2. Create the repo in ADO
curl -s -u ":${ADO_PAT}" -X POST \
-H "Content-Type: application/json" \
-d "{\"name\": \"REPO_NAME\", \"project\": {\"id\": \"f598871b-da06-4acc-8748-6e70b21ff8fb\"}}" \
"https://dev.azure.com/wasteology/Wasteology/_apis/git/repositories?api-version=7.1"

# 3. Clone the template locally, rename remote, push
git clone https://dev.azure.com/wasteology/Wasteology/_git/wg-template REPO_NAME
cd REPO_NAME
git -c credential.helper= \
-c "credential.helper=!f() { echo username=x; echo password=${ADO_PAT}; }; f" \
remote set-url origin https://dev.azure.com/wasteology/Wasteology/_git/REPO_NAME
git -c credential.helper= \
-c "credential.helper=!f() { echo username=x; echo password=${ADO_PAT}; }; f" \
push -u origin main

Adding a New Project — Complete Checklist

This extends the existing checklist in lat.md/claude-standard#Adding a New Project — Checklist with the template-first step:

[ ] 0. Create repo from wg-template (see curl recipe above) — replaces steps 4–9 below
[ ] 1. Set ADO_REPO in .claude/skills/ado/SKILL.md to this repo's name
[ ] 2. Fill in CLAUDE.md placeholders (project name, stack section, key concepts)
[ ] 3. Copy tier-appropriate .env keys from 1Password / teammate into .env
[ ] 4. Add tier-specific .claude/settings.json permissions (Tier 2a: ruff/uv; Tier 3a: az/acr)
[ ] 5. Add tier-specific skills (deploy, prefect-flow-ops, validate, infra — per tier)
[ ] 6. Fill in lat.md/architecture.md with the project's real architecture
[ ] 7. Run `lat check` — must pass with zero errors before first commit
[ ] 8. Register in wg-orchestration: create manifests/builtin/<repo>.toml
[ ] 9. Verify ADO_PAT push works: run git-update from a feature branch
PO decision point

Should wg-template be an ADO "repository template" (enforced via UI, prevents creating non-templated repos) or simply a repo developers clone manually? ADO supports marking a repo as a template, which surfaces it in the "Create repository" dialog. Recommended: mark it as a template AND document the curl path for automation.


A.3 — Branching Strategy

Current state

wg-orchestration and wdp-palantiri use main as the default branch with feature branches merged via PRs. mars/Orbit auto-deploys prod on merge to main with one-reviewer protection. wasteology-docs uses an ADO pipeline triggered on push to main. No written standard exists; conventions vary by repo.

Trunk-based development with short-lived feature branches. main is always deployable. Feature branches live no longer than one working day for small changes, three days for larger ones. No long-lived develop, staging, or release branches.

Branch naming convention

<type>/<short-description>

feat/add-cietrade-expense-sync
fix/cietrade-duplicate-upsert
chore/update-lat-md-schema
docs/docusaurus-cli-commands

Types map directly to ADO work item dispatch types:

Branch prefixADO typeDispatch mapping
feat/Issuefeature → Issue
fix/Issuebug → Issue
chore/Taskchore → Task
docs/Taskdocs → Task

The ADW pipeline (adws/adw_plan_build.py) generates branch names from work item titles using the same type mapping — branches it creates will naturally follow this convention.

PR policies on main (required for every repo)

Set these via ADO Branch Policies (Repos → Branches → main → Branch policies):

PolicySettingRationale
Require a minimum number of reviewers1 reviewerCatches bugs, spreads knowledge
Check for linked work itemsRequiredEnables traceability (see A.5)
Build validation1 required pipelinePrevents broken main
Require comment resolutionOnPRs are not merged with open questions
Limit merge strategiesSquash onlyClean linear history on main
Automatically include reviewersPer-repo code ownersRoute PRs to the right person

Note on squash merges: Squash merge keeps main history linear and makes git bisect effective. The ADW pipeline's set_pr_auto_complete() already uses mergeStrategy: "squash" by default. Individual commit history is preserved on the feature branch until it is deleted post-merge.

The canonical ADO push pattern

Never use git push origin bare — the global git store credential helper is interactive and will hang in WSL. Always use the inline PAT helper:

ADO_PAT=$(grep ADO_PAT .env | cut -d= -f2 | tr -d '"')
BRANCH=$(git rev-parse --abbrev-ref HEAD)
git -c credential.helper= \
-c "credential.helper=!f() { echo username=x; echo password=${ADO_PAT}; }; f" \
push -u origin "${BRANCH}"

This pattern is encoded in every repo's .claude/commands/git-update.md and .claude/commands/pull_request.md. The -c credential.helper= flag (empty value) clears the inherited global helper so git never falls back to an interactive prompt.

PO decision point

How many required reviewers on main? One is the minimum; for repos that go directly to production (e.g., wdp-palantiri, mars/Orbit), two reviewers is safer. Admin override (bypass) should be reserved for emergency hotfixes only, and any bypass should require a follow-up review within 24 hours.


A.4 — GitHub → ADO Repo Migration Mechanics

Current state

All core Wasteology repos have already migrated to ADO as single origin. GitHub repos are treated as archival. The migration decision (whether to migrate remaining repos, including GCP-hosted tooling) lives in another section of this document — this section covers only the repo-level mechanics.

How it works (for each remaining GitHub repo)

Step 1 — Import history into ADO

ADO's built-in import preserves full git history including all branches and tags:

ADO UI: Repos → Import repository
→ Source type: Git
→ Clone URL: https://github.com/wasteology/<repo>.git
→ (check "Requires Authentication" if private)

Or via REST (automatable for bulk migration):

curl -s -u ":${ADO_PAT}" -X POST \
-H "Content-Type: application/json" \
-d '{
"parameters": {
"gitSource": {"url": "https://github.com/wasteology/REPO_NAME.git"},
"tfvcSource": null,
"deleteServiceEndpointAfterImportIsDone": true
}
}' \
"https://dev.azure.com/wasteology/Wasteology/_apis/git/repositories/REPO_ID/importRequests?api-version=7.1"

Step 2 — Re-point local remotes

git remote set-url origin https://dev.azure.com/wasteology/Wasteology/_git/REPO_NAME

Verify with:

git -c credential.helper= \
-c "credential.helper=!f() { echo username=x; echo password=${ADO_PAT}; }; f" \
fetch --dry-run

Step 3 — Port CI/CD pipelines

SourceADO equivalent
GitHub Actions .github/workflows/*.ymlazure-pipelines.yml at repo root
GitHub Actions on: push triggerADO: trigger: branches: include: [main]
GitHub Actions secretsADO: pipeline variables or variable groups (Library)
GitHub Actions environmentsADO: Deployment environments with approval gates

Step 4 — Archive the GitHub repo

Mark the GitHub repo as archived (read-only) rather than deleting it. Preserves any external links, issues, and the public record of the project's history.

Step 5 — Update project manifest

In wg-orchestration/manifests/builtin/<project>.toml, update ado_repo and clear github_repo:

[project]
ado_repo = "dev.azure.com/wasteology/Wasteology/_git/REPO_NAME"
github_repo = "" # archived, not canonical

A.5 — ADO Boards + Work Item / Commit / PR Linking

Current state

The ADW pipeline creates ADO work items automatically (tagged adw;dispatch;{project}) and sets PR auto-complete with transitionWorkItems: true. Manual work items are created via ops digest runner or the ADO UI. Work item↔commit linking via AB#<id> syntax is used by the ADW pipeline but not consistently by humans.

Work item types — Basic process

Wasteology's ADO project uses the Basic process template. This is a hard constraint — the process template cannot be changed after project creation without recreating the project.

Basic typeMaps toWhen to use
EpicInitiative / OKRMulti-week effort spanning multiple work items
IssueFeature or bugAny user-visible change (new capability or defect fix)
TaskChore, docs, infraInternal work with no direct user impact

There is no Bug or User Story type. ADO will return HTTP 400 if you attempt to create one. adws/dispatch.py maps bug → Issue, feature → Issue, chore → Task, docs → Task. Follow this mapping in the UI too.

AB#<id> linking — commit and PR syntax

ADO automatically creates a link between a work item and a commit or PR when the string AB#<id> appears in the commit message or PR title/description.

# Commit message — links to work item 247
git commit -m "feat: add portal sweep auto-clear AB#247"

# PR title — links and can auto-transition the work item
# "Closes AB#247" in PR description moves work item to Done on merge
SyntaxWhere to put itEffect
AB#247Commit messageCreates a link (no state change)
AB#247PR title or descriptionCreates a link
Fixes AB#247PR descriptionCloses work item on PR completion
Closes AB#247PR descriptionCloses work item on PR completion

The ADW pipeline uses workItemRefs: [{"id": "<id>"}] in the PR creation payload instead of inline syntax — both methods produce the same link.

PR auto-complete + work item transition

The canonical pattern (already implemented in adws/ado.py):

# Get the authenticated user's real UUID (zero UUIDs are silently rejected)
USER_ID=$(curl -s -u ":${ADO_PAT}" \
"https://dev.azure.com/wasteology/_apis/connectionData" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['authenticatedUser']['id'])")

# Set auto-complete with work item transition
curl -s -u ":${ADO_PAT}" -X PATCH \
-H "Content-Type: application/json" \
-d "{
\"autoCompleteSetBy\": {\"id\": \"${USER_ID}\"},
\"completionOptions\": {
\"mergeStrategy\": \"squash\",
\"deleteSourceBranch\": true,
\"transitionWorkItems\": true
}
}" \
"https://dev.azure.com/wasteology/Wasteology/_apis/git/repositories/REPO_NAME/pullrequests/${PR_ID}?api-version=7.1"

transitionWorkItems: true moves linked Issues and Tasks to Done automatically when the PR is squash-merged. This is the primary mechanism that keeps Boards current without manual state management.

Iterations / sprints (basics)

ADO iterations map to sprint timelines. For teams not yet running formal sprints, a minimal setup is:

  1. Create a root iteration named Wasteology (the project name).
  2. Create child iterations named by date: 2026-Q3-Sprint-1, 2026-Q3-Sprint-2, etc.
  3. Assign work items to iterations in the Boards backlog view or via the work item System.IterationPath field.

The ADW pipeline does not set IterationPath on created work items — they land in the backlog root by default. A sprint-aware team would triage the backlog at the start of each sprint and assign items to the current iteration.

PO decision point

Does the product owner want formal sprint cadence with velocity tracking, or a Kanban-style flow where work items move across columns (To Do → Doing → Done) without time-boxing? Kanban is lower overhead and suits the current team size and async ADW workflow better. Either can be configured within the Basic process template without any ADO project changes.

ColumnWork item stateWhat it means
To DoActiveCreated, not yet started
DoingActive (in-progress)Branch exists, work underway
DoneClosedPR merged, transitionWorkItems closed the item

ADO Basic process has only Active and Closed states. The Boards "Doing" column is a visual lane, not a separate state — items move there when you drag them on the board, but the underlying state stays Active until the PR merges.


Summary Reference

TopicKey decisionWhere it lives
ADO orgwasteology (not kgwasteology)All git remotes, all API calls
ADO projectSingle project WasteologyADO UI, project GUID f598871b-...
Work item groupingArea Paths (not separate ADO projects)ADO UI: Project Settings → Team Configuration
Template repowg-template (seed every new repo from this)dev.azure.com/wasteology/Wasteology/_git/wg-template
PAT required scopesCode R/W + Work Items R/W1Password, shared across all repos
Default branchmain (always deployable)Enforced via branch policy
Merge strategySquash onlyBranch policy + ADW completionOptions
Work item typesIssue, Epic, Task (Basic process — no Bug/User Story)adws/dispatch.py type mapping
Commit linkingAB#<id> in commit message or PR descriptionAutomatic ADO recognition
PR completionAuto-complete + transitionWorkItems: trueadws/ado.py:set_pr_auto_complete()
Push authInline PAT credential helper (never bare git push).claude/commands/git-update.md in every repo