Skip to content

Maturity-gated release tooling (./uw dev)#233

Merged
lmoresi merged 2 commits into
developmentfrom
docs/maturity-gated-release
Jun 12, 2026
Merged

Maturity-gated release tooling (./uw dev)#233
lmoresi merged 2 commits into
developmentfrom
docs/maturity-gated-release

Conversation

@lmoresi

@lmoresi lmoresi commented Jun 12, 2026

Copy link
Copy Markdown
Member

What this adds

A process and tooling for promoting work from development to main with confidence: when a feature is announced as released, it has been validated. main accumulates whatever stable work comes over each quarter; the release notes — not branch surgery — distinguish features that are validated-and-guaranteed from those merely present-but-unguaranteed. So the MMPDE mover's code can be on main while the release announces only the validated features as supported.

This is usable this cycle for the Q4 (AFY) release.

New ./uw dev namespace

Developer/maintenance tools are grouped under ./uw dev <tool> (mirrors ./uw worktree). The main ./uw --help hides them on the stripped-down runtime/default envs and shows them by default on *-dev envs; ./uw --help all reveals them anywhere. Running a dev tool off a dev env prints a soft note but still runs.

Command Purpose
dev release-check Per-feature validation gate (dry-run) — which features currently pass
dev release Orchestrate the quarterly development → main promotion: RC tag, full validation + gate, scaffold notes, open the dev→main PR. Stops before the production tag so main's PR + review gate stays intact
dev branch-hygiene Report/prune branches merged into development (never touches open-PR branches)
dev feature-audit List feature-branch PR merges available to release
dev format / dev type-check moved here from the user-facing surface

How the guarantee works

  • docs/release-notes/feature-manifest.yaml declares each shippable feature and the tests that validate it. Supported is tied to the existing tier_a/tier_b markers by construction — there is no new per-feature marker to maintain.
  • scripts/release_gate.py resolves announced = min(claim, tests_result): an owner can be cautious (claim preview though tests pass), and the gate can downgrade (claim supported but a test fails). It never blocks the merge — only the announcement changes. It also renders the Supported / Preview notes sections.
  • scripts/check_manifest_coverage.py guards against drift (a tier_a test no feature references).

Seed manifest (verified against the real suite)

Feature Claim Achieved Why
Units and Scaling supported supported 4/4 tier_a pass
Winslow Mesh Smoother supported supported 6/6 tier_a pass
MMPDE Mover preview preview tests pass but held at preview (not yet guaranteed)
FMG supported experimental no tier_a suite yet — honest "needs a test" signal

More feature themes (snapshot toolkit, AdvDiff/SL, VEP-faults, solver infra) are candidates to add for Q4 — dev feature-audit lists the 35 merges to triage.

Docs

New docs/developer/guides/release-process.md; branching-strategy.md and the release-notes TEMPLATE.md updated.

Underworld development team with AI support from Claude Code

Introduce a process and tooling for promoting work from development to main
with confidence: when a feature is announced as released, it has been validated.
main accumulates whatever stable work comes over each quarter; the release notes
(not branch surgery) distinguish validated-and-guaranteed features from those
merely present-but-unguaranteed.

Tooling, grouped under a new ./uw dev namespace (env-aware help hides it on the
stripped-down runtime/default envs; reveal with ./uw --help all):

  dev release-check   Per-feature validation gate (dry-run)
  dev release         Orchestrate the quarterly dev->main promotion (RC tag,
                      validate, scaffold notes, open PR; stops before the
                      production tag so main's review gate stays intact)
  dev branch-hygiene  Report/prune branches merged into development
  dev feature-audit   List feature-branch PR merges available to release
  dev format/type-check  (moved here from the user-facing surface)

Mechanism:
  - docs/release-notes/feature-manifest.yaml declares each shippable feature and
    the tests that validate it. "Supported" is tied to the existing tier_a/b
    markers by construction; there is no new per-feature marker to maintain.
  - scripts/release_gate.py resolves announced = min(claim, tests_result) and
    renders the Supported / Preview release-notes sections.
  - scripts/check_manifest_coverage.py guards against drift (a tier_a test no
    feature references).

Docs: new release-process.md guide; branching-strategy.md and the release-notes
TEMPLATE.md updated. Seed manifest covers units, mesh-smoothing (supported),
the MMPDE mover (preview) and FMG (downgraded until it has a tier_a suite).

Underworld development team with AI support from Claude Code
Copilot AI review requested due to automatic review settings June 12, 2026 20:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a maturity-gated release promotion workflow and associated maintainer tooling under ./uw dev, so quarterly development → main merges can ship stable code while release notes only announce features that are validated by tiered tests.

Changes:

  • Introduces ./uw dev subcommands for release orchestration, per-feature release gating, feature auditing, and branch hygiene.
  • Adds a feature manifest plus Python tooling to run per-feature pytest selections and render Supported/Preview release-notes sections.
  • Documents the maturity-gated quarterly promotion process and updates release-note templates/branching guidance.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
uw Adds dev namespace, developer help, and release/branch tooling command implementations.
scripts/release_gate.py Implements the per-feature validation gate (manifest-driven pytest selection + reporting/JSON/notes rendering).
scripts/check_manifest_coverage.py Adds a drift check to ensure tier_a tests are referenced by the manifest.
docs/release-notes/TEMPLATE.md Updates the release-notes template with generated Supported/Preview sections.
docs/release-notes/feature-manifest.yaml Seeds the manifest describing features, claims, and validating tests.
docs/developer/guides/release-process.md Documents the maturity-gated release process and new tooling.
docs/developer/guides/branching-strategy.md Documents maturity-gated promotion as part of the branching strategy.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread uw Outdated
Comment on lines +1726 to +1732
git fetch --quiet origin 2>/dev/null || git fetch origin
local behind=$(git rev-list --count HEAD..origin/development 2>/dev/null || echo 0)
if [ "$behind" -gt 0 ]; then
echo -e " ${YELLOW}Local development is $behind commit(s) behind origin. Pull first.${NC}"; exit 1
fi
local ahead=$(git rev-list --count origin/main..origin/development 2>/dev/null || echo 0)
echo -e " ${GREEN}✓${NC} on development, clean, $ahead commit(s) ahead of main"
Comment thread uw
Comment on lines +1554 to +1561
# Head branches of open PRs — never prune these.
local open_pr_branches=""
if command -v gh >/dev/null 2>&1; then
open_pr_branches=$(gh pr list --state open --limit 200 \
--json headRefName --jq '.[].headRefName' 2>/dev/null)
else
echo -e " ${YELLOW}gh not found — cannot cross-check open PRs; nothing will be pruned.${NC}"
fi
Comment thread uw Outdated
Comment thread uw Outdated
Comment thread scripts/release_gate.py Outdated
Comment on lines +160 to +175
# pytest exit 5 == no tests collected (empty selection under markers)
if proc.returncode == 5:
result["note"] = "selection is empty under tier_a/b markers"
result["tests_maturity"] = "experimental"
else:
counts = _parse_junit(junit_path)
result.update(counts)
n_real = counts["n_selected"] - counts["n_skipped"]
if n_real <= 0:
result["tests_maturity"] = "experimental"
result["note"] = "all selected tests skipped"
elif counts["n_failed"] > 0:
result["tests_maturity"] = "preview"
result["note"] = f"{counts['n_failed']} test(s) failed"
else:
result["tests_maturity"] = "supported"
Comment thread scripts/check_manifest_coverage.py
Comment on lines +100 to +102
- non-empty selection under `tier_a or tier_b` **and** all pass → `supported`
- tests ran but some failed, or only unvalidated/`tier_c` tests exist → `preview`
- no tests at all → `experimental`
- release preflight: reject local unpushed commits so the RC tag can't point
  at commits origin/CI haven't seen (was only guarding "behind", not "ahead")
- branch-hygiene: only allow --prune when the open-PR cross-check genuinely
  succeeds; an unauth'd/failed gh query no longer reads as "no open PRs"
- release_gate: treat pytest exit 2-4 as an execution error (flagged, never
  "supported") instead of silently falling through to "experimental/skipped"
- coverage + gate globs: expand with recursive=True so ** patterns match
- fix ./uw release -> ./uw dev release in comment/usage strings
- align release-process.md maturity rules with the implementation
  (empty tier_a/b selection / only tier_c -> experimental, not preview)

Underworld development team with AI support from Claude Code
@lmoresi

lmoresi commented Jun 12, 2026

Copy link
Copy Markdown
Member Author

Addressed all 7 Copilot comments in bce2a1d:

  • preflight unpushed commitsdev release now also rejects local commits ahead of origin/development, so an RC tag can't point at commits CI/reviewers haven't seen.
  • prune safetydev branch-hygiene --prune now only runs when the open-PR cross-check genuinely succeeds; an unauthenticated/failed gh query no longer reads as "no open PRs".
  • pytest error codes — the gate treats pytest exit 2–4 as a flagged execution error (never "supported") instead of silently reporting experimental/skipped.
  • recursive globs — manifest path expansion uses recursive=True in both the gate and the coverage check, so ** patterns match.
  • command strings./uw release./uw dev release in the remaining comment/usage text.
  • doc/impl alignment — release-process.md now matches the gate: an empty tier_a/b selection (e.g. only tier_c) → experimental, not preview.

@lmoresi lmoresi merged commit 9da90ab into development Jun 12, 2026
1 check passed
@lmoresi lmoresi deleted the docs/maturity-gated-release branch June 13, 2026 00:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants