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.
Recommended standard
| Concern | Standard | Rationale |
|---|---|---|
| Python version | 3.12, pinned in .python-version at repo root | Matches current prod containers; uv reads this file automatically |
| Dependency/venv manager | uv only — no bare pip, no python -m venv | Already in use on all Wasteology Python projects; single binary, fast, lockfile-native |
| Lockfile | uv.lock committed to every repo | Reproducible installs across dev/CI/prod without version drift |
| Linter + formatter | ruff (lint + format in one tool) | Already configured in wg-orchestration; zero configuration needed for new repos |
| Test runner | pytest | Already in use; pairs naturally with uv run pytest |
| Platform | WSL2 on Windows, macOS natively | All Azure CLI + Docker ACR builds tested on WSL2 — do not use Git Bash or PowerShell |
| Editor | VS Code with the Python + Ruff extensions recommended | Enables 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.
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.
Recommended standard
Lockfile discipline
uv.lockis always committed. It is not in.gitignore.- CI runs
uv sync --frozen— the--frozenflag 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.
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 (
asyncpgwrappers, retry logic) that are duplicated acrosswg-orchestration,wdp-import-pipeline, andwdp-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
| Command | Description | Backed by |
|---|---|---|
wg status | System health: all projects, pipeline pass/fail, any alerts | ops status |
wg standup | Generate 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 commit | New — calls ADO API + cookiecutter template |
wg open-ticket <title> | Create an ADO work item (Issue or Task) in the active sprint and print the URL | ops route + ADO API |
wg check | Run lint + format-check + tests in the current repo | uv 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 $EDITOR | New — thin wrapper around spec template |
wg docs | Open the internal Docusaurus docs site in the default browser | Static URL lookup |
wg pipelines [--failed] | List recent Prefect flow runs; --failed filters to failures only | ops prefect runs |
wg upgrade | Self-update: uv tool install --upgrade wg-cli from the ADO Artifacts feed | uv tool |
wg version | Print CLI version and check for updates | Package 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
| Concern | Recommendation |
|---|---|
| Owner | The incoming product owner is the business owner; the senior developer is the technical maintainer |
| Contribution | Any developer can open a PR to add or change a wg command; the maintainer approves |
| Graduation path | Commands that grow complex flags or become power-user tools move to ops; the wg wrapper stays thin |
| Deprecation | If a wg command is superseded by a better ops equivalent, wg prints a deprecation notice and delegates for one release cycle before removal |
| Versioning | Semantic 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.
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. Thewg new-repocommand 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 buildingwg new-repoonly 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-clitemplate repo. A reasonable sequence: (1) agree on the command surface here, (2) create the ADO repo and Artifacts feed, (3) build and shipwg status,wg standup,wg check, andwg upgradefirst (these are pure delegation — lowest effort, highest daily value), (4) addwg new-repoandwg scaffold agentin a second sprint once the template repos are agreed upon.
Summary
| Decision | Recommendation | Status |
|---|---|---|
| Python version | 3.12, pinned via .python-version | Adopt immediately |
| Dependency manager | uv only, uv.lock committed | Adopt immediately |
| CI install | uv sync --frozen | Adopt immediately |
| Linter/formatter | ruff (lint + format) | Adopt immediately |
| Test runner | pytest | Adopt immediately |
| Internal package distribution | ADO Artifacts feed (wasteology-feed) | Create feed now; publish when first package is ready |
| Thin CLI name | wg (pending collision check) | PO decision |
| Thin CLI Phase 1 commands | status, standup, check, upgrade | Build in Sprint 1 of CLI project |
| Thin CLI Phase 2 commands | new-repo, scaffold agent, open-ticket | Build after template repo is stable |