GTR097 — UpgradeToValid: the minimal-bump orchestrator

Contract: declares the minimum vendored profile at or above the tool’s baseline that it validates at; structural-only by composition; never lowers a profile.

The transform

Given a floor (the tool’s declared profile=, or Galaxy’s 16.01 legacy default when undeclared):

  1. Probe oldest_valid_profile(document, floor=floor) — the oldest vendored profile at or above the floor the (already-repaired) tool validates at as-is. If found, declare exactly it via GTR007 (UpdateProfile(ceiling=needed)) and stop.

  2. Otherwise step through the registered single-step upgrade_vN codemods (GTR008, GTR093, GTR009, GTR010, GTR011) for the sticking version, re-probing after each advancing step, and declare the first profile at or above the floor a step unblocks — the declaration lands once, at the minimum.

  3. A stall (a sticking version with no registered upgrade, or a step that cannot advance the tool) reverts any structural steps it tried (a snapshot restore) and leaves the tool byte-untouched, reporting the floor via unreachable_floor (the caller fails the tool closed).

Proof

Every constituent step is individually proven on its own document (GTR012 sequences the same set); this orchestrator adds only validity-gated sequencing toward a minimum, not a maximum:

  • Never lowers a profile. oldest_valid_profile only considers profiles at or above the floor, and the floor is the tool’s own baseline, so the declared profile after the transform is always >= baseline. The bump-up-only UpdateProfile reinforces this: it never rewrites a declaration to an older version.

  • Lands at the minimum. Step 1 returns the oldest validating profile at or above the floor, so when the tool validates as-is no structural change runs and profile= rises no further than required (it is a byte no-op when the tool already validates at its declared baseline). In the stepped path the declaration is deferred until a profile at or above the floor validates, so no intermediate profile is ever declared.

  • Structural-only. The only mutations are the proven upgrade_vN steps and the profile declaration; no <command>, Cheetah, or runtime construct is touched, so behaviour at the declared profile is the behaviour the steps already preserve.

  • Terminates. The stepped loop advances only when newest_valid_profile strictly improves and halts on a repeat (the seen guard) over the finite vendored profile set.

This is the engine of the shipped default upgrade (codemod docs/decisions.md §50, registry D22): profile= moves only when the tool is invalid where it sits, running this after repair instead of walking to the behaviour ceiling (the opt-in --modernize path keeps using GTR012). The facade short-circuits before this orchestrator in the kept and undeclared cases, so it runs only when a bump is actually needed. See codemod docs/decisions.md §49 for the mechanism decision.