Skip to main content

B. Developer Environment & CLI

Wasteology already has a working Python toolchain: uv for dependency management, ruff for linting and formatting, pytest for tests, and a 20-command-group ops CLI built with Typer. The problem is not a missing stack — it is that none of this is required, so new developers improvise, and onboarding takes days instead of hours. This section codifies the existing practice as the standard, proposes where to draw the line on tooling, and specifies a thinner CLI that non-power-users can reach for every day without wading through ops.


B1. Standard Developer Environment

Current state

There is no enforced Python version across projects. Some developers run system Python 3.10; others have 3.12 via pyenv; WSL setups vary. Dependency installation ranges from bare pip install -r requirements.txt (no lockfile) to uv sync depending on which project a developer picked up first. Editor configuration (formatting on save, linting) is entirely personal.

ConcernStandardRationale
Python version3.12, pinned in .python-version at repo rootMatches current prod containers; uv reads this file automatically
Dependency/venv manageruv only — no bare pip, no python -m venvAlready in use on all Wasteology Python projects; single binary, fast, lockfile-native
Lockfileuv.lock committed to every repoReproducible installs across dev/CI/prod without version drift
Linter + formatterruff (lint + format in one tool)Already configured in wg-orchestration; zero configuration needed for new repos
Test runnerpytestAlready in use; pairs naturally with uv run pytest
PlatformWSL2 on Windows, macOS nativelyAll Azure CLI + Docker ACR builds tested on WSL2 — do not use Git Bash or PowerShell
EditorVS Code with the Python + Ruff extensions recommendedEnables format-on-save and inline diagnostics aligned with CI checks

How it works

Bootstrapping a new developer:

# 1. Install uv (one-time, global)
curl -LsSf https://astral.sh/uv/install.sh | sh

# 2. Clone a repo and install dependencies (reads .python-version + uv.lock)
git clone https://dev.azure.com/wasteology/Wasteology/_git/<repo>
cd <repo>
uv sync --frozen # exact lockfile install — no surprises

# 3. Verify toolchain
uv run pytest # run tests
uv run ruff check . # lint
uv run ruff format --check . # format check

Adding a new dependency:

uv add <package>          # updates pyproject.toml + uv.lock atomically
git add uv.lock pyproject.toml
git commit -m "chore: add <package>"

Never edit pyproject.toml by hand for dependencies — uv add/remove keeps the lockfile consistent. Never use pip install directly inside a project virtualenv; the lockfile will not capture it and CI will diverge.

Installing CLI tools globally (not project-scoped):

uv tool install ruff      # system-wide ruff, not project-scoped
uv tool install <wg-cli> # the thin company CLI (see Section B3)

uv tool installs into an isolated environment on PATH — no virtualenv activation needed. This is the distribution mechanism for both ruff and the proposed thin company CLI.

PO decision point

Python 3.12 vs 3.11. Most Wasteology containers already run 3.12. If any project has a hard dependency pinned to 3.11 (check python_requires in pyproject.toml), that project gets a documented exception until it migrates. Recommend setting a 90-day migration window for any exceptions found during the onboarding audit.


B2. Dependency Management Standard with uv

Current state

wg-orchestration uses uv properly: pyproject.toml, uv.lock committed, and uv sync --frozen in CI. Other projects vary — some have requirements.txt with unpinned versions, some have no lockfile at all. Internal shared code (e.g., digest utilities) is copy-pasted rather than published as a package.

Lockfile discipline

  • uv.lock is always committed. It is not in .gitignore.
  • CI runs uv sync --frozen — the --frozen flag fails if the lockfile is out of date, preventing "works on my machine" failures.
  • Developers run uv sync (without --frozen) locally after pulling to update their venv.

CI install pattern (Azure Pipelines YAML snippet)

- script: |
curl -LsSf https://astral.sh/uv/install.sh | sh
uv sync --frozen
displayName: Install dependencies (frozen)

- script: |
uv run ruff check .
uv run ruff format --check .
uv run pytest --tb=short
displayName: Lint and test

uv is fast enough (< 5 s for a warm cache) that caching the venv in CI is optional, not required.

Internal package distribution via ADO Artifacts

When a library needs to be shared across repos (e.g., a shared Wasteology data-model package, or the thin CLI itself), publish it to an ADO Artifacts feed rather than copy-pasting code.

# Configure uv to pull from the private feed
# Add to pyproject.toml:
[[tool.uv.index]]
name = "wasteology-feed"
url = "https://pkgs.dev.azure.com/wasteology/Wasteology/_packaging/wasteology-feed/pypi/simple/"

PAT-based auth is handled via uv's keyring integration or a .netrc entry — the same PAT already used for ADO git access works. CI uses the $(System.AccessToken) pipeline token, which has feed read access by default.

When to create an internal package (decision heuristic):

  • Code is copied into 2+ repos → package it.
  • Code has its own tests and version history → package it.
  • Code is tightly coupled to one project's internals → keep it local.
PO decision point

ADO Artifacts feed creation. Creating a feed requires a one-time ADO project admin action. Recommend creating a wasteology-feed feed now, even before publishing anything, so the URL is stable and can be embedded in new repo templates. Alternatively, defer until the thin CLI is ready to ship (Section B3) — that will be the first real package to distribute.

Shared library scope. The immediate candidates for a shared internal package are: (1) the Wasteology database connection helpers (asyncpg wrappers, retry logic) that are duplicated across wg-orchestration, wdp-import-pipeline, and wdp-palantiri; and (2) the thin CLI itself. Everything else can stay local until the 2-repo duplication threshold is crossed.


B3. The Thin Company CLI — Proposal

Current state

The ops CLI (wg-orchestration/cli/main.py) has 20+ command groups covering Prefect, Sling, dbt, n8n, Azure, digest generation, portal reconciliation, HubSpot, AI agents, tech-stack visualization, and an Oregon Trail game. It is the right tool for a senior developer or the AI agent pipeline. It is the wrong default for a new developer, a project manager, or anyone who just wants to open a ticket or check system health.

New developers currently have no obvious entry point. They either get overwhelmed by ops --help or never discover the CLI exists.

What the thin CLI is

A curated, opinionated subset — not a rewrite. The thin CLI (wg or another short alias — see PO decision point) wraps or re-exposes the 20% of ops that 80% of the team needs daily, plus a few operations that are currently manual (repo bootstrapping, access requests). It has no domain-specific flags, no advanced options, and a --help that fits on one screen.

The thin CLI does not replace ops. Power users keep ops. The thin CLI reduces the on-ramp for everyone else and gives the team a single place to point new hires.

Proposed command surface

CommandDescriptionBacked by
wg statusSystem health: all projects, pipeline pass/fail, any alertsops status
wg standupGenerate today's standup digest (prints to terminal)ops digest standup
wg new-repo <name> [--tier 2a|3a|4a]Scaffold a new repo from the Wasteology template, open ADO repo, push initial commitNew — calls ADO API + cookiecutter template
wg open-ticket <title>Create an ADO work item (Issue or Task) in the active sprint and print the URLops route + ADO API
wg checkRun lint + format-check + tests in the current repouv run ruff check . && uv run ruff format --check . && uv run pytest --tb=short
wg access <resource>Print the access-request runbook for a resource (Prefect, Azure sub, ADO, dbt Cloud, etc.)Static lookup table + runbook links
wg scaffold agent <name>Stub an ADW agentic task spec under specs/ and open it in $EDITORNew — thin wrapper around spec template
wg docsOpen the internal Docusaurus docs site in the default browserStatic URL lookup
wg pipelines [--failed]List recent Prefect flow runs; --failed filters to failures onlyops prefect runs
wg upgradeSelf-update: uv tool install --upgrade wg-cli from the ADO Artifacts feeduv tool
wg versionPrint CLI version and check for updatesPackage metadata

Total surface: 11 commands, no sub-subcommands. Every command has a one-line description. wg --help fits in 30 lines.

How it works

Repository: A new, thin repo — wg-cli — under dev.azure.com/wasteology/Wasteology. It has its own pyproject.toml, uv.lock, and a minimal Typer app. It does not import from wg-orchestration — it calls ops as a subprocess for commands that delegate to it, or it calls the same underlying APIs directly where the ops dependency chain would pull in too much.

Distribution:

# Developer install (one-time)
uv tool install wg-cli \
--index https://pkgs.dev.azure.com/wasteology/Wasteology/_packaging/wasteology-feed/pypi/simple/

# After install, available globally
wg status
wg standup

CI/CD: The wg-cli ADO pipeline publishes a new version to the wasteology-feed on every merge to main. Version is set by a pyproject.toml bump (semantic versioning, automated by the pipeline). Developers run wg upgrade to pull the latest version.

Shell completion:

wg --install-completion bash   # or zsh

Typer's native completion works for the thin CLI the same way it does for ops.

Ownership and governance

ConcernRecommendation
OwnerThe incoming product owner is the business owner; the senior developer is the technical maintainer
ContributionAny developer can open a PR to add or change a wg command; the maintainer approves
Graduation pathCommands that grow complex flags or become power-user tools move to ops; the wg wrapper stays thin
DeprecationIf a wg command is superseded by a better ops equivalent, wg prints a deprecation notice and delegates for one release cycle before removal
VersioningSemantic versioning (MAJOR.MINOR.PATCH). Minor bumps for new commands, patch for bug fixes, major for breaking changes to existing command signatures

Relationship between wg and ops

wg (thin CLI — daily driver)
└── delegates to ops where ops already does it well
└── new code only for onboarding-specific operations (new-repo, access, scaffold)
└── opinionated defaults, no advanced flags

ops (power-user CLI — wg-orchestration)
└── full command surface (20+ groups)
└── all integration-specific commands (sling, n8n, dbt, hubspot, recon, ...)
└── invoked as: ops <command> OR uv run ops <command> (from wg-orchestration venv)

Neither tool replaces the other. New developers start with wg. Power users and the ADW pipeline use ops. Senior developers use both.

PO decision point

CLI name (wg vs waste vs wg vs something else). wg is short and matches the company namespace convention (wg-orchestration, wg-cli, etc.). However, wg is already the Linux WireGuard command on some systems — check whether any developer machines have wg on PATH from WireGuard. If collision is a concern, wst or wasteology are safe alternatives. Recommend deciding on the name before the ADO Artifacts feed URL is published, since the package name appears in the install command.

Build vs buy for new-repo. The wg new-repo command requires a cookiecutter (or similar) template repo and ADO API calls to create the repository. This is the most complex command in the thin CLI. An alternative is a static Confluence/Docusaurus page with a manual checklist. Recommend building wg new-repo only after the template repo is agreed upon and tested — ship the rest of the thin CLI first.

When to build this. The thin CLI is a proposal, not a committed deliverable. The prerequisite is a stable wg-cli template repo. A reasonable sequence: (1) agree on the command surface here, (2) create the ADO repo and Artifacts feed, (3) build and ship wg status, wg standup, wg check, and wg upgrade first (these are pure delegation — lowest effort, highest daily value), (4) add wg new-repo and wg scaffold agent in a second sprint once the template repos are agreed upon.


Summary

DecisionRecommendationStatus
Python version3.12, pinned via .python-versionAdopt immediately
Dependency manageruv only, uv.lock committedAdopt immediately
CI installuv sync --frozenAdopt immediately
Linter/formatterruff (lint + format)Adopt immediately
Test runnerpytestAdopt immediately
Internal package distributionADO Artifacts feed (wasteology-feed)Create feed now; publish when first package is ready
Thin CLI namewg (pending collision check)PO decision
Thin CLI Phase 1 commandsstatus, standup, check, upgradeBuild in Sprint 1 of CLI project
Thin CLI Phase 2 commandsnew-repo, scaffold agent, open-ticketBuild after template repo is stable