Skip to content

Commit 475ece5

Browse files
committed
docs: add ADR-022 per-kind artifact maturity and deprecation policy
Records what apiVersion AICR artifacts carry at v1 GA, what a bump owes the version it replaces, and what apiVersion a newly introduced kind starts at. Amends ADR-011: replaces its unconditional dual-accept transition window (section 4), extends its compatibility gate to the catalog loader (section 3), leaves sections 1 and 2 unchanged. Eight decisions, the load-bearing ones: - Project v1 and artifact v1 are separate axes. ROADMAP section 2 asks for a baseline, a gate, and a deprecation channel, not for every kind to reach aicr.run/v1. A gate over a v1beta1 schema still catches unintended breakage. - Per-kind maturity map. Snapshot, Recipe, RecipeCriteria, and bundle provenance go to aicr.run/v1; AICRConfig and the profile-bearing kinds sit at aicr.run/v1beta1. Whatever an integrator's code binds to must be at v1. - The deprecation window is conditional on the level being retired: alpha none, beta two releases, GA not removed within a major. The pkg/header godoc's hard-break language is correct for alpha and gains that condition rather than being reversed. - New kinds start on the current track. aicr.run/v1alpha1 in particular has never been valid; ADR-013 moved the version to v1alpha2 at the domain rename. - External data fails closed. Extends the accept-known/reject-unknown gate to the catalog loader, which today checks kind only and lets an older binary silently resolve an unspecialized recipe. Proposal only, no code. Signed-off-by: Mark Chmarny <mark@chmarny.com>
1 parent 3faf301 commit 475ece5

2 files changed

Lines changed: 174 additions & 0 deletions

File tree

docs/design/011-artifact-apiversion-policy.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# ADR-011: Artifact apiVersion Policy and Compatibility Gate
22

3+
> **Amended by [ADR-022](022-artifact-maturity-and-deprecation.md).** ADR-022
4+
> introduces a per-kind maturity map for v1 and makes the deprecation window
5+
> conditional on the level being retired (alpha none, beta two releases, GA not
6+
> removed within a major). It **replaces §4** below, whose dual-accept rule was
7+
> stated unconditionally, and **extends §3** to the catalog loader. §1 and §2
8+
> stand unchanged.
9+
310
> **Amended by [ADR-015](015-recipe-configuration-profiles.md).** ADR-015
411
> introduces kind-scoped version evolution: `RecipeMetadata` and
512
> `RecipeResult` accept `aicr.run/v1alpha3` for profile-bearing artifacts
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# ADR-022: Per-Kind Artifact Maturity and the v1 Deprecation Policy
2+
3+
## Status
4+
5+
Proposed. Amends [ADR-011](011-artifact-apiversion-policy.md): §4 (transition
6+
window) is replaced, §3 (compatibility gate) is extended to the catalog loader,
7+
and §1–§2 stand unchanged. Builds on [ADR-015](015-recipe-configuration-profiles.md),
8+
which already introduced kind-scoped version evolution as an amendment to ADR-011.
9+
10+
## Problem
11+
12+
Every AICR artifact carries an alpha `apiVersion`. ROADMAP
13+
[§2](../../ROADMAP.md#2-stability) promises a frozen, diff-gated surface at v1,
14+
and the Kubernetes convention that `v1alpha2` invokes — may be dropped or changed
15+
without notice — is the opposite of that promise. Two alpha versions coexist
16+
(`aicr.run/v1alpha2` for most kinds, `aicr.run/v1alpha3` for the profile-bearing
17+
`RecipeMetadata` and `RecipeResult`), which compounds it.
18+
19+
Three questions have no recorded answer:
20+
21+
1. **What `apiVersion` do artifacts carry at v1 GA?**
22+
2. **What does a bump owe the previous version?** ADR-011 §4 says dual-accept with
23+
a transition window. The `pkg/header` godoc says the opposite — a hard break
24+
with no window — and `IsSupportedAPIVersion` implements the godoc while
25+
`IsSupportedRecipeResultAPIVersion` implements the ADR. Both statements are
26+
unconditional, which is the actual gap: neither says *when* each applies.
27+
3. **What `apiVersion` does a newly introduced kind start at?** Nothing answers
28+
this, so new kinds guess.
29+
30+
## Non-Goals
31+
32+
- **REST path versioning.** ADR-011 scopes non-artifact `apiVersion`s as a
33+
non-goal and that stands. Whether the stable REST family is `/v1` or `/v2` is
34+
recorded separately under #2112.
35+
- **Tagging v1.0.0.** This ADR is prerequisite work, not the release.
36+
- **The CLI, REST, Go SDK, and bundle-layout freezes.** Those are #2111, #2112,
37+
#2113 under epic #2370. This ADR settles only what the artifacts are called and
38+
what a change to them owes consumers.
39+
40+
## Decision
41+
42+
### 1. Project v1 and artifact `v1` are separate axes
43+
44+
AICR reaching v1.0.0 does not require every artifact kind to reach
45+
`aicr.run/v1`. ROADMAP §2 asks for a committed baseline, a CI diff-gate, and a
46+
deprecation channel. A gate over a `v1beta1` schema is a real gate: it catches
47+
*unintended* breakage, which is what the freeze promises. The maturity string
48+
governs *intended* breakage, and it is set per kind.
49+
50+
This mirrors Kubernetes, which shipped 1.0 with beta APIs and serves beta
51+
alongside GA in every release since.
52+
53+
### 2. Per-kind maturity map
54+
55+
| Kind | Today | Target | Rationale |
56+
|---|---|---|---|
57+
| `Snapshot` | `v1alpha2` | `aicr.run/v1` | Settled shape; the first artifact an integrator reads |
58+
| `Recipe`, `RecipeCriteria` | `v1alpha2` | `aicr.run/v1` | 105 shipped overlays of exercise |
59+
| Bundle provenance (`localformat.ProvenanceAPIVersion`) | `v1alpha2` | `aicr.run/v1` | Rides in the bundle, which is what downstream integrates against |
60+
| `AICRConfig` | `v1alpha2` | `aicr.run/v1beta1` | Actively growing: #2026 bound 2 of 5 spec sections, #2245 binds the rest. Do not freeze a schema mid-expansion |
61+
| `RecipeMetadata`, `RecipeResult` | `v1alpha3` | `aicr.run/v1beta1` | Newest (ADR-015), 2 overlays, opt-in via profiles |
62+
63+
The line: **whatever an integrator's code binds to must be at `v1`.** That is
64+
`pkg/client/v1`, the REST surface, `Snapshot`, `Recipe`, and the bundle. Local
65+
configuration and the profile-bearing kinds can sit at beta honestly.
66+
67+
`v1alpha3` does **not** converge into the promoted version. `RecipeResult` stays
68+
distinct at beta, so `RecipeProfileAPIVersion` survives as the discriminator and
69+
the bidirectional profile/apiVersion validation in `pkg/recipe/profile.go` is
70+
unchanged.
71+
72+
### 3. The bump happens now, as a hard break
73+
74+
While every kind is alpha, a bump owes nothing (see §4). The 256 files carrying a
75+
version literal are regenerated in lockstep, exactly as
76+
[ADR-013](013-aicr-run-domain-migration.md) did, and no dual-accept path is
77+
carried forward. ADR-013 made this argument already: doing it before v1 "is the
78+
cheapest possible moment — there is no stable contract to honor yet." That
79+
argument expires at GA, which is why it is spent now.
80+
81+
### 4. The deprecation window is conditional on the level being retired
82+
83+
This **replaces ADR-011 §4**, whose dual-accept rule was stated unconditionally.
84+
The window owed is a function of the maturity of the version being removed:
85+
86+
| Level being retired | Obligation |
87+
|---|---|
88+
| Alpha | None. May be removed in any release, no prior notice |
89+
| Beta | Readable for **2 releases** after deprecation |
90+
| GA | Not removed within a major version |
91+
92+
The `pkg/header` godoc gains this condition. Its hard-break language is correct
93+
*for alpha* and must not be read as the general rule.
94+
95+
**Two releases is deliberate, not inherited.** The Kubernetes equivalent is 9
96+
months or 3 minor releases; at AICR's ~2-week cadence that would be ~18 releases.
97+
A short window means the read gate carries at most one retired version, which
98+
keeps the accept-known logic trivial. The cost is that a consumer who upgrades
99+
less than monthly can miss a window.
100+
101+
### 5. Never deprecate toward a less stable version
102+
103+
GA may replace beta and alpha. Beta may replace beta and alpha, never GA. Alpha
104+
may replace only alpha. This is what makes a mixed-maturity map safe: promoting
105+
`Snapshot` to `v1` is a one-way door for `Snapshot` and binds no other kind.
106+
107+
### 6. Split each bump across two releases
108+
109+
Release N adds the new version to the read gate. Release N+1 flips the emitter.
110+
A consumer who rolls back one release can still read what they generated.
111+
112+
AICR has no conversion layer — a file is one version, take it or leave it — so
113+
this matters more here than in Kubernetes, where the apiserver converts between
114+
served versions.
115+
116+
**This composes with §4 rather than duplicating it.** For a beta kind: N adds
117+
read support, N+1 emits the new version and deprecates the old, and the old stays
118+
readable through N+3. Three releases of overlap total. The two "2 releases" are
119+
different clocks and should not be conflated.
120+
121+
### 7. A new kind starts on the current track
122+
123+
A kind introduced today is stamped `aicr.run/v1alpha2` (or the post-bump
124+
equivalent once §3 lands), never a version the tree does not already accept.
125+
`aicr.run/v1alpha1` in particular has never been valid: ADR-013 moved the version
126+
to `v1alpha2` *at* the domain rename, so the legacy pairing was
127+
`aicr.nvidia.com/v1alpha1`.
128+
129+
Every new kind gets a row in the §2 map and an entry in the read gate in the same
130+
change that introduces it.
131+
132+
### 8. External data must be compatible with the binary reading it, and fails closed
133+
134+
This **extends ADR-011 §3** to the catalog loader, which today checks `kind` only
135+
and never `apiVersion`. ADR-015 documented the consequence: a released binary
136+
pointed at a newer catalog silently resolves an unspecialized recipe.
137+
138+
Silent divergence is the dangerous direction. The accept-known / reject-unknown
139+
gate applies per kind at every external-data boundary, including
140+
`--data` catalogs. An unknown `apiVersion` is an `ErrCodeInvalidRequest` naming
141+
the value, the expected value, and the remediation — never a silent downgrade.
142+
143+
Consequence: catalog authors need a published statement of which binary versions
144+
accept which catalog versions. Loud breakage is the intent.
145+
146+
## Consequences
147+
148+
- Two version tracks exist at v1 — `aicr.run/v1` and `aicr.run/v1beta1` — instead
149+
of today's two alpha tracks.
150+
- `pkg/header` grows from two gates to roughly four, one per track, following the
151+
`IsSupportedRecipeResultAPIVersion` pattern ADR-015 established.
152+
- A one-time regeneration touches the 256 files carrying a version literal. No
153+
transition window is offered, and artifacts stamped with a prior group/version
154+
must be regenerated. This is the last release in which that is free.
155+
- `AICRConfig` and the profile-bearing kinds ship at beta and are *expected* to
156+
break after v1.0.0, which makes the deprecation channel (#2115) load-bearing
157+
rather than a documentation task.
158+
- A catalog that declares a kind an older binary does not know now fails loudly
159+
where it previously resolved something plausible and wrong.
160+
161+
## References
162+
163+
- [ADR-011](011-artifact-apiversion-policy.md) — artifact `apiVersion` policy and compatibility gate
164+
- [ADR-013](013-aicr-run-domain-migration.md)`aicr.run` domain migration, the precedent for a pre-v1 hard break
165+
- [ADR-015](015-recipe-configuration-profiles.md) — recipe configuration profiles, which introduced kind-scoped evolution
166+
- [ROADMAP §2 Stability](../../ROADMAP.md#2-stability)
167+
- [Kubernetes deprecation policy](https://kubernetes.io/docs/reference/using-api/deprecation-policy/)

0 commit comments

Comments
 (0)