caixa_core/render.rs
1//! Render-side helpers shared by every per-Servico renderer
2//! ([`caixa-helm`], [`caixa-flux`]) — the canonical place for "if the
3//! M2 typed slot is non-empty, emit its camelCase YAML fragment under
4//! the agreed key" patterns to live exactly once.
5//!
6//! Until this module landed both renderers carried an inline ~20-line
7//! block per render entry-point that:
8//!
9//! 1. Checked `caixa.limits.is_some() && !limits.is_empty()`.
10//! 2. Called `serde_yaml::to_value(limits).unwrap_or(Value::Null)` —
11//! silently swallowing every serialization error as a `null`-shaped
12//! fragment that would render as `limits: null` in the values block,
13//! indistinguishable from "the author omitted the slot" downstream.
14//! 3. Inserted under the camelCase key `"limits"` with `or_insert`
15//! semantics so explicit `spec.*` fields from the ComputeUnit YAML
16//! take precedence over the manifest-derived overlay.
17//! 4. Repeated the same shape for `:behavior` → `"behavior"` and
18//! `:upgrade-from` → `"upgradeFrom"`.
19//!
20//! That's the duplication budget violated three ways: same emptiness
21//! check, same camelCase key, same precedence rule, written twice
22//! verbatim. THEORY.md §I.3.5 ("Generation first, composition second,
23//! hand-authoring last; the duplication budget is zero") promotes that
24//! to a build-time concern: every recurring shape lives in a typed
25//! helper before its third occurrence — and PRIME DIRECTIVE work is
26//! exactly that lift.
27//!
28//! [`servico_m2_overlay`] is that helper. Renderers iterate the map it
29//! returns and merge each `(key, value)` pair into their target with
30//! their own map type's `entry().or_insert()` (so `spec.*` precedence
31//! is preserved by construction).
32
33use std::collections::BTreeMap;
34use std::path::{Component, Path, PathBuf};
35use thiserror::Error;
36
37use crate::{Caixa, CaixaKind};
38
39/// Errors the render helpers can raise.
40#[derive(Debug, Error)]
41pub enum RenderError {
42 /// `serde_yaml::to_value` failed for one of the M2 typed slots —
43 /// theoretically impossible for the canonical
44 /// [`crate::LimitsSpec`] / [`crate::BehaviorSpec`] /
45 /// [`crate::UpgradeFromEntry`] types (all derive Serialize without
46 /// fallible custom impls), but surfaced rather than swallowed so a
47 /// future slot whose Serialize impl gains a fallible branch
48 /// surfaces the failure to the caller instead of silently rendering
49 /// as `null` (the prior inline block's behavior).
50 #[error("yaml serialization of M2 slot {slot}: {source}")]
51 Yaml {
52 slot: &'static str,
53 #[source]
54 source: serde_yaml::Error,
55 },
56}
57
58/// Typed kind-mismatch view: the canonical surface every per-kind
59/// `caixa-<target>` renderer raises when it's handed a [`Caixa`] whose
60/// `:kind` doesn't match the kind that renderer is targeting. Carries
61/// the offending caixa's `:nome` alongside the expected/actual kinds,
62/// so the diagnostic reads `caixa "<nome>": expected :kind <expected>,
63/// got <actual>` — naming which caixa needs author attention, not just
64/// which kind the renderer rejected.
65///
66/// Lifted from three identical-shape per-renderer arms in
67/// `caixa-helm` ([`Error::NotAServico`][helm-err]), `caixa-flux`
68/// ([`Error::NotAServico`][flux-err]) and `caixa-mesh`
69/// ([`Error::NotAnAplicacao`][mesh-err]). The prior arms each carried
70/// only the actual [`CaixaKind`], leaving the user to grep for which
71/// `caixa.lisp` triggered the mismatch — exactly the
72/// "feira verb whose error path doesn't name the offending caixa"
73/// punch-list item the compounding-mandate protocol calls out.
74///
75/// Renderers wrap this view in their own [`thiserror`] `Error` enum
76/// via `#[from]`; the `?` operator at every kind-checking call site
77/// turns the [`require_kind`] result into the renderer's local error
78/// type with no manual conversion.
79///
80/// [helm-err]: https://docs.rs/caixa-helm
81/// [flux-err]: https://docs.rs/caixa-flux
82/// [mesh-err]: https://docs.rs/caixa-mesh
83#[derive(Debug, Clone, PartialEq, Eq, Error)]
84#[error("caixa {nome:?}: expected :kind {expected:?}, got {actual:?}")]
85pub struct KindMismatch {
86 /// The offending caixa's `:nome` — names which `caixa.lisp` the
87 /// renderer was handed, so the diagnostic doesn't require the
88 /// user to grep for it.
89 pub nome: String,
90 /// The `:kind` this renderer targets.
91 pub expected: CaixaKind,
92 /// The `:kind` the offending caixa actually carries.
93 pub actual: CaixaKind,
94}
95
96/// Predicate: assert that `caixa.kind == expected`, returning a typed
97/// [`KindMismatch`] view (carrying [`Caixa::nome`]) on rejection. The
98/// canonical entry-point every per-kind renderer wraps in its own
99/// [`thiserror`] `Error` variant via `#[from]` — the call site
100/// becomes a single `caixa_core::require_kind(caixa, CaixaKind::X)?;`
101/// in place of the prior inline `if caixa.kind != CaixaKind::X {
102/// return Err(Error::NotAnX(caixa.kind)); }` block.
103///
104/// Lifted to a single helper so a future per-kind renderer
105/// (`caixa-otel`, the future per-Aplicacao CR materializer the M3.x
106/// roadmap acknowledges, the future per-Supervisor reconciler
107/// renderer) gets the same naming-the-offending-caixa diagnostic for
108/// free, and a future change to the diagnostic format (e.g. adding
109/// a [`Caixa::versao`] suffix once multi-version-skew authoring lands)
110/// is one edit here, not a coordinated rewrite of every renderer.
111///
112/// # Errors
113///
114/// Returns [`KindMismatch`] when `caixa.kind != expected`. The error
115/// carries the caixa's `:nome` so the diagnostic names the offending
116/// `caixa.lisp` — same shape every renderer's `Error::From<KindMismatch>`
117/// converts into the renderer's local error type.
118pub fn require_kind(caixa: &Caixa, expected: CaixaKind) -> Result<(), KindMismatch> {
119 if caixa.kind() == expected {
120 Ok(())
121 } else {
122 Err(KindMismatch {
123 nome: caixa.nome().to_string(),
124 expected,
125 actual: caixa.kind(),
126 })
127 }
128}
129
130/// Typed `:ci`-slot-absence view: the canonical surface every per-`Acao`
131/// consumer raises when it's handed a `:kind Acao` [`Caixa`] whose `:ci`
132/// slot is absent. Carries the offending caixa's `:nome` so the diagnostic
133/// reads `caixa "<nome>": :kind Acao requires a :ci slot` — naming which
134/// `caixa.lisp` needs author attention, not just the axis the consumer
135/// rejected.
136///
137/// Lifted from `caixa-actions`' inline
138/// `.ok_or_else(|| Error::MissingCi { nome: caixa.nome().to_string() })`
139/// gate so a future per-`Acao` consumer (the deferred
140/// `sui-supercacheci::canteiro::emit_gha` workflow renderer, the future
141/// per-`Acao` CR materializer that mirrors the sibling per-`Servico` and
142/// per-`Aplicacao` materializers the M4 roadmap acknowledges) reaches for
143/// the same typed view via `#[from]` instead of re-inlining the same
144/// `.ok_or_else(...)` construction.
145///
146/// Peer of [`KindMismatch`] on the per-renderer kind-gate axis and
147/// [`ServicoCountMismatch`] on the per-Servico V0-count-gate axis — the
148/// third typed named-caixa entry-gate view every per-kind
149/// `caixa-<target>` renderer wraps via `#[from]` in its own
150/// [`thiserror`] `Error` enum.
151#[derive(Debug, Clone, PartialEq, Eq, Error)]
152#[error("caixa {nome:?}: :kind Acao requires a :ci slot")]
153pub struct MissingCiSlot {
154 /// The offending caixa's `:nome` — names which `caixa.lisp` the
155 /// consumer was handed, so the diagnostic doesn't require the user
156 /// to grep for it.
157 pub nome: String,
158}
159
160/// Predicate: assert that `caixa.ci().is_some()`, returning the borrowed
161/// [`canteiro_types::CiRun`] on success and a typed [`MissingCiSlot`] view
162/// (carrying [`Caixa::nome`]) on rejection. The canonical entry-point
163/// every per-`Acao` consumer wraps in its own [`thiserror`] `Error`
164/// variant via `#[from]` — the call site becomes a single
165/// `let ci = caixa_core::require_ci(caixa)?;` in place of the prior
166/// two-line
167/// `let ci = caixa.ci().ok_or_else(|| Error::MissingCi { nome: caixa.nome().to_string() })?;`
168/// block.
169///
170/// Returns `&CiRun` (rather than `()` like the peer [`require_kind`] and
171/// [`require_single_servico`] predicates on the same substrate entry-gate
172/// axis) because every caller then reaches for the borrowed `:ci` slot's
173/// [`canteiro_types::CiRun`] to decompose / render / emit — projecting
174/// the successful borrow through the same `?` step folds the check and
175/// the bind onto one call site, matching how every present + roadmapped
176/// per-`Acao` consumer uses the slot.
177///
178/// Lifted to a single helper so the `:ci`-slot-presence gate — the same
179/// axis the [`crate::LayoutError::MissingCi`] emission gates on at
180/// `feira build` time — lives in exactly one place across every future
181/// per-`Acao` consumer: a future `sui-supercacheci::canteiro::emit_gha`
182/// workflow renderer (the deferred `caixa-actions` next step named in
183/// its own crate docs), a future per-`Acao` CR materializer, and every
184/// consumer downstream reaches for the same typed helper and gets the
185/// same named-the-offending-caixa diagnostic for free.
186///
187/// Same trajectory as [`require_kind`] / [`KindMismatch`] on the peer
188/// per-renderer kind-gate axis and [`require_single_servico`] /
189/// [`ServicoCountMismatch`] on the peer per-Servico V0-count-gate axis:
190/// one `caixa_core::require_*` helper per typed entry-gate axis, so the
191/// diagnostic shape (named caixa, named field) is uniform across the
192/// substrate, and every per-kind renderer's `Error::From<*>` `#[from]`
193/// arm gets the diagnostic-naming-the-offending-caixa contract for free.
194///
195/// # Errors
196///
197/// Returns [`MissingCiSlot`] when `caixa.ci().is_none()` — every
198/// non-`Acao` kind lands here (the sibling
199/// [`crate::LayoutError::CiOnNonAcao`] gate refuses a declared `:ci` on
200/// any other kind at `feira build` time, so a callsite that gates on
201/// `:kind Acao` first via [`require_kind`] will only ever surface this
202/// arm for a `:kind Acao` caixa that hasn't declared its `:ci` yet).
203/// The error carries the caixa's `:nome` so the diagnostic names the
204/// offending `caixa.lisp` — same shape every consumer's
205/// `Error::From<MissingCiSlot>` converts into the consumer's local error
206/// type.
207pub fn require_ci(caixa: &Caixa) -> Result<&canteiro_types::CiRun, MissingCiSlot> {
208 caixa.ci().ok_or_else(|| MissingCiSlot {
209 nome: caixa.nome().to_string(),
210 })
211}
212
213/// Typed `:ci`-decompose-failure view: the canonical surface every
214/// per-`Acao` consumer raises when [`canteiro_types::decompose`] refuses
215/// the caixa's declared `:ci` run (a duplicate node name, a dependency
216/// on an undeclared node, a dependency cycle — every failure mode the
217/// sibling [`canteiro_types::DecomposeError`] enumerates). Carries the
218/// offending caixa's `:nome` alongside the borrowed
219/// [`canteiro_types::DecomposeError`] source so the diagnostic reads
220/// `caixa "<nome>": :ci decompose failed: <source>` — naming which
221/// `caixa.lisp` needs author attention, not just the axis the consumer
222/// rejected.
223///
224/// Lifted from `caixa-actions`' inline `Error::Decompose { nome: String,
225/// #[source] source: DecomposeError }` variant so a future per-`Acao`
226/// consumer (the deferred `sui-supercacheci::canteiro::emit_gha`
227/// workflow renderer named in the `caixa-actions` crate docs, the
228/// future per-`Acao` CR materializer that mirrors the sibling
229/// per-`Servico` / per-`Aplicacao` materializers the M4 roadmap
230/// acknowledges) reaches for the same typed view via `#[from]` instead
231/// of re-inlining the same `nome: String, #[source] source:
232/// DecomposeError` construction on its own call site — the second
233/// typed named-caixa diagnostic axis on the per-`Acao` consumer surface
234/// after the peer [`MissingCiSlot`] presence-gate axis.
235///
236/// Peer of [`MissingCiSlot`] on the per-`Acao` `:ci`-slot diagnostic
237/// axis (the presence gate reaches for [`MissingCiSlot`] via
238/// [`require_ci`]; the decompose gate reaches for [`CiDecomposeFailure`]
239/// on the borrowed [`canteiro_types::CiRun`] the presence gate returns).
240/// Peer of [`KindMismatch`] / [`ServicoCountMismatch`] on the sibling
241/// per-renderer entry-gate diagnostic axes — extends the same "one
242/// typed view per axis, carrying the offending caixa's `:nome` +
243/// axis-specific detail, wrapped by every consumer via `#[from]`"
244/// discipline onto the [`canteiro_types::decompose`] axis on the
245/// per-`Acao` consumer surface.
246///
247/// The `source` field carries the borrowed
248/// [`canteiro_types::DecomposeError`] verbatim (rather than collapsing
249/// to a single opaque axis) so a future consumer that wants to fan on
250/// the specific decompose-failure arm — a `feira lint` sub-diagnostic
251/// that offers a `:deps`-repair suggestion on the `MissingDependency`
252/// arm but not the `Cycle` arm, a future per-`Acao` CR materializer's
253/// admission webhook that surfaces the cycle path on rejection —
254/// reaches for `err.source` directly rather than re-parsing the Display
255/// bytes.
256///
257/// [`DecomposeError`]: canteiro_types::DecomposeError
258#[derive(Debug, Error)]
259#[error("caixa {nome:?}: :ci decompose failed: {source}")]
260pub struct CiDecomposeFailure {
261 /// The offending caixa's `:nome` — names which `caixa.lisp` the
262 /// consumer was handed, so the diagnostic doesn't require the user
263 /// to grep for it. Constructed via the lifted [`crate::Caixa::nome`]
264 /// accessor's `.to_string()` extension, matching the peer
265 /// [`MissingCiSlot::nome`] / [`KindMismatch::nome`] /
266 /// [`ServicoCountMismatch::nome`] `nome`-carrying axes.
267 pub nome: String,
268 /// The [`canteiro_types::decompose`] error the caixa's `:ci` run
269 /// tripped on — carried verbatim so a consumer that fans on the
270 /// specific arm (`Cycle` / `MissingDependency` / `DuplicateNode` /
271 /// …) reaches for the typed source rather than re-parsing the
272 /// Display bytes.
273 #[source]
274 pub source: canteiro_types::DecomposeError,
275}
276
277/// Predicate: decompose a borrowed [`canteiro_types::CiRun`] into its
278/// typed [`canteiro_types::CanteiroDag`] via
279/// [`canteiro_types::decompose`], wrapping any
280/// [`canteiro_types::DecomposeError`] in a typed [`CiDecomposeFailure`]
281/// view (carrying [`Caixa::nome`]) on rejection. The canonical
282/// entry-point every per-`Acao` consumer wraps in its own
283/// [`thiserror`] `Error` variant via `#[from]` — the call site becomes
284/// a single `let cd = caixa_core::decompose_ci(caixa, ci)?;` in place
285/// of the prior inline
286/// `let cd = canteiro_types::decompose(ci).map_err(|source| CiDecomposeFailure { nome: caixa.nome().to_string(), source })?;`
287/// block.
288///
289/// Takes the borrowed [`canteiro_types::CiRun`] as a separate argument
290/// (rather than re-borrowing it through [`require_ci`] internally) so
291/// the axis stays single-purpose — the sibling [`require_ci`] presence
292/// gate returns the borrowed slot, this predicate consumes it, and the
293/// two together form the substrate-canonical two-line per-`Acao` prelude
294/// `let ci = caixa_core::require_ci(caixa)?; let cd = caixa_core::decompose_ci(caixa, ci)?;`
295/// every present + roadmapped per-`Acao` consumer runs at its
296/// entry-point (matching how the sibling per-Servico entry-gate axes
297/// keep [`require_kind`] and [`require_single_servico`] as separate
298/// primitives, then compose them into the V0-shape
299/// [`require_v0_servico_shape`] helper — the compound `require + decompose`
300/// helper is a peer-lift for a later commit when a second per-`Acao`
301/// consumer arrives). The `caixa: &Caixa` argument is what makes the
302/// diagnostic name the offending `caixa.lisp` — the borrowed
303/// [`Caixa::nome`] accessor projects through the typed-view
304/// constructor unchanged, matching the peer [`require_ci`] /
305/// [`require_kind`] / [`require_single_servico`] typed-view constructors.
306///
307/// Lifted to a single helper so the [`canteiro_types::decompose`]
308/// axis — the same axis every per-`Acao` consumer runs on its declared
309/// `:ci` slot — lives in exactly one place across every future
310/// per-`Acao` consumer: a future `sui-supercacheci::canteiro::emit_gha`
311/// workflow renderer (the deferred `caixa-actions` next step named in
312/// its own crate docs), a future per-`Acao` CR materializer's admission
313/// webhook, a future `feira lint` sub-diagnostic that offers a
314/// `:deps`-repair suggestion on the [`canteiro_types::DecomposeError::MissingDependency`]
315/// arm but not the [`canteiro_types::DecomposeError::Cycle`] arm — every
316/// consumer reaches for the same one-liner + `#[from]` and gets the
317/// diagnostic-naming-the-offending-caixa contract for free.
318///
319/// Same trajectory as [`require_kind`] / [`KindMismatch`] on the peer
320/// per-renderer kind-gate axis, [`require_single_servico`] /
321/// [`ServicoCountMismatch`] on the peer per-Servico V0-count-gate
322/// axis, and [`require_ci`] / [`MissingCiSlot`] on the peer per-`Acao`
323/// presence-gate axis: one `caixa_core::require_*`/`decompose_ci`
324/// helper per typed axis, so the diagnostic shape (named caixa, named
325/// field) is uniform across the substrate, and every consumer's
326/// `Error::From<*>` `#[from]` arm gets the diagnostic-naming-the-
327/// offending-caixa contract for free.
328///
329/// # Errors
330///
331/// Returns [`CiDecomposeFailure`] when [`canteiro_types::decompose`]
332/// refuses the borrowed `:ci` run — every failure mode the sibling
333/// [`canteiro_types::DecomposeError`] enumerates (a duplicate node
334/// name, a dependency on an undeclared node, a dependency cycle) lands
335/// on this arm. The error carries the caixa's `:nome` + the underlying
336/// [`canteiro_types::DecomposeError`] verbatim so the diagnostic names
337/// the offending `caixa.lisp` and a consumer that fans on the specific
338/// arm reaches for `err.source` directly rather than re-parsing the
339/// Display bytes — same shape every consumer's
340/// `Error::From<CiDecomposeFailure>` converts into the consumer's local
341/// error type.
342pub fn decompose_ci(
343 caixa: &Caixa,
344 ci: &canteiro_types::CiRun,
345) -> Result<canteiro_types::CanteiroDag, CiDecomposeFailure> {
346 canteiro_types::decompose(ci).map_err(|source| CiDecomposeFailure {
347 nome: caixa.nome().to_string(),
348 source,
349 })
350}
351
352/// Substrate-canonical per-`Acao` declared-edge-count projection every
353/// consumer of a borrowed [`canteiro_types::CiRun`] that needs the total
354/// number of author-declared `deps` edges across every
355/// [`canteiro_types::CiNode`] keys off — returns the plain [`usize`] sum
356/// `ci.nodes.iter().map(|n| n.deps.len()).sum()` verbatim, without
357/// running [`canteiro_types::decompose`] again (the count is a property
358/// of the borrowed run's shape, not of the owned
359/// [`canteiro_types::CanteiroDag`] the sibling [`decompose_ci`] returns
360/// — an author-declared cycle carries the same edge count as an
361/// author-declared linear DAG of the same node-and-dep list).
362///
363/// The declared-edge-count axis carries the "how many `deps` edges did
364/// this repo's CI author write?" projection every per-`Acao` consumer
365/// downstream fans on: the `caixa_actions::RenderedAcao::edge_count`
366/// artifact the M0 renderer's `validate` returns (paired with the
367/// topological node-name list from `cd.topo_order()`), the deferred
368/// `sui-supercacheci::canteiro::emit_gha` workflow renderer's
369/// per-workflow `jobs.<job>.needs` count reconciliation pass (each
370/// `needs` entry maps 1:1 to a `deps` edge, so a renderer that emits N
371/// edges must have consumed exactly `declared_edge_count` `needs`
372/// entries across the fan-out), a future `feira lint --acao` per-caixa
373/// admission verb's per-repo declared-edge summary, a future M4
374/// `acao.pleme.io/v1alpha1/Acao` CR materializer's admission webhook
375/// spanning the declared edge count against a per-tenant complexity cap.
376///
377/// Prior to this lift the `ci.nodes.iter().map(|n| n.deps.len()).sum()`
378/// expression was inlined at two sites — `caixa_actions::validate`'s
379/// `edge_count` field construction at `caixa-actions/src/lib.rs:159`
380/// (the M0 per-`Acao` renderer's sole production consumer) and its own
381/// [`require_acao_view`] byte-parity pin at `caixa-actions/src/lib.rs:735`
382/// (which reconstructs the same sum through the compound helper's
383/// returned `&CiRun` to pin that the two paths agree) — two open-coded
384/// arithmetic expressions that expressed no compile-time link back to
385/// the typed [`canteiro_types::CiRun`] axis, so a future refactor of
386/// the declared-edge-count shape (a promotion of the plain [`usize`]
387/// sum to a `{intra_workspace, cross_workspace}` split once
388/// [`canteiro_types::CiNode`] grows a workspace-scoped edge kind, a
389/// per-`:ci` `deps`-edge-canonicalization pass that collapses duplicate
390/// edges once the canteiro-types axis grows a set-shaped `deps`
391/// representation, a per-env-class edge-weight overlay once the M4
392/// `EnvClass` axis grows a per-edge cost model) would have had to be
393/// threaded through both open-coded copies in lockstep or the M0
394/// renderer's `edge_count` artifact would silently disagree with its
395/// own byte-parity pin. Lifting the projection to a typed method on the
396/// substrate primitive means every downstream consumer of the `Acao`'s
397/// declared-edge-count surface reaches for exactly one typed
398/// dispatch — the resolver's accept-set migrates as a unit on any
399/// future axis addition.
400///
401/// The docstring on [`require_acao_view`] already named this expression
402/// verbatim ("the borrowed run for per-[`canteiro_types::CiNode`] axes
403/// (`ci.nodes.iter().map(|n| n.deps.len()).sum()` for the declared edge
404/// count …)") but the substrate carried no primitive for it — the
405/// citation was documentation-only, and the two open-coded call sites
406/// re-expressed the arithmetic each time. This lift closes that gap:
407/// the docstring now cites the substrate primitive by name and every
408/// consumer reaches for the same [`ci_declared_edge_count`] one-liner.
409///
410/// Peer of the sibling [`require_ci`] / [`decompose_ci`] /
411/// [`require_acao_view`] per-`Acao` primitives on the substrate's
412/// per-kind renderer entry-gate surface, extended onto the "borrowed
413/// [`canteiro_types::CiRun`] scalar projection" axis (the two prior
414/// primitives return borrowed / owned structural artifacts; this one
415/// returns a plain [`usize`] scalar over the borrowed run's node-list
416/// shape). Same "one typed dispatch on the substrate primitive, thin
417/// projections at each consumer" discipline the peer per-`Aplicacao`
418/// [`crate::aplicacao::AplicacaoSpec::port_for_destination`] scalar
419/// projection carries on the per-Aplicacao `:entrada` port-resolution
420/// axis, extended onto the per-`Acao` `:ci` declared-edge-count axis.
421///
422/// Named `ci_declared_edge_count` (rather than `declared_edge_count`)
423/// to keep the substrate-side helper namespace explicit that the input
424/// axis is a `:ci` slot — matching the peer [`require_ci`] /
425/// [`decompose_ci`] `ci_`-prefix-shaped naming convention the sibling
426/// per-`Acao` substrate primitives already carry, so a caller reading
427/// `caixa_core::ci_declared_edge_count(ci)` sees the axis at the
428/// helper name rather than at a lifted-out `use` alias.
429#[must_use]
430pub fn ci_declared_edge_count(ci: &canteiro_types::CiRun) -> usize {
431 ci.nodes.iter().map(|n| n.deps.len()).sum()
432}
433
434/// Typed `:servicos`-count-mismatch view: the canonical surface every
435/// per-Servico `caixa-<target>` renderer raises when it's handed a
436/// [`Caixa`] whose `:servicos` list doesn't carry exactly one entry —
437/// the V0 contract every Servico-kind caixa satisfies (`caixa-helm`'s
438/// `render_chart_for_servico`, `caixa-flux`'s `programs_yaml_entry`, the
439/// future per-Servico OCI/wasm packager). Carries the offending caixa's
440/// `:nome` alongside the actual count, so the diagnostic reads `caixa
441/// "<nome>": :servicos must declare exactly one entry for V0 (got
442/// <count>)` — naming which `caixa.lisp` needs author attention, not
443/// just the count the renderer rejected.
444///
445/// Lifted from two identical-shape per-renderer arms in
446/// [`caixa-helm`][helm-err] and [`caixa-flux`][flux-err]
447/// (`Error::UnsupportedServicoCount(usize)`). The prior arms each
448/// carried only the actual count, leaving the user to grep for which
449/// `caixa.lisp` triggered the mismatch — exactly the "feira verb whose
450/// error path doesn't name the offending caixa" punch-list item the
451/// compounding-mandate protocol calls out. Same trajectory as
452/// [`KindMismatch`] (which lifted the prior `NotAServico(CaixaKind)` /
453/// `NotAnAplicacao(CaixaKind)` per-renderer arms into a typed view
454/// naming the offending caixa).
455///
456/// Renderers wrap this view in their own [`thiserror`] `Error` enum
457/// via `#[from]`; the `?` operator at every count-checking call site
458/// turns the [`require_single_servico`] result into the renderer's
459/// local error type with no manual conversion. Peer to [`require_kind`]
460/// on the V0 Servico-shape gate axis (the kind gate refuses the wrong
461/// `:kind`; this gate refuses the wrong `:servicos` count) — every
462/// per-Servico renderer chains both at its entry point.
463///
464/// [helm-err]: https://docs.rs/caixa-helm
465/// [flux-err]: https://docs.rs/caixa-flux
466#[derive(Debug, Clone, PartialEq, Eq, Error)]
467#[error("caixa {nome:?}: :servicos must declare exactly one entry for V0 (got {count})")]
468pub struct ServicoCountMismatch {
469 /// The offending caixa's `:nome` — names which `caixa.lisp` the
470 /// renderer was handed, so the diagnostic doesn't require the
471 /// user to grep for it.
472 pub nome: String,
473 /// The `:servicos` list length the offending caixa actually carries.
474 /// The expected count is fixed at 1 by the V0 contract — every
475 /// `:kind Servico` caixa declares exactly one `ComputeUnit` YAML
476 /// pointer, matching the one Helm chart / one programs.yaml entry
477 /// each renderer emits.
478 pub count: usize,
479}
480
481/// Predicate: assert that `caixa.servicos.len() == 1`, returning a typed
482/// [`ServicoCountMismatch`] view (carrying [`Caixa::nome`] + the actual
483/// count) on rejection. The canonical entry-point every per-Servico
484/// renderer wraps in its own [`thiserror`] `Error` variant via
485/// `#[from]` — the call site becomes a single
486/// `caixa_core::require_single_servico(caixa)?;` in place of the prior
487/// inline `if caixa.servicos.len() != 1 { return
488/// Err(Error::UnsupportedServicoCount(caixa.servicos.len())); }`
489/// block.
490///
491/// Lifted to a single helper so the V0 `:servicos`-singularity invariant
492/// — the same shape the [`crate::Caixa::validate_code_paths`] doc
493/// comment already names as load-bearing on caixa-helm + caixa-flux
494/// (caixa-core/src/manifest.rs:4108) — lives in exactly one place across
495/// every per-Servico renderer. A future per-Servico renderer
496/// (`caixa-otel`, the future per-Servico OCI packager, the future M4
497/// `wasm.pleme.io/v1alpha1/ComputeUnit` CR materializer) gets the same
498/// naming-the-offending-caixa diagnostic for free, and a future change
499/// to the V0 invariant (e.g. allowing multi-servico Servicos when the
500/// component-model multi-world boundary lands in M5) is one edit here,
501/// not a coordinated rewrite of every renderer's per-arm
502/// `UnsupportedServicoCount` check.
503///
504/// Same trajectory as [`require_kind`] / [`KindMismatch`] on the peer
505/// V0 Servico-shape axis: every per-Servico renderer reaches for one
506/// `caixa_core::require_*` helper per V0 invariant, so the diagnostic
507/// shape (named caixa, named field) is uniform across the substrate.
508///
509/// # Errors
510///
511/// Returns [`ServicoCountMismatch`] when `caixa.servicos.len() != 1`
512/// (both empty and ≥ 2 land on this arm — the V0 contract requires
513/// *exactly* one entry, not *at-least* one). The error carries the
514/// caixa's `:nome` + the offending count so the diagnostic names the
515/// offending `caixa.lisp` — same shape every renderer's
516/// `Error::From<ServicoCountMismatch>` converts into the renderer's
517/// local error type.
518pub fn require_single_servico(caixa: &Caixa) -> Result<(), ServicoCountMismatch> {
519 if caixa.servicos().len() == 1 {
520 Ok(())
521 } else {
522 Err(ServicoCountMismatch {
523 nome: caixa.nome().to_string(),
524 count: caixa.servicos().len(),
525 })
526 }
527}
528
529/// Compound V0-shape entry gate: the canonical two-line
530/// `require_kind(caixa, Servico)? + require_single_servico(caixa)?`
531/// prelude every per-Servico `caixa-<target>` renderer runs at its
532/// entry-point, collapsed onto one call the caller reads as intent
533/// ("gate the input on the V0 Servico shape") rather than two
534/// hand-spelled predicate calls.
535///
536/// The pair names one contract with two axes: `:kind` is `Servico`
537/// (this is a per-Servico renderer's input, not a `Biblioteca` /
538/// `Binario` / `Supervisor` / `Aplicacao` mis-hand-off) *and*
539/// `:servicos.len() == 1` (the V0 contract every Servico caixa
540/// satisfies — one `ComputeUnit` YAML pointer, matching the one Helm
541/// chart / programs.yaml entry / cluster bundle each per-Servico
542/// renderer emits). Both axes must hold together — a `:kind Servico`
543/// caixa with two `:servicos` entries and a `:kind Aplicacao` caixa
544/// with one `:servicos` entry are equally invalid at every per-Servico
545/// renderer's entry-point — so lifting the pair onto one helper names
546/// the compound contract at each call site the way the M2 typed slots'
547/// [`servico_m2_overlay`] names the compound `:limits`+`:behavior`+
548/// `:upgrade-from` overlay contract at each call site.
549///
550/// Three production call sites previously carried the two-line pair
551/// inline:
552///
553/// * `caixa-flux`'s [`programs_yaml_entry`][flux-yaml] (the
554/// aggregator-path programs.yaml entry emitter);
555/// * `caixa-flux`'s [`cluster_bundle`][flux-bundle] (the standalone
556/// `GitRepository` + `HelmRelease` + `Kustomization` trio emitter);
557/// * `caixa-helm`'s
558/// [`render_chart_for_servico_with`][helm-chart] (the per-program
559/// `lareira-<nome>` Helm chart emitter).
560///
561/// Each site now reads `caixa_core::require_v0_servico_shape(caixa)?`
562/// instead of the two-line pair. A future per-Servico renderer
563/// (`caixa-otel`, the future per-Servico OCI packager, the future M4
564/// `wasm.pleme.io/v1alpha1/ComputeUnit` CR materializer,
565/// MESH-COMPOSITION §III.2 #5) gets the compound V0-shape gate for
566/// free with one call, instead of re-inlining the two-line pair — and
567/// a future change to the V0 contract (e.g. adding a
568/// `:kind Servico`-only `:computeunits`-slot-shape gate when the
569/// component-model multi-world boundary lands in M5) is one edit here,
570/// not a coordinated rewrite of every renderer's inline pair.
571///
572/// The generic error type `E` accepts every renderer's local
573/// [`thiserror`] `Error` enum that carries both [`KindMismatch`] and
574/// [`ServicoCountMismatch`] via `#[from]` (`caixa_flux::Error`,
575/// `caixa_helm::Error`, and every future per-Servico renderer that
576/// wires both `#[from]` arms as the diagnostic-naming-the-offending-
577/// caixa contract already requires). Type inference at the call site
578/// resolves `E` from the caller's `?` return type, so the call reads
579/// as `caixa_core::require_v0_servico_shape(caixa)?` with no explicit
580/// turbofish — the same one-liner shape every peer `require_kind` /
581/// `require_single_servico` call site already reads as.
582///
583/// Peer to [`require_kind`] on the single-axis kind gate and
584/// [`require_single_servico`] on the single-axis count gate — both
585/// primitives stay public because per-non-Servico renderers
586/// (`caixa-mesh`'s per-Aplicacao gate, `caixa-feira`'s
587/// `first_servico_path` per-verb gate that composes both predicates
588/// with `anyhow::Context`) reach for the individual predicates rather
589/// than the compound one. Peer to [`servico_m2_overlay`] on the
590/// sibling per-Servico compound-contract surface: `servico_m2_overlay`
591/// names the compound M2 emit-side contract, `require_v0_servico_shape`
592/// names the compound V0 gate-side contract, both per-Servico shape.
593///
594/// [flux-yaml]: https://docs.rs/caixa-flux
595/// [flux-bundle]: https://docs.rs/caixa-flux
596/// [helm-chart]: https://docs.rs/caixa-helm
597///
598/// # Errors
599///
600/// Returns the caller's `E` wrapping a [`KindMismatch`] when
601/// `caixa.kind != CaixaKind::Servico`, or a [`ServicoCountMismatch`]
602/// when `caixa.servicos.len() != 1`. Order matches the two-line pair
603/// this replaces: the kind gate fires first, so a
604/// `:kind Aplicacao` caixa with zero `:servicos` entries surfaces the
605/// kind mismatch (the more actionable diagnostic — the author has the
606/// wrong `:kind`) rather than the count mismatch (a downstream
607/// consequence of the mis-kinded input).
608pub fn require_v0_servico_shape<E>(caixa: &Caixa) -> Result<(), E>
609where
610 E: From<KindMismatch> + From<ServicoCountMismatch>,
611{
612 require_kind(caixa, CaixaKind::Servico)?;
613 require_single_servico(caixa)?;
614 Ok(())
615}
616
617/// Compound per-Aplicacao entry gate: the canonical three-line
618/// `require_kind(caixa, CaixaKind::Aplicacao)? +
619/// caixa.aplicacao_view().expect(…) + spec.validate()?` prelude every
620/// per-Aplicacao `caixa-<target>` renderer runs at its entry-point,
621/// collapsed onto one call the caller reads as intent ("gate the input
622/// on the V0 Aplicacao shape and hand back a validated
623/// [`crate::aplicacao::AplicacaoSpec`]") rather than three hand-spelled
624/// steps.
625///
626/// The cascade names one contract with three axes: `:kind` is
627/// `Aplicacao` (this is a per-Aplicacao renderer's input, not a
628/// `Biblioteca` / `Binario` / `Servico` / `Supervisor` / `Acao`
629/// mis-hand-off), the [`Caixa::aplicacao_view`] fold-in succeeds (which
630/// [`require_kind`]-on-`Aplicacao` guarantees per its own doc pin —
631/// [`Caixa::aplicacao_view`] returns `Some` iff `caixa.kind().is_aplicacao()`),
632/// *and* the folded [`crate::aplicacao::AplicacaoSpec`] passes its own
633/// M3 typed-shape validation ([`crate::aplicacao::AplicacaoSpec::validate`]:
634/// non-empty `:membros`, DNS-1123 member names, semver-valid `:versao`
635/// requirements, `:contratos` referencing only declared members,
636/// `:placement Sharded` carrying `:shard-key`, `:placement`
637/// `Replicated`/`SingleNode` carrying `:clusters`, and so on across
638/// every M3 typed slot). All three axes must hold together — a
639/// `:kind Servico` caixa carrying a well-formed `:membros`/`:contratos`
640/// stanza (the manifest field's documented "silently ignored" case)
641/// and a `:kind Aplicacao` caixa with an empty `:membros` are equally
642/// invalid at every per-Aplicacao renderer's entry-point — so lifting
643/// the three-arm cascade onto one helper names the compound contract
644/// at each call site the way the sibling per-Servico
645/// [`require_v0_servico_shape`] compound gate already names the
646/// two-axis compound V0 Servico-shape contract.
647///
648/// Three production call sites in `caixa-mesh` previously funneled
649/// through the crate-local `typed_view` wrapper which itself carried
650/// the three-line cascade inline:
651///
652/// * `caixa-mesh`'s [`programs_for_aplicacao`][mesh-programs] (the
653/// `lareira-fleet-programs`-aggregator programs.yaml fan-out
654/// emitter);
655/// * `caixa-mesh`'s [`cilium_network_policies`][mesh-cnp] (the
656/// per-`(:de, :para)` L7 Cilium CRD emitter);
657/// * `caixa-mesh`'s [`gateway_routes`][mesh-gw] (the per-`:entrada`
658/// K8s Gateway API v1 Gateway + HTTPRoute emitter).
659///
660/// The crate-local `caixa_mesh::typed_view` wrapper now reads as a
661/// one-liner `caixa_core::require_aplicacao_view::<Error>(caixa)`. A
662/// future per-Aplicacao renderer (`caixa-tatara`'s per-Aplicacao
663/// [`process_for_aplicacao`][tatara] downstream axes when they grow a
664/// spec-consuming validate arm, the deferred
665/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's admission
666/// webhook, a future `feira validate --aplicacao` per-caixa admission
667/// verb) gets the compound three-arm gate for free with one call,
668/// instead of re-inlining the three-line cascade — and a future change
669/// to the V0 Aplicacao contract (e.g. adding a `:kind Aplicacao`-only
670/// `:membros`-cross-cluster-uniqueness gate when the M4 federated-app
671/// boundary lands) is one edit here, not a coordinated rewrite of
672/// every per-Aplicacao renderer's inline cascade.
673///
674/// The generic error type `E` accepts every per-Aplicacao renderer's
675/// local [`thiserror`] `Error` enum that carries both [`KindMismatch`]
676/// and [`crate::aplicacao::AplicacaoError`] via `#[from]`
677/// (`caixa_mesh::Error`, and every future per-Aplicacao renderer that
678/// wires both `#[from]` arms as the diagnostic-naming-the-offending-
679/// caixa contract already requires). Type inference at the call site
680/// resolves `E` from the caller's `?` return type, though a caller
681/// that assigns the result directly to a `Result<AplicacaoSpec,
682/// Error>` binding may need a turbofish
683/// (`::<Error>`) — matching the sibling `require_v0_servico_shape::<Error>`
684/// turbofish convention the peer per-Servico call sites already read.
685///
686/// Peer to [`require_v0_servico_shape`] on the sibling per-Servico
687/// entry-gate axis and [`require_kind`] / [`require_ci`] /
688/// [`decompose_ci`] on the sibling per-`Acao` entry-gate axis — every
689/// per-kind renderer's entry-gate cascade now lives in exactly one
690/// substrate primitive.
691///
692/// [mesh-programs]: https://docs.rs/caixa-mesh
693/// [mesh-cnp]: https://docs.rs/caixa-mesh
694/// [mesh-gw]: https://docs.rs/caixa-mesh
695/// [tatara]: https://docs.rs/caixa-tatara
696///
697/// # Errors
698///
699/// Returns the caller's `E` wrapping a [`KindMismatch`] when
700/// `caixa.kind != CaixaKind::Aplicacao`, or a
701/// [`crate::aplicacao::AplicacaoError`] when the folded
702/// [`crate::aplicacao::AplicacaoSpec`] fails its typed-shape
703/// validation. Order matches the three-line cascade this replaces: the
704/// kind gate fires first, so a `:kind Servico` caixa with a
705/// well-formed `:membros` stanza surfaces the kind mismatch (the more
706/// actionable diagnostic — the author has the wrong `:kind`) rather
707/// than the `AplicacaoError` (which the [`Caixa::aplicacao_view`]
708/// fold-in never even reaches on a non-`Aplicacao` kind).
709///
710/// # Panics
711///
712/// Never in practice — the internal [`Caixa::aplicacao_view`] unwrap
713/// is guarded by the preceding [`require_kind`]-on-`Aplicacao` gate,
714/// and [`Caixa::aplicacao_view`]'s own doc pin guarantees
715/// `Some`-return iff `caixa.kind().is_aplicacao()`. A future
716/// [`Caixa::aplicacao_view`] refactor that decouples `Some`-return
717/// from `caixa.kind().is_aplicacao()` would trip this panic at the
718/// first per-Aplicacao renderer call site, not silently return `Err(E)`
719/// at every one — the panic message names the substrate invariant so
720/// the offending edit is obvious.
721pub fn require_aplicacao_view<E>(caixa: &Caixa) -> Result<crate::aplicacao::AplicacaoSpec, E>
722where
723 E: From<KindMismatch> + From<crate::aplicacao::AplicacaoError>,
724{
725 require_kind(caixa, CaixaKind::Aplicacao)?;
726 let spec = caixa
727 .aplicacao_view()
728 .expect("require_kind(Aplicacao) guarantees Caixa::aplicacao_view returns Some");
729 spec.validate()?;
730 Ok(spec)
731}
732
733/// Compound per-`Acao` entry gate: the canonical three-line
734/// `require_kind(caixa, CaixaKind::Acao)? + require_ci(caixa)? +
735/// decompose_ci(caixa, ci)?` prelude every per-`Acao` `caixa-<target>`
736/// consumer runs at its entry-point, collapsed onto one call the caller
737/// reads as intent ("gate the input on the V0 Acao shape and hand back
738/// the borrowed [`canteiro_types::CiRun`] + the decomposed
739/// [`canteiro_types::CanteiroDag`]") rather than three hand-spelled
740/// steps.
741///
742/// The cascade names one contract with three axes: `:kind` is `Acao`
743/// (this is a per-`Acao` consumer's input, not a `Biblioteca` /
744/// `Binario` / `Servico` / `Supervisor` / `Aplicacao` mis-hand-off),
745/// the `:ci` slot is present ([`require_ci`] returns the borrowed
746/// [`canteiro_types::CiRun`]), *and* the declared run decomposes
747/// cleanly through [`canteiro_types::decompose`] (a duplicate node
748/// name, a missing dep, a cycle — every [`canteiro_types::DecomposeError`]
749/// arm — surfaces via [`CiDecomposeFailure`]). All three axes must
750/// hold together — so lifting the three-arm cascade onto one helper
751/// names the compound contract at each call site the way the sibling
752/// per-Servico [`require_v0_servico_shape`] compound gate already
753/// names the two-axis compound V0 Servico-shape contract and the
754/// sibling per-Aplicacao [`require_aplicacao_view`] compound gate
755/// names the three-arm compound per-Aplicacao entry-gate contract.
756///
757/// Returns the borrowed [`canteiro_types::CiRun`] paired with the
758/// owned [`canteiro_types::CanteiroDag`] `decompose_ci` produced —
759/// both are the load-bearing artifacts every per-`Acao` consumer
760/// reads past the gate: the borrowed run for
761/// per-[`canteiro_types::CiNode`] axes (the substrate primitive
762/// [`ci_declared_edge_count`] for the declared edge count, the
763/// deferred `sui-supercacheci::canteiro::emit_gha` per-node YAML emit
764/// surface), the owned DAG for topological order (`cd.topo_order()`,
765/// which the substrate's own [`decompose_ci`] pass-through-on-success
766/// contract at [`decompose_ci_accepts_valid_ci_run_and_returns_canteiro_dag`]
767/// pins as infallible on the accepted arm).
768///
769/// The current single production call site — `caixa-actions::validate` —
770/// previously carried the three-line prelude inline:
771///
772/// ```ignore
773/// caixa_core::require_kind(caixa, CaixaKind::Acao)?;
774/// let ci = caixa_core::require_ci(caixa)?;
775/// let cd = caixa_core::decompose_ci(caixa, ci)?;
776/// ```
777///
778/// It now reads as a one-liner
779/// `let (ci, cd) = caixa_core::require_acao_view::<Error>(caixa)?;`.
780/// Every deferred per-`Acao` consumer named in the `caixa-actions` crate
781/// docs (the `sui-supercacheci::canteiro::emit_gha` workflow renderer, a
782/// future `acao.pleme.io/v1alpha1/Acao` CR materializer's admission
783/// webhook, a future `feira validate --acao` per-caixa admission verb)
784/// gets the compound three-arm gate for free with one call, instead of
785/// re-inlining the three-line prelude — and a future change to the V0
786/// Acao contract (an M4 [`canteiro_types::CiRun`] `:workspace`-scoped
787/// admission gate the CR materializer resolves at admission time, a
788/// per-`:ci` cross-node capability-audit prelude the Pony-inspired
789/// capability-typing roadmap acknowledges) is one edit here on the
790/// compound helper, not a coordinated rewrite across every per-`Acao`
791/// consumer's inline three-line prelude.
792///
793/// The generic error type `E` accepts every per-`Acao` consumer's
794/// local [`thiserror`] `Error` enum that carries all three of
795/// [`KindMismatch`], [`MissingCiSlot`], and [`CiDecomposeFailure`]
796/// via `#[from]` (`caixa_actions::Error` today, and every future
797/// per-`Acao` consumer that wires the same three `#[from]` arms as
798/// the diagnostic-naming-the-offending-caixa contract already
799/// requires). Type inference at the call site resolves `E` from the
800/// caller's `?` return type, though a caller that assigns the result
801/// directly to a `Result<(&CiRun, CanteiroDag), Error>` binding may
802/// need a turbofish (`::<Error>`) — matching the sibling
803/// `require_aplicacao_view::<Error>` turbofish convention the peer
804/// per-Aplicacao call site already reads.
805///
806/// Peer to [`require_v0_servico_shape`] on the sibling per-Servico
807/// entry-gate axis and [`require_aplicacao_view`] on the sibling
808/// per-Aplicacao entry-gate axis — every per-kind renderer's
809/// entry-gate cascade now lives in exactly one substrate primitive.
810///
811/// # Errors
812///
813/// Returns the caller's `E` wrapping a [`KindMismatch`] when
814/// `caixa.kind != CaixaKind::Acao`, a [`MissingCiSlot`] when the
815/// caixa's `:ci` slot is absent past the kind gate, or a
816/// [`CiDecomposeFailure`] when [`canteiro_types::decompose`] refuses
817/// the borrowed run. Order matches the three-line prelude this
818/// replaces: the kind gate fires first (so a `:kind Servico` caixa
819/// carrying a well-formed `:ci` stanza — the manifest field's
820/// documented "silently ignored" case on a non-`Acao` kind —
821/// surfaces the kind mismatch, the more actionable diagnostic), then
822/// the presence gate, then the decompose gate.
823pub fn require_acao_view<E>(
824 caixa: &Caixa,
825) -> Result<(&canteiro_types::CiRun, canteiro_types::CanteiroDag), E>
826where
827 E: From<KindMismatch> + From<MissingCiSlot> + From<CiDecomposeFailure>,
828{
829 require_kind(caixa, CaixaKind::Acao)?;
830 let ci = require_ci(caixa)?;
831 let cd = decompose_ci(caixa, ci)?;
832 Ok((ci, cd))
833}
834
835/// One rendered artifact — a `(path, contents)` pair every per-target
836/// `caixa-<target>` renderer emits at every leaf of its output tree.
837/// Carries the sandboxed relative path the substrate writes the artifact
838/// under (relative to the renderer-chosen output root — the per-chart
839/// directory for [`caixa-helm`][cf-helm]'s `lareira-<nome>` chart tree,
840/// the per-caixa `./clusters/<cluster>/services/<nome>/` sub-tree for
841/// [`caixa-flux`][cf-flux]'s [`cluster_bundle`][cb] Flux v2 CR trio)
842/// alongside the pre-serialized byte contents the substrate writes to it.
843///
844/// Lifted from two identical-shape per-renderer arms in
845/// [`caixa-flux`][cf-flux] (`BundleFile { path: PathBuf, contents:
846/// String }`) and [`caixa-helm`][cf-helm] (`ChartFile { path: PathBuf,
847/// contents: String }`) — same field pair, same derives (`Debug + Clone
848/// + PartialEq + Eq`), no per-type impls — carrying the same "one
849/// rendered leaf artifact" contract twice. Every prior per-target
850/// renderer had reinvented the same two-field record because there was
851/// no substrate-side canonical `(path, contents)` shape to reach for;
852/// the future per-target renderers the M4/M5 roadmap acknowledges
853/// (`caixa-otel`'s per-collector-config emit, the future per-Aplicacao
854/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-CR YAML
855/// emit, the future per-Supervisor reconciler renderer's per-child
856/// bundle emit) would have re-added a third and fourth clone of the
857/// same record — exactly the "render-side patterns recurring ≥2 times
858/// across `caixa-helm` / `caixa-flux` / `caixa-mesh` become helpers.
859/// Duplication is a bug. (PRIME DIRECTIVE.)" compounding-mandate slot
860/// item.
861///
862/// Both prior arms remain as public `pub type BundleFile =
863/// caixa_core::RenderedFile;` / `pub type ChartFile =
864/// caixa_core::RenderedFile;` aliases at their crate boundary so every
865/// existing struct-literal construction site
866/// (`BundleFile { path: …, contents: … }` / `ChartFile { path: …,
867/// contents: … }`), every field-access site (`.path` / `.contents`),
868/// and every derive-fed navigator (`==` equality pins, `Debug`
869/// formatting probes) resolves through the type alias to the canonical
870/// [`RenderedFile`] with no per-call-site edit — Rust type aliases
871/// carry the same `#[derive]`-generated `Debug`/`Clone`/`PartialEq`/
872/// `Eq` impls as their canonical, so the shared-shape contract lives
873/// at one type definition instead of two verbatim clones drifting
874/// silently on any future rebrand.
875///
876/// Peer to the [`KindMismatch`] / [`ServicoCountMismatch`] typed-view
877/// lifts on the sibling per-renderer-error-diagnostic-shape axis: both
878/// families lift a per-renderer duplicated record onto a canonical
879/// substrate-side type, so a future per-target renderer joins the
880/// pattern by re-exporting one alias instead of open-coding another
881/// clone.
882///
883/// The `path` axis carries the sandboxed relative path — the same
884/// [`is_sandboxed_relative_path`] discipline the [`Caixa::validate_code_paths`]
885/// invariant enforces at the manifest-side path axis. No renderer today
886/// runs the predicate against the emit-side per-`RenderedFile.path`
887/// — the paths are picked from substrate-canonical `&'static str`
888/// filename constants ([`FLUX_GITREPOSITORY_YAML_FILENAME`],
889/// [`FLUX_HELMRELEASE_YAML_FILENAME`], [`FLUX_KUSTOMIZATION_YAML_FILENAME`],
890/// [`HELM_CHART_YAML_FILENAME`], [`HELM_VALUES_YAML_FILENAME`]) rather
891/// than author input, so a per-emit-time sandbox check would be
892/// belt-and-suspenders — but the shared type shape makes a future
893/// sandbox-at-emit-time invariant a one-place add across every
894/// per-target renderer.
895///
896/// [cf-flux]: https://docs.rs/caixa-flux
897/// [cf-helm]: https://docs.rs/caixa-helm
898/// [cb]: https://docs.rs/caixa-flux/latest/caixa_flux/fn.cluster_bundle.html
899#[derive(Debug, Clone, PartialEq, Eq)]
900pub struct RenderedFile {
901 /// Sandboxed relative path the substrate writes the artifact under
902 /// (relative to the renderer-chosen output root). Substrate-canonical
903 /// filename constants ([`FLUX_GITREPOSITORY_YAML_FILENAME`] /
904 /// [`FLUX_HELMRELEASE_YAML_FILENAME`] /
905 /// [`FLUX_KUSTOMIZATION_YAML_FILENAME`] for the `caixa-flux`
906 /// [`cluster_bundle`] Flux v2 CR trio, [`HELM_CHART_YAML_FILENAME`] /
907 /// [`HELM_VALUES_YAML_FILENAME`] for the `caixa-helm`
908 /// `lareira-<nome>` chart directory) source every path today.
909 pub path: PathBuf,
910 /// The rendered byte contents — a pre-serialized UTF-8 body every
911 /// downstream writer (`caixa-flux::cluster_bundle`'s
912 /// per-`GitRepository`/`HelmRelease`/`Kustomization` YAML emit,
913 /// `caixa-helm::render_chart_for_servico`'s per-`Chart.yaml`/
914 /// `values.yaml`/`README.md` chart-directory emit) hands to
915 /// `std::fs::write` verbatim under the paired [`Self::path`].
916 pub contents: String,
917}
918
919impl RenderedFile {
920 /// Construct a [`RenderedFile`] from its two axes — the sandboxed
921 /// relative `path` the substrate writes the artifact under and the
922 /// pre-serialized UTF-8 `contents` the paired `std::fs::write`
923 /// hands to that path. Accepts anything convertible into a
924 /// [`PathBuf`] (`&'static str` from the substrate-canonical
925 /// filename constants [`HELM_CHART_YAML_FILENAME`] /
926 /// [`HELM_VALUES_YAML_FILENAME`] / [`FLUX_GITREPOSITORY_YAML_FILENAME`]
927 /// / [`FLUX_HELMRELEASE_YAML_FILENAME`] /
928 /// [`FLUX_KUSTOMIZATION_YAML_FILENAME`] every current per-target
929 /// renderer picks its per-artifact leaf path from, `String` /
930 /// `PathBuf` for future author-supplied paths) and anything
931 /// convertible into [`String`] (the `serde_yaml::to_string` /
932 /// `format!` outputs every current renderer already threads into
933 /// the paired `contents` field).
934 ///
935 /// Lifted from six identical-shape struct-literal construction
936 /// sites — three per-artifact leaves in
937 /// [`caixa-helm`][cf-helm]'s `render_chart_for_servico_with`
938 /// (`Chart.yaml`, `values.yaml`, `README.md`) and three per-CR
939 /// leaves in [`caixa-flux`][cf-flux]'s [`cluster_bundle`][cb]
940 /// (`gitrepository.yaml`, `helmrelease.yaml`,
941 /// `kustomization.yaml`) — each of which open-coded a four-line
942 /// `<Xxx>File { path: PathBuf::from(FILENAME_CONST), contents: <body> }`
943 /// block that re-derived the same `PathBuf::from(&str)` wrap +
944 /// the same two-field assembly. Every existing struct-
945 /// literal construction (the type-alias identity pins at
946 /// [`caixa_flux::tests::bundle_file_alias_resolves_to_caixa_core_rendered_file`]
947 /// / [`caixa_helm::tests::chart_file_alias_resolves_to_caixa_core_rendered_file`],
948 /// the substrate-side field-shape pins in this crate's test
949 /// module) continues to compile — [`RenderedFile::new`] is an
950 /// additive inherent constructor that leaves the `pub path` /
951 /// `pub contents` field visibility untouched, so a future rebrand
952 /// on the record shape (a per-artifact hash / provenance field
953 /// addition, a per-artifact write-mode discriminator once
954 /// per-cluster-writer sandboxing lands) still reaches every
955 /// per-target renderer through this canonical constructor + the
956 /// existing struct-literal pinning by construction. Peer to the
957 /// sibling substrate-side canonical-composer surface
958 /// ([`oci_chart_ref`] / [`cilium_network_policy_name`] /
959 /// [`gateway_api_http_route_name`] / [`lareira_chart_name`]) —
960 /// each is a canonical `&'static fn(&str, …) -> String` composer
961 /// that every per-target renderer routes through instead of
962 /// re-deriving the same encoding inline.
963 ///
964 /// A future per-target renderer (`caixa-otel`'s per-collector-
965 /// config emit, the future `mesh.pleme.io/v1alpha1/Aplicacao` CR
966 /// materializer's per-CR YAML emit, the future per-Supervisor
967 /// reconciler renderer's per-child bundle emit) that constructs a
968 /// [`RenderedFile`] now reaches for [`RenderedFile::new`] and
969 /// participates in the same substrate-side per-artifact-
970 /// construction contract, so any addition here (say, a
971 /// `sandboxed_relative_path` invariant check on `path` at
972 /// construction time, the `is_sandboxed_relative_path`
973 /// discipline the docstring above acknowledges is not yet run at
974 /// emit time) reaches every per-target renderer through one
975 /// caixa-core edit instead of a coordinated six-site rewrite.
976 ///
977 /// [cf-helm]: https://docs.rs/caixa-helm
978 /// [cf-flux]: https://docs.rs/caixa-flux
979 /// [cb]: https://docs.rs/caixa-flux/latest/caixa_flux/fn.cluster_bundle.html
980 #[must_use]
981 pub fn new<P, S>(path: P, contents: S) -> Self
982 where
983 P: Into<PathBuf>,
984 S: Into<String>,
985 {
986 Self {
987 path: path.into(),
988 contents: contents.into(),
989 }
990 }
991}
992
993/// Predicate: find the first ASCII whitespace byte in `s`, or `None` if
994/// none of the string's bytes match `u8::is_ascii_whitespace`.
995///
996/// The canonical drift class this closes across every typed-magnitude
997/// codec in caixa-core (`limits::parse_byte_size` backing
998/// `:limits :memory`, `limits::parse_duration` backing `:limits
999/// :wall-clock`, `limits::parse_millicores` backing `:limits :cpu`,
1000/// `supervisor::duration_codec::parse` backing `:supervisor
1001/// :restart-window` / `:politicas :timeout` / `:politicas
1002/// :circuit-breaker :window`, and `aplicacao::rate_limit_codec::parse`
1003/// backing `:politicas :rate-limit`) is the ASCII subset of Unicode
1004/// `White_Space`: space (`0x20`), tab (`0x09`), LF (`0x0A`), FF
1005/// (`0x0C`), CR (`0x0D`) — the five WhatWG-conformant "ASCII whitespace"
1006/// bytes (deliberately narrower than POSIX's `[:space:]` which also
1007/// admits VT `0x0B`). Every downstream YAML / JSON / TOML parser can
1008/// feed any of these bytes through a quoted-scalar value verbatim, so
1009/// a paste-from-shell-history `"500m "` (trailing space), a
1010/// paste-from-aligned-doc `" 64MiB"` (leading space from YAML-quoted-
1011/// plain-scalar alignment), a paste-from-typography `"30 s"`
1012/// (whitespace between magnitude and unit), a paste-from-indented-doc
1013/// `"\t100/s"` (YAML-block-scalar tab byte), or a multi-line-paste
1014/// `"30s\n"` (trailing LF) all survive the top-level `s.trim()`
1015/// discipline and yield the same typed value at each codec — but
1016/// serde round-trips to a *different* canonical form on the next
1017/// emit, breaking the THEORY.md Part V render-determinism contract
1018/// every typed slot carries.
1019///
1020/// Peer of [`find_non_ascii_whitespace_char`] — the two predicates
1021/// together partition the full Unicode `White_Space` axis (this one
1022/// on the ASCII byte range, its peer on the strictly-complementary
1023/// non-ASCII `char` range), and every typed-magnitude codec in
1024/// caixa-core calls both back-to-back at parse entry so the codec's
1025/// accepted set matches its emitted set on the full axis,
1026/// structurally. Same "single lifted source of truth" discipline the
1027/// peer non-ASCII arm's 1b75b38 landing pinned: drift between any two
1028/// codec sites' ASCII-whitespace-rejection set becomes a single-edit
1029/// fix at this predicate rather than five independent scans
1030/// diverging over time, and a future stricter classification
1031/// (BOM `\u{FEFF}` / ZWSP `\u{200B}` / ZWJ `\u{200D}` — the
1032/// "invisible but not `char::is_whitespace`" class that the
1033/// deliberate exclusion in `find_non_ascii_whitespace_char` leaves
1034/// for a follow-up, if a downstream slot proves those are drift
1035/// classes) can extend at this shared site in one edit rather than
1036/// five. Peer of [`is_dns_1123_label`] / [`is_gateway_api_http_path`]
1037/// / [`is_git_repo_url`] — same "typed-slot's valid set matches its
1038/// codec's accepted set, structurally" discipline carried at the
1039/// codec layer.
1040#[must_use]
1041pub fn find_ascii_whitespace_byte(s: &str) -> Option<u8> {
1042 s.bytes().find(|b| b.is_ascii_whitespace())
1043}
1044
1045/// Predicate: find the first non-ASCII Unicode-`White_Space` character in
1046/// `s`, or `None` if every character lies in the ASCII byte range.
1047///
1048/// The canonical drift class this closes across every typed-magnitude
1049/// codec in caixa-core (`limits::parse_byte_size` backing
1050/// `:limits :memory`, `limits::parse_duration` backing `:limits
1051/// :wall-clock`, `supervisor::duration_codec::parse` backing
1052/// `:supervisor :restart-window` / `:politicas :timeout` /
1053/// `:politicas :circuit-breaker :window`, and
1054/// `aplicacao::rate_limit_codec::parse` backing `:politicas
1055/// :rate-limit`) is the non-ASCII subset of Unicode `White_Space`: NBSP
1056/// (`\u{00A0}`), OGHAM SPACE MARK (`\u{1680}`), the EN-QUAD /
1057/// EM-QUAD / EN-SPACE / EM-SPACE / THREE-PER-EM-SPACE /
1058/// FOUR-PER-EM-SPACE / SIX-PER-EM-SPACE / FIGURE-SPACE /
1059/// PUNCTUATION-SPACE / THIN-SPACE / HAIR-SPACE band
1060/// (`\u{2000}`..=`\u{200A}`), LINE SEPARATOR (`\u{2028}`), PARAGRAPH
1061/// SEPARATOR (`\u{2029}`), NARROW NBSP (`\u{202F}`), MEDIUM
1062/// MATHEMATICAL SPACE (`\u{205F}`), and IDEOGRAPHIC SPACE
1063/// (`\u{3000}`). Every one of these characters is
1064/// [`char::is_whitespace`]`() && !`[`char::is_ascii`]`()`, and every
1065/// one of them is silently stripped by [`str::trim`] at the top of
1066/// each codec's parse entry — `str::trim` uses `char::is_whitespace`,
1067/// which is Unicode `White_Space`, strictly wider than the byte-set
1068/// `u8::is_ascii_whitespace` the pre-gate arm on each codec already
1069/// refuses. So a paste-from-typography `"\u{00A0}64MiB"` (NBSP
1070/// leading) survives the byte-scan (none of its bytes match
1071/// `is_ascii_whitespace`), lands on the top-level `s.trim()` which
1072/// silently strips the NBSP, parses to `64 * 1024 * 1024` bytes, and
1073/// serde round-trips to the *different* canonical `"64MiB"` on next
1074/// emit — breaking the THEORY.md Part V render-determinism contract
1075/// every typed slot carries. Same class on every peer codec:
1076/// `"\u{2028}30s"` (paste-from-web-doc line-separator prefix) →
1077/// `Duration::from_secs(30)` → `"30s"`; `"\u{00A0}100/s"`
1078/// (paste-from-typography NBSP prefix on `:politicas :rate-limit`) →
1079/// `RateLimit { 100, 1s }` → `"100/s"`. The ASCII-whitespace-only
1080/// `is_ascii_whitespace` byte-scan closed on each codec by the
1081/// immediate predecessors (`limits::parse_byte_size` — 24a8ad4;
1082/// `limits::parse_duration` — ebc3a75; `supervisor::duration_codec`
1083/// — a7ae622; `rate_limit_codec` — 1ad7755) covers space (`0x20`),
1084/// tab (`0x09`), LF (`0x0A`), FF (`0x0C`), CR (`0x0D`); this
1085/// predicate closes the strictly-complementary non-ASCII Unicode
1086/// `White_Space` class in one lifted source of truth across all four
1087/// codec sites in one landing — the trajectory the 24a8ad4 commit
1088/// body's `Forward compounding` bullet explicitly named ("the next
1089/// canonical-form-drift trajectory … can land as a single lifted
1090/// predicate across all four codec sites in one follow-up run rather
1091/// than four independent extensions").
1092///
1093/// The predicate is deliberately narrower than "any non-ASCII
1094/// codepoint" — the byte-set restrictions on the accepted magnitude
1095/// (`b.is_ascii_digit()` on the digit-only arm, `is_ascii_alphabetic`
1096/// on the unit-suffix split) already refuse every non-`White_Space`
1097/// non-ASCII codepoint at a downstream arm with a `BadByteMagnitude`
1098/// / `BadDurationMagnitude` / equivalent diagnostic. This predicate's
1099/// job is exclusively to name the drift class — the Unicode
1100/// whitespace subset that survives the byte-scan but that
1101/// `str::trim` silently swallows — so the codec's diagnostic can
1102/// carry the offending [`char`] and its `U+XXXX` codepoint verbatim
1103/// rather than laundering the value through a generic "bad
1104/// magnitude" arm at a downstream site far from the paste-origin.
1105/// Peer of [`is_dns_1123_label`] / [`is_gateway_api_http_path`] /
1106/// [`is_git_repo_url`] — same "typed-slot's valid set matches its
1107/// codec's accepted set, structurally" discipline carried at the
1108/// codec layer.
1109///
1110/// Note that BOM (`\u{FEFF}`, ZERO WIDTH NO-BREAK SPACE) and ZWSP
1111/// (`\u{200B}`, ZERO WIDTH SPACE) are deliberately *outside* this
1112/// predicate's rejection set — both have `char::is_whitespace() ==
1113/// false` per the Unicode `White_Space` property, so `str::trim`
1114/// does *not* silently strip either, and both currently land on the
1115/// downstream `BadByteMagnitude` / `BadDurationMagnitude` arm at
1116/// parse time with the byte-shape diagnostic intact. Adding them
1117/// here would over-fire on an accepted-diagnostic class already
1118/// closed at a peer arm — the render-determinism contract is
1119/// unbroken on those inputs today.
1120#[must_use]
1121pub fn find_non_ascii_whitespace_char(s: &str) -> Option<char> {
1122 s.chars().find(|c| c.is_whitespace() && !c.is_ascii())
1123}
1124
1125/// Predicate: `s` carries a leading-zero-padded magnitude — its length
1126/// exceeds one byte and its first byte is ASCII `'0'`.
1127///
1128/// The canonical drift class this closes across every typed-magnitude
1129/// codec in caixa-core (`limits::parse_byte_size` backing `:limits
1130/// :memory` — cea9a78; `limits::parse_duration` backing `:limits
1131/// :wall-clock` — 39762d7; `limits::parse_millicores` backing
1132/// `:limits :cpu` — the sixth codec surface;
1133/// `supervisor::duration_codec::parse` backing `:supervisor
1134/// :restart-window` / `:politicas :timeout` / `:politicas
1135/// :circuit-breaker :window` — 9178904; and
1136/// `aplicacao::rate_limit_codec::parse` backing `:politicas
1137/// :rate-limit` — 4f46830) is the leading-zero-padded magnitude
1138/// shape: every downstream typed-magnitude codec's `render_*`
1139/// canonicalizer emits the leading-zero-stripped form, so a
1140/// leading-zero magnitude (`"030s"`, `"0100/s"`, `"0500m"`,
1141/// `"0064MiB"`, `"01h"`) round-trips through `render_*` to a
1142/// *different* canonical string on the next emit (`"30s"`, `"100/s"`,
1143/// `"500m"`, `"64MiB"`, `"1h"`) — breaking the THEORY.md Part V
1144/// render-determinism contract every typed slot carries the same way
1145/// the leading-`+` shape did before the digit-only arm landed.
1146///
1147/// The predicate deliberately admits the single-byte magnitude `"0"`
1148/// (returning `false`) — every codec's `render_*` canonicalizer emits
1149/// `"0"` / `"0s"` / `"0m"` / `"0/s"` verbatim for the zero magnitude,
1150/// so the single-byte form round-trips losslessly through the codec
1151/// layer. The downstream semantic-zero gates
1152/// ([`crate::LimitsError::MemoryZero`],
1153/// [`crate::LimitsError::WallClockZero`],
1154/// [`crate::LimitsError::CpuZero`],
1155/// [`crate::SupervisorError::ZeroRestartWindow`],
1156/// [`crate::AplicacaoError::PolicyTimeoutZero`],
1157/// [`crate::AplicacaoError::PolicyCircuitBreakerWindowZero`],
1158/// [`crate::AplicacaoError::PolicyRateLimitZero`]) refuse the
1159/// semantic-zero authoring at the typed-validate layer above; the
1160/// codec-layer / typed-validate-layer partition between
1161/// canonical-form drift (this arm) and semantic-zero (the downstream
1162/// gate) remains stable across every codec site.
1163///
1164/// Peer of [`find_ascii_whitespace_byte`] /
1165/// [`find_non_ascii_whitespace_char`] on the same
1166/// canonical-form-drift axis at the codec layer: those two predicates
1167/// close the whitespace drift class (paste-from-shell-history /
1168/// paste-from-typography), this one closes the leading-zero-padding
1169/// drift class (paste-from-fixed-width-alignment /
1170/// paste-from-columnar-report). Same "single lifted source of truth"
1171/// discipline: drift between any two codec sites' leading-zero
1172/// rejection set becomes a single-edit fix at this predicate rather
1173/// than five independent `s.len() > 1 && s.as_bytes()[0] == b'0'`
1174/// scans diverging over time. A future stricter classification
1175/// closing at this shared site (a hypothetical `"00"` shape whose
1176/// diagnostic distinguishes explicit-zero-padding from the accepted
1177/// canonical `"0"`, or a future higher base like `"0x0100"` whose
1178/// magnitude prefix would trip this arm before the digit-only gate
1179/// catches the `x`) extends at one location rather than five. Peer of
1180/// [`is_dns_1123_label`] / [`is_gateway_api_http_path`] /
1181/// [`is_git_repo_url`] — same "typed-slot's valid set matches its
1182/// codec's accepted set, structurally" discipline carried at the
1183/// codec layer.
1184#[must_use]
1185pub fn is_leading_zero_padded_magnitude(s: &str) -> bool {
1186 s.len() > 1 && s.as_bytes()[0] == b'0'
1187}
1188
1189/// Predicate: `s` is a non-empty digit-only magnitude — every byte is
1190/// an ASCII digit `[0-9]`.
1191///
1192/// The canonical drift class this closes across every typed-magnitude
1193/// codec in caixa-core (`limits::parse_byte_size` backing
1194/// `:limits :memory`, `limits::parse_duration` backing `:limits
1195/// :wall-clock`, `limits::parse_millicores` backing `:limits :cpu`,
1196/// `supervisor::duration_codec::parse` backing `:supervisor
1197/// :restart-window` / `:politicas :timeout` / `:politicas
1198/// :circuit-breaker :window`, and `aplicacao::rate_limit_codec::parse`
1199/// backing `:politicas :rate-limit`) is the non-digit-only magnitude
1200/// shape: every downstream typed-magnitude codec's `render_*`
1201/// canonicalizer emits a bare integer magnitude with no leading sign
1202/// (`+` / `-`), no decimal point, and no exponent, so a signed
1203/// magnitude (`"+30s"`, `"+500m"`, `"+100/s"`, `"+64MiB"`) or a
1204/// fractional / decimal magnitude (`"1.5s"`, `"0.5m"`, `"1.0/s"`,
1205/// `"1.5KiB"`) round-trips through `render_*` to a *different*
1206/// canonical string on the next emit (`"30s"`, `"500m"`, `"100/s"`,
1207/// `"64MiB"`, `"1500ms"`, `"30s"`, `"1/s"`, `"1KiB"`) — breaking the
1208/// THEORY.md Part V render-determinism contract every typed slot
1209/// carries.
1210///
1211/// The predicate deliberately treats the empty string as non-digit-only
1212/// (returning `false`) so an upstream codec that hasn't already
1213/// refused the empty-magnitude shape on its own `Empty*` / `Bad*` arm
1214/// still routes empty input to the non-canonical branch rather than
1215/// silently accepting it via the vacuous `bytes().all(_)` truth. Every
1216/// current codec site refuses empty magnitudes on a prior arm before
1217/// this predicate is consulted (`limits::parse_byte_size`'s `num_trim`
1218/// empty branch, `limits::parse_duration`'s `num_trim` empty branch,
1219/// `limits::parse_millicores`'s `magnitude.is_empty()` branch,
1220/// `supervisor::duration_codec::parse`'s `num_trim` empty branch,
1221/// `aplicacao::rate_limit_codec::parse`'s `rate_trim` empty branch),
1222/// so on the reachable inputs the empty-string clause is a no-op; the
1223/// clause is defense-in-depth for a future codec that reaches for this
1224/// predicate before landing its own upstream empty-magnitude arm.
1225///
1226/// The predicate deliberately admits the single-byte magnitude `"0"`
1227/// (returning `true`) — every codec's `render_*` canonicalizer emits
1228/// `"0"` / `"0s"` / `"0m"` / `"0/s"` verbatim for the zero magnitude,
1229/// so the single-byte form round-trips losslessly through the codec
1230/// layer. The downstream semantic-zero gates
1231/// ([`crate::LimitsError::MemoryZero`],
1232/// [`crate::LimitsError::WallClockZero`],
1233/// [`crate::LimitsError::CpuZero`],
1234/// [`crate::SupervisorError::ZeroRestartWindow`],
1235/// [`crate::AplicacaoError::PolicyTimeoutZero`],
1236/// [`crate::AplicacaoError::PolicyCircuitBreakerWindowZero`],
1237/// [`crate::AplicacaoError::PolicyRateLimitZero`]) refuse the
1238/// semantic-zero authoring at the typed-validate layer above; the
1239/// codec-layer / typed-validate-layer partition between
1240/// canonical-form drift (this arm) and semantic-zero (the downstream
1241/// gate) remains stable across every codec site.
1242///
1243/// Peer of [`find_ascii_whitespace_byte`] /
1244/// [`find_non_ascii_whitespace_char`] /
1245/// [`is_leading_zero_padded_magnitude`] on the same
1246/// canonical-form-drift axis at the codec layer: those three
1247/// predicates close the whitespace and leading-zero-padding drift
1248/// classes (paste-from-shell-history / paste-from-typography /
1249/// paste-from-fixed-width-alignment / paste-from-columnar-report),
1250/// this one closes the leading-sign / fractional / decimal /
1251/// exponent-shape drift class (paste-from-signed-report /
1252/// paste-from-floating-point-source / paste-from-scientific-notation).
1253/// Same "single lifted source of truth" discipline: drift between any
1254/// two codec sites' digit-only rejection set becomes a single-edit
1255/// fix at this predicate rather than five independent
1256/// `!<var>.is_empty() && <var>.bytes().all(|b| b.is_ascii_digit())`
1257/// scans diverging over time. Peer of [`is_dns_1123_label`] /
1258/// [`is_gateway_api_http_path`] / [`is_git_repo_url`] — same
1259/// "typed-slot's valid set matches its codec's accepted set,
1260/// structurally" discipline carried at the codec layer.
1261#[must_use]
1262pub fn is_digit_only_magnitude(s: &str) -> bool {
1263 !s.is_empty() && s.bytes().all(|b| b.is_ascii_digit())
1264}
1265
1266/// K8s DNS-1123 label rule's max length, in bytes — the floor each
1267/// apiserver-side schema enforces independently on every `metadata.name`
1268/// / Service name / label value axis a validated identifier lands in.
1269///
1270/// Per-axis breakdown of why 63 is the strictest among the rules each
1271/// validated DNS-1123-label-shaped identifier passes through:
1272///
1273/// * `:membros :caixa` lands as the rendered programs.yaml entry's
1274/// `name:` (consumed by `lareira-fleet-programs` to derive the
1275/// `wasm.pleme.io/v1alpha1/ComputeUnit.metadata.name`), as the K8s
1276/// [`Service`][svc] `metadata.name` the future `app-operator`
1277/// provisions per-member (DNS-1035 label rule:
1278/// `[a-z]([-a-z0-9]*[a-z0-9])?` max 63), as the
1279/// [`LABEL_PROGRAM`] label value (K8s label value rule:
1280/// `[a-z0-9]([-a-z0-9_.]*[a-z0-9])?` max 63), and as a component of
1281/// the composed `<aplicacao>-<de>-to-<para>` `CiliumNetworkPolicy`
1282/// `metadata.name`.
1283/// * `:placement :clusters` lands as the K8s context name keying
1284/// every per-cluster `kubeconfig`, as the `clusters[]` filter the
1285/// `lareira-fleet-programs` aggregator applies to scope programs
1286/// to their owning cluster, and as the namespace prefix /
1287/// `cluster.x-k8s.io/v1beta1/Cluster.metadata.name` cluster
1288/// identity the future M4 cross-cluster fan-out emits per entry —
1289/// all DNS-1123-label territory.
1290/// * `:children :caixa` lands as the rendered
1291/// `wasm.pleme.io/v1alpha1/ComputeUnit.metadata.name` per child the
1292/// supervisor materializes, as the [`LABEL_PROGRAM`] label value on
1293/// every emitted child's pod identity, and as the per-child
1294/// [`Service`][svc] `metadata.name` the future wasm-operator
1295/// provisions — every K8s apiserver-side schema on each landing site
1296/// enforces the same DNS-1123 label rule on admission.
1297///
1298/// Lifted to one const so a future identifier axis reaching for the
1299/// same rule (the future per-Servico `:nome` gate at the Caixa-load
1300/// boundary, the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
1301/// per-member / per-cluster validators, the future per-Aplicacao
1302/// `:nome` gate when `feira init` lands DNS-1123 enforcement on the
1303/// scaffold's `--nome` flag) reads the limit from one place.
1304///
1305/// [svc]: https://kubernetes.io/docs/concepts/services-networking/service/
1306pub const DNS_1123_LABEL_MAX_LEN: usize = 63;
1307
1308/// Predicate: assert that `s` is a valid K8s DNS-1123 label. The
1309/// contract — exactly the regex the K8s apiserver enforces on every
1310/// `metadata.name` / Service name / label value via OpenAPI v3 admission
1311/// validation, `[a-z0-9]([-a-z0-9]*[a-z0-9])?` with a 63-byte cap:
1312///
1313/// - 1..=63 bytes ([`DNS_1123_LABEL_MAX_LEN`] cap);
1314/// - lowercase ASCII alphanumeric + hyphen (`[a-z0-9-]` only; no
1315/// uppercase — K8s rejects, no underscore — DNS-1123 forbids, no
1316/// dot — a single label is not a subdomain, no Unicode/IDN — must
1317/// be pre-encoded);
1318/// - non-hyphen ASCII alphanumeric at both label boundaries
1319/// (no `-foo`, no `foo-`).
1320///
1321/// Returns the parser-shaped reason on rejection (without wrapping in
1322/// any error variant) so each per-axis caller — `validate_membro_caixa`
1323/// for `:membros :caixa`, `validate_placement_cluster` for
1324/// `:placement :clusters`, `validate_child_caixa` for `:children :caixa`,
1325/// every future per-axis lift (the per-Servico `:nome` gate at the
1326/// Caixa-load boundary, the M4 CR materializer's per-member /
1327/// per-cluster validators) — wraps the same reason in its own typed
1328/// `*Error::*Invalid { <axis>, reason }` variant. The reason wording is
1329/// axis-agnostic ("DNS-1123 labels allow only `[a-z0-9-]`") so every
1330/// call site reading the same diagnostic points at the same rule —
1331/// drift between any two axes' rule enforcement is a build error
1332/// visible at this predicate, not a per-renderer "this passed validate
1333/// but failed admission" surprise.
1334///
1335/// Empty input is rejected at the call site (each axis has its own
1336/// narrower `*Empty` variant — [`crate::AplicacaoError::MembroCaixaEmpty`],
1337/// [`crate::AplicacaoError::PlacementClusterEmpty`],
1338/// [`crate::SupervisorError::EmptyChildName`]) before this predicate
1339/// is consulted, mirroring `validate_entrada_host`'s empty-first
1340/// cascade (c7d05ec). The predicate body re-checks empty defensively
1341/// so it can be called from any future call site without a shape-
1342/// mismatch footgun — the same "defensive re-check" discipline every
1343/// peer value-shape predicate ([`is_gateway_api_http_path`] line 730,
1344/// [`is_wit_world_ref`] line 937, [`is_nats_subject`] line 1387,
1345/// [`is_wasi_keyvalue_slot`] line 1612, [`is_git_ref_name`] line 1777)
1346/// carries. Without the defensive re-check, calling
1347/// `is_dns_1123_label("")` panics at `bytes[0]` on the empty-slice
1348/// index below (`bytes[0].is_ascii_alphanumeric()` — the boundary
1349/// arm's `s.as_bytes()[0]` access reads past the end of the empty
1350/// slice), a `panic!` far from the source caixa.lisp on any future
1351/// call site that misses the pre-check. The peer predicates all
1352/// return `Err("must not be empty")` on this input; this arm brings
1353/// `is_dns_1123_label` in line with the same defensive contract.
1354///
1355/// Lifted from `caixa-core::aplicacao` (where it was first inlined for
1356/// `:membros :caixa` in 3f9d7a0 and then reused for `:placement :clusters`
1357/// in 6cbb900) so the third axis reaching for the rule (`:children
1358/// :caixa` on the supervisor tree) lands as a thin five-line wrapper
1359/// rather than re-inlining 40 lines of regex enforcement. The
1360/// "before its third occurrence" boundary the PRIME DIRECTIVE
1361/// duplication-budget rule draws (THEORY.md §I.3.5: "the duplication
1362/// budget is zero") promotes the predicate to a typed substrate-side
1363/// primitive on the same trajectory the M2-overlay and label-selector
1364/// helpers (9e3a057, 9d09cfb, 9dbeafd, 31455a7, 07a4544) already follow.
1365///
1366/// # Errors
1367///
1368/// Returns the parser-shaped reason naming the specific violation
1369/// (length / boundary / character-class), without wrapping in any
1370/// error variant — every caller maps the same `String` into its own
1371/// typed `*Invalid { <axis>, reason }` enum variant.
1372pub fn is_dns_1123_label(s: &str) -> Result<(), String> {
1373 if s.is_empty() {
1374 return Err("must not be empty".to_string());
1375 }
1376 if s.len() > DNS_1123_LABEL_MAX_LEN {
1377 return Err(format!(
1378 "exceeds DNS-1123 label max length of {DNS_1123_LABEL_MAX_LEN} bytes \
1379 (got {} bytes; the K8s apiserver rejects longer names at admission \
1380 time on every Service / Pod / CR `metadata.name` axis)",
1381 s.len()
1382 ));
1383 }
1384 let bytes = s.as_bytes();
1385 if !bytes[0].is_ascii_alphanumeric() || !bytes[bytes.len() - 1].is_ascii_alphanumeric() {
1386 return Err("must start and end with an ASCII alphanumeric character \
1387 (no leading or trailing `-`; DNS-1123 label rule)"
1388 .to_string());
1389 }
1390 for &b in bytes {
1391 let valid = b.is_ascii_digit() || b.is_ascii_lowercase() || b == b'-';
1392 if !valid {
1393 let msg = if b.is_ascii_uppercase() {
1394 format!(
1395 "contains uppercase character {ch:?} (K8s DNS-1123 label \
1396 names are lowercase-only; use {lower:?})",
1397 ch = b as char,
1398 lower = s.to_ascii_lowercase()
1399 )
1400 } else if b == b'_' {
1401 "contains `_` (DNS-1123 labels allow only `[a-z0-9-]`; use `-` \
1402 instead)"
1403 .to_string()
1404 } else if b == b'.' {
1405 "contains `.` (a single DNS-1123 label is not a subdomain; \
1406 split into separate entries or use `-` to namespace)"
1407 .to_string()
1408 } else {
1409 format!(
1410 "contains invalid character {ch:?} (DNS-1123 labels allow \
1411 only `[a-z0-9-]`)",
1412 ch = b as char
1413 )
1414 };
1415 return Err(msg);
1416 }
1417 }
1418 Ok(())
1419}
1420
1421/// K8s Gateway API v1 `HTTPPathMatch.value` max length, in bytes —
1422/// the apiserver-side `OpenAPI` schema's `maxLength: 1024` cap. Lifted
1423/// to a typed const so a future axis reaching for the same bound (the
1424/// M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-path
1425/// validator, the future per-`HTTPRouteRule` per-path-match emission
1426/// when M4 lands per-rule overrides, the future `:politicas`-derived
1427/// per-edge HTTP path overlay's per-path validator) reads the limit
1428/// from one place. The two landed call sites — `:entrada :paths`
1429/// entries (caixa-mesh's `HTTPRoute.spec.rules[].matches[].path.value`
1430/// emission) and `:contratos :endpoint` (caixa-mesh's Cilium L7
1431/// `path:` rule emission, caixa-mesh/src/lib.rs:311) — both inherit
1432/// the same cap; drift between either landing site and the K8s CRD
1433/// schema surfaces at this one const.
1434pub const GATEWAY_API_HTTP_PATH_MAX_LEN: usize = 1024;
1435
1436/// K8s Gateway API v1 `Listener.hostname` and
1437/// `HTTPRoute.spec.hostnames[]` max length, in bytes — the apiserver-side
1438/// `OpenAPI` schema's `maxLength: 253` cap, ultimately the RFC 1035 / RFC
1439/// 1123 DNS name limit (255 wire bytes minus the trailing-dot + one length
1440/// prefix). Lifted to a typed const so a future axis reaching for the same
1441/// bound (the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
1442/// per-`:entrada :host` validator, the future per-`Certificate` SAN emitter
1443/// keying off `:entrada :host` for cert-manager, the future
1444/// multi-`:entrada` host-collision gate when M4 lands `:entrada` as a
1445/// `Vec`) reads the limit from one place. The sole landed call site — the
1446/// `:entrada :host` axis's total-length gate at
1447/// [`crate::AplicacaoSpec::validate`] via `validate_entrada_host` — reads
1448/// this constant verbatim; drift between the landing site and the K8s CRD
1449/// schema surfaces at this one const rather than a per-renderer "this
1450/// passed validate but failed admission" surprise.
1451///
1452/// Peer of [`GATEWAY_API_HTTP_PATH_MAX_LEN`] on the sibling per-route
1453/// path-value cap axis — both are apiserver-side `maxLength:` bounds on
1454/// Gateway API v1 landing sites the pleme-io substrate emits, both lift
1455/// to `caixa-core::render` so the M4 CR materializer's per-axis
1456/// validators (per-host, per-path) read from one place. Same "typed const
1457/// so the bound has exactly one source of truth" discipline every peer
1458/// upper bound in this crate carries
1459/// ([`DNS_1123_LABEL_MAX_LEN`], [`NATS_SUBJECT_MAX_LEN`],
1460/// [`WASI_KV_SLOT_MAX_LEN`], [`WIT_IDENT_MAX_LEN`],
1461/// [`crate::LIMITS_MEMORY_WASM32_MAX_BYTES`],
1462/// [`crate::POLICY_TIMEOUT_MAX`], [`crate::POLICY_RETRIES_MAX`]).
1463///
1464/// The per-label max within the hostname is [`DNS_1123_LABEL_MAX_LEN`]
1465/// (63): every `.`-separated label in a Gateway API v1 Hostname is a
1466/// DNS-1123 label under the apiserver's OpenAPI regex
1467/// `[a-z0-9]([-a-z0-9]*[a-z0-9])?`, so drift between the total-length
1468/// cap here and the per-label cap on the peer constant is impossible by
1469/// construction.
1470pub const GATEWAY_API_HOSTNAME_MAX_LEN: usize = 253;
1471
1472/// K8s Gateway API v1 `Gateway.spec.listeners[].port` — the substrate's
1473/// canonical port scalar every Aplicacao-level
1474/// [`caixa_mesh::gateway_routes`][cm] -emitted `Gateway`'s sole per-
1475/// listener HTTP-listener-port axis reads from. IANA-registered as the
1476/// well-known `http` service port (RFC 9110 §4.2.2 / RFC 3986 §3.2.3 —
1477/// the port implied by an `http://<host>/…` URL when the authority
1478/// carries no explicit `:<port>` selector), so the substrate's external
1479/// `:entrada` HTTP flow surfaces at `http://<entrada.host>/` with no
1480/// per-client port override.
1481///
1482/// Semantically distinct from [`crate::DEFAULT_SERVICO_PORT`] (8080)
1483/// on the sibling per-Servico L4 axis — that constant is the port each
1484/// in-cluster Servico's `pleme-computeunit`-emitted K8s `Service`
1485/// listens on (the destination side of every mesh flow); this constant
1486/// is the port the Aplicacao's own external Gateway listens on (the
1487/// external ingress side, K8s-Gateway-API-CRD-controller-visible).
1488/// Two axes, two lifts — a future rebrand on either axis (the
1489/// substrate moving external HTTP to `:443` under mTLS-terminated
1490/// listeners, the substrate moving in-cluster Servicos onto `:80`
1491/// once the well-known port is freed) lands on its own canonical
1492/// const without coupling either axis to the other's rebrand cycle.
1493///
1494/// Until this lift landed the value `80` lived at one production-code
1495/// call site: the `listener.insert(KUBE_KEY_PORT, …)` call at
1496/// `caixa-mesh/src/lib.rs:2588` inside
1497/// [`caixa_mesh::gateway_routes`][cm]'s per-Aplicacao `Gateway`
1498/// emitter. A future Gateway API v1 promotion moving the well-known
1499/// external HTTP listener to a substrate-chosen alternative — the
1500/// substrate moving to `:443` once cert-manager-issued
1501/// per-`:entrada :host` certificates land and the external listener
1502/// becomes HTTPS-by-default (matching the mTLS-by-default trajectory
1503/// [`crate::DEFAULT_SERVICO_PORT`]'s docstring names), a per-cluster
1504/// override the operator pins through a future `:entrada :port` slot
1505/// promoted from Servico-side (`:entrada :port` today's typed slot
1506/// names the destination Servico port, not the Gateway listener
1507/// port) — without a coordinated edit would silently emit a
1508/// `Gateway` whose per-listener HTTP-listener-port axis the K8s
1509/// Gateway API v1 controller admits at the drifted port and the
1510/// gateway-class-controller (Cilium's Envoy sidecar today) opens on
1511/// the drifted port too, so every external `:entrada` HTTP flow
1512/// drops at the first hop with no diagnostic naming the drift root
1513/// cause. Lifting the literal to a shared typed `u16` const closes
1514/// the drift footgun structurally — every consumer reads from the
1515/// same lifted constant, so any rebrand reaches every site by
1516/// construction.
1517///
1518/// Mirrors the [`crate::DEFAULT_SERVICO_PORT`] lift (a085b26) on the
1519/// peer per-renderer canonical-K8s-port-axis typed `u16` const — both
1520/// are IANA-registered service-port scalars the substrate's mesh
1521/// renderer emits under a K8s CRD's `port:` axis, both lift to
1522/// `caixa-core::render` so any future substrate-side port migration
1523/// (external HTTP `:80 → :443`, in-cluster Servico `:8080 → :80`)
1524/// lands at exactly one const per axis. Same "typed const so the
1525/// scalar has exactly one source of truth" discipline every peer
1526/// scalar in this crate carries ([`GATEWAY_API_HOSTNAME_MAX_LEN`],
1527/// [`GATEWAY_API_HTTP_PATH_MAX_LEN`], [`DNS_1123_LABEL_MAX_LEN`],
1528/// [`WIT_IDENT_MAX_LEN`]).
1529///
1530/// [cm]: ../../caixa_mesh/fn.gateway_routes.html
1531pub const GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT: u16 = 80;
1532
1533/// K8s Gateway API v1 `Gateway.spec.listeners[].name` — the substrate's
1534/// canonical author-chosen listener-name scalar every Aplicacao-level
1535/// [`caixa_mesh::gateway_routes`][cm] -emitted `Gateway`'s sole per-
1536/// listener name-discriminator axis reads from. Gateway API v1's
1537/// `Listener.name` is `SectionName`-typed (a required DNS-1123 label
1538/// unique within the parent Gateway's listener list — see the upstream
1539/// docs at
1540/// <https://gateway-api.sigs.k8s.io/api-types/gateway/#listeners> and
1541/// the type reference at
1542/// <https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.SectionName>);
1543/// downstream `HTTPRoute.spec.parentRefs[].sectionName` selectors bind
1544/// to this exact byte-string when the author wants to attach a route
1545/// to one specific listener out of a multi-listener Gateway. The V0
1546/// substrate emits exactly one HTTP listener per Aplicacao, so the
1547/// name is arbitrary from the CRD's perspective — the substrate picks
1548/// the byte-string `"http"` as the canonical short name (matching the
1549/// listener's protocol axis [`GATEWAY_API_PROTOCOL_HTTP`] in kind, but
1550/// not in bytes: this is the lowercase-ASCII listener-name identifier,
1551/// the sibling protocol scalar is the uppercase-ASCII
1552/// `ProtocolType` enum value the Gateway API v1 CRD schema pins).
1553///
1554/// Semantically distinct from every peer `"http"`-shaped byte-string
1555/// in the substrate:
1556///
1557/// - [`crate::GATEWAY_API_PROTOCOL_HTTP`] (`"HTTP"`) — the listener's
1558/// `spec.listeners[].protocol` `ProtocolType` enum value the
1559/// Gateway API v1 CRD schema pins to the uppercase-ASCII spelling;
1560/// this constant names the arbitrary author-chosen listener-name
1561/// identifier at the sibling `spec.listeners[].name` axis instead,
1562/// and the two carry different case shapes on purpose;
1563/// - [`crate::CILIUM_KEY_HTTP`] (`"http"`) — the Cilium CRD's per-
1564/// `toPorts[]` L7-HTTP-rule-list-discriminator container-axis key
1565/// (`spec.ingress[].toPorts[].rules.http`), a CRD-schema-pinned
1566/// field name the Cilium project's per-CRD-schema-migration cycle
1567/// controls; this constant names an Aplicacao-side arbitrary
1568/// listener-name at a distinct K8s Gateway API CRD path, and the
1569/// substrate can move it without touching the Cilium schema.
1570///
1571/// Byte-identical to [`CILIUM_KEY_HTTP`] today (both spell out the
1572/// four ASCII bytes `h`, `t`, `t`, `p`), but the two lifted axes name
1573/// semantically distinct surfaces — a future substrate-side listener-
1574/// name rebrand (say, `"http" → "http-v1"` once the Aplicacao renders
1575/// multiple listeners under the HTTPS-by-default trajectory) must
1576/// reach this consumer without dragging the Cilium schema key with it.
1577///
1578/// Until this lift landed the value `"http"` lived at one production-
1579/// code call site: the `listener.insert(GATEWAY_API_KEY_NAME, "http")`
1580/// call inside [`caixa_mesh::gateway_routes`][cm]'s per-Aplicacao
1581/// `Gateway` emitter. A future Gateway API v2 rebrand of the well-
1582/// known short listener-name (a substrate-side migration to a longer
1583/// discriminator once multi-listener Gateways ship, an operator-pinned
1584/// override the future `:entrada :listener-name` slot promotes) —
1585/// without a coordinated edit — would silently emit a `Gateway`
1586/// whose listener carries the drifted identifier, so every downstream
1587/// `HTTPRoute` `sectionName` selector authored against the substrate's
1588/// prior canonical name misses its listener, and every external
1589/// `:entrada` HTTP flow drops at attachment time with no diagnostic
1590/// naming the listener-name drift root cause. Lifting the literal to
1591/// a shared typed `&'static str` const closes the drift footgun
1592/// structurally — every consumer reads from the same lifted constant,
1593/// so any rebrand reaches every site by construction.
1594///
1595/// Mirrors the [`GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT`] lift
1596/// (cd60fde) on the peer per-listener HTTP-listener-port scalar-axis —
1597/// both are Aplicacao-side substrate-canonical scalar-value pins the
1598/// sole per-Aplicacao `Gateway` emitter reaches for, and both lift to
1599/// `caixa-core::render` so a future substrate-side rebrand on either
1600/// listener axis (`:port` → `:443`, `:name` → `"http-v1"`) lands at
1601/// exactly one const per axis. Same "typed const so the scalar has
1602/// exactly one source of truth" discipline every peer scalar in this
1603/// crate carries ([`DEFAULT_GATEWAY_CLASS_NAME`],
1604/// [`GATEWAY_API_PROTOCOL_HTTP`],
1605/// [`GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX`]).
1606///
1607/// [cm]: ../../caixa_mesh/fn.gateway_routes.html
1608pub const GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME: &str = "http";
1609
1610/// K8s Gateway API v1 `HTTPRoute.spec.rules[].matches[].path.value`
1611/// substrate-side catch-all path — the fallback URL path every
1612/// Aplicacao-level [`caixa_mesh::gateway_routes`][cm] -emitted
1613/// `HTTPRoute` renders when the typed `:entrada :paths` slot is
1614/// empty, so an author who declares an external `:entrada` but no
1615/// per-path rule surface still gets a route whose sole
1616/// `HTTPPathMatch` matches every incoming request under the
1617/// paired [`GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX`] discriminator.
1618/// K8s Gateway API v1's `PathPrefix` matcher over the bare-root
1619/// `"/"` is the canonical catch-all shape — the upstream docs at
1620/// <https://gateway-api.sigs.k8s.io/api-types/httproute/#path-based-routing>
1621/// pin the `PathPrefix "/"` combination as the "match anything the
1622/// listener admits" idiom every gateway-class controller (Cilium's
1623/// Envoy today, Envoy Gateway / Istio Gateway on the peer
1624/// controllers) treats as the equivalent of "no path predicate"
1625/// under the CRD schema.
1626///
1627/// Until this lift landed the value `"/"` lived at one production-
1628/// code call site: the `vec!["/"]` fallback arm inside
1629/// [`caixa_mesh::gateway_routes`][cm]'s `let paths: Vec<&str> =
1630/// if entrada.paths.is_empty() { vec!["/"] } else { … }` branch, the
1631/// sole per-Aplicacao HTTPRoute per-rule path-list resolver that
1632/// surfaces the catch-all URL path whenever the typed `:entrada
1633/// :paths` list is empty. A future substrate-side rebrand of the
1634/// catch-all shape — a hypothetical migration to Gateway API v2's
1635/// `Exact ""` idiom, an operator-pinned per-Aplicacao override the
1636/// future `:entrada :default-path` slot promotes, a per-controller
1637/// variant that treats `"/"` as a literal prefix rather than the
1638/// catch-all — without a coordinated edit would silently emit an
1639/// `HTTPRoute` whose sole path-match predicate rejects every
1640/// incoming request at the drifted shape, so every external
1641/// `:entrada` HTTP flow drops at the first hop with no diagnostic
1642/// naming the catch-all-path drift root cause. Lifting the literal
1643/// to a shared typed `&'static str` const closes the drift footgun
1644/// structurally — every consumer reads from the same lifted constant,
1645/// so any rebrand reaches every site by construction.
1646///
1647/// Semantically distinct from every peer HTTP-path byte-string in the
1648/// substrate. The typed [`Entrada::paths`] admission grammar
1649/// ([`is_gateway_api_http_path`] + [`GATEWAY_API_HTTP_PATH_MAX_LEN`])
1650/// admits the bare-root `"/"` at the author's slot; this constant
1651/// names the substrate's *emit-side* choice for the same byte-string
1652/// at the *no-author-input* path — the two axes carry the identical
1653/// shape today by design (the substrate's catch-all round-trips
1654/// through the same admission grammar the author's explicit `"/"`
1655/// would clear), and the paired
1656/// [`gateway_api_default_http_route_path_carries_valid_gateway_api_http_path_shape`]
1657/// cross-axis pin closes the invariant at build time.
1658///
1659/// Mirrors the [`GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME`] (a12dcdd) /
1660/// [`GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT`] (cd60fde) lifts on the
1661/// peer per-listener substrate-canonical scalar-value axes — all
1662/// three are Aplicacao-side substrate-canonical scalar-value pins the
1663/// sole per-Aplicacao mesh emitter reaches for at a K8s Gateway API
1664/// v1 CRD sub-path, and all three lift to `caixa-core::render` so a
1665/// future substrate-side rebrand on any one axis lands at exactly one
1666/// const per axis. Same "typed const so the scalar has exactly one
1667/// source of truth" discipline every peer scalar in this crate
1668/// carries ([`DEFAULT_GATEWAY_CLASS_NAME`],
1669/// [`GATEWAY_API_PROTOCOL_HTTP`],
1670/// [`GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX`]).
1671///
1672/// [cm]: ../../caixa_mesh/fn.gateway_routes.html
1673pub const GATEWAY_API_DEFAULT_HTTP_ROUTE_PATH: &str = "/";
1674
1675/// Predicate: assert that `path` is a valid HTTP path under both the
1676/// K8s Gateway API v1 `HTTPPathMatch.value` admission grammar AND the
1677/// Cilium L7 `path:` rule grammar — the two landing sites every
1678/// validated pleme-io HTTP-shaped path lands in. The contract:
1679///
1680/// - 1..=[`GATEWAY_API_HTTP_PATH_MAX_LEN`] (1024) bytes;
1681/// - leading `/` (the `PathPrefix` invariant — pre-checked at the
1682/// call site by each axis's narrower `*NotAbsolute` variant;
1683/// re-checked here so the predicate is usable from any future
1684/// call site without a shape-mismatch footgun);
1685/// - no consecutive `/` characters (HTTP path matchers reject
1686/// `//` — collapse to a single `/`);
1687/// - no `/./` or `/../` segments (and no trailing `/.` or `/..`) —
1688/// path-traversal and no-op segments are rejected outright;
1689/// - no `?` (query separator: queries are matched separately via
1690/// `HTTPRoute` `queryParams`, never in the path);
1691/// - no `#` (fragment separator: fragments are client-side and
1692/// never reach the gateway);
1693/// - no whitespace (space, tab — must be percent-encoded as `%20`);
1694/// - no ASCII control characters (`0x00..0x1F`, `0x7F`);
1695/// - no non-ASCII bytes (`>= 0x80`) — RFC 3986 requires `%XX`
1696/// percent-encoding for anything outside the ASCII unreserved +
1697/// reserved set;
1698/// - no printable-ASCII byte outside the K8s Gateway API
1699/// `HTTPPathMatch.value` apiserver-side `OpenAPI` regex
1700/// `^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$`
1701/// accepted set — namely `"` `<` `>` `[` `\` `]` `^` `` ` `` `{`
1702/// `|` `}`. These eleven bytes are printable ASCII but RFC 3986's
1703/// `pchar = unreserved / pct-encoded / sub-delims / ":" / "@"`
1704/// grammar excludes them, so the apiserver rejects them at
1705/// admission time on every `HTTPRoute.spec.rules[].matches[].
1706/// path.value` landing site and the Cilium L7 path matcher
1707/// refuses them too. Percent-encode (`%XX`) if the literal byte
1708/// is intended.
1709///
1710/// Returns the parser-shaped reason on rejection (without wrapping in
1711/// any error variant) so each per-axis caller — `validate_entrada_path`
1712/// for `:entrada :paths` entries, `WitContract::target` for the HTTP-
1713/// shaped `:contratos :endpoint` axis, every future per-path lift
1714/// (the M4 CR materializer's per-path validator, the future
1715/// per-`HTTPRouteRule` per-path-match emission) — wraps the same
1716/// reason in its own typed `*Invalid { <axis>, reason }` variant. The
1717/// reason wording is axis-agnostic ("HTTP path matchers reject
1718/// `//`") so every call site reading the same diagnostic points at
1719/// the same rule; drift between any two axes' rule enforcement is a
1720/// build error visible at this predicate, not a per-renderer "this
1721/// passed validate but failed admission" surprise.
1722///
1723/// Empty input is rejected at the call site (each axis has its own
1724/// narrower `*Empty` variant — [`crate::AplicacaoError::EntradaPathEmpty`],
1725/// [`crate::AplicacaoError::ContratoEndpointEmpty`]) before this
1726/// predicate is consulted, mirroring `is_dns_1123_label`'s empty-first
1727/// cascade. The predicate body re-checks empty + leading-`/`
1728/// defensively so it can be called from any future call site without
1729/// a shape-mismatch footgun.
1730///
1731/// Lifted from `caixa-core::aplicacao::validate_entrada_path` (where
1732/// it was first inlined for `:entrada :paths` in 55410e4) at the
1733/// second occurrence of the HTTP-path-grammar — the `:contratos
1734/// :endpoint` axis (c4213a4 gated non-empty + leading-`/` only,
1735/// silently passing the same authoring footguns the `:entrada :paths`
1736/// gate catches) — so the second axis lands as a thin three-line
1737/// wrapper at the per-axis call site rather than re-inlining 90 lines
1738/// of grammar enforcement. Same compounding shape as
1739/// `is_dns_1123_label` (lifted at its third occurrence in 31bfa43)
1740/// and the M2-overlay / label-selector helpers (9e3a057, 9d09cfb,
1741/// 9dbeafd, 31455a7, 07a4544) on the render side — each lifted a
1742/// recurring shape into a typed primitive at the threshold where the
1743/// duplication budget would otherwise have been exceeded.
1744///
1745/// # Errors
1746///
1747/// Returns the parser-shaped reason naming the specific violation
1748/// (length / character-class / segment / consecutive-slash), without
1749/// wrapping in any error variant — every caller maps the same
1750/// `String` into its own typed `*Invalid { <axis>, reason }` enum
1751/// variant.
1752pub fn is_gateway_api_http_path(path: &str) -> Result<(), String> {
1753 if path.is_empty() {
1754 return Err("must not be empty".to_string());
1755 }
1756 if !path.starts_with('/') {
1757 return Err("must start with `/` (HTTP path matchers require a leading `/`)".to_string());
1758 }
1759 if path.len() > GATEWAY_API_HTTP_PATH_MAX_LEN {
1760 return Err(format!(
1761 "exceeds HTTP path max length of {GATEWAY_API_HTTP_PATH_MAX_LEN} bytes \
1762 (got {} bytes; both the K8s Gateway API HTTPPathMatch.value OpenAPI \
1763 schema and the Cilium L7 path matcher reject longer values at \
1764 admission time)",
1765 path.len()
1766 ));
1767 }
1768 for &b in path.as_bytes() {
1769 let reason = if b == b'?' {
1770 Some(
1771 "must not contain `?` (queries are matched separately via HTTPRoute \
1772 `queryParams`, not in the path; drop the `?…` suffix)"
1773 .to_string(),
1774 )
1775 } else if b == b'#' {
1776 Some(
1777 "must not contain `#` (fragments are client-side and never reach \
1778 the gateway; drop the `#…` suffix)"
1779 .to_string(),
1780 )
1781 } else if b == b' ' || b == b'\t' {
1782 Some(format!(
1783 "must not contain whitespace character {ch:?} (percent-encode as `%20` \
1784 or use `-`/`_` instead)",
1785 ch = b as char
1786 ))
1787 } else if b < 0x20 || b == 0x7F {
1788 Some(format!(
1789 "must not contain control character 0x{b:02x} (HTTP path characters \
1790 must be printable ASCII; the K8s Gateway API HTTPPathMatch.value and \
1791 Cilium L7 path matcher both reject control characters at admission \
1792 time)"
1793 ))
1794 } else if b >= 0x80 {
1795 Some(format!(
1796 "must not contain non-ASCII byte 0x{b:02x} (RFC 3986 requires \
1797 percent-encoding `%XX` for characters outside the ASCII unreserved \
1798 + reserved set)"
1799 ))
1800 } else if matches!(
1801 b,
1802 b'"' | b'<' | b'>' | b'[' | b'\\' | b']' | b'^' | b'`' | b'{' | b'|' | b'}'
1803 ) {
1804 // The eleven printable-ASCII bytes outside the K8s Gateway
1805 // API HTTPPathMatch.value apiserver-side OpenAPI regex
1806 // accepted set. RFC 3986 §3.3 `pchar = unreserved /
1807 // pct-encoded / sub-delims / ":" / "@"` excludes them from
1808 // every path-segment, so the apiserver rejects them at
1809 // admission time on every
1810 // `HTTPRoute.spec.rules[].matches[].path.value` landing site
1811 // (and the Cilium L7 path matcher follows the same grammar).
1812 // Until this gate landed `validate` only refused `?`, `#`,
1813 // whitespace, control characters, and non-ASCII bytes; the
1814 // canonical author-side "I wrote a path-template variable"
1815 // / "I copied an OpenAPI route" footguns silently passed
1816 // (`/api/cart/{id}` — Gateway API uses `:foo` for path
1817 // parameters, not `{foo}`; `/api/cart[0]` — index-bracket
1818 // shape; `/api/<placeholder>` — angle-bracket placeholder;
1819 // `/api\path` — Windows path-separator typo; `/api/^foo` —
1820 // accidental shell-regex character) and the failure surfaced
1821 // at apply time as a Gateway API webhook rejection naming
1822 // the offending byte but not the offending caixa.lisp slot.
1823 // Lifting the rejection to caixa-build time makes the
1824 // canonical Gateway API HTTPPathMatch.value accepted set a
1825 // structural property of every validated `:entrada :paths`
1826 // entry and every typed-HTTP `:contratos :endpoint` payload,
1827 // mirroring the c7d05ec / 55410e4 / 4f0390b trajectory each
1828 // brought the per-axis accepted set to match the apiserver
1829 // accepted set verbatim.
1830 Some(format!(
1831 "must not contain reserved character {ch:?} (RFC 3986 \
1832 path-segment grammar — and the K8s Gateway API \
1833 HTTPPathMatch.value apiserver-side OpenAPI regex \
1834 `^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{{2}})+$` — \
1835 exclude this byte from the `pchar = unreserved / pct-encoded \
1836 / sub-delims / \":\" / \"@\"` set; percent-encode as \
1837 `%{b:02X}` if the literal character is intended)",
1838 ch = b as char
1839 ))
1840 } else {
1841 None
1842 };
1843 if let Some(r) = reason {
1844 return Err(r);
1845 }
1846 }
1847 if path.contains("//") {
1848 return Err(
1849 "must not contain consecutive `/` characters (HTTP path matchers reject \
1850 `//`; collapse to a single `/`)"
1851 .to_string(),
1852 );
1853 }
1854 if path.contains("/./") || path == "/." || path.ends_with("/.") {
1855 return Err(
1856 "must not contain the `.` segment (`/./` or trailing `/.`); it is \
1857 semantically a no-op and HTTP path matchers reject it"
1858 .to_string(),
1859 );
1860 }
1861 if path.contains("/../") || path == "/.." || path.ends_with("/..") {
1862 return Err(
1863 "must not contain the `..` parent-segment (`/../` or trailing `/..`); \
1864 path traversal is rejected by HTTP path matchers"
1865 .to_string(),
1866 );
1867 }
1868 Ok(())
1869}
1870
1871/// Max length, in bytes, of a single typed `:contratos :wit` world
1872/// reference passing the [`is_wit_world_ref`] predicate. 128 bytes —
1873/// roughly 8× the longest real-world WIT reference the caixa-mesh test
1874/// fixtures carry (`wasi:keyvalue/store` = 19 bytes) and the WIT registry
1875/// references its peers under (`wasi:http/proxy@0.2.0` = 21 bytes), so
1876/// the cap exists to reject the paste-from-binary footgun (a multi-line
1877/// blob accidentally landed in the `:wit` slot) rather than to constrain
1878/// legitimate authoring. Lifted as a typed const so a future axis
1879/// reaching for the same bound (the M4 per-edge WIT registry resolver,
1880/// the future `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
1881/// per-contract WIT validator) reads from one place.
1882pub const WIT_IDENT_MAX_LEN: usize = 128;
1883
1884/// Predicate: assert that `s` is a valid WIT (WebAssembly Component
1885/// Model) world reference — the canonical shape every typed
1886/// `:contratos :wit` value carries. The contract — modeled on the
1887/// [WIT IDL grammar][wit] (`namespace:package(/interface)*(@version)?`)
1888/// restricted to the lowercase subset the pleme-io substrate dispatches
1889/// on:
1890///
1891/// - 1..=[`WIT_IDENT_MAX_LEN`] (128) bytes;
1892/// - no whitespace, no control characters, no non-ASCII bytes;
1893/// - exactly one `:` separator splitting the namespace from the
1894/// package — `wasi:http/proxy`, `nats:pub-sub`, `wasi:keyvalue/store`
1895/// (no `:` = there's no namespace to dispatch on; multiple `:` =
1896/// the package half can't parse);
1897/// - an optional `/`-separated interface suffix (one or more
1898/// segments — the WIT grammar allows `('/' id)+` after the package);
1899/// - an optional `@<version>` suffix (one trailing `@` only; the
1900/// version body is a structurally valid SemVer 2.0.0 version —
1901/// non-empty, restricted to the accepted set `[0-9A-Za-z.\-+]`, AND
1902/// round-trippable through [`semver::Version::parse`]: three-part
1903/// `major.minor.patch` numeric core mandatory (two-part `1.0` and
1904/// four-part `1.0.0.0` reject), no leading zeros in numeric
1905/// identifiers (`01.0.0` rejects), no empty pre-release / build-
1906/// metadata identifiers (`1.0.0-` and `1.0.0-.rc1` reject); the WIT
1907/// IDL binds `simple-version` to SemVer verbatim so every byte-set-
1908/// valid but shape-invalid version body fails the upstream WIT
1909/// parser at consume time);
1910/// - every identifier segment (namespace, package, each interface)
1911/// is a lowercase kebab-case ASCII identifier: `[a-z]([a-z0-9]|-)*`,
1912/// starting with a lowercase letter, no consecutive `-`, no
1913/// trailing `-`.
1914///
1915/// Lowercase-only is deliberate — the substrate's
1916/// [`crate::aplicacao::WitContract::is_http`] / `is_pubsub` / `is_store`
1917/// dispatch keys off the lowercase canonical prefix (`wasi:http/`,
1918/// `nats:`, `wasi:keyvalue/`, `kafka:`, `kv:`, `http:`). An uppercase
1919/// `WASI:HTTP/proxy` is structurally a valid WIT identifier under the
1920/// upstream IDL grammar but silently falls through every `is_*` arm and
1921/// renders as a capability-only L4-only edge — the canonical "I thought
1922/// I had L7 HTTP routing, got L4-only" footgun. Lifting the lowercase
1923/// rule to caixa-build time makes the dispatch reachable-by-construction:
1924/// every validated `:wit` value matches exactly one of the three typed
1925/// dispatch arms (or the explicit capability arm), structurally.
1926///
1927/// Returns the parser-shaped reason on rejection (without wrapping in
1928/// any error variant) so each per-axis caller — `WitContract::target`
1929/// for the `:contratos :wit` axis at validate time, the future M4 CR
1930/// materializer's per-contract WIT validator, the future per-edge WIT
1931/// registry resolver — wraps the same reason in its own typed
1932/// `*Invalid { <axis>, reason }` variant. The reason wording is
1933/// axis-agnostic ("WIT identifiers allow only `[a-z0-9-]`") so every
1934/// call site reading the same diagnostic points at the same rule;
1935/// drift between any two axes' rule enforcement is a build error
1936/// visible at this predicate, not a per-renderer "this passed validate
1937/// but silently demoted to capability-only" surprise.
1938///
1939/// Empty input is rejected here (defensively) and at the call site via
1940/// the narrower [`crate::AplicacaoError::EmptyWit`] variant — the same
1941/// empty-first cascade [`is_dns_1123_label`] and
1942/// [`is_gateway_api_http_path`] carry.
1943///
1944/// Lifted as a typed substrate-side primitive on the same trajectory
1945/// the M2-overlay and label-selector helpers (9e3a057, 9d09cfb, 9dbeafd,
1946/// 31455a7, 07a4544) and the value-shape predicates (`is_dns_1123_label`,
1947/// `is_gateway_api_http_path`) already follow — the typed slot's valid
1948/// set matches its dispatch's accepted set, structurally.
1949///
1950/// [wit]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md
1951///
1952/// # Errors
1953///
1954/// Returns the parser-shaped reason naming the specific violation
1955/// (length / separator / character-class / kebab-shape / SemVer 2.0.0
1956/// structural invariant), without wrapping in any error variant —
1957/// every caller maps the same `String` into its own typed `*Invalid
1958/// { <axis>, reason }` enum variant.
1959pub fn is_wit_world_ref(s: &str) -> Result<(), String> {
1960 if s.is_empty() {
1961 return Err("must not be empty".to_string());
1962 }
1963 if s.len() > WIT_IDENT_MAX_LEN {
1964 return Err(format!(
1965 "exceeds WIT world-reference max length of {WIT_IDENT_MAX_LEN} bytes \
1966 (got {} bytes; legitimate WIT references rarely exceed ~32 bytes — \
1967 this length suggests a paste-from-binary or multi-line blob landed \
1968 in the `:wit` slot)",
1969 s.len()
1970 ));
1971 }
1972 for &b in s.as_bytes() {
1973 if b.is_ascii_whitespace() {
1974 return Err(format!(
1975 "must not contain whitespace character {ch:?} (WIT world references \
1976 are single tokens with no whitespace between identifier segments)",
1977 ch = b as char
1978 ));
1979 }
1980 if b < 0x20 || b == 0x7F {
1981 return Err(format!(
1982 "must not contain control character 0x{b:02x} (WIT world references \
1983 are printable ASCII tokens)"
1984 ));
1985 }
1986 if b >= 0x80 {
1987 return Err(format!(
1988 "must not contain non-ASCII byte 0x{b:02x} (WIT world references \
1989 are restricted to ASCII identifiers + the `:` / `/` / `@` / `-` \
1990 separators)"
1991 ));
1992 }
1993 }
1994 // Split off the optional `@<version>` suffix first so the
1995 // namespace/package parse below operates on a clean
1996 // `<ns>:<pkg>(/<iface>)*` head.
1997 let (head, version) = match s.split_once('@') {
1998 Some((h, v)) => (h, Some(v)),
1999 None => (s, None),
2000 };
2001 if let Some(ver) = version {
2002 if ver.is_empty() {
2003 return Err(
2004 "trailing `@` must be followed by a version (e.g. `@0.2.0`); drop \
2005 the trailing `@` to omit the version pin"
2006 .to_string(),
2007 );
2008 }
2009 if ver.contains('@') {
2010 return Err(
2011 "must contain at most one `@` separator (the optional version suffix \
2012 is `@<version>`, not `@<ver>@<ver>`)"
2013 .to_string(),
2014 );
2015 }
2016 if ver.contains(':') || ver.contains('/') {
2017 return Err(format!(
2018 "version suffix {ver:?} must not contain `:` or `/` (those separators \
2019 are reserved for the namespace and interface axes; the version body \
2020 is opaque)"
2021 ));
2022 }
2023 // Byte-set gate on the `@<version>` body: SemVer 2.0.0 restricts
2024 // every legal version to the accepted set
2025 // `[0-9A-Za-z.\-+]` — the digit + letter alphabet for the
2026 // `major.minor.patch` numeric core, the `.` segment separator,
2027 // and the `-` / `+` sigils that introduce the optional
2028 // pre-release and build-metadata suffixes. The WIT IDL binds
2029 // `simple-version` to SemVer verbatim (WebAssembly Component
2030 // Model design doc `WIT.md#versions` — `version` is parsed
2031 // through the `semver` crate), so any printable-ASCII byte
2032 // outside that set is guaranteed to fail the upstream WIT
2033 // parser at consume time. Until this gate landed the outer
2034 // whitespace / control / non-ASCII loop above rejected the
2035 // whitespace + control + non-ASCII slices of the byte axis
2036 // and the narrower `contains('@')` / `contains(':' | '/')`
2037 // arms above closed the WIT-reserved separator bytes, but
2038 // every other printable-ASCII byte (`?`, `#`, `!`, `$`, `%`,
2039 // `&`, `'`, `"`, `(`, `)`, `*`, `,`, `;`, `<`, `=`, `>`, `[`,
2040 // `\`, `]`, `^`, `` ` ``, `{`, `|`, `}`, `~`) silently rode
2041 // through — the canonical author-side footguns
2042 // (`wasi:http/proxy@0.2.0?rc1` — URL-query-separator paste
2043 // where the author copied a versioned link and the trailing
2044 // `?ref=…` came along; `wasi:http/proxy@0.2.0#build` —
2045 // URL-fragment paste; `wasi:http/proxy@0.2.0 alpha` — the
2046 // outer whitespace loop already catches this, but before that
2047 // loop landed the space rode through too; `wasi:http/proxy@
2048 // 0.2.0!alpha` — accidental history-expansion `!`;
2049 // `wasi:http/proxy@0.2.0(rc1)` — parenthetical annotation
2050 // from a doc comment) all passed `validate` and failed at
2051 // WIT-parse time far from the source caixa.lisp with a
2052 // parser diagnostic that names the offending byte but not
2053 // the offending `:contratos :wit` slot. Lifting the rejection
2054 // to caixa-build time closes the byte-set axis structurally
2055 // — every validated `@<version>` body matches the SemVer
2056 // 2.0.0 accepted set, and drift between the typed slot's
2057 // accepted set and the upstream WIT parser's accepted set is
2058 // impossible-by-construction.
2059 //
2060 // Same top-and-bottom-edge discipline the peer axes carry —
2061 // [`is_gateway_api_http_path`]'s eleven-byte RFC-3986-reserved
2062 // rejection set for `:entrada :paths` / `:contratos :endpoint`,
2063 // [`is_nats_subject`]'s strict `[A-Za-z0-9_-]` per-token
2064 // character set for `:contratos :subject`,
2065 // [`is_wit_kebab_id`]'s lowercase-kebab enforcement for the
2066 // WIT namespace/package/interface segments — the typed slot's
2067 // valid set matches the downstream parser's accepted set,
2068 // structurally.
2069 for &b in ver.as_bytes() {
2070 let valid = b.is_ascii_alphanumeric() || b == b'.' || b == b'-' || b == b'+';
2071 if !valid {
2072 return Err(format!(
2073 "version suffix {ver:?} contains invalid character {ch:?} \
2074 (SemVer 2.0.0 restricts the `@<version>` body to the accepted \
2075 set `[0-9A-Za-z.\\-+]` — digits + letters for the \
2076 `major.minor.patch` numeric core, `.` for segment separators, \
2077 `-` for the pre-release suffix, `+` for the build-metadata \
2078 suffix; every other byte fails the upstream WIT parser at \
2079 consume time)",
2080 ch = b as char
2081 ));
2082 }
2083 }
2084 // Structural SemVer 2.0.0 parse on the `@<version>` body: every
2085 // byte-set-valid version body (`[0-9A-Za-z.\-+]`, the accepted-
2086 // set arm above) is not necessarily a *structurally* valid
2087 // SemVer version. SemVer 2.0.0 imposes shape rules on top of the
2088 // byte set — three-part `major.minor.patch` mandatory (two-part
2089 // `1.0` and four-part `1.0.0.0` reject), no leading zeros in
2090 // numeric identifiers (`01.0.0` rejects, `10.0.0` accepts,
2091 // `1.0.0-01` rejects while `1.0.0-alpha01` accepts because the
2092 // pre-release identifier is alphanumeric not numeric), no empty
2093 // identifiers (`1.0.0-` and `1.0.0+` reject; `1.0.0-.rc1` and
2094 // `1.0.0-alpha..beta` reject; `1.0.0+.abc` and
2095 // `1.0.0+build..42` reject). Until this gate landed the byte-set
2096 // arm above closed only the per-byte accepted set, and every
2097 // *shape*-invalid version body — the canonical author-side
2098 // paste footguns (`wasi:http/proxy@1.0` two-part-numeric-core
2099 // paste from a Node.js `"engines"` field, `wasi:http/proxy@1`
2100 // one-part paste from a Docker `:v1` tag, `wasi:http/proxy@v0.2.0`
2101 // `v`-prefixed git-tag paste that strayed into the version body,
2102 // `wasi:http/proxy@01.0.0` mistaken zero-padded major from a
2103 // date-based version scheme, `wasi:http/proxy@1.0.0.0` four-part
2104 // paste from a Microsoft / Java build-number convention,
2105 // `wasi:http/proxy@1.0.0-` half-typed pre-release the author
2106 // started and left dangling, `wasi:http/proxy@1.0.0+` peer for
2107 // build-metadata) rode through the byte-set gate and failed at
2108 // WIT-parse time (the WIT IDL's `simple-version` binds through
2109 // the `semver` crate at consume time — see WebAssembly Component
2110 // Model design doc `WIT.md#versions`, and both the M4
2111 // `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-
2112 // contract WIT validator (MESH-COMPOSITION §III.2 #5) and the
2113 // future per-edge WIT registry resolver strict-parse the
2114 // `@<version>` body through the same crate). Failure surfaced
2115 // far from the source `caixa.lisp` with a bare `semver::Error`
2116 // that names the specific structural violation but not the
2117 // offending `:contratos :wit` slot. Lifting the parse to caixa-
2118 // build time closes the structural axis: every validated
2119 // `@<version>` body past this call is byte-for-byte round-
2120 // trippable through [`semver::Version::parse`] without re-
2121 // checking at any downstream WIT-consumer layer.
2122 //
2123 // Thin wrapper around [`semver::Version::parse`] — the same
2124 // parser [`crate::Caixa::validate_versao`] (the peer top-level
2125 // `:versao` axis) and [`crate::CaixaVersion::parse`] consume,
2126 // so the accepted set is structurally identical across every
2127 // `:versao`-shaped axis the substrate carries. Maps the
2128 // `semver::Error` reason verbatim into a self-locating
2129 // diagnostic naming the offending version body + the SemVer
2130 // 2.0.0 canonical shape the author intended, so the failure
2131 // is grep-locatable in the `caixa.lisp` (search for
2132 // `:wit "…@<value>"`) and fixable in one edit. Same top-and-
2133 // bottom-edge discipline the peer typed-codec axes carry —
2134 // every typed slot whose accepted set the substrate reads is
2135 // strict-parsed against the downstream consumer's canonical
2136 // parser at build time, not at apply time.
2137 if let Err(e) = semver::Version::parse(ver) {
2138 return Err(format!(
2139 "version suffix {ver:?} is not a structurally valid SemVer 2.0.0 \
2140 version: {e} (the WIT IDL binds `@<version>` to SemVer 2.0.0 \
2141 verbatim — three-part `major.minor.patch` numeric core, no \
2142 leading zeros in numeric identifiers, no empty pre-release / \
2143 build-metadata identifiers; every other shape fails the \
2144 upstream WIT parser at consume time)"
2145 ));
2146 }
2147 }
2148 // Then split the head on `:` — exactly one separator, splitting the
2149 // namespace from the package(/interface) body.
2150 let Some((ns, rest)) = head.split_once(':') else {
2151 return Err(format!(
2152 "must contain a `:` separating the namespace from the package (e.g. \
2153 `wasi:http/proxy`); got {s:?} with no `:` — pleme-io dispatches `:wit` \
2154 values on the canonical `<namespace>:<package>` shape and silently \
2155 demotes unmatched shapes to a capability-only L4 edge"
2156 ));
2157 };
2158 if rest.contains(':') {
2159 return Err(format!(
2160 "must contain exactly one `:` separator (between namespace and package); \
2161 got {s:?} with multiple `:`"
2162 ));
2163 }
2164 is_wit_kebab_id(ns)
2165 .map_err(|r| format!("namespace {ns:?} is not a valid WIT identifier: {r}"))?;
2166 let mut segments = rest.split('/');
2167 let pkg = segments.next().unwrap_or("");
2168 is_wit_kebab_id(pkg)
2169 .map_err(|r| format!("package {pkg:?} is not a valid WIT identifier: {r}"))?;
2170 for iface in segments {
2171 is_wit_kebab_id(iface)
2172 .map_err(|r| format!("interface {iface:?} is not a valid WIT identifier: {r}"))?;
2173 }
2174 Ok(())
2175}
2176
2177/// Predicate: assert that `s` is a lowercase kebab-case ASCII identifier
2178/// — the WIT IDL `id ::= word ('-' word)*` rule restricted to the
2179/// lowercase `word ::= [a-z][a-z0-9]*` arm the pleme-io substrate
2180/// dispatches on. Private because every legitimate caller flows through
2181/// [`is_wit_world_ref`] (which segments the world reference and runs
2182/// this predicate per segment); exposing it directly would invite
2183/// per-axis WIT-shape gates that re-implement the segmenting logic
2184/// inline.
2185fn is_wit_kebab_id(s: &str) -> Result<(), String> {
2186 if s.is_empty() {
2187 return Err("must not be empty".to_string());
2188 }
2189 let bytes = s.as_bytes();
2190 if !bytes[0].is_ascii_lowercase() {
2191 let msg = if bytes[0].is_ascii_uppercase() {
2192 format!(
2193 "must start with a lowercase ASCII letter (got uppercase {ch:?}); \
2194 pleme-io dispatches `:wit` values on the lowercase canonical shape \
2195 — `wasi:http/proxy` is recognized, `WASI:HTTP/proxy` is silently \
2196 demoted to a capability-only edge",
2197 ch = bytes[0] as char
2198 )
2199 } else if bytes[0].is_ascii_digit() {
2200 format!(
2201 "must start with a lowercase ASCII letter (got digit {ch:?}); WIT \
2202 identifiers begin with a letter, not a digit",
2203 ch = bytes[0] as char
2204 )
2205 } else if bytes[0] == b'-' {
2206 "must not start with `-` (WIT identifiers are kebab-case words; the \
2207 leading character is a lowercase letter)"
2208 .to_string()
2209 } else {
2210 format!(
2211 "must start with a lowercase ASCII letter (got {ch:?}); WIT \
2212 identifiers allow only `[a-z0-9-]`",
2213 ch = bytes[0] as char
2214 )
2215 };
2216 return Err(msg);
2217 }
2218 if bytes[bytes.len() - 1] == b'-' {
2219 return Err(
2220 "must not end with `-` (WIT identifiers are kebab-case words separated \
2221 by single hyphens; no trailing `-`)"
2222 .to_string(),
2223 );
2224 }
2225 let mut prev_hyphen = false;
2226 for &b in bytes {
2227 if b == b'-' {
2228 if prev_hyphen {
2229 return Err(
2230 "must not contain consecutive `-` characters (WIT identifiers \
2231 join words with single hyphens, not `--`)"
2232 .to_string(),
2233 );
2234 }
2235 prev_hyphen = true;
2236 continue;
2237 }
2238 let after_hyphen = prev_hyphen;
2239 prev_hyphen = false;
2240 if b.is_ascii_uppercase() {
2241 return Err(format!(
2242 "must be lowercase (got uppercase character {ch:?}); pleme-io \
2243 dispatches `:wit` values on the lowercase canonical shape — \
2244 `wasi:http/proxy` is recognized, `WASI:HTTP/proxy` is silently \
2245 demoted to a capability-only edge",
2246 ch = b as char
2247 ));
2248 }
2249 // Per-word first-byte gate. The doc-comment above binds this
2250 // predicate to the WIT IDL rule `id ::= word ('-' word)*` with
2251 // `word ::= [a-z][a-z0-9]*` — each hyphen-separated word must
2252 // begin with a lowercase letter, not a digit. The full-id
2253 // first-byte arm above ([`is_wit_kebab_id`] line ~974) closes
2254 // the leading-digit / leading-hyphen / leading-uppercase footguns
2255 // for the *first* word (`"1http"`, `"-http"`, `"Http"`); this
2256 // arm closes the same "word must begin with a lowercase letter"
2257 // rule for *every subsequent* word after a `-` separator. Until
2258 // this gate landed the byte-set arm below accepted `[a-z0-9-]`
2259 // uniformly across all positions, so an identifier like
2260 // `"pub-1sub"` / `"proxy-2beta"` / `"cap-9"` passed the byte-set
2261 // gate (every byte lies in `[a-z0-9-]`), passed the leading-`-`
2262 // arm (the first byte is `p`/`c`, not `-`), passed the
2263 // consecutive-`-` arm (no `--`), passed the trailing-`-` arm
2264 // (last byte is a lowercase letter or digit, not `-`), and was
2265 // silently accepted — the canonical `abc-<digit>*` word-shape
2266 // footgun where an author's paste-from-versioned-slug (`"proxy-2"`
2267 // from a `v2`-tagged interface hand-transcribed) or a
2268 // programmatic string-interpolation (`format!("{stem}-{n}")` with
2269 // a numeric `n`) landed in the `:contratos :wit` slot's segment.
2270 // The upstream WIT parser (WebAssembly/component-model spec §WIT
2271 // grammar; `wit-parser` crate's `id!` production) then failed at
2272 // WIT-parse time far from the source caixa.lisp with a parser
2273 // diagnostic that names the offending byte but not the offending
2274 // `:contratos :wit` slot, and the typed slot's accepted set drifted
2275 // from the upstream parser's accepted set on the exact class the
2276 // doc-comment above already documented as rejected — a
2277 // documentation-vs-implementation drift, not a novel rule. Lifting
2278 // the rejection to caixa-build time closes the per-word-first-byte
2279 // axis structurally — every validated WIT identifier past this
2280 // predicate matches the WIT IDL word grammar per-word, not just at
2281 // the first byte, and drift between the typed slot's accepted set
2282 // and the upstream WIT parser's accepted set is impossible-by-
2283 // construction on the digit-after-hyphen axis (the last remaining
2284 // documented-but-unenforced arm on the WIT kebab predicate).
2285 //
2286 // Same top-and-bottom-edge discipline the peer axes carry: every
2287 // caller ([`is_wit_world_ref`] on the `:contratos :wit` axis, and
2288 // through it the M3 `WitContract::target` cross-check at
2289 // [`crate::AplicacaoSpec::validate`]) now refuses the canonical
2290 // author-side "word two starts with a version-shape digit paste"
2291 // footgun at validate time rather than at wit-parser consume
2292 // time. Same trajectory as bb4e6c4 (`is_wit_world_ref` byte-set
2293 // gate on the `@<version>` suffix) and 9f7b894 (`is_wit_world_ref`
2294 // structural SemVer 2.0.0 parse on the `@<version>` suffix) on
2295 // the peer per-suffix axes — the same "typed-slot's accepted set
2296 // matches the downstream parser's accepted set, structurally"
2297 // discipline extended here from the version-body axis to the
2298 // per-word first-byte axis of the identifier body itself.
2299 if after_hyphen && b.is_ascii_digit() {
2300 return Err(format!(
2301 "word after `-` starts with digit {ch:?} (WIT identifiers are \
2302 `id ::= word ('-' word)*` with `word ::= [a-z][a-z0-9]*` — every \
2303 word begins with a lowercase letter, not a digit; the upstream WIT \
2304 parser rejects an identifier of this shape at consume time. Insert \
2305 a lowercase-letter prefix on the offending word — `pub-v1sub` \
2306 instead of `pub-1sub`, `proxy-v2beta` instead of `proxy-2beta`)",
2307 ch = b as char
2308 ));
2309 }
2310 if !(b.is_ascii_lowercase() || b.is_ascii_digit()) {
2311 let msg = if b == b'_' {
2312 "contains `_` (WIT identifiers are kebab-case; use `-` between \
2313 words instead of `_`)"
2314 .to_string()
2315 } else if b == b'.' {
2316 "contains `.` (WIT identifiers are single kebab-case words; split \
2317 into separate namespace/package/interface segments via `:` and \
2318 `/` instead of `.`)"
2319 .to_string()
2320 } else {
2321 format!(
2322 "contains invalid character {ch:?} (WIT identifiers allow only \
2323 `[a-z0-9-]`)",
2324 ch = b as char
2325 )
2326 };
2327 return Err(msg);
2328 }
2329 }
2330 Ok(())
2331}
2332
2333/// Max length, in bytes, of a single typed `:contratos :subject` NATS
2334/// subject passing the [`is_nats_subject`] predicate. 256 bytes —
2335/// matches the upstream NATS Java client's `MAX_SUBJECT_LENGTH`
2336/// constant and sits well above the longest legitimate subject the
2337/// caixa-mesh test fixtures + example checkout-aplicacao carry
2338/// (`"checkout.events.charge.failed"` = 30 bytes, `"rio.events.order.charged"`
2339/// = 25 bytes). The cap exists to reject the paste-from-binary footgun
2340/// (a multi-line blob accidentally landed in the `:subject` slot)
2341/// rather than to constrain legitimate authoring. Lifted as a typed
2342/// const so a future axis reaching for the same bound (the M4
2343/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-subject
2344/// validator, the future NATS Stream/Consumer CR emitter for the
2345/// `nats:pub-sub` branch of `:contratos`, the future per-edge
2346/// `:politicas`-derived NATS-aware policy overlay) reads from one
2347/// place.
2348pub const NATS_SUBJECT_MAX_LEN: usize = 256;
2349
2350/// Predicate: assert that `s` is a valid NATS subject — the canonical
2351/// shape every typed `:contratos :subject` value carries. The
2352/// contract — modeled on the [NATS subject grammar][nats] (dot-
2353/// separated tokens with `*` / `>` wildcards), restricted to the
2354/// strict `[A-Za-z0-9_-]` per-token character set the NATS server's
2355/// subject parser accepts at runtime:
2356///
2357/// - 1..=[`NATS_SUBJECT_MAX_LEN`] (256) bytes;
2358/// - no whitespace, no control characters, no non-ASCII bytes
2359/// (RFC 3986 requires `%XX` percent-encoding for non-ASCII; NATS
2360/// subjects predate that and reject any byte outside the strict
2361/// ASCII identifier set);
2362/// - one-or-more `.`-separated tokens — no leading `.`, no trailing
2363/// `.`, no consecutive `.` (NATS rejects empty tokens between
2364/// separators);
2365/// - each token is one of:
2366/// - a concrete identifier `[A-Za-z0-9_-]+` (NATS subjects are
2367/// case-sensitive; unlike DNS-1123 we don't lowercase-fold,
2368/// and underscores are permitted since NATS itself accepts them
2369/// in tokens);
2370/// - the `*` single-token wildcard (matches exactly one token;
2371/// allowed at any segment position);
2372/// - the `>` multi-token wildcard (matches one-or-more trailing
2373/// tokens; allowed ONLY as the final segment — `foo.>` matches
2374/// `foo.bar` / `foo.bar.baz`, `foo.>.bar` is rejected outright).
2375///
2376/// Returns the parser-shaped reason on rejection (without wrapping in
2377/// any error variant) so each per-axis caller — `WitContract::target`
2378/// for the `:contratos :subject` axis at validate time, the future M4
2379/// CR materializer's per-subject validator, the future NATS Stream/
2380/// Consumer CR emitter — wraps the same reason in its own typed
2381/// `*Invalid { <axis>, reason }` variant. The reason wording is axis-
2382/// agnostic ("NATS subjects reject empty tokens between separators")
2383/// so every call site reading the same diagnostic points at the same
2384/// rule; drift between any two axes' rule enforcement is a build
2385/// error visible at this predicate, not a per-renderer "this passed
2386/// validate but the NATS server rejected at publish/subscribe" surprise.
2387///
2388/// Empty input is rejected here (defensively) and at the call site via
2389/// the narrower [`crate::AplicacaoError::ContratoSubjectEmpty`] variant
2390/// — the same empty-first cascade [`is_dns_1123_label`],
2391/// [`is_gateway_api_http_path`], and [`is_wit_world_ref`] carry.
2392///
2393/// Lifted as a typed substrate-side primitive on the same trajectory
2394/// the M2-overlay and label-selector helpers (9e3a057, 9d09cfb,
2395/// 9dbeafd, 31455a7, 07a4544) and the value-shape predicates
2396/// (`is_dns_1123_label`, `is_gateway_api_http_path`,
2397/// `is_wit_world_ref`) already follow — the typed slot's valid set
2398/// matches the NATS server's accepted set, structurally.
2399///
2400/// [nats]: https://docs.nats.io/nats-concepts/subjects
2401///
2402/// # Errors
2403///
2404/// Returns the parser-shaped reason naming the specific violation
2405/// (length / separator / character-class / wildcard-position), without
2406/// wrapping in any error variant — every caller maps the same
2407/// `String` into its own typed `*Invalid { <axis>, reason }` enum
2408/// variant.
2409pub fn is_nats_subject(s: &str) -> Result<(), String> {
2410 if s.is_empty() {
2411 return Err("must not be empty".to_string());
2412 }
2413 if s.len() > NATS_SUBJECT_MAX_LEN {
2414 return Err(format!(
2415 "exceeds NATS subject max length of {NATS_SUBJECT_MAX_LEN} bytes \
2416 (got {} bytes; legitimate NATS subjects rarely exceed ~64 bytes — \
2417 this length suggests a paste-from-binary or multi-line blob landed \
2418 in the `:subject` slot)",
2419 s.len()
2420 ));
2421 }
2422 for &b in s.as_bytes() {
2423 if b == b' ' || b == b'\t' {
2424 return Err(format!(
2425 "must not contain whitespace character {ch:?} (NATS subjects \
2426 are single tokens with no whitespace between dot-separated \
2427 segments)",
2428 ch = b as char
2429 ));
2430 }
2431 if b < 0x20 || b == 0x7F {
2432 return Err(format!(
2433 "must not contain control character 0x{b:02x} (NATS subjects \
2434 are printable ASCII tokens; the NATS server's subject parser \
2435 rejects control characters at publish/subscribe time)"
2436 ));
2437 }
2438 if b >= 0x80 {
2439 return Err(format!(
2440 "must not contain non-ASCII byte 0x{b:02x} (NATS subjects \
2441 are restricted to `[A-Za-z0-9_-]` per token + the `.` \
2442 separator and the `*` / `>` wildcards)"
2443 ));
2444 }
2445 }
2446 if s.starts_with('.') {
2447 return Err(
2448 "must not start with `.` (NATS subjects reject empty leading \
2449 tokens; drop the leading `.` separator)"
2450 .to_string(),
2451 );
2452 }
2453 if s.ends_with('.') {
2454 return Err(
2455 "must not end with `.` (NATS subjects reject empty trailing \
2456 tokens; use the `>` multi-token wildcard to match arbitrary \
2457 trailing segments instead)"
2458 .to_string(),
2459 );
2460 }
2461 if s.contains("..") {
2462 return Err(
2463 "must not contain consecutive `.` characters (NATS subjects \
2464 reject empty tokens between separators; use the `*` single-\
2465 token wildcard to match any one token)"
2466 .to_string(),
2467 );
2468 }
2469 let segments: Vec<&str> = s.split('.').collect();
2470 let last_idx = segments.len() - 1;
2471 for (i, seg) in segments.iter().enumerate() {
2472 is_nats_subject_segment(seg, i, last_idx)?;
2473 }
2474 Ok(())
2475}
2476
2477/// Predicate: assert that `seg` is a valid NATS subject token at index
2478/// `i` of a `total = last_idx + 1`-segment subject. Private because
2479/// every legitimate caller flows through [`is_nats_subject`] (which
2480/// splits the subject on `.` and runs this predicate per segment);
2481/// exposing it directly would invite per-axis NATS-segment gates that
2482/// re-implement the splitting logic inline.
2483///
2484/// Mirrors the [`is_wit_kebab_id`] / [`is_wit_world_ref`] private-helper
2485/// pair on the WIT predicate.
2486fn is_nats_subject_segment(seg: &str, i: usize, last_idx: usize) -> Result<(), String> {
2487 if seg == "*" {
2488 return Ok(());
2489 }
2490 if seg == ">" {
2491 if i != last_idx {
2492 return Err(format!(
2493 "the `>` multi-token wildcard is only allowed as the \
2494 final segment (got `>` at segment {one_based} of {total}; \
2495 move to the end or use `*` for a single-token wildcard)",
2496 one_based = i + 1,
2497 total = last_idx + 1
2498 ));
2499 }
2500 return Ok(());
2501 }
2502 for &b in seg.as_bytes() {
2503 let valid = b.is_ascii_alphanumeric() || b == b'_' || b == b'-';
2504 if !valid {
2505 let msg = if b == b'*' {
2506 "contains `*` mid-segment (NATS wildcards are standalone \
2507 tokens — `foo.*.bar` matches one middle token, `foo*` \
2508 does not; split into separate `.`-separated segments)"
2509 .to_string()
2510 } else if b == b'>' {
2511 "contains `>` mid-segment (NATS wildcards are standalone \
2512 tokens — `foo.>` matches all trailing tokens, `foo>` \
2513 does not; split into separate `.`-separated segments)"
2514 .to_string()
2515 } else {
2516 format!(
2517 "contains invalid character {ch:?} in subject segment \
2518 (NATS subject tokens allow only `[A-Za-z0-9_-]`; use \
2519 `_` or `-` instead)",
2520 ch = b as char
2521 )
2522 };
2523 return Err(msg);
2524 }
2525 }
2526 Ok(())
2527}
2528
2529/// Max length, in bytes, of a single typed `:contratos :slot` WASI
2530/// keyvalue store key/template passing the [`is_wasi_keyvalue_slot`]
2531/// predicate. 512 bytes — generously above the longest realistic slot
2532/// template (`"checkout/$orderId"` = 17 bytes, `"users:{tenant}/{id}"`
2533/// = 19 bytes, `"session.tokens.<sid>"` = 20 bytes) and well under any
2534/// canonical WASI-keyvalue backend's per-key limit (etcd: 1.5 MB,
2535/// DynamoDB partition+sort key: 2 KB combined, Redis: 512 MB — the cap
2536/// is chosen for the *template* slot a typed `:contratos` edge
2537/// authors, not the realized key at runtime). The cap exists to reject
2538/// the paste-from-binary footgun (a multi-line blob accidentally landed
2539/// in the `:slot` slot) rather than to constrain legitimate authoring.
2540/// Lifted as a typed const so a future axis reaching for the same
2541/// bound (the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
2542/// per-slot validator, the future per-Servico `:capabilities`
2543/// `wasi:keyvalue/store` axis's per-slot validator when M4 lands
2544/// per-capability typed slots, the future per-edge `:politicas`-derived
2545/// kv-backend-aware policy overlay's per-slot validator) reads from
2546/// one place. Same lift trajectory as [`NATS_SUBJECT_MAX_LEN`] (which
2547/// caps the peer pub-sub payload axis at 256 bytes — twice that here
2548/// because kv slot templates legitimately compose more `/`-separated
2549/// path segments + template variables than NATS subjects do
2550/// `.`-separated tokens).
2551pub const WASI_KV_SLOT_MAX_LEN: usize = 512;
2552
2553/// Predicate: assert that `s` is a valid WASI keyvalue store slot
2554/// template — the canonical shape every typed `:contratos :slot` value
2555/// carries when its `:wit` dispatch resolves to the
2556/// [`WitTarget::Store`][st] arm (`wasi:keyvalue/store`, `kv:*`). The
2557/// WASI keyvalue 0.2 specification ([`bucket = string`, `key = string`,
2558/// both opaque][wasi-kv]) places no syntactic constraints on the key
2559/// shape, so the substrate enforces the canonical printable-ASCII
2560/// floor every realistic kv backend admits: no raw whitespace, no
2561/// control bytes, no non-ASCII bytes, length-bounded by
2562/// [`WASI_KV_SLOT_MAX_LEN`]. The grammar:
2563///
2564/// - 1..=[`WASI_KV_SLOT_MAX_LEN`] (512) bytes;
2565/// - no whitespace (space, tab — kv slot templates are single-token
2566/// identifiers / path expressions, whitespace is the canonical
2567/// paste-from-doc footgun whose runtime behavior varies
2568/// unpredictably across backends — etcd accepts, Redis accepts
2569/// but rejects subsequent CLI ops, DynamoDB rejects on write);
2570/// - no ASCII control characters (`0x00..0x1F`, `0x7F`) — every
2571/// kv backend either rejects on write (DynamoDB, etcd) or admits
2572/// and silently breaks at the next read (Redis: `\r\n` corrupts
2573/// the RESP protocol framing if the slot template is rendered
2574/// directly into a key without re-encoding);
2575/// - no non-ASCII bytes (`>= 0x80`) — RFC 3986-style percent-
2576/// encoding (`%XX`) is the substrate's canonical UTF-8 escape
2577/// for kv slot templates the author wants to namespace by
2578/// non-ASCII identifier; raw non-ASCII silently differs between
2579/// backends (etcd preserves bytes verbatim; Redis-via-RESP3 may
2580/// re-encode; DynamoDB rejects).
2581///
2582/// The predicate is intentionally permissive on structure: all
2583/// printable ASCII bytes (`0x21..0x7E`) are admitted, including
2584/// `/` (path separators), `:` (namespace separators), `.`
2585/// (dot-namespacing), `-`/`_` (identifier separators), `$`/`{`/`}`/`<`/`>`
2586/// (template-variable syntaxes — the canonical `"checkout/$orderId"`
2587/// shape carries `$`-prefixed identifiers, alternate `"users:{id}"` /
2588/// `"session.<sid>"` shapes carry `{}` / `<>` brackets), and the
2589/// remaining ASCII punctuation. The substrate doesn't know which kv
2590/// backend the runtime resolves [`WitTarget::Store`][st] to — that
2591/// choice is per-cluster, made by the operator's kv-provider binding
2592/// — so the typed slot enforces the intersection-floor every backend
2593/// admits rather than any one backend's stricter superset.
2594///
2595/// Returns the parser-shaped reason on rejection (without wrapping in
2596/// any error variant) so each per-axis caller — [`WitContract::target`]
2597/// for the `:contratos :slot` axis at validate time, the future M4 CR
2598/// materializer's per-slot validator, the future per-Servico
2599/// `:capabilities wasi:keyvalue/store` per-slot validator — wraps the
2600/// same reason in its own typed `*Invalid { <axis>, reason }` variant.
2601/// The reason wording is axis-agnostic ("kv slot templates reject raw
2602/// whitespace") so every call site reading the same diagnostic points
2603/// at the same rule; drift between any two axes' rule enforcement is
2604/// a build error visible at this predicate, not a per-renderer "this
2605/// passed validate but the kv backend rejected on first write"
2606/// surprise.
2607///
2608/// Empty input is rejected here (defensively) and at the call site
2609/// via the narrower [`crate::AplicacaoError::ContratoSlotEmpty`]
2610/// variant — the same empty-first cascade [`is_dns_1123_label`],
2611/// [`is_gateway_api_http_path`], [`is_wit_world_ref`], and
2612/// [`is_nats_subject`] all carry.
2613///
2614/// Lifted as a typed substrate-side primitive on the same trajectory
2615/// the peer payload-axis predicates ([`is_gateway_api_http_path`] for
2616/// `:endpoint`, [`is_nats_subject`] for `:subject`) already follow —
2617/// the typed slot's valid set matches the kv backend intersection-
2618/// floor's accepted set, structurally. The fifth value-shape primitive
2619/// to land in [`crate::render`] after [`is_dns_1123_label`],
2620/// [`is_gateway_api_http_path`], [`is_wit_world_ref`], and
2621/// [`is_nats_subject`] — and the one that closes the trajectory across
2622/// every typed payload axis the [`WitContract::target`] dispatch
2623/// carries (HTTP `:endpoint`, PubSub `:subject`, Store `:slot`).
2624///
2625/// [st]: crate::WitTarget::Store
2626/// [wasi-kv]: https://github.com/WebAssembly/wasi-keyvalue
2627///
2628/// # Errors
2629///
2630/// Returns the parser-shaped reason naming the specific violation
2631/// (length / whitespace / control / non-ASCII), without wrapping in
2632/// any error variant — every caller maps the same `String` into its
2633/// own typed `*Invalid { <axis>, reason }` enum variant.
2634pub fn is_wasi_keyvalue_slot(s: &str) -> Result<(), String> {
2635 if s.is_empty() {
2636 return Err("must not be empty".to_string());
2637 }
2638 if s.len() > WASI_KV_SLOT_MAX_LEN {
2639 return Err(format!(
2640 "exceeds WASI keyvalue slot max length of {WASI_KV_SLOT_MAX_LEN} bytes \
2641 (got {} bytes; legitimate kv slot templates rarely exceed ~64 bytes — \
2642 this length suggests a paste-from-binary or multi-line blob landed in \
2643 the `:slot` slot)",
2644 s.len()
2645 ));
2646 }
2647 for &b in s.as_bytes() {
2648 if b == b' ' || b == b'\t' {
2649 return Err(format!(
2650 "must not contain whitespace character {ch:?} (kv slot templates \
2651 are single-token identifiers / path expressions; raw whitespace \
2652 behaves unpredictably across kv backends — percent-encode as `%20` \
2653 or use `-`/`_` to namespace)",
2654 ch = b as char
2655 ));
2656 }
2657 if b < 0x20 || b == 0x7F {
2658 return Err(format!(
2659 "must not contain control character 0x{b:02x} (kv slot templates \
2660 are printable ASCII; control bytes either get rejected on write \
2661 by strict backends — DynamoDB, etcd — or silently corrupt the \
2662 next read on permissive ones — Redis RESP framing)"
2663 ));
2664 }
2665 if b >= 0x80 {
2666 return Err(format!(
2667 "must not contain non-ASCII byte 0x{b:02x} (RFC 3986 requires \
2668 percent-encoding `%XX` for characters outside the ASCII unreserved \
2669 + reserved set; raw non-ASCII bytes are admitted by some kv backends \
2670 verbatim and re-encoded by others — the typed slot's value set is \
2671 the intersection-floor every backend admits identically)"
2672 ));
2673 }
2674 }
2675 Ok(())
2676}
2677
2678/// Max length, in bytes, of a single typed git ref name passing the
2679/// [`is_git_ref_name`] predicate. 255 bytes — matches the POSIX
2680/// `NAME_MAX` filesystem-component limit every Git porcelain ultimately
2681/// stores refs into (loose `refs/<category>/<name>` files under
2682/// `.git/refs/`, packed-refs index entries). Refs that exceed this cap
2683/// fail to land on disk at clone/fetch time on every realistic
2684/// filesystem (ext4, btrfs, xfs, APFS, NTFS), so a `:tag` / `:branch`
2685/// past that length is unsourceable in practice. The cap exists to
2686/// reject the paste-from-binary footgun (a multi-line blob accidentally
2687/// landed in the `:tag` slot) rather than to constrain legitimate
2688/// authoring — realistic tag/branch names rarely exceed ~32 bytes
2689/// (`"v0.1.0"` = 6 bytes, `"release-1.0-alpha.1"` = 19 bytes,
2690/// `"feature/checkout-rewrite"` = 24 bytes). Lifted as a typed const
2691/// so a future axis reaching for the same bound (the future
2692/// `lacre.lisp` ref-shape gate on resolved-pin axes, the future M4
2693/// per-dep CR materializer's per-pin validator) reads from one place.
2694pub const GIT_REF_NAME_MAX_LEN: usize = 255;
2695
2696/// Predicate: assert that `s` is a valid Git ref name under the
2697/// `git check-ref-format --allow-onelevel` rule set — the canonical
2698/// shape every typed `:fonte (:tipo git …)` `:tag` / `:branch` value
2699/// carries. The contract — modeled on the [`git check-ref-format`][gcr]
2700/// grammar the Git porcelain enforces at clone/fetch/checkout time,
2701/// with the multi-component requirement waived (`:tag "v0.1.0"` and
2702/// `:branch "main"` are both single-component refs, the canonical
2703/// leaf form for caixa's `:fonte` pin axes):
2704///
2705/// - 1..=[`GIT_REF_NAME_MAX_LEN`] (255) bytes — the POSIX `NAME_MAX`
2706/// filesystem-component limit Git's loose-ref `.git/refs/<cat>/<name>`
2707/// storage tops out at;
2708/// - no ASCII control characters (`0x00..=0x1F`, `0x7F`) — Git's
2709/// refname parser rejects them, and the `\r` / `\n` arms are the
2710/// canonical "the paste-from-doc spans multiple lines" footgun;
2711/// - no whitespace (space, tab) — Git's refname parser rejects them
2712/// too; a `:tag "v0.1.0 "` (trailing space, from a copy-paste)
2713/// silently passes string emptiness checks and fails at
2714/// `git fetch origin tag 'v0.1.0 '` with a quoting-confused error
2715/// far from the source caixa.lisp;
2716/// - no non-ASCII bytes (`>= 0x80`) — Git's refname rules predate
2717/// UTF-8 normalization (NFC vs NFD on APFS silently rewrites the
2718/// ref body, breaking the lacre's content addressing); the
2719/// intersection-floor every realistic Git host accepts is ASCII
2720/// identifiers + the small punctuation set below;
2721/// - no `~`, `^`, `:`, `?`, `*`, `[`, `\` anywhere — Git reserves
2722/// these for revision-grammar expressions (`HEAD~3`, `HEAD^`,
2723/// `:/searched`, glob wildcards, refspec brackets, Windows-path
2724/// backslash);
2725/// - no `@{` sequence — Git's reflog grammar (`HEAD@{2 hours ago}`,
2726/// `branch@{upstream}`);
2727/// - the bare `@` is not a valid refname (it's the alias for `HEAD`);
2728/// - no `..` anywhere (Git's `<rev1>..<rev2>` range syntax + the
2729/// `.` / `..` parent-traversal footgun);
2730/// - per `/`-separated component: must not begin with `.` (Git
2731/// refuses to follow loose `.git/refs/<cat>/.<name>` files), must
2732/// not end with `.lock` (Git's atomic-rename guard suffix), must
2733/// not be empty (`//` rejected by the no-empty-component arm
2734/// below);
2735/// - no leading `/`, no trailing `/`, no consecutive `//`;
2736/// - no trailing `.` on the whole ref (Git rejects `<name>.`);
2737/// - no `refs/heads/` or `refs/tags/` prefix — the canonical "I
2738/// copied the fully-qualified ref name out of `git show-ref`
2739/// instead of the leaf" footgun (per [`theory/FLAKE-DEDUP.md`][fd]
2740/// `BranchName` constructor rules); the caixa-resolver prepends
2741/// the category prefix at clone time, so an author-side
2742/// `:branch "refs/heads/main"` resolves to a literal ref named
2743/// `refs/heads/refs/heads/main` on disk.
2744///
2745/// Returns the parser-shaped reason on rejection (without wrapping in
2746/// any error variant) so each per-axis caller — `DepSource::validate`
2747/// for the `:fonte :tag` / `:fonte :branch` axes at validate time,
2748/// the future per-pin gate on `lacre.lisp` resolved-ref axes, the
2749/// future M4 per-dep CR materializer's per-pin validator — wraps the
2750/// same reason in its own typed `*Invalid { <axis>, reason }` variant.
2751/// The reason wording is axis-agnostic ("git ref names reject ASCII
2752/// control characters") so every call site reading the same diagnostic
2753/// points at the same rule; drift between any two axes' rule
2754/// enforcement is a build error visible at this predicate, not a
2755/// per-renderer "this passed validate but `git fetch` rejected at
2756/// clone time" surprise.
2757///
2758/// Empty input is rejected here (defensively) and at each call site
2759/// via the narrower [`crate::DepError::FontePinEmpty`] variant — the
2760/// same empty-first cascade [`is_dns_1123_label`],
2761/// [`is_gateway_api_http_path`], [`is_wit_world_ref`],
2762/// [`is_nats_subject`], and [`is_wasi_keyvalue_slot`] all carry.
2763///
2764/// `:rev` is intentionally NOT routed through this predicate — its
2765/// author-surface shape is a hex commit-ID (`[0-9a-f]+`), not a
2766/// refname; a dedicated `is_git_oid` predicate on the parallel
2767/// hex-shape trajectory carries the reproducibility contract. Routing
2768/// `:rev` through `is_git_ref_name` would admit `:rev "main"`,
2769/// defeating the reproducibility contract `:rev` carries vs.
2770/// `:branch` / `:tag`. The reverse mis-slot — a canonical OID
2771/// (40-char SHA-1 or 64-char SHA-256 lowercase hex) pasted into the
2772/// `:tag` / `:branch` slot — is closed by this predicate too: a
2773/// pre-emption arm below rejects any value whose width and byte set
2774/// match the canonical OID shape, surfacing the cross-axis mis-slot
2775/// at validate time with a diagnostic pointing the author at the
2776/// `:rev` slot. The two predicates' valid sets intersect at exactly
2777/// the empty set, structurally.
2778///
2779/// Lifted as a typed substrate-side primitive on the same trajectory
2780/// the peer value-shape predicates ([`is_dns_1123_label`],
2781/// [`is_gateway_api_http_path`], [`is_wit_world_ref`],
2782/// [`is_nats_subject`], [`is_wasi_keyvalue_slot`]) already follow —
2783/// the typed slot's valid set matches the Git porcelain's accepted
2784/// set, structurally. The sixth value-shape primitive to land in
2785/// [`crate::render`], and the first to gate a non-K8s downstream
2786/// landing surface (git CLI invocation from caixa-resolver, vs. the
2787/// K8s apiserver / NATS server / WASI kv backend for the prior five).
2788///
2789/// [gcr]: https://git-scm.com/docs/git-check-ref-format
2790/// [fd]: pleme-io/theory/FLAKE-DEDUP.md §1 `BranchName`
2791///
2792/// # Errors
2793///
2794/// Returns the parser-shaped reason naming the specific violation
2795/// (length / control-char / forbidden-char / component-shape / prefix),
2796/// without wrapping in any error variant — every caller maps the same
2797/// `String` into its own typed `*Invalid { <axis>, reason }` enum
2798/// variant.
2799pub fn is_git_ref_name(s: &str) -> Result<(), String> {
2800 if s.is_empty() {
2801 return Err("must not be empty".to_string());
2802 }
2803 if s.len() > GIT_REF_NAME_MAX_LEN {
2804 return Err(format!(
2805 "exceeds git ref name max length of {GIT_REF_NAME_MAX_LEN} bytes \
2806 (got {} bytes; legitimate tag/branch names rarely exceed ~32 bytes — \
2807 this length suggests a paste-from-binary or multi-line blob landed \
2808 in the `:tag` / `:branch` slot)",
2809 s.len()
2810 ));
2811 }
2812 // Canonical-OID-shape pre-emption — the structural partition the
2813 // doc-comment above promises and [`crate::DepSource::validate`]
2814 // routes the `:fonte` pin axes through ([`is_git_ref_name`] for
2815 // `:tag` + `:branch`, [`is_git_oid`] for `:rev`): a value that's
2816 // exactly the canonical Git commit-OID width
2817 // ([`GIT_OID_SHA1_LEN`] (40) lowercase-hex for SHA-1,
2818 // [`GIT_OID_SHA256_LEN`] (64) lowercase-hex for SHA-256) is the
2819 // shape `is_git_oid` accepts; the two predicates' valid sets must
2820 // intersect at exactly the empty set, so a value of that shape is
2821 // rejected here. Without this arm a canonical lowercase-hex OID of
2822 // either canonical width passes every other refname-shape arm in
2823 // this predicate — pure-hex strings carry none of the forbidden
2824 // characters, no `..` / `@{` / leading-`/` / trailing-`/` /
2825 // `.lock`-suffix / `refs/heads/`-prefix — and the cross-axis
2826 // partition silently fails on the canonical "I copied the SHA out
2827 // of `git show --format=%H` and pasted it into `:tag` / `:branch`"
2828 // mis-slot footgun. The pleme-io discipline (CAIXA-SDLC §V — the
2829 // `:rev` slot carries the reproducibility contract; `:tag` /
2830 // `:branch` resolve to whatever the upstream has tagged / `HEAD`
2831 // today) requires that an OID-shaped value live under `:rev`, never
2832 // under `:tag` / `:branch`; this arm makes that discipline a typed
2833 // structural property, not a convention.
2834 //
2835 // Uppercase hex (`"DEADBEEF…"` 40 chars) is intentionally NOT
2836 // matched here — uppercase letters are legitimate in refnames per
2837 // `git check-ref-format`, so an uppercase 40/64-char hex string is a
2838 // valid refname (`is_git_ref_name` accepts it); the `:rev` axis
2839 // separately rejects uppercase via [`is_git_oid`]'s lowercase-only
2840 // contract. Off-canonical lengths (39 / 41 / 63 / 65 hex chars) are
2841 // also intentionally NOT matched — abbreviated commit IDs are
2842 // ambiguous across repository history but they're not canonical
2843 // OIDs either; they remain accepted as refnames here (consistent
2844 // with `is_git_oid` already rejecting them via its exact-width
2845 // check).
2846 if (s.len() == GIT_OID_SHA1_LEN || s.len() == GIT_OID_SHA256_LEN)
2847 && s.bytes().all(|b| matches!(b, b'0'..=b'9' | b'a'..=b'f'))
2848 {
2849 return Err(format!(
2850 "looks like a canonical Git commit OID ({len} lowercase hex \
2851 characters — the SHA-{algo} OID width); pleme-io's `:fonte` axes \
2852 partition refnames vs. commit OIDs structurally, so a value of \
2853 this shape belongs in the `:rev` slot (which routes through \
2854 `is_git_oid` for the reproducibility contract — one immutable \
2855 commit, forever), not `:tag` / `:branch` (which route through \
2856 this predicate for human-readable refs `git fetch` resolves at \
2857 clone time). Move the value to `:rev`; keeping it under `:tag` \
2858 / `:branch` is the canonical paste-from-`git show --format=%H` \
2859 mis-slot footgun, silently demoting an OID to a refname-shaped \
2860 pin the resolver would attempt to `git fetch tag '<sha>'` and \
2861 fail at clone time with a quoting-confused porcelain error far \
2862 from the source caixa.lisp.",
2863 len = s.len(),
2864 algo = if s.len() == GIT_OID_SHA1_LEN {
2865 "1"
2866 } else {
2867 "256"
2868 },
2869 ));
2870 }
2871 if s.starts_with('-') {
2872 return Err(
2873 "must not start with `-` (the canonical CLI-argument-injection \
2874 footgun on the `:tag` / `:branch` axis — caixa-resolver's \
2875 `git::checkout` invocation routes the ref name verbatim into \
2876 `git checkout --quiet --detach <ref>` (caixa-resolver/src/git.rs:41) \
2877 without a `--` argument-list terminator, so a leading `-` value \
2878 (`:tag \"-stable\"`, `:branch \"-X\"`, `:tag \"-c=core.merge=ours\"`) \
2879 silently escapes the subprocess argument boundary and gets \
2880 reinterpreted by `git checkout`'s argument parser as a CLI flag — \
2881 the canonical short-flag / long-option / config-injection vector. \
2882 Git's `check-ref-format` grammar does NOT reject a leading `-` \
2883 (it admits the byte mid-name as a legitimate kebab separator), so \
2884 every prior shape arm on this predicate passes the value through; \
2885 the diagnostic moves the gate to the subprocess-argument \
2886 boundary the resolver consumes. Peer with the \
2887 [`is_git_repo_url`] leading-`-` arm (the CLI-arg-injection \
2888 vector on the sibling `:repo` axis where `git clone <repo>` \
2889 reinterprets a leading `-` as a flag like `-upload-pack=…` / \
2890 `--config=…`), [`is_cargo_feature_name`] leading-`-` arm, and \
2891 [`is_dns_1123_label`] leading-`-` arm — every single-token typed \
2892 string slot the substrate routes through a downstream subprocess \
2893 / parser rejects the same leading-byte CLI-arg-injection shape \
2894 at validate time. Drop the leading `-`; use a kebab-separator-\
2895 between-alphanumeric-segments form like `\"v0.1.0\"` / \
2896 `\"feature-x\"` / `\"main\"` instead)"
2897 .to_string(),
2898 );
2899 }
2900 for &b in s.as_bytes() {
2901 if b == b' ' || b == b'\t' {
2902 return Err(format!(
2903 "must not contain whitespace character {ch:?} (git ref names are \
2904 single tokens with no whitespace — a trailing space in a `:tag` \
2905 / `:branch` value is the canonical paste-from-doc footgun, \
2906 silently breaking `git fetch <remote> tag '<value> '` at \
2907 clone time)",
2908 ch = b as char
2909 ));
2910 }
2911 if b < 0x20 || b == 0x7F {
2912 return Err(format!(
2913 "must not contain control character 0x{b:02x} (git ref names are \
2914 printable ASCII; `\\r` / `\\n` are the canonical \
2915 paste-from-multiline-doc footgun and break git's refname parser \
2916 at every porcelain entry point)"
2917 ));
2918 }
2919 if b >= 0x80 {
2920 return Err(format!(
2921 "must not contain non-ASCII byte 0x{b:02x} (git's refname rules \
2922 predate UTF-8 normalization — APFS NFC/NFD silently rewrites the \
2923 ref body, breaking the lacre's content addressing across \
2924 platforms; the intersection-floor every git host admits is ASCII)"
2925 ));
2926 }
2927 match b {
2928 b'~' => {
2929 return Err("must not contain `~` (git reserves `~` for the revision \
2930 grammar — `HEAD~3` means `parent of parent of parent of \
2931 HEAD`; the bare character is not admitted in a refname)"
2932 .to_string());
2933 }
2934 b'^' => {
2935 return Err("must not contain `^` (git reserves `^` for the revision \
2936 grammar — `HEAD^` means `first parent of HEAD`; the bare \
2937 character is not admitted in a refname)"
2938 .to_string());
2939 }
2940 b':' => {
2941 return Err("must not contain `:` (git reserves `:` for revspec / \
2942 refspec separators — `:refs/heads/...`, `<src>:<dst>`)"
2943 .to_string());
2944 }
2945 b'?' => {
2946 return Err("must not contain `?` (git reserves `?` for refspec glob \
2947 wildcards)"
2948 .to_string());
2949 }
2950 b'*' => {
2951 return Err("must not contain `*` (git reserves `*` for refspec glob \
2952 wildcards — `refs/heads/*:refs/remotes/origin/*`)"
2953 .to_string());
2954 }
2955 b'[' => {
2956 return Err("must not contain `[` (git reserves `[` for refspec \
2957 bracketed-glob syntax)"
2958 .to_string());
2959 }
2960 b'\\' => {
2961 return Err("must not contain `\\` (git's refname grammar rejects \
2962 backslash — the canonical Windows-path-leak footgun; use \
2963 `/` for hierarchical refs)"
2964 .to_string());
2965 }
2966 _ => {}
2967 }
2968 }
2969 if s.contains("..") {
2970 return Err(
2971 "must not contain `..` (git reserves `..` for the `<rev1>..<rev2>` \
2972 range grammar; a `..` component would also escape the loose-ref \
2973 directory tree at clone time)"
2974 .to_string(),
2975 );
2976 }
2977 if s.contains("@{") {
2978 return Err(
2979 "must not contain `@{` (git reserves `@{` for the reflog grammar \
2980 — `branch@{upstream}`, `HEAD@{2 hours ago}`)"
2981 .to_string(),
2982 );
2983 }
2984 if s == "@" {
2985 return Err(
2986 "must not be the bare `@` (git aliases `@` to `HEAD`; a `:tag` / \
2987 `:branch` named `@` is unsourceable)"
2988 .to_string(),
2989 );
2990 }
2991 if s.starts_with('/') {
2992 return Err(
2993 "must not begin with `/` (git refnames are relative to the ref \
2994 category prefix the resolver prepends — drop the leading `/`)"
2995 .to_string(),
2996 );
2997 }
2998 if s.ends_with('/') {
2999 return Err(
3000 "must not end with `/` (git refnames are leaf-or-multi-component; \
3001 a trailing `/` would resolve to an empty final component)"
3002 .to_string(),
3003 );
3004 }
3005 if s.contains("//") {
3006 return Err(
3007 "must not contain consecutive `/` characters (git refnames reject \
3008 empty components between separators)"
3009 .to_string(),
3010 );
3011 }
3012 if s.ends_with('.') {
3013 return Err(
3014 "must not end with `.` (git refnames reject a trailing `.` — \
3015 `<name>.` collides with the `<name>.lock` atomic-rename guard \
3016 suffix on case-insensitive filesystems)"
3017 .to_string(),
3018 );
3019 }
3020 if s.starts_with("refs/heads/") || s.starts_with("refs/tags/") {
3021 return Err(format!(
3022 "must not carry the fully-qualified `refs/heads/` or `refs/tags/` \
3023 prefix (this is the canonical `git show-ref` output-leak footgun; \
3024 the caixa-resolver prepends the category prefix at clone time, so \
3025 a `:branch \"refs/heads/main\"` would resolve to a literal ref \
3026 named `refs/heads/refs/heads/main` on disk — drop the prefix and \
3027 pass the leaf: `{leaf:?}`)",
3028 leaf = s
3029 .strip_prefix("refs/heads/")
3030 .or_else(|| s.strip_prefix("refs/tags/"))
3031 .unwrap_or(s),
3032 ));
3033 }
3034 for (i, component) in s.split('/').enumerate() {
3035 if component.starts_with('.') {
3036 return Err(format!(
3037 "component {component:?} (segment {one_based} of the `/`-split \
3038 refname) must not begin with `.` (git refuses to follow loose \
3039 `.git/refs/<cat>/.<name>` files)",
3040 one_based = i + 1,
3041 ));
3042 }
3043 // Case-insensitive `.lock` check: git enforces the `.lock`
3044 // suffix as the atomic-rename guard on case-sensitive
3045 // filesystems (refs/heads/main.lock collides with the
3046 // in-flight update lockfile); on case-insensitive
3047 // filesystems (APFS default, NTFS, HFS+) the `.LOCK` /
3048 // `.Lock` variants collide identically. Rejecting all case
3049 // permutations matches the broader-rejection intent on the
3050 // axis the lacre pipeline ultimately stores into.
3051 if component.len() >= 5
3052 && component.as_bytes()[component.len() - 5..].eq_ignore_ascii_case(b".lock")
3053 {
3054 return Err(format!(
3055 "component {component:?} (segment {one_based} of the `/`-split \
3056 refname) must not end with `.lock` (git uses the `.lock` \
3057 suffix as the atomic-rename guard for in-flight ref updates; \
3058 a refname ending in `.lock` is unwritable, and the suffix is \
3059 case-insensitive on the case-insensitive filesystems Git \
3060 supports — APFS default, NTFS, HFS+)",
3061 one_based = i + 1,
3062 ));
3063 }
3064 }
3065 Ok(())
3066}
3067
3068/// Length, in lowercase-hex characters, of a full Git SHA-1 commit
3069/// OID — the canonical commit identifier every `git rev-parse HEAD`
3070/// invocation emits on a SHA-1-hashed repository. `git`'s loose-object
3071/// store keys every object under `.git/objects/<first-2-hex>/<last-38-hex>`,
3072/// so the full 40-char OID is the address-of-truth the porcelain consumes
3073/// at `git fetch <remote> <40-hex>` and `git checkout <40-hex>` time;
3074/// abbreviated OIDs are admitted by the porcelain through a separate
3075/// prefix-lookup pass and are ambiguous across repository history (a 7-char
3076/// prefix that resolves to one commit today can become a collision tomorrow
3077/// as the repo grows). Lifted as a typed const so the `:fonte :rev`
3078/// validate gate, the future lacre-side resolved-rev gate, and the future
3079/// M4 per-dep CR materializer's per-pin validator all read from one place.
3080pub const GIT_OID_SHA1_LEN: usize = 40;
3081
3082/// Length, in lowercase-hex characters, of a full Git SHA-256 commit
3083/// OID — the canonical commit identifier on a SHA-256-hashed repository
3084/// (Git's [`extensions.objectFormat = sha256`][gitsha256] mode, GA since
3085/// Git 2.42 / Oct 2023). Doubled width vs. SHA-1: 256 bits = 64 hex chars.
3086/// Carried alongside [`GIT_OID_SHA1_LEN`] so the typed `:rev` slot admits
3087/// either canonical hash-algorithm OID without per-renderer branching;
3088/// the lacre's BLAKE3 content-addressing (THEORY.md §IV — typed reproducibility
3089/// envelope) is orthogonal to the upstream git's chosen object hash and
3090/// neither OID width should leak into downstream code paths.
3091///
3092/// [gitsha256]: https://git-scm.com/docs/hash-function-transition
3093pub const GIT_OID_SHA256_LEN: usize = 64;
3094
3095/// Predicate: assert that `s` is a valid Git commit OID — the canonical
3096/// shape the typed `:fonte (:tipo git …)` `:rev` axis carries. The
3097/// reproducibility contract `:rev` carries vs. `:tag` / `:branch`
3098/// (CAIXA-SDLC §V — Substrate; `:tag` resolves to whatever the upstream
3099/// has tagged today, `:branch` to whatever the upstream's HEAD points at
3100/// today, `:rev` to exactly one immutable commit forever — same shape
3101/// Unison's [content-addressed code identity][unison] gives terms by
3102/// construction: the hash is the address, the address never moves):
3103///
3104/// - exactly [`GIT_OID_SHA1_LEN`] (40, SHA-1) or [`GIT_OID_SHA256_LEN`]
3105/// (64, SHA-256) characters — the two canonical Git hash-algorithm
3106/// widths; anything in between is an abbreviated prefix (the
3107/// canonical `git log --short` / `git rev-parse --short HEAD`
3108/// paste-from-release-notes footgun), which is ambiguous across
3109/// repository history and surfaces at clone time as an
3110/// [`ambiguous argument`][gitambig] error far from the source
3111/// caixa.lisp;
3112/// - every byte in `[0-9a-f]` (lowercase ASCII hex) — `git rev-parse`
3113/// and `git show --format=%H` both emit lowercase exclusively, so an
3114/// uppercase-bearing `:rev` round-trips inconsistently across the
3115/// resolver's `git fetch <remote> <:rev>` ↔ `git rev-parse HEAD`
3116/// equality-check pipeline and fails the lacre's content-addressing
3117/// equality probe with a confusing case-only diff;
3118/// - no whitespace, no control bytes, no non-ASCII, no refname
3119/// punctuation (`~ ^ : ? * [ \`), no `/` separators — every
3120/// character outside `[0-9a-f]` is rejected on the same predicate
3121/// arm, so a `:rev "main"` (the canonical "I conflated `:rev`
3122/// and `:branch`" footgun) lands at the same gate as a
3123/// `:rev "v0.1.0"` (`:tag` mis-slot) or a `:rev "c0ffee:scratch"`
3124/// (refname-shape leak); the typed `:rev` slot's valid set
3125/// intersects the `:tag` / `:branch` slot's valid set at exactly
3126/// the empty set, structurally — every refname is rejected here,
3127/// every OID is rejected by [`is_git_ref_name`].
3128/// - not the all-zero null-OID sentinel (`"0000…0000"` — 40 zeros
3129/// at SHA-1 width, 64 zeros at SHA-256 width). Git reserves this
3130/// value as the "no commit" sentinel in `git update-ref` /
3131/// pre-receive hook flows (`<old-value>` for create, `<new-value>`
3132/// for delete) and no commit in any object database has this OID,
3133/// so a `:rev "0000…0000"` is structurally impossible to resolve.
3134/// The canonical "I copy-pasted the sentinel out of `git
3135/// update-ref --stdin` docs / pre-receive hook example" footgun
3136/// would otherwise pass every other shape arm (canonical length,
3137/// lowercase hex) and surface at `git fetch <remote> 0000…0000`
3138/// time with a quoting-confused "couldn't find remote ref" error
3139/// far from the source caixa.lisp, with the lacre's content-
3140/// address locked to a `git:0000…0000` closure that never equals
3141/// any upstream's actual `HEAD`. Mirrors `is_git_ref_name`'s
3142/// canonical-OID-shape pre-emption arm (line 1322) — both
3143/// predicates carry one self-aware arm that catches values
3144/// structurally valid for the alphabet but operationally
3145/// meaningless on the typed axis.
3146///
3147/// Returns the parser-shaped reason on rejection (without wrapping in
3148/// any error variant) so each per-axis caller — [`crate::DepError::FontePinShape`]
3149/// at validate time on the `:fonte :rev` axis, the future per-pin gate
3150/// on `lacre.lisp` resolved-rev axes, the future M4 per-dep CR
3151/// materializer's per-pin validator — wraps the same reason in its own
3152/// typed `*Invalid { axis, reason }` variant. The reason wording is
3153/// axis-agnostic ("git commit OIDs are lowercase hex (`[0-9a-f]`)") so
3154/// every call site reading the same diagnostic points at the same rule.
3155///
3156/// Empty input is rejected here (defensively) and at each call site via
3157/// the narrower [`crate::DepError::FontePinEmpty`] variant — the same
3158/// empty-first cascade [`is_dns_1123_label`], [`is_gateway_api_http_path`],
3159/// [`is_wit_world_ref`], [`is_nats_subject`], [`is_wasi_keyvalue_slot`],
3160/// and [`is_git_ref_name`] all carry.
3161///
3162/// Sibling of [`is_git_ref_name`]: the two predicates together bracket
3163/// the `:fonte` pin axes — refname-shaped (`:tag` / `:branch`) vs.
3164/// hex-OID-shaped (`:rev`) — so an authored value lands in exactly one
3165/// of the two valid sets, and a cross-axis mis-slot (`:rev "main"` /
3166/// `:tag "deadbeef…"`) is a build error at the offending axis's
3167/// predicate, not a clone-time surprise.
3168///
3169/// [unison]: https://www.unison-lang.org/docs/the-big-idea/
3170/// [gitambig]: https://git-scm.com/docs/git-rev-parse#_specifying_revisions
3171///
3172/// # Errors
3173///
3174/// Returns the parser-shaped reason naming the specific violation
3175/// (length / character-class), without wrapping in any error variant —
3176/// every caller maps the same `String` into its own typed
3177/// `*Invalid { axis, reason }` enum variant.
3178pub fn is_git_oid(s: &str) -> Result<(), String> {
3179 if s.is_empty() {
3180 return Err("must not be empty".to_string());
3181 }
3182 let len = s.len();
3183 if len != GIT_OID_SHA1_LEN && len != GIT_OID_SHA256_LEN {
3184 return Err(format!(
3185 "git commit OIDs are exactly {GIT_OID_SHA1_LEN} hex chars (SHA-1) or \
3186 {GIT_OID_SHA256_LEN} hex chars (SHA-256); got {len} chars (an \
3187 abbreviated commit ID is ambiguous across repository history — \
3188 `git log --short` / `git rev-parse --short HEAD` emit prefixes for \
3189 human display only, not as reproducible commit addresses; pin the \
3190 full OID so the resolver's `git fetch <remote> <:rev>` and the \
3191 lacre's content-addressing equality probe both resolve to exactly \
3192 one immutable commit, forever)"
3193 ));
3194 }
3195 for (i, b) in s.bytes().enumerate() {
3196 match b {
3197 b'0'..=b'9' | b'a'..=b'f' => {}
3198 b'A'..=b'F' => {
3199 return Err(format!(
3200 "git commit OIDs are lowercase hex (`[0-9a-f]`); got \
3201 uppercase character {ch:?} at byte {i} (git porcelain \
3202 emits OIDs lowercase exclusively — `git rev-parse HEAD` \
3203 and `git show --format=%H` both lowercase on output; a \
3204 `:rev` value with `[A-F]` round-trips inconsistently \
3205 across the resolver's fetch ↔ `git rev-parse HEAD` \
3206 equality-check pipeline and fails the lacre's \
3207 content-addressing probe with a confusing case-only diff)",
3208 ch = b as char
3209 ));
3210 }
3211 _ => {
3212 return Err(format!(
3213 "git commit OIDs are lowercase hex (`[0-9a-f]`); got non-hex \
3214 character {ch:?} at byte {i} (the `:rev` slot's value-shape \
3215 contract is a hex commit ID — for refname-shaped pins \
3216 (`v0.1.0`, `main`, `feature/checkout`) use `:tag` or \
3217 `:branch`, not `:rev`; the substrate's `is_git_ref_name` \
3218 and `is_git_oid` predicates partition the `:fonte` axes \
3219 structurally, so a cross-axis mis-slot lands at the \
3220 offending axis's predicate, not at clone time)",
3221 ch = b as char
3222 ));
3223 }
3224 }
3225 }
3226 // Null-OID sentinel pre-emption — the all-zero hex string is git's
3227 // canonical "no commit" sentinel (used in `git update-ref` /
3228 // pre-receive hook flows as the old-value side of ref-create and the
3229 // new-value side of ref-delete) and never names a real commit in any
3230 // repo's object database. A `:rev "0000000000000000000000000000000000000000"`
3231 // (SHA-1 width) or `:rev "0000…0000"` (SHA-256 width) is the canonical
3232 // "I copy-pasted the no-such-commit sentinel out of `git
3233 // update-ref --stdin` docs / pre-receive hook example" footgun: it's
3234 // shape-valid hex of canonical width but resolves to nothing at
3235 // `git fetch <remote> 0000…0000` time and surfaces as a fetch failure
3236 // far from the source caixa.lisp, with the lacre's
3237 // content-addressing probe locked to a non-resolvable `git:0000…0000`
3238 // closure that never equals any upstream's actual `HEAD`. Rejecting
3239 // at the predicate keeps the `:rev` slot's accepted set aligned with
3240 // its documented reproducibility contract — "exactly one immutable
3241 // commit, forever" — by structurally refusing the only OID-shaped
3242 // value the contract cannot uphold (no commit means no immutable
3243 // resolution). Same pre-emption shape `is_git_ref_name`'s canonical-
3244 // OID-shape pre-emption arm (caixa-core/src/render.rs:1322) carries
3245 // — both predicates carry one self-aware arm that catches values
3246 // structurally valid for the alphabet but operationally meaningless
3247 // on the typed axis.
3248 if s.bytes().all(|b| b == b'0') {
3249 return Err(format!(
3250 "must not be the all-zero null-OID sentinel ({len} `0` \
3251 characters — git's canonical `no-such-commit` value used by \
3252 `git update-ref` / pre-receive hook flows to indicate ref \
3253 create/delete; no commit in any object database has this OID, \
3254 so the resolver's `git fetch <remote> 0000…0000` would fail \
3255 far from the source caixa.lisp and the lacre would lock to a \
3256 `git:0000…0000` closure that never equals any upstream's \
3257 actual `HEAD`. The `:rev` slot's reproducibility contract \
3258 requires a *real* commit OID — the canonical authoring shape \
3259 is the lowercase-hex value `git rev-parse HEAD` emits for an \
3260 actual commit, like `\"c99fdb36abc7d3e1f4a5b6789012345678901234\"`)"
3261 ));
3262 }
3263 Ok(())
3264}
3265
3266/// `:fonte (:tipo git :repo …)` value max length, in bytes — a generous
3267/// URL-shaped cap covering every documented author surface (the
3268/// `github:org/repo` shorthand, the `https://` / `ssh://` / `git://` /
3269/// `file://` URL schemes, the `git@host:path` scp-style SSH form). The
3270/// cap mirrors the conservative ceiling typical HTTP gateways and git
3271/// porcelain entries enforce on URL inputs (the OWASP-recommended URL
3272/// max of 2048 bytes); a `:repo` value above this bound is structurally
3273/// untenable on every realistic landing site — the caixa-resolver's
3274/// `git clone <repo>` invocation, the future M4
3275/// `mesh.pleme.io/v1alpha1/Caixa` CR materializer's per-dep `repo:`
3276/// axis, the future lacre BLAKE3 closure's resolved-repo identity — and
3277/// a value of that length is almost certainly a paste-from-binary slug
3278/// or a multi-line blob that landed in the slot.
3279///
3280/// Lifted as a typed `pub const` (rather than an inline literal at the
3281/// [`is_git_repo_url`] call site) so a future axis reaching for the same
3282/// bound (the future lacre-side resolved-repo gate, the M4 CR
3283/// materializer's per-dep `repo:` admission webhook) reads from one
3284/// place. Same shape every other typed bound in this module carries
3285/// ([`DNS_1123_LABEL_MAX_LEN`], [`GATEWAY_API_HTTP_PATH_MAX_LEN`],
3286/// [`NATS_SUBJECT_MAX_LEN`], [`WASI_KV_SLOT_MAX_LEN`],
3287/// [`GIT_REF_NAME_MAX_LEN`]).
3288pub const GIT_REPO_URL_MAX_LEN: usize = 2048;
3289
3290/// Predicate: assert that `s` is a value-shape-valid `:fonte (:tipo git
3291/// :repo …)` value — the canonical shape every typed `:deps :fonte`
3292/// (and future `:deps-dev :fonte`) git-source carries. The contract —
3293/// modeled on the intersection of (a) the git porcelain's URL-parser
3294/// accepted set the caixa-resolver invokes at `git clone <repo>` time,
3295/// (b) the OWASP URL-shape guidance for author-surface inputs that flow
3296/// to a CLI subprocess, and (c) the typed slot's documented accepted
3297/// shapes ([`crate::DepSource::Git`] doc comment: `github:org/repo`
3298/// shorthand, `https://…` / `ssh://…` / `git://…` / `file://…` URL
3299/// schemes, `git@host:path` scp-style SSH):
3300///
3301/// - 1..=[`GIT_REPO_URL_MAX_LEN`] (2048) bytes;
3302/// - must not start with `-` (the canonical CLI-argument-injection
3303/// footgun — `git clone <repo>` interprets a leading `-` as a CLI
3304/// flag, so a `:repo "-upload-pack=evil"` value escapes the
3305/// subprocess argument boundary and runs an attacker-controlled
3306/// command; the `--` separator workaround does not fix the typed
3307/// slot's accepted set, the gate rejects the shape upstream);
3308/// - no whitespace (space, tab) — every documented form is a single
3309/// token without whitespace; a `:repo "github:p/x "` (trailing
3310/// space, paste-from-doc) silently passes the empty check and
3311/// surfaces at `git clone` time with a quoting-confused error far
3312/// from the source caixa.lisp;
3313/// - no ASCII control characters (`0x00..=0x1F`, `0x7F`) — the `\r`
3314/// / `\n` arms are the canonical "the paste-from-multiline-doc
3315/// spans multiple lines" footgun, and CRLF injection at the URL
3316/// boundary is a class of subprocess-arg attack;
3317/// - no non-ASCII bytes (`>= 0x80`) — IDN hosts must be pre-encoded
3318/// as Punycode (`xn--…`); raw non-ASCII silently breaks at git's
3319/// URL parser and may round-trip inconsistently across NFC/NFD
3320/// normalization on APFS / case-folding filesystems, the same
3321/// intersection-floor [`is_git_ref_name`] enforces on the peer
3322/// refname axes;
3323/// - no `#` URL-fragment-identifier byte (RFC 3986 §3.5) — every
3324/// documented `:repo` shape (`github:org/repo` shorthand,
3325/// `https://…` / `ssh://…` / `git://…` / `file://…` URL schemes,
3326/// `git@host:path` scp-style SSH) carries none; libcurl's URL
3327/// parser (the layer `git clone <https-url>` invokes) and git's
3328/// own URL handlers strip the `#fragment` tail before opening
3329/// the transport, so the byte rides verbatim into the lacre's
3330/// per-dep content-address (`conteudo: format!("git:{repo}…")`,
3331/// caixa-resolver/src/resolve.rs) but is silently dropped on the
3332/// wire — two repos whose values differ only in their fragment
3333/// anchor (`":repo "https://github.com/foo/bar#readme"` vs
3334/// `":repo "https://github.com/foo/bar#L42"`) resolve to the
3335/// byte-identical upstream `git clone` but lock to two distinct
3336/// BLAKE3 closures, defeating the THEORY.md §V.2 render-
3337/// determinism contract. The canonical "I copy-pasted the
3338/// permalink-to-line / anchor-to-README URL out of the browser
3339/// address bar and forgot to trim the `#`-tail" footgun, and the
3340/// symmetric "I confused the Nix flake-ref idiom (`github:foo/
3341/// bar#packageName`) with the bare git `:repo` shape" footgun;
3342/// `:repo` is a git URL, not a Nix flake reference, so the `#`-
3343/// suffix is structurally meaningless on this axis;
3344/// - no `?` URL-query-component byte (RFC 3986 §3.4) — every
3345/// documented `:repo` shape (`github:org/repo` shorthand,
3346/// `https://…` / `ssh://…` / `git://…` / `file://…` URL schemes,
3347/// `git@host:path` scp-style SSH) carries none; GitHub /
3348/// GitLab / Bitbucket all silently ignore the `?query` tail on
3349/// a repo URL (the canonical `https://github.com/foo/bar?
3350/// tab=readme-ov-file` browser-tab deep-link, the `?ref=main`
3351/// GitHub-tree-URL parameter, the `?utm_source=…` campaign-
3352/// tracker shape every social-share / newsletter / Slack
3353/// unfurl appends) and serve the same repo regardless, so the
3354/// byte rides verbatim into the lacre's per-dep content-
3355/// address but is silently masked at the wire — two repos
3356/// whose values differ only in their query tail
3357/// (`":repo "https://github.com/foo/bar?tab=readme-ov-file"` vs
3358/// `":repo "https://github.com/foo/bar?utm_source=twitter"`)
3359/// resolve to the byte-identical upstream `git clone` but lock
3360/// to two distinct BLAKE3 closures, defeating the THEORY.md
3361/// §V.2 render-determinism contract on the same axis the `#`
3362/// fragment arm closes. The Smart-HTTP transport (the layer
3363/// `git clone <https-url>` uses) appends its own
3364/// `?service=git-upload-pack` query internally; an
3365/// author-supplied `?` byte additionally collides with that
3366/// internal axis at every git porcelain entry-point. The
3367/// canonical "I copy-pasted the GitHub tree-URL out of the
3368/// browser address bar and forgot to trim the `?tab=…` /
3369/// `?ref=…` tail" footgun, peer with the `#` fragment arm on
3370/// the same paste-from-browser-address-bar trajectory;
3371/// - no embedded `\` byte (RFC 3986 §3.3 reserves `/` as the path-
3372/// segment separator; no URL grammar admits `\`) — every
3373/// documented `:repo` shape (`github:org/repo` shorthand,
3374/// `https://…` / `ssh://…` / `git://…` / `file://…` URL schemes,
3375/// `git@host:path` scp-style SSH) uses `/` as the path separator.
3376/// The canonical Windows-path-confusion footgun: an author
3377/// pastes `file:///C:\Users\me\repo` from a Windows Explorer
3378/// address bar / PowerShell `Get-Location` output, or
3379/// `https://github.com\foo\bar` after a Win32 shell mangled
3380/// the slashes, or the bare Windows-rooted path `C:\repo` into
3381/// a slot expecting a `file://` URL. libcurl's URL parser
3382/// (the layer `git clone <https-url>` invokes) silently
3383/// translates `\` → `/` on some platforms and refuses it on
3384/// others — the byte rides verbatim into the lacre's per-dep
3385/// content-address but is silently rewritten or rejected at
3386/// the wire, defeating the THEORY.md §V.2 render-determinism
3387/// contract on the same axis the `#` fragment / `?` query arms
3388/// close. The peer [`DepError::FonteCaminhoBackslash`] arm
3389/// (commit 3a4e1d7) closes the same byte on the sibling
3390/// `:fonte :caminho` path-fonte axis; this arm closes the
3391/// URL-grammar axis so every byte past `is_git_repo_url`
3392/// reaches `git clone`'s wire-format intact;
3393/// - no embedded `{` / `}` byte — RFC 3986 §2 excludes the pair
3394/// from URL syntax (they sit in the 'delims' / 'unwise' byte
3395/// set every URL parser is required to refuse or percent-
3396/// encode), and RFC 6570 reserves the matched pair for URI
3397/// Template placeholders (the canonical
3398/// `https://{host}/{org}/{repo}` substitution shape every
3399/// `OpenAPI` / Swagger / Postman / GitHub Octokit client library
3400/// / Helm chart-URL fragment carries). The canonical 'I forgot
3401/// to resolve the template placeholder' footgun: an author
3402/// pastes `:repo "https://github.com/{org}/{repo}"` from a
3403/// README quick-start snippet, an `OpenAPI` `servers:` URL, a
3404/// Helm chart's `home:` template, or the Mustache / Handlebars
3405/// `{{org}}/{{repo}}` doubled-brace substitution form every
3406/// CI / `IaC` templating engine emits, expecting the substrate
3407/// to resolve the placeholder downstream. libcurl percent-
3408/// encodes `{` / `}` to `%7B` / `%7D` on the wire so the byte
3409/// round-trips inconsistently between the lacre's per-dep
3410/// content-address and the resolver's `git clone <repo>`
3411/// invocation, defeating the THEORY.md §V.2 render-
3412/// determinism contract on the same axis the `#` fragment /
3413/// `?` query / `\` backslash arms close; every git porcelain
3414/// entry-point additionally fetches a nonexistent
3415/// `{placeholder}`-named path far from the source caixa.lisp;
3416/// - no embedded `<` / `>` byte — RFC 3986 §2 excludes the pair
3417/// from URL syntax under the same 'delims' / 'unwise' banner the
3418/// `{` / `}` arm cites, and no git URL grammar admits either byte:
3419/// the WHATWG URL spec's 'fragment percent-encode set' maps `<`
3420/// → `%3C` and `>` → `%3E` so every conformant URL parser
3421/// refuses or rewrites the literal byte on the wire. Beyond the
3422/// URL-grammar violation, every POSIX shell lexes `<` as the
3423/// input-redirection operator and `>` as the output-redirection
3424/// operator — the canonical paste-from-shell-prompt footgun the
3425/// peer [`DepError::FonteCaminhoShellRedirection`] arm
3426/// (commit e457141) closes on the sibling `:fonte :caminho`
3427/// path-fonte axis. The byte rides verbatim into the lacre's
3428/// per-dep content-address while libcurl percent-encodes it on
3429/// the wire — two authors whose `:repo` values differ only in
3430/// `<`/`>` presence resolve to the byte-identical upstream
3431/// `git clone` but lock to two distinct BLAKE3 closures,
3432/// defeating the THEORY.md §V.2 render-determinism contract on
3433/// the same axis the `#` fragment / `?` query / `\` backslash /
3434/// `{` / `}` template arms close;
3435/// - no embedded `` ` `` (backtick) byte — RFC 3986 §2 lists the
3436/// backtick in the 'delims' / 'unwise' set every URL parser is
3437/// required to refuse or percent-encode, and no git URL grammar
3438/// admits the byte: the WHATWG URL spec's 'fragment percent-
3439/// encode set' maps `` ` `` → `%60` so every conformant URL
3440/// parser refuses or rewrites the literal byte on the wire.
3441/// Beyond the URL-grammar violation, every POSIX shell lexes the
3442/// backtick as the legacy command-substitution operator
3443/// (`` `<cmd>` `` runs `<cmd>` in a subshell and substitutes its
3444/// stdout) — the canonical paste-from-shell-prompt RCE-class
3445/// footgun the peer [`crate::DepError::FonteCaminhoShellCommandSubstitution`]
3446/// arm (commit c4d62b3) closes on the sibling `:fonte :caminho`
3447/// path-fonte axis. The byte rides verbatim into the lacre's
3448/// per-dep content-address while libcurl percent-encodes it on
3449/// the wire — two authors whose `:repo` values differ only in
3450/// backtick presence resolve to the byte-identical upstream `git
3451/// clone` but lock to two distinct BLAKE3 closures, defeating
3452/// the THEORY.md §V.2 render-determinism contract on the same
3453/// axis the `#` fragment / `?` query / `\` backslash / `{` / `}`
3454/// template / `<` / `>` shell-redirection arms close;
3455/// - must contain a `:` separator at a non-leading position — every
3456/// documented form carries one (`github:org/repo`, `https://…`,
3457/// `ssh://…`, `git://…`, `file://…`, `git@host:path`); the
3458/// bare `org/repo` (no scheme) shape is ambiguous (could be a
3459/// filesystem path or a missing scheme) and silently passes
3460/// downstream git porcelain as a local relative path rather than
3461/// the intended GitHub-shorthand expansion. A leading `:` (`":foo"`)
3462/// is the canonical "empty scheme" footgun and is rejected too.
3463///
3464/// Returns the parser-shaped reason on rejection (without wrapping in
3465/// any error variant) so each per-axis caller — [`crate::DepError::FonteRepoShape`]
3466/// at validate time on the `:fonte :repo` axis, the future per-pin gate
3467/// on `lacre.lisp` resolved-repo axes, the future M4 per-dep CR
3468/// materializer's per-repo validator — wraps the same reason in its
3469/// own typed `*Invalid { axis, reason }` variant. The reason wording is
3470/// axis-agnostic ("git repo URLs reject whitespace") so every call site
3471/// reading the same diagnostic points at the same rule; drift between
3472/// any two axes' rule enforcement is a build error visible at this
3473/// predicate, not a per-resolver "this passed validate but `git clone`
3474/// rejected" surprise.
3475///
3476/// Empty input is rejected here (defensively) and at each call site via
3477/// the narrower [`crate::DepError::FonteRepoEmpty`] variant — the same
3478/// empty-first cascade [`is_dns_1123_label`], [`is_gateway_api_http_path`],
3479/// [`is_wit_world_ref`], [`is_nats_subject`], [`is_wasi_keyvalue_slot`],
3480/// [`is_git_ref_name`], and [`is_git_oid`] all carry.
3481///
3482/// Lifted as the seventh value-shape primitive in this module, peer with
3483/// [`is_git_ref_name`] (the `:fonte :tag` / `:fonte :branch` refname-
3484/// shaped axes) and [`is_git_oid`] (the `:fonte :rev` commit-OID axis) —
3485/// together they bracket the typed `:fonte` slot end-to-end: the
3486/// `:repo` URL axis (gate here), the refname-pin axes (gate via
3487/// `is_git_ref_name`), the OID-pin axis (gate via `is_git_oid`). Every
3488/// validated `:fonte (:tipo git …)` past `DepSource::validate` is
3489/// guaranteed-acceptable by the caixa-resolver's `git clone`/`git
3490/// fetch`/`git checkout` invocations, structurally — the parser-of-
3491/// record divergence the prior trajectory closed on the pin axes is
3492/// now closed on the last unsealed `:fonte` axis.
3493///
3494/// # Errors
3495///
3496/// Returns the parser-shaped reason naming the specific violation
3497/// (length / leading-`-` / whitespace / control-char / non-ASCII /
3498/// fragment-`#` / query-`?` / backslash-`\` / template-`{`-or-`}` /
3499/// shell-redirection-`<`-or-`>` / shell-command-substitution-backtick /
3500/// missing-`:` separator / leading-`:`), without wrapping in any error
3501/// variant — every caller maps the same `String` into its own typed
3502/// `*Invalid { axis, reason }` enum variant.
3503#[allow(
3504 clippy::too_many_lines,
3505 reason = "the per-byte rejection cascade is structurally flat by design — \
3506 every arm carries its own self-locating diagnostic with the offending \
3507 byte named verbatim plus the canonical paste-from-shape footgun the \
3508 gate closes, so collapsing onto a single shared `for &b in …` loop \
3509 would regress the per-arm `feira lint` consumer surface — peer with \
3510 the `clippy::too_many_lines` allow on `DepSource::validate_caminho` \
3511 (caixa-core/src/dep.rs:323) on the same cascade-shape rationale"
3512)]
3513pub fn is_git_repo_url(s: &str) -> Result<(), String> {
3514 if s.is_empty() {
3515 return Err("must not be empty".to_string());
3516 }
3517 if s.len() > GIT_REPO_URL_MAX_LEN {
3518 return Err(format!(
3519 "exceeds git repo URL max length of {GIT_REPO_URL_MAX_LEN} bytes \
3520 (got {} bytes; legitimate `github:org/repo` shorthands and \
3521 `https://…` / `ssh://…` / `git://…` / `file://…` URLs rarely \
3522 exceed ~128 bytes — this length suggests a paste-from-binary or \
3523 multi-line blob landed in the `:repo` slot)",
3524 s.len()
3525 ));
3526 }
3527 if s.starts_with('-') {
3528 return Err(
3529 "must not start with `-` (the canonical CLI-argument-injection \
3530 footgun — `git clone <repo>` interprets a leading `-` as a CLI \
3531 flag, so a `-upload-pack=…` / `--config=…` value escapes the \
3532 subprocess argument boundary; use a scheme prefix like \
3533 `github:org/repo`, `https://host/path`, `ssh://[user@]host/path`, \
3534 `git://host/path`, `git@host:path`, or `file:///path` for the \
3535 intended source)"
3536 .to_string(),
3537 );
3538 }
3539 for &b in s.as_bytes() {
3540 if b == b' ' || b == b'\t' {
3541 return Err(format!(
3542 "must not contain whitespace character {ch:?} (git repo URLs \
3543 are single tokens with no whitespace — a trailing space in a \
3544 `:repo` value is the canonical paste-from-doc footgun, \
3545 silently breaking `git clone '<value> '` at clone time)",
3546 ch = b as char
3547 ));
3548 }
3549 if b < 0x20 || b == 0x7F {
3550 return Err(format!(
3551 "must not contain control character 0x{b:02x} (git repo URLs \
3552 are printable ASCII; `\\r` / `\\n` are the canonical paste-\
3553 from-multiline-doc footgun and break git's URL parser at \
3554 every porcelain entry point, plus CRLF at the URL boundary \
3555 is a class of subprocess-arg injection)"
3556 ));
3557 }
3558 if b >= 0x80 {
3559 return Err(format!(
3560 "must not contain non-ASCII byte 0x{b:02x} (IDN hosts must be \
3561 pre-encoded as Punycode `xn--…`; raw non-ASCII silently \
3562 breaks at git's URL parser and round-trips inconsistently \
3563 across NFC/NFD normalization on APFS / case-folding \
3564 filesystems)"
3565 ));
3566 }
3567 if b == b'#' {
3568 return Err("must not contain `#` (RFC 3986 §3.5 URL fragment \
3569 identifier; libcurl's URL parser — the layer `git \
3570 clone <https-url>` invokes — strips the `#fragment` \
3571 tail before opening the transport, so the byte rides \
3572 verbatim into the lacre's per-dep content-address but \
3573 is silently dropped on the wire, defeating the \
3574 THEORY.md §V.2 render-determinism contract: two \
3575 authors whose `:repo` values differ only in their \
3576 fragment anchor (`#readme` vs `#L42`) resolve to the \
3577 byte-identical upstream `git clone` but lock to two \
3578 distinct BLAKE3 closures. The canonical \
3579 paste-from-browser-address-bar footgun (every web URL \
3580 to a README section / line-permalink carries one), \
3581 and the canonical \"I confused the Nix flake-ref \
3582 idiom (`github:foo/bar#packageName`) with the bare \
3583 git `:repo` shape\" footgun — `:repo` is a git URL, \
3584 not a Nix flake reference, so the `#`-suffix is \
3585 structurally meaningless on this axis. Drop the \
3586 `#fragment` tail; pin the ref via the typed `:tag` / \
3587 `:branch` / `:rev` slot instead)"
3588 .to_string());
3589 }
3590 if b == b'?' {
3591 return Err("must not contain `?` (RFC 3986 §3.4 URL query \
3592 component; every documented `:fonte :repo` shape \
3593 (`github:org/repo` shorthand, `https://…` / \
3594 `ssh://…` / `git://…` / `file://…` URL schemes, \
3595 `git@host:path` scp-style SSH) carries none. GitHub / \
3596 GitLab / Bitbucket all silently ignore the `?query` \
3597 tail on a repo URL and serve the same repo \
3598 regardless, so the byte rides verbatim into the \
3599 lacre's per-dep content-address but is silently \
3600 masked at the wire — two authors whose `:repo` \
3601 values differ only in their query tail \
3602 (`?tab=readme-ov-file` vs `?utm_source=twitter`) \
3603 resolve to the byte-identical upstream `git clone` \
3604 but lock to two distinct BLAKE3 closures, defeating \
3605 the THEORY.md §V.2 render-determinism contract on \
3606 the same axis the fragment-`#` arm closes. The \
3607 Smart-HTTP transport (the layer \
3608 `git clone <https-url>` uses) additionally appends \
3609 its own `?service=git-upload-pack` query internally; \
3610 an author-supplied `?` byte collides with that \
3611 internal axis at every git porcelain entry-point. \
3612 The canonical paste-from-browser-address-bar \
3613 footgun (`?tab=readme-ov-file` GitHub-tab deep-link, \
3614 `?ref=main` GitHub-tree-URL parameter, \
3615 `?utm_source=…` campaign-tracker every social-share / \
3616 newsletter / Slack-unfurl appends). Drop the \
3617 `?query` tail; pin the ref via the typed `:tag` / \
3618 `:branch` / `:rev` slot instead)"
3619 .to_string());
3620 }
3621 if b == b'\\' {
3622 return Err("must not contain `\\` (RFC 3986 §3.3 reserves \
3623 `/` as the URL path-segment separator; no URL grammar \
3624 admits `\\`. Every documented `:fonte :repo` shape \
3625 (`github:org/repo` shorthand, `https://…` / \
3626 `ssh://…` / `git://…` / `file://…` URL schemes, \
3627 `git@host:path` scp-style SSH) uses `/` as the path \
3628 separator. The canonical Windows-path-confusion \
3629 footgun: an author pastes `file:///C:\\Users\\me\\repo` \
3630 from a Windows Explorer address bar / PowerShell \
3631 `Get-Location` output, `https://github.com\\foo\\bar` \
3632 after a Win32 shell mangled the slashes, or the bare \
3633 Windows-rooted path `C:\\repo` into a slot expecting a \
3634 `file://` URL. libcurl's URL parser (the layer \
3635 `git clone <https-url>` invokes) silently translates \
3636 `\\` to `/` on some platforms and refuses it on others, \
3637 so the byte rides verbatim into the lacre's per-dep \
3638 content-address but is silently rewritten or rejected \
3639 at the wire, defeating the THEORY.md §V.2 render-\
3640 determinism contract on the same axis the fragment-`#` \
3641 and query-`?` arms close. The peer \
3642 `DepError::FonteCaminhoBackslash` arm (commit 3a4e1d7) \
3643 closes the same byte on the sibling `:fonte :caminho` \
3644 path-fonte axis; this arm closes the URL-grammar axis. \
3645 Drop the `\\` — use `/` for URL path separators, or \
3646 author the `file:///C:/path` form with forward slashes \
3647 (the canonical RFC 8089 file-URI shape on Windows-\
3648 rooted paths))"
3649 .to_string());
3650 }
3651 if b == b'{' || b == b'}' {
3652 return Err(format!(
3653 "must not contain `{ch}` (RFC 3986 §2 excludes `{{` / `}}` \
3654 from URL syntax — they sit in the 'delims' / 'unwise' \
3655 byte set every URL parser is required to refuse or \
3656 percent-encode; RFC 6570 reserves the matched pair for \
3657 URI Template placeholders (the canonical \
3658 `https://{{host}}/{{org}}/{{repo}}` substitution shape \
3659 every OpenAPI / Swagger / Postman / GitHub Octokit \
3660 client library / Helm chart-URL fragment carries). The \
3661 canonical 'I forgot to resolve the template \
3662 placeholder' footgun: an author pastes \
3663 `:repo \"https://github.com/{{org}}/{{repo}}\"` from a \
3664 README's quick-start snippet, an OpenAPI spec's \
3665 `servers:` URL, a Helm chart's `home:` template, or \
3666 the Mustache / Handlebars `{{{{org}}}}/{{{{repo}}}}` \
3667 doubled-brace substitution form every CI / IaC \
3668 templating engine emits, expecting the substrate to \
3669 resolve the placeholder downstream. libcurl percent-\
3670 encodes `{{` / `}}` to `%7B` / `%7D` on the wire (so \
3671 the byte round-trips inconsistently between the \
3672 lacre's per-dep content-address and the resolver's \
3673 `git clone <repo>` invocation, defeating the THEORY.md \
3674 §V.2 render-determinism contract on the same axis the \
3675 fragment-`#`, query-`?`, and backslash-`\\` arms close) \
3676 while every git porcelain entry-point fetches a \
3677 nonexistent literal-`{{placeholder}}`-named path far \
3678 from the source caixa.lisp. Resolve the placeholder at \
3679 author time — substitute the literal org / repo name \
3680 (`https://github.com/pleme-io/hello-rio`), or use \
3681 `:fonte (:tipo path :caminho \"<local-path>\")` for a \
3682 local workspace dep)",
3683 ch = b as char
3684 ));
3685 }
3686 if b == b'<' || b == b'>' {
3687 return Err(format!(
3688 "must not contain `{ch}` (RFC 3986 §2 excludes `<` / `>` \
3689 from URL syntax — they sit in the 'delims' / 'unwise' \
3690 byte set every URL parser is required to refuse or \
3691 percent-encode, peer with the `{{` / `}}` URI Template \
3692 arm on the same paragraph of the same RFC. No git URL \
3693 grammar admits either byte: the `github:org/repo` \
3694 shorthand carries an alphanumeric / `-` / `_` / `/` \
3695 alphabet, every `https://` / `ssh://` / `git://` / \
3696 `file://` URL scheme percent-encodes `<` to `%3C` and \
3697 `>` to `%3E` on the wire (the WHATWG URL spec's \
3698 'fragment percent-encode set' canonical mapping every \
3699 conformant URL parser applies), and the `git@host:path` \
3700 scp-style SSH shape names a POSIX path component that \
3701 carries no shell-metachar bytes. Beyond the URL-grammar \
3702 violation, every POSIX shell (sh / bash / zsh / dash / \
3703 ksh / fish / nushell) lexes `<` as the input-redirection \
3704 operator and `>` as the output-redirection operator — \
3705 a `:repo \"https://github.com/foo/bar>build.log\"` (the \
3706 canonical 'I pasted a shell pipeline that wrote build \
3707 output and forgot to trim the redirect' footgun) or \
3708 `:repo \"<README.md\"` (the symmetric input-redirection \
3709 paste idiom every doc-quick-start `git clone <…>` line \
3710 footnotes) is the canonical paste-from-shell-prompt \
3711 footgun the typed slot's accepted set must exclude. The \
3712 byte rides verbatim into the lacre's per-dep content-\
3713 address (`conteudo: format!(\"git:{{repo}}\")` peer of \
3714 the path-axis embedding at caixa-resolver/src/resolve.rs:189) \
3715 and into the resolver's `git clone <repo>` \
3716 (caixa-resolver/src/git.rs:21) subprocess invocation, \
3717 where libcurl's URL parser percent-encodes the byte on \
3718 the wire — so two authors whose `:repo` values differ \
3719 only in their `<`/`>` presence (one paste-trimmed the \
3720 redirect tail, the other didn't) resolve to the byte-\
3721 identical upstream `git clone` but lock to two distinct \
3722 BLAKE3 closures, defeating the THEORY.md §V.2 render-\
3723 determinism contract on the same axis the fragment-`#`, \
3724 query-`?`, backslash-`\\`, and template-`{{` / `}}` arms \
3725 close. The peer `:fonte :caminho` axis (e457141) closes \
3726 the same `<` / `>` byte under the shell-redirection \
3727 banner via `DepError::FonteCaminhoShellRedirection`; the \
3728 peer `:entrada :paths` axis closes the same bytes as part \
3729 of `is_gateway_api_http_path`'s eleven-byte RFC-3986-\
3730 reserved set; the peer `:fonte :tag` / `:fonte :branch` \
3731 axes (e70d213) close the same bytes as part of \
3732 `is_git_ref_name`'s shell-metachar-injection cascade. \
3733 The `:repo` URL axis was the last typed git-source \
3734 surface still admitting these two bytes; this arm closes \
3735 the gap so the substrate-wide 'no shell-redirection / \
3736 RFC-3986-unwise byte anywhere in a typed git-source slot' \
3737 invariant is now structurally consistent across every \
3738 git-source-shaped typed surface. Drop the `<` / `>` tail \
3739 — pin the ref via the typed `:tag` / `:branch` / `:rev` \
3740 slot, or use `:fonte (:tipo path :caminho \"<local-path>\")` \
3741 for a local workspace dep)",
3742 ch = b as char
3743 ));
3744 }
3745 if b == b'`' {
3746 return Err(
3747 "must not contain `` ` `` (RFC 3986 §2 lists the backtick byte \
3748 in the 'delims' / 'unwise' set every URL parser is required \
3749 to refuse or percent-encode, peer with the `<` / `>` \
3750 shell-redirection arm on the same paragraph of the same RFC. \
3751 No git URL grammar admits the byte: the `github:org/repo` \
3752 shorthand carries an alphanumeric / `-` / `_` / `/` alphabet, \
3753 every `https://` / `ssh://` / `git://` / `file://` URL scheme \
3754 percent-encodes `` ` `` to `%60` on the wire (the WHATWG URL \
3755 spec's 'fragment percent-encode set' canonical mapping every \
3756 conformant URL parser applies), and the `git@host:path` \
3757 scp-style SSH shape names a POSIX path component that \
3758 carries no shell-metachar bytes. Beyond the URL-grammar \
3759 violation, every POSIX shell (sh / bash / zsh / dash / ksh / \
3760 fish) lexes the backtick as the legacy command-substitution \
3761 operator — `` `<cmd>` `` runs `<cmd>` in a subshell and \
3762 substitutes its stdout, the canonical RCE-class injection \
3763 vector when a string lands in a shell context. A `:repo \
3764 \"https://github.com/foo/`whoami`/bar\"` (the canonical \
3765 paste-from-shell-prompt footgun where the author copies a \
3766 backtick-templated URL from a doc / README quick-start \
3767 snippet that expected the substrate to substitute the value \
3768 downstream) or the symmetric `:repo \"`git config user.name`\"` \
3769 (the dynamic-config-substitution paste idiom every \
3770 dev-environment-setup script footnotes) is the canonical \
3771 paste-from-shell-prompt footgun the typed slot's accepted \
3772 set must exclude. The byte rides verbatim into the lacre's \
3773 per-dep content-address (`conteudo: format!(\"git:{repo}\")` \
3774 peer of the path-axis embedding at \
3775 caixa-resolver/src/resolve.rs) and into the resolver's `git \
3776 clone <repo>` (caixa-resolver/src/git.rs) subprocess \
3777 invocation, where libcurl's URL parser percent-encodes the \
3778 byte on the wire — so two authors whose `:repo` values \
3779 differ only in their backtick presence (one paste-trimmed \
3780 the substitution wrapper, the other didn't) resolve to the \
3781 byte-identical upstream `git clone` but lock to two distinct \
3782 BLAKE3 closures, defeating the THEORY.md §V.2 render-\
3783 determinism contract on the same axis the fragment-`#`, \
3784 query-`?`, backslash-`\\`, template-`{` / `}`, and \
3785 shell-redirection-`<` / `>` arms close. The peer `:fonte \
3786 :caminho` axis (c4d62b3) closes the same byte under the \
3787 shell-command-substitution banner via \
3788 `DepError::FonteCaminhoShellCommandSubstitution`; the peer \
3789 `:entrada :paths` axis closes the same byte as part of \
3790 `is_gateway_api_http_path`'s eleven-byte RFC-3986-reserved \
3791 set. Drop the backtick wrapper — substitute the literal \
3792 value at author time, or use `:fonte (:tipo path :caminho \
3793 \"<local-path>\")` for a local workspace dep)"
3794 .to_string(),
3795 );
3796 }
3797 if b == b'|' {
3798 return Err("must not contain `|` (RFC 3986 §2 lists the pipe byte in \
3799 the 'unwise' set every URL parser is required to refuse or \
3800 percent-encode, peer with the `{` / `}` URI Template, \
3801 `<` / `>` shell-redirection, and `` ` `` shell-command-\
3802 substitution arms on the same paragraph of the same RFC. \
3803 No git URL grammar admits the byte: the `github:org/repo` \
3804 shorthand carries an alphanumeric / `-` / `_` / `/` \
3805 alphabet, every `https://` / `ssh://` / `git://` / \
3806 `file://` URL scheme percent-encodes `|` to `%7C` on the \
3807 wire (the WHATWG URL spec's 'fragment percent-encode set' \
3808 canonical mapping every conformant URL parser applies), \
3809 and the `git@host:path` scp-style SSH shape names a POSIX \
3810 path component that carries no shell-metachar bytes. \
3811 Beyond the URL-grammar violation, every POSIX shell (sh / \
3812 bash / zsh / dash / ksh / fish / nushell) lexes `|` as the \
3813 pipe operator — `<cmd1> | <cmd2>` streams cmd1's stdout to \
3814 cmd2's stdin, the canonical command-chaining injection \
3815 vector when a string lands in a shell context. A `:repo \
3816 \"https://github.com/foo/bar|tee build.log\"` (the \
3817 canonical 'I pasted a shell pipeline that tee'd build \
3818 output and forgot to trim the pipe tail' footgun) or \
3819 `:repo \"github:p/x|cat\"` (the symmetric paste-from-\
3820 shell-prompt idiom every quick-start `git clone <…> | …` \
3821 line footnotes) is the canonical paste-from-shell-prompt \
3822 footgun the typed slot's accepted set must exclude. The \
3823 byte rides verbatim into the lacre's per-dep content-\
3824 address (`conteudo: format!(\"git:{repo}\")` peer of the \
3825 path-axis embedding at caixa-resolver/src/resolve.rs) and \
3826 into the resolver's `git clone <repo>` \
3827 (caixa-resolver/src/git.rs) subprocess invocation, where \
3828 libcurl's URL parser percent-encodes the byte on the wire \
3829 — so two authors whose `:repo` values differ only in \
3830 their pipe presence (one paste-trimmed the pipeline tail, \
3831 the other didn't) resolve to the byte-identical upstream \
3832 `git clone` but lock to two distinct BLAKE3 closures, \
3833 defeating the THEORY.md §V.2 render-determinism contract \
3834 on the same axis the fragment-`#`, query-`?`, backslash-\
3835 `\\`, template-`{` / `}`, shell-redirection-`<` / `>`, \
3836 and backtick-`` ` `` arms close. The peer `:fonte \
3837 :caminho` axis (124106f) closes the same byte under the \
3838 shell-pipe banner via `DepError::FonteCaminhoShellPipe`; \
3839 the peer `:entrada :paths` axis closes the same byte as \
3840 part of `is_gateway_api_http_path`'s eleven-byte \
3841 RFC-3986-reserved set; the peer `:fonte :tag` / `:fonte \
3842 :branch` axes close the same byte as part of \
3843 `is_git_ref_name`'s shell-metachar-injection cascade. \
3844 Drop the pipe tail — substitute the literal value at \
3845 author time, or use `:fonte (:tipo path :caminho \
3846 \"<local-path>\")` for a local workspace dep)"
3847 .to_string());
3848 }
3849 if b == b';' {
3850 return Err("must not contain `;` (RFC 3986 §2 lists the semicolon \
3851 byte in the 'sub-delims' / reserved set every URL parser is \
3852 required to percent-encode at the path-segment boundary, peer \
3853 with the `{` / `}` URI Template, `<` / `>` shell-redirection, \
3854 `` ` `` shell-command-substitution, and `|` shell-pipe arms on \
3855 the same paragraph of the same RFC. No git URL grammar admits \
3856 the byte: the `github:org/repo` shorthand carries an \
3857 alphanumeric / `-` / `_` / `/` alphabet, every `https://` / \
3858 `ssh://` / `git://` / `file://` URL scheme percent-encodes `;` \
3859 to `%3B` on the wire (the WHATWG URL spec's 'fragment percent-\
3860 encode set' canonical mapping every conformant URL parser \
3861 applies), and the `git@host:path` scp-style SSH shape names a \
3862 POSIX path component that carries no shell-metachar bytes. \
3863 Beyond the URL-grammar violation, every POSIX shell (sh / \
3864 bash / zsh / dash / ksh / fish / nushell) lexes `;` as the \
3865 sequential-command terminator — `<cmd1>; <cmd2>` fires `<cmd2>` \
3866 regardless of `<cmd1>`'s exit status, the canonical \
3867 command-chaining injection vector when a string lands in a \
3868 shell context. A `:repo \
3869 \"https://github.com/foo/bar; rm -rf build\"` (the canonical \
3870 'I pasted a shell one-liner that chained a cleanup tail after \
3871 the URL and forgot to trim the `; <cmd>` tail' footgun) or \
3872 `:repo \"github:p/x;;y\"` (the symmetric paste-from-POSIX-\
3873 `case`-arm `;;` terminator idiom every shell-snippet footnotes) \
3874 is the canonical paste-from-shell-prompt footgun the typed \
3875 slot's accepted set must exclude. The byte rides verbatim into \
3876 the lacre's per-dep content-address (`conteudo: \
3877 format!(\"git:{repo}\")` peer of the path-axis embedding at \
3878 caixa-resolver/src/resolve.rs) and into the resolver's `git \
3879 clone <repo>` (caixa-resolver/src/git.rs) subprocess \
3880 invocation, where libcurl's URL parser percent-encodes the \
3881 byte on the wire — so two authors whose `:repo` values differ \
3882 only in their semicolon presence (one paste-trimmed the \
3883 sequential-command tail, the other didn't) resolve to the \
3884 byte-identical upstream `git clone` but lock to two distinct \
3885 BLAKE3 closures, defeating the THEORY.md §V.2 render-\
3886 determinism contract on the same axis the fragment-`#`, \
3887 query-`?`, backslash-`\\`, template-`{` / `}`, \
3888 shell-redirection-`<` / `>`, backtick-`` ` ``, and \
3889 shell-pipe-`|` arms close. The peer `:fonte :caminho` axis \
3890 (05c358e) closes the same byte under the shell-command-\
3891 separator banner via `DepError::FonteCaminhoShellSemicolon`; \
3892 the peer `:entrada :paths` axis closes the same byte as part \
3893 of `is_gateway_api_http_path`'s eleven-byte RFC-3986-reserved \
3894 set; the peer `:fonte :tag` / `:fonte :branch` axes close the \
3895 same byte as part of `is_git_ref_name`'s shell-metachar-\
3896 injection cascade. Drop the `;` tail — substitute the literal \
3897 value at author time, or use `:fonte (:tipo path :caminho \
3898 \"<local-path>\")` for a local workspace dep)"
3899 .to_string());
3900 }
3901 if b == b'&' {
3902 return Err("must not contain `&` (RFC 3986 §2 lists the ampersand \
3903 byte in the 'sub-delims' / reserved set every URL parser is \
3904 required to percent-encode at the path-segment boundary, peer \
3905 with the `{` / `}` URI Template, `<` / `>` shell-redirection, \
3906 `` ` `` shell-command-substitution, `|` shell-pipe, and `;` \
3907 shell-command-separator arms on the same paragraph of the same \
3908 RFC. The byte is also the canonical RFC 3986 §3.4 URL query \
3909 `key=value` pair separator (`?a=1&b=2`), but the prior `?` arm \
3910 already excludes any `?query` tail on a `:repo` value — every \
3911 documented `:fonte :repo` shape (`github:org/repo` shorthand, \
3912 `https://…` / `ssh://…` / `git://…` / `file://…` URL schemes, \
3913 `git@host:path` scp-style SSH) carries no query component, so \
3914 the `&` byte cannot appear in a legitimate query position past \
3915 the `?` gate either. Every `https://` / `ssh://` / `git://` / \
3916 `file://` URL scheme percent-encodes `&` to `%26` on the wire \
3917 (the WHATWG URL spec's 'fragment percent-encode set' canonical \
3918 mapping every conformant URL parser applies), and the \
3919 `git@host:path` scp-style SSH shape names a POSIX path \
3920 component that carries no shell-metachar bytes. Beyond the \
3921 URL-grammar violation, every interactive shell (bash / zsh / \
3922 fish / nushell) lexes `&` two ways: single `&` as the \
3923 background-task terminator that detaches the prior command \
3924 into the background and returns control to the prompt \
3925 immediately (the canonical `cmd &` idiom every long-running \
3926 pipeline uses), and double `&&` as the logical-AND list \
3927 operator that fires the next command only if the prior \
3928 command succeeded (the canonical `make && make install` idiom \
3929 every build script carries). A `:repo \
3930 \"https://github.com/foo/bar & sleep 1\"` (the canonical \
3931 'I pasted a `git clone <url> & sleep 1` background-launch \
3932 one-liner and forgot to trim the `& <cmd>` tail' footgun) or \
3933 `:repo \"github:p/x && echo done\"` (the symmetric \
3934 paste-from-shell-prompt `cd path && cmd` build-chain idiom \
3935 every quick-start `git clone <…> && cd <…>` line footnotes) \
3936 is the canonical paste-from-shell-prompt footgun the typed \
3937 slot's accepted set must exclude. The byte rides verbatim \
3938 into the lacre's per-dep content-address (`conteudo: \
3939 format!(\"git:{repo}\")` peer of the path-axis embedding at \
3940 caixa-resolver/src/resolve.rs) and into the resolver's `git \
3941 clone <repo>` (caixa-resolver/src/git.rs) subprocess \
3942 invocation, where libcurl's URL parser percent-encodes the \
3943 byte on the wire — so two authors whose `:repo` values \
3944 differ only in their ampersand presence (one paste-trimmed \
3945 the background-launch tail, the other didn't) resolve to \
3946 the byte-identical upstream `git clone` but lock to two \
3947 distinct BLAKE3 closures, defeating the THEORY.md §V.2 \
3948 render-determinism contract on the same axis the \
3949 fragment-`#`, query-`?`, backslash-`\\`, template-`{` / `}`, \
3950 shell-redirection-`<` / `>`, backtick-`` ` ``, shell-pipe-`|`, \
3951 and shell-command-separator-`;` arms close. The peer `:fonte \
3952 :caminho` axis (e12e4f3) closes the same byte under the \
3953 shell-background / logical-AND banner via \
3954 `DepError::FonteCaminhoShellBackground`; the peer `:entrada \
3955 :paths` axis closes the same byte as part of \
3956 `is_gateway_api_http_path`'s eleven-byte RFC-3986-reserved \
3957 set; the peer `:fonte :tag` / `:fonte :branch` axes close \
3958 the same byte as part of `is_git_ref_name`'s shell-metachar-\
3959 injection cascade. Drop the `&` tail — substitute the literal \
3960 value at author time, or use `:fonte (:tipo path :caminho \
3961 \"<local-path>\")` for a local workspace dep)"
3962 .to_string());
3963 }
3964 if b == b'$' {
3965 return Err("must not contain `$` (RFC 3986 §2 lists the dollar \
3966 byte in the 'sub-delims' / reserved set every URL parser is \
3967 required to percent-encode at the path-segment boundary, peer \
3968 with the `;` shell-command-separator and `&` shell-background \
3969 arms on the same paragraph of the same RFC. No git URL grammar \
3970 admits the byte: the `github:org/repo` shorthand carries an \
3971 alphanumeric / `-` / `_` / `/` alphabet, every `https://` / \
3972 `ssh://` / `git://` / `file://` URL scheme percent-encodes `$` \
3973 to `%24` on the wire (the WHATWG URL spec's 'fragment percent-\
3974 encode set' canonical mapping every conformant URL parser \
3975 applies), and the `git@host:path` scp-style SSH shape names a \
3976 POSIX path component that carries no shell-metachar bytes. \
3977 Beyond the URL-grammar violation, every POSIX shell (sh / \
3978 bash / zsh / dash / ksh / fish / nushell) lexes `$` as the \
3979 variable-expansion / command-substitution operator: `$<name>` \
3980 / `${{<name>}}` expands a named variable, `$(<cmd>)` runs a \
3981 subshell and substitutes its stdout, and `$((<expr>))` \
3982 evaluates an arithmetic expression — every form is a \
3983 host-layout / environment-state leak when the byte lands in \
3984 a value the resolver passes to a shell-spawned subprocess. A \
3985 `:repo \"https://github.com/$ORG/caixa-teia\"` (the canonical \
3986 'I pasted a shell one-liner that expanded `$ORG` against the \
3987 author's local environment and forgot to substitute the \
3988 literal org name' footgun, identical to the f4efe9c peer arm \
3989 on the sibling `:caminho` axis that closes `\"$HOME/work/…\"` \
3990 / `\"${{WORKSPACE}}/…\"`) or `:repo \"github:p/$(whoami)/x\"` \
3991 (the symmetric paste-from-shell-prompt command-substitution \
3992 idiom every dev-environment-setup script footnotes) is the \
3993 canonical paste-from-shell-prompt footgun the typed slot's \
3994 accepted set must exclude. The byte rides verbatim into the \
3995 lacre's per-dep content-address (`conteudo: \
3996 format!(\"git:{repo}\")` peer of the path-axis embedding at \
3997 caixa-resolver/src/resolve.rs) and into the resolver's `git \
3998 clone <repo>` (caixa-resolver/src/git.rs) subprocess \
3999 invocation, where libcurl's URL parser percent-encodes the \
4000 byte on the wire — so two authors whose `:repo` values \
4001 differ only in their dollar presence (one substituted the \
4002 literal value at author time, the other didn't) resolve to \
4003 the byte-identical upstream `git clone` but lock to two \
4004 distinct BLAKE3 closures, defeating the THEORY.md §V.2 \
4005 render-determinism contract on the same axis the \
4006 fragment-`#`, query-`?`, backslash-`\\`, template-`{` / `}`, \
4007 shell-redirection-`<` / `>`, backtick-`` ` ``, shell-pipe-`|`, \
4008 shell-command-separator-`;`, and shell-background-`&` arms \
4009 close. Beyond the determinism axis, a value like \
4010 `\"github:$HOME/x\"` is a structural host-layout leak: two \
4011 authors with the same `:repo` slot but different `$HOME` \
4012 / `$WORKSPACE` / `$PWD` resolve different upstream URLs at \
4013 different times — the lacre, far from being a substrate-wide \
4014 identity, becomes a per-workstation snapshot of the author's \
4015 shell environment. The peer `:fonte :caminho` axis (f4efe9c) \
4016 closes the leading-`$` byte under the shell-variable-\
4017 expansion banner via `DepError::FonteCaminhoVarExpansion`; \
4018 the peer `:entrada :paths` axis closes the same byte as part \
4019 of `is_gateway_api_http_path`'s eleven-byte RFC-3986-reserved \
4020 set; the peer `:fonte :tag` / `:fonte :branch` axes close \
4021 the same byte as part of `is_git_ref_name`'s shell-metachar-\
4022 injection cascade — the `:caminho` axis closes only the \
4023 leading position because absolute / tilde / var arms there \
4024 are leading-byte sentinels, but the `:repo` URL axis closes \
4025 the byte anywhere because every per-byte arm on this surface \
4026 is positional-agnostic (the substitution / leak shapes \
4027 `\"https://$HOST/p/x\"` and `\"github:p/$(whoami)\"` both \
4028 carry the byte mid-string). Drop the `$` — substitute the \
4029 literal value at author time, or use `:fonte (:tipo path \
4030 :caminho \"<local-path>\")` for a local workspace dep)"
4031 .to_string());
4032 }
4033 if b == b'*' {
4034 return Err("must not contain `*` (RFC 3986 §2 lists the asterisk \
4035 byte in the 'sub-delims' / reserved set every URL parser is \
4036 required to percent-encode at the path-segment boundary, peer \
4037 with the `;` shell-command-separator, `&` shell-background, \
4038 and `$` shell-variable-expansion arms on the same paragraph of \
4039 the same RFC. No git URL grammar admits the byte: the \
4040 `github:org/repo` shorthand carries an alphanumeric / `-` / \
4041 `_` / `/` alphabet, every `https://` / `ssh://` / `git://` / \
4042 `file://` URL scheme percent-encodes `*` to `%2A` on the wire \
4043 (the WHATWG URL spec's 'special-query percent-encode set' \
4044 canonical mapping every conformant URL parser applies), and \
4045 the `git@host:path` scp-style SSH shape names a POSIX path \
4046 component that carries no shell-metachar bytes. Beyond the \
4047 URL-grammar violation, every POSIX shell (sh / bash / zsh / \
4048 dash / ksh / fish / nushell) lexes `*` as the \
4049 pathname-expansion / glob wildcard operator: a single `*` \
4050 matches any sequence of characters in a path component \
4051 (including the empty sequence), `**` matches across `/` \
4052 boundaries under bash's `globstar` shopt, and `foo*` resolves \
4053 against the cwd-relative filesystem at command-substitution \
4054 time. Beyond shell glob semantics, git itself lexes `*` as \
4055 the refspec wildcard operator (`refs/heads/*:refs/remotes/\
4056 origin/*` — the same byte the peer `is_git_ref_name` \
4057 predicate refuses on `:fonte :tag` / `:fonte :branch`), so a \
4058 `:repo` value carrying `*` is structurally ambiguous with \
4059 every refspec parser the resolver invokes downstream. A \
4060 `:repo \"https://github.com/pleme-io/caixa-*\"` (the canonical \
4061 'I pasted a `ls github.com/pleme-io/caixa-*` shell-listing \
4062 tail and forgot to substitute the literal repo name' \
4063 footgun, identical to the cf9034b peer arm on the sibling \
4064 `:caminho` axis that closes `\"../caixa-teia/*\"`) or `:repo \
4065 \"github:p/*\"` (the symmetric paste-from-shell-prompt \
4066 glob-expansion idiom every quick-listing one-liner footnotes) \
4067 is the canonical paste-from-shell-prompt footgun the typed \
4068 slot's accepted set must exclude. The byte rides verbatim \
4069 into the lacre's per-dep content-address (`conteudo: \
4070 format!(\"git:{repo}\")` peer of the path-axis embedding at \
4071 caixa-resolver/src/resolve.rs) and into the resolver's `git \
4072 clone <repo>` (caixa-resolver/src/git.rs) subprocess \
4073 invocation, where libcurl's URL parser percent-encodes the \
4074 byte on the wire — so two authors whose `:repo` values \
4075 differ only in their asterisk presence (one substituted the \
4076 literal repo name at author time, the other didn't) resolve \
4077 to the byte-identical upstream `git clone` but lock to two \
4078 distinct BLAKE3 closures, defeating the THEORY.md §V.2 \
4079 render-determinism contract on the same axis the \
4080 fragment-`#`, query-`?`, backslash-`\\`, template-`{` / `}`, \
4081 shell-redirection-`<` / `>`, backtick-`` ` ``, shell-pipe-`|`, \
4082 shell-command-separator-`;`, shell-background-`&`, and \
4083 shell-variable-expansion-`$` arms close. The peer `:fonte \
4084 :caminho` axis (cf9034b) closes the same byte under the \
4085 shell-glob / pathname-expansion banner via \
4086 `DepError::FonteCaminhoShellGlob`; the peer `:fonte :tag` / \
4087 `:fonte :branch` axes close the same byte as part of \
4088 `is_git_ref_name`'s refspec-wildcard cascade. Drop the `*` — \
4089 substitute the literal repo name at author time, or use \
4090 `:fonte (:tipo path :caminho \"<local-path>\")` for a local \
4091 workspace dep)"
4092 .to_string());
4093 }
4094 if b == b'(' || b == b')' {
4095 return Err(format!(
4096 "must not contain `{ch}` (RFC 3986 §2 excludes `(` / `)` \
4097 from URL syntax — they sit in the 'sub-delims' / reserved \
4098 byte set every URL parser is required to percent-encode at \
4099 the path-segment boundary, peer with the `;` \
4100 shell-command-separator, `&` shell-background, `$` \
4101 shell-variable-expansion, and `*` shell-glob arms on the \
4102 same paragraph of the same RFC. No git URL grammar admits \
4103 either byte: the `github:org/repo` shorthand carries an \
4104 alphanumeric / `-` / `_` / `/` alphabet, every `https://` / \
4105 `ssh://` / `git://` / `file://` URL scheme percent-encodes \
4106 `(` to `%28` and `)` to `%29` on the wire (the WHATWG URL \
4107 spec's 'special-query percent-encode set' canonical mapping \
4108 every conformant URL parser applies), and the \
4109 `git@host:path` scp-style SSH shape names a POSIX path \
4110 component that carries no shell-metachar bytes. Beyond the \
4111 URL-grammar violation, every POSIX shell (sh / bash / zsh / \
4112 dash / ksh / fish / nushell) lexes `(` / `)` as the \
4113 subshell-grouping operator: `(<cmd>)` runs `<cmd>` in a \
4114 child shell with a fresh environment scope (the canonical \
4115 idiom for sandboxing a `cd` or variable assignment), and \
4116 `$(<cmd>)` is the modern Bourne command-substitution shape \
4117 the prior `$` arm closes the leading byte of — the closing \
4118 `)` byte completes that substitution shape and must be \
4119 refused on the same axis. The byte pair is additionally the \
4120 canonical regex-alternation grouping operator (`(foo|bar)`) \
4121 every doc / README quick-start snippet folds into a paste-\
4122 from-doc footgun shape, and the bash brace-expansion \
4123 alternation form (`{{foo,bar}}`) the prior `{{` / `}}` URI \
4124 Template arm closes on the curly-brace axis routes the \
4125 same alternation intent through the parenthesis axis on \
4126 every POSIX-portable script. A `:repo \
4127 \"https://github.com/(foo|bar)/repo\"` (the canonical 'I \
4128 pasted a regex-alternation form from a doc / README and \
4129 forgot to substitute one literal org' footgun) or `:repo \
4130 \"github:p/x(date)\"` (the symmetric paste-from-shell-\
4131 prompt subshell-grouping idiom every dynamic-config-\
4132 substitution one-liner footnotes) is the canonical paste-\
4133 from-shell-prompt footgun the typed slot's accepted set \
4134 must exclude. The byte rides verbatim into the lacre's \
4135 per-dep content-address (`conteudo: \
4136 format!(\"git:{{repo}}\")` peer of the path-axis embedding \
4137 at caixa-resolver/src/resolve.rs) and into the resolver's \
4138 `git clone <repo>` (caixa-resolver/src/git.rs) subprocess \
4139 invocation, where libcurl's URL parser percent-encodes the \
4140 byte on the wire — so two authors whose `:repo` values \
4141 differ only in their parenthesis presence (one paste-\
4142 trimmed the grouping wrapper, the other didn't) resolve to \
4143 the byte-identical upstream `git clone` but lock to two \
4144 distinct BLAKE3 closures, defeating the THEORY.md §V.2 \
4145 render-determinism contract on the same axis the \
4146 fragment-`#`, query-`?`, backslash-`\\`, template-`{{` / \
4147 `}}`, shell-redirection-`<` / `>`, backtick-`` ` ``, \
4148 shell-pipe-`|`, shell-command-separator-`;`, shell-\
4149 background-`&`, shell-variable-expansion-`$`, and shell-\
4150 glob-`*` arms close. Drop the `(` / `)` wrapper — \
4151 substitute the literal value at author time, or use \
4152 `:fonte (:tipo path :caminho \"<local-path>\")` for a local \
4153 workspace dep)",
4154 ch = b as char
4155 ));
4156 }
4157 if b == b'"' {
4158 return Err("must not contain `\"` (RFC 3986 §2 lists the \
4159 double-quote byte in the 'delims' set every URL parser is \
4160 required to refuse or percent-encode, peer with the `<` / \
4161 `>` shell-redirection and `` ` `` shell-command-substitution \
4162 arms on the same paragraph of the same RFC — the four-byte \
4163 'delims' subset (`<`, `>`, `\"`, `` ` ``) is the strictest \
4164 of the §2 reserved classes, every member structurally \
4165 incompatible with every URL grammar at every position. No \
4166 git URL grammar admits the byte: the `github:org/repo` \
4167 shorthand carries an alphanumeric / `-` / `_` / `/` \
4168 alphabet, every `https://` / `ssh://` / `git://` / \
4169 `file://` URL scheme percent-encodes `\"` to `%22` on the \
4170 wire (the WHATWG URL spec's 'C0 control percent-encode \
4171 set' canonical mapping every conformant URL parser \
4172 applies), and the `git@host:path` scp-style SSH shape \
4173 names a POSIX path component that carries no \
4174 shell-metachar bytes. Beyond the URL-grammar violation, \
4175 every POSIX shell (sh / bash / zsh / dash / ksh / fish / \
4176 nushell) lexes `\"` as the double-quote string delimiter — \
4177 a `\"<text>\"` form suppresses word-splitting and \
4178 pathname-expansion on `<text>` while still expanding `$`, \
4179 `` ` ``, and `\\` substitutions inside, the canonical \
4180 'quote the URL so the shell doesn't re-lex the bytes' \
4181 idiom every doc / README quick-start snippet wraps the \
4182 URL argument with. A `:repo \
4183 \"\\\"https://github.com/pleme-io/caixa-teia\\\"\"` (the \
4184 canonical paste-from-doc footgun where the author copies \
4185 `$ git clone \"https://…\"` from a README's quick-start \
4186 snippet and keeps the surrounding double-quote bytes — \
4187 the doc quotes the URL so the shell doesn't re-lex \
4188 metachars inside, but the typed slot is itself a \
4189 byte-level string parser, not a shell context, so the \
4190 quote bytes ride into the value verbatim) or `:repo \
4191 \"github:p/x\\\"tail\"` (the symmetric stray-quote paste \
4192 idiom every shell-history `git clone …` line footnotes) \
4193 is the canonical paste-from-shell-quoting footgun the \
4194 typed slot's accepted set must exclude. The byte rides \
4195 verbatim into the lacre's per-dep content-address \
4196 (`conteudo: format!(\"git:{repo}\")` peer of the path-\
4197 axis embedding at caixa-resolver/src/resolve.rs) and into \
4198 the resolver's `git clone <repo>` \
4199 (caixa-resolver/src/git.rs) subprocess invocation, where \
4200 libcurl's URL parser percent-encodes the byte on the wire \
4201 — so two authors whose `:repo` values differ only in \
4202 their double-quote presence (one paste-trimmed the quote \
4203 wrapper, the other didn't) resolve to the byte-identical \
4204 upstream `git clone` but lock to two distinct BLAKE3 \
4205 closures, defeating the THEORY.md §V.2 render-determinism \
4206 contract on the same axis the fragment-`#`, query-`?`, \
4207 backslash-`\\`, template-`{` / `}`, shell-redirection-\
4208 `<` / `>`, backtick-`` ` ``, shell-pipe-`|`, \
4209 shell-command-separator-`;`, shell-background-`&`, \
4210 shell-variable-expansion-`$`, shell-glob-`*`, and \
4211 shell-subshell-grouping-`(` / `)` arms close. The peer \
4212 `:entrada :paths` axis closes the same byte as part of \
4213 `is_gateway_api_http_path`'s RFC-3986-reserved set; the \
4214 `:fonte :tag` / `:fonte :branch` axes close the same byte \
4215 as part of `is_git_ref_name`'s shell-metachar-injection \
4216 cascade. Drop the `\"` wrapper — paste only the URL \
4217 between the quotes, or use `:fonte (:tipo path :caminho \
4218 \"<local-path>\")` for a local workspace dep)"
4219 .to_string());
4220 }
4221 if b == b'\'' {
4222 return Err("must not contain `'` (RFC 3986 §2.2 lists the \
4223 single-quote byte in the 'sub-delims' set the URL grammar \
4224 admits inside a path segment but every WHATWG-conformant \
4225 special-scheme URL parser percent-encodes inside a query \
4226 component via the 'special-query percent-encode set' — \
4227 the peer position the prior `*` / `(` / `)` 'sub-delims' \
4228 arms close and the partner ASCII string-delimiter to the \
4229 `\"` 'delims' double-quote byte the prior arm closes. The \
4230 byte is the second ASCII shell-string-delimiter — `\"` \
4231 and `'` are the only two ASCII bytes a byte-level string \
4232 parser sharing a value-shape with a shell argument must \
4233 refuse on a URL-shaped slot for paste-from-doc safety. No \
4234 documented `:fonte :repo` shape admits the byte: the \
4235 `github:org/repo` shorthand carries an alphanumeric / `-` \
4236 / `_` / `/` alphabet, every `https://` / `ssh://` / \
4237 `git://` / `file://` URL scheme keeps host / path bodies \
4238 inside the `unreserved` alphanumeric / `-` / `.` / `_` / \
4239 `~` set that excludes the byte, and the `git@host:path` \
4240 scp-style SSH shape names a POSIX path component that \
4241 carries no shell-metachar bytes. Every POSIX shell (sh / \
4242 bash / zsh / dash / ksh / fish / nushell) lexes `'` as \
4243 the single-quote / strong-quote string delimiter — a \
4244 `'<text>'` form suppresses every form of expansion on \
4245 `<text>` (no `$`, no `` ` ``, no `\\`, no glob, no \
4246 word-splitting), the canonical 'strong-quote the URL so \
4247 the shell doesn't re-lex anything inside' idiom every \
4248 doc / README quick-start snippet wraps the URL argument \
4249 with as the stricter, security-conscious alternative to \
4250 the `\"…\"` weak-quote shape the prior arm closes. A \
4251 `:repo \"'https://github.com/pleme-io/caixa-teia'\"` (the \
4252 canonical paste-from-doc-shell-quoting footgun where the \
4253 author copies `$ git clone 'https://…'` from a README's \
4254 quick-start snippet and keeps the surrounding strong-\
4255 quote bytes — the doc strong-quotes the URL so the shell \
4256 doesn't re-lex any metachars inside, but the typed slot \
4257 is itself a byte-level string parser, not a shell \
4258 context, so the quote bytes ride into the value verbatim; \
4259 the strong-quote idiom is more common than `\"…\"` in \
4260 security-conscious docs because it forecloses every \
4261 expansion the weak-quote form still admits inside) or \
4262 `:repo \"github:p/x'tail\"` (the symmetric stray-quote \
4263 paste idiom every shell-history `git clone …` line \
4264 carries when the author paste-trimmed one boundary but \
4265 not the other) is the canonical paste-from-shell-quoting \
4266 footgun the typed slot's accepted set must exclude. The \
4267 byte additionally carries the canonical English-\
4268 typography apostrophe footgun: an author writes `:repo \
4269 \"github:p/repo's-fork\"` (the possessive-form paste-\
4270 from-prose idiom every README / commit-message / chat-\
4271 thread reference to a repo carries) expecting the \
4272 substrate to coerce it to a kebab-case slug; the byte \
4273 rides verbatim into the lacre's per-dep content-address \
4274 (`conteudo: format!(\"git:{repo}\")` peer of the path-\
4275 axis embedding at caixa-resolver/src/resolve.rs) and \
4276 into the resolver's `git clone <repo>` (caixa-resolver/\
4277 src/git.rs) subprocess invocation, where the upstream \
4278 host's git porcelain fetches a literal apostrophe-bearing \
4279 path that no host's repo registry resolves (GitHub / \
4280 GitLab / Bitbucket / Codeberg / sourcehut all reject `'` \
4281 in repo slugs at admission time) — so the lacre locks \
4282 to a `git:github:p/repo's-fork` closure that never \
4283 resolves at clone time, surfacing as a quoting-confused \
4284 'remote ref not found' porcelain error far from the \
4285 source caixa.lisp, defeating the THEORY.md §V.2 render-\
4286 determinism contract on the same axis the fragment-`#`, \
4287 query-`?`, backslash-`\\`, template-`{` / `}`, \
4288 shell-redirection-`<` / `>`, backtick-`` ` ``, \
4289 shell-pipe-`|`, shell-command-separator-`;`, shell-\
4290 background-`&`, shell-variable-expansion-`$`, shell-\
4291 glob-`*`, shell-subshell-grouping-`(` / `)`, and shell-\
4292 double-quote-`\"` arms close. Together with the prior \
4293 `\"` arm, this arm closes both ASCII shell-string-\
4294 delimiter bytes on the typed `:repo` URL axis — every \
4295 byte the canonical `git clone <repo>` doc-paste idiom \
4296 wraps the URL argument with is now refused at validate \
4297 time, before the byte rides into the lacre or the \
4298 resolver subprocess. Drop the `'` wrapper — paste only \
4299 the URL between the quotes, or use `:fonte (:tipo path \
4300 :caminho \"<local-path>\")` for a local workspace dep)"
4301 .to_string());
4302 }
4303 if b == b'!' {
4304 return Err("must not contain `!` (RFC 3986 §2.2 lists the bang byte \
4305 in the 'sub-delims' set the URL grammar admits inside a \
4306 path segment but every WHATWG-conformant special-scheme \
4307 URL parser percent-encodes inside a query component via \
4308 the 'special-query percent-encode set' — the peer position \
4309 the prior `*` / `(` / `)` / `'` 'sub-delims' arms close. \
4310 No documented `:fonte :repo` shape admits the byte: the \
4311 `github:org/repo` shorthand carries an alphanumeric / `-` \
4312 / `_` / `/` alphabet, every `https://` / `ssh://` / \
4313 `git://` / `file://` URL scheme keeps host / path bodies \
4314 inside the RFC 3986 `unreserved` alphanumeric / `-` / \
4315 `.` / `_` / `~` set that excludes the byte, and the \
4316 `git@host:path` scp-style SSH shape names a POSIX path \
4317 component that carries no shell-metachar bytes. Beyond \
4318 the URL-grammar question, every interactive POSIX shell \
4319 with history enabled (bash / ksh / zsh's `bashcompat` \
4320 mode / csh / tcsh) lexes `!` as the history-expansion \
4321 prefix — `!command` re-runs the most recent history \
4322 entry beginning with `command`, `!!` re-runs the prior \
4323 command verbatim, `!$` substitutes the last word of the \
4324 prior command, `!:N` substitutes the Nth word, the \
4325 canonical RCE-class injection vector when a string lands \
4326 in a shell context with `set -o histexpand` (bash's \
4327 default for interactive sessions). A `:repo \
4328 \"https://github.com/foo/bar!sudo\"` (the canonical \
4329 paste-from-shell-history footgun where the author copies \
4330 a `git clone <url>!sudo make install` one-liner from a \
4331 README's quick-start snippet, intending the trailing \
4332 `!sudo` as a shell-history reference but the typed slot \
4333 is itself a byte-level string parser, not a shell \
4334 context, so the bytes ride into the value verbatim) or \
4335 `:repo \"github:p/repo!!\"` (the symmetric `!!` repeat-\
4336 prior-command paste idiom every shell-history `git \
4337 clone …` retry line carries) is the canonical paste-\
4338 from-shell-history footgun the typed slot's accepted \
4339 set must exclude. Beyond shell-history, the bang byte \
4340 carries the canonical English-typography emphasis \
4341 footgun: an author writes `:repo \
4342 \"github:p/awesome-repo!\"` (the exclamation-form paste-\
4343 from-prose idiom every README / chat-thread / commit-\
4344 message reference to an enthusiastically-named repo \
4345 carries) expecting the substrate to coerce it to a \
4346 kebab-case slug; the byte rides verbatim into the \
4347 lacre's per-dep content-address (`conteudo: \
4348 format!(\"git:{repo}\")` peer of the path-axis \
4349 embedding at caixa-resolver/src/resolve.rs) and into \
4350 the resolver's `git clone <repo>` (caixa-resolver/\
4351 src/git.rs) subprocess invocation, where the upstream \
4352 host's git porcelain fetches a literal bang-bearing \
4353 path that no host's repo registry resolves (GitHub / \
4354 GitLab / Bitbucket / Codeberg / sourcehut all reject \
4355 `!` in repo slugs at admission time) — so the lacre \
4356 locks to a `git:github:p/awesome-repo!` closure that \
4357 never resolves at clone time, surfacing as a 'remote \
4358 ref not found' porcelain error far from the source \
4359 caixa.lisp, defeating the THEORY.md §V.2 render-\
4360 determinism contract on the same axis the fragment-\
4361 `#`, query-`?`, backslash-`\\`, template-`{` / `}`, \
4362 shell-redirection-`<` / `>`, backtick-`` ` ``, shell-\
4363 pipe-`|`, shell-command-separator-`;`, shell-\
4364 background-`&`, shell-variable-expansion-`$`, shell-\
4365 glob-`*`, shell-subshell-grouping-`(` / `)`, shell-\
4366 double-quote-`\"`, and shell-single-quote-`'` arms \
4367 close. The peer `:fonte :tag` / `:fonte :branch` axes \
4368 (`is_git_ref_name`) deliberately admit `!` (git's \
4369 `check-ref-format` accepts it as a printable byte and \
4370 the bang carries no refname-grammar meaning); the \
4371 `:entrada :paths` axis (`is_gateway_api_http_path`) \
4372 similarly admits it (K8s Gateway API HTTPPathMatch.value \
4373 OpenAPI regex accepts it). `:repo` is substrate-\
4374 internal and strictly narrower than its upstream \
4375 grammar by design, so the divergence is intentional: \
4376 the shell-history-expansion footgun is real on the \
4377 typed `:fonte :repo` axis (every `git clone <url>` \
4378 invocation crosses a shell boundary at the caixa-\
4379 resolver / `Command::new(\"git\")` subprocess layer) \
4380 in a way it isn't on the refname / HTTP-path axes that \
4381 never reach shell context. Drop the trailing `!` — \
4382 author the bare alphanumeric / `-` / `_` slug, or use \
4383 `:fonte (:tipo path :caminho \"<local-path>\")` for a \
4384 local workspace dep)"
4385 .to_string());
4386 }
4387 if b == b',' {
4388 return Err("must not contain `,` (RFC 3986 §2.2 lists the comma byte \
4389 in the 'sub-delims' set the URL grammar admits inside a \
4390 path segment but every WHATWG-conformant special-scheme \
4391 URL parser percent-encodes it inside both the path and \
4392 query percent-encode sets — the peer position the prior \
4393 `!` / `*` / `(` / `)` / `'` 'sub-delims' arms close. No \
4394 documented `:fonte :repo` shape admits the byte: the \
4395 `github:org/repo` shorthand carries an alphanumeric / `-` \
4396 / `_` / `/` alphabet, every `https://` / `ssh://` / \
4397 `git://` / `file://` URL scheme keeps host / path bodies \
4398 inside the RFC 3986 `unreserved` alphanumeric / `-` / \
4399 `.` / `_` / `~` set that excludes the byte, and the \
4400 `git@host:path` scp-style SSH shape names a POSIX path \
4401 component that carries no list-separator bytes (every \
4402 forge — GitHub / GitLab / Bitbucket / Codeberg / \
4403 sourcehut — refuses `,` in repo slugs at admission time). \
4404 Beyond the URL-grammar question, the comma byte carries \
4405 the canonical list-separator-belongs-to-list-grammar \
4406 footgun across every parser-of-record `:fonte :repo` \
4407 lands in: an author copies a `git clone <urlA>, <urlB>` \
4408 paste-from-CSV-list one-liner from a multi-repo \
4409 bootstrap doc (the canonical `git clone --recurse-\
4410 submodules <a>, <b>, <c>` README-quickstart idiom every \
4411 mono-repo carries) or pastes a JSON-array literal `[\"a\", \
4412 \"b\", \"c\"]` from a tooling-config snippet stripped \
4413 of its brackets, intending the comma to separate \
4414 multiple repo entries but the typed `:repo` slot names \
4415 *one* repo (the list-separator belongs to the list \
4416 grammar of the enclosing `:deps` slot, not to the \
4417 individual `:repo` value). A `:repo \
4418 \"github:p/a,github:p/b\"` silently passed every prior \
4419 arm and rode into the lacre's per-dep content-address \
4420 (`conteudo: format!(\"git:{repo}\")` peer of the path-\
4421 axis embedding at caixa-resolver/src/resolve.rs) and \
4422 into the resolver's `git clone <repo>` (caixa-\
4423 resolver/src/git.rs) subprocess invocation, where the \
4424 upstream host's git porcelain fetched a literal comma-\
4425 bearing path that no host's repo registry resolves — \
4426 so the lacre locks to a `git:github:p/a,github:p/b` \
4427 closure that never resolves at clone time, surfacing as \
4428 a 'remote ref not found' porcelain error far from the \
4429 source caixa.lisp, defeating the THEORY.md §V.2 render-\
4430 determinism contract on the same axis the fragment-\
4431 `#`, query-`?`, backslash-`\\`, template-`{` / `}`, \
4432 shell-redirection-`<` / `>`, backtick-`` ` ``, shell-\
4433 pipe-`|`, shell-command-separator-`;`, shell-\
4434 background-`&`, shell-variable-expansion-`$`, shell-\
4435 glob-`*`, shell-subshell-grouping-`(` / `)`, shell-\
4436 double-quote-`\"`, shell-single-quote-`'`, and shell-\
4437 history-`!` arms close. Beyond the multi-repo paste, \
4438 the byte carries the canonical English-typography \
4439 trailing-`,` paste-from-prose footgun: an author writes \
4440 `:repo \"github:pleme-io/caixa-feira,\"` (the trailing \
4441 comma every README-prose list-of-projects sentence \
4442 carries, mistakenly retained when the slug is pasted \
4443 mid-sentence) expecting the substrate to coerce it to \
4444 a kebab-case slug; the byte rides verbatim. The peer \
4445 `:fonte :tag` / `:fonte :branch` axes \
4446 (`is_git_ref_name`) deliberately admit `,` (git's \
4447 `check-ref-format` accepts it as a printable byte and \
4448 the comma carries no refname-grammar meaning); the \
4449 `:entrada :paths` axis (`is_gateway_api_http_path`) \
4450 similarly admits it (K8s Gateway API HTTPPathMatch.value \
4451 OpenAPI regex accepts it). `:repo` is substrate-\
4452 internal and strictly narrower than its upstream \
4453 grammar by design, so the divergence is intentional: \
4454 the list-separator-belongs-to-list-grammar footgun is \
4455 real on the typed `:fonte :repo` axis (every `:deps` \
4456 entry names exactly one repo and the comma between \
4457 entries belongs to the `:deps` list grammar, never to \
4458 the value) in a way it isn't on the refname / HTTP-\
4459 path axes whose grammars admit the byte without \
4460 confusion. Drop the trailing `,` — author the bare \
4461 alphanumeric / `-` / `_` slug, or split into multiple \
4462 `:deps` entries to express multiple repos)"
4463 .to_string());
4464 }
4465 if b == b'=' {
4466 return Err("must not contain `=` (RFC 3986 §2.2 lists the equals byte \
4467 in the 'sub-delims' set — the URL grammar admits the byte \
4468 inside a path segment, but every WHATWG-conformant special-\
4469 scheme URL parser percent-encodes it inside a query \
4470 component via the 'special-query percent-encode set' (the \
4471 same set the prior `,` / `!` / `*` / `(` / `)` / `'` sub-\
4472 delims arms close on, peer with the immediately prior `,` \
4473 arm on the same paragraph of the same RFC). No documented \
4474 `:fonte :repo` shape admits the byte: the `github:org/repo` \
4475 shorthand carries an alphanumeric / `-` / `_` / `/` \
4476 alphabet, every `https://` / `ssh://` / `git://` / \
4477 `file://` URL scheme keeps host / path bodies inside the \
4478 RFC 3986 `unreserved` alphanumeric / `-` / `.` / `_` / `~` \
4479 set that excludes the byte, and the `git@host:path` scp-\
4480 style SSH shape names a POSIX path component that carries \
4481 no key-value-separator bytes (every forge — GitHub / \
4482 GitLab / Bitbucket / Codeberg / sourcehut — refuses `=` in \
4483 repo slugs at admission time). Beyond the URL-grammar \
4484 question, the equals byte carries three canonical paste-\
4485 from-doc footguns the typed `:repo` slot's accepted set \
4486 must exclude. First, the URL-query key-value-separator \
4487 paste: an author copies `https://github.com/p/x?ref=main` \
4488 from a browser address bar / GitHub-tree-URL deep-link / \
4489 `?utm_source=…` campaign-tracker query string; the prior \
4490 `?` arm (a68f818) closes the query-prefix byte but every \
4491 paste-from-doc snippet that lost its `?` prefix (a copy-\
4492 paste that started mid-query, a shell-pipeline that \
4493 stripped the leading `?` via `cut -d?`, a docs example \
4494 that documented the bare `key=value` pairs without the \
4495 leading `?`) lands a `:repo \"github:p/x ref=main\"` whose \
4496 `=` byte is now the load-bearing footgun. Second, the \
4497 shell env-var-assignment paste: every POSIX shell (sh / \
4498 bash / zsh / dash / ksh / fish) lexes `KEY=VALUE` at the \
4499 start of a command line as a one-shot env-var assignment \
4500 scoped to that command (`GIT_TERMINAL_PROMPT=0 git clone \
4501 <url>` runs `git clone` with the prompt suppressed, \
4502 `GIT_SSL_NO_VERIFY=1 git clone <url>` skips TLS \
4503 verification, `HTTPS_PROXY=… git clone <url>` overrides \
4504 the proxy) — the canonical paste-from-shell-history idiom \
4505 every git-troubleshooting README documents. An author \
4506 copies `:repo \"GIT_TERMINAL_PROMPT=0 https://github.com/\
4507 p/x\"` from a shell-prompt one-liner and the env-var \
4508 prefix rides verbatim into the value, defeating the \
4509 substrate's typed `:repo` axis (the env-var prefix \
4510 belongs to the shell context, not to the URL). Third, the \
4511 git-CLI-flag paste: every `git` porcelain entry-point \
4512 accepts `--config <key>=<value>` (`git -c \
4513 protocol.file.allow=always clone …`, `git -c \
4514 http.extraHeader=…`) and `git config --get <key>` outputs \
4515 `<key>=<value>`-shaped lines; an author copies \
4516 `url=https://github.com/p/x` from `git config --get-all \
4517 remote.origin.url` output or a `.gitconfig` `[remote \
4518 \"origin\"] url = https://…` ini-stanza paste and the \
4519 `url=` prefix rides verbatim into the typed `:repo` slot \
4520 (the ini-key-prefix belongs to the gitconfig grammar, not \
4521 to the URL value). A `:repo \"GIT_TERMINAL_PROMPT=0 \
4522 https://github.com/p/x\"` or `:repo \"url=https://github.\
4523 com/p/x\"` silently passed every prior arm; the byte rode \
4524 into the lacre's per-dep content-address (`conteudo: \
4525 format!(\"git:{repo}\")` peer of the path-axis embedding \
4526 at caixa-resolver/src/resolve.rs) and into the resolver's \
4527 `git clone <repo>` (caixa-resolver/src/git.rs) subprocess \
4528 invocation, where libcurl's URL parser percent-encodes \
4529 the byte to `%3D` on the wire — so two authors whose \
4530 `:repo` values differ only in their `=` presence resolve \
4531 to the byte-identical upstream `git clone` but lock to \
4532 two distinct BLAKE3 closures, defeating the THEORY.md \
4533 §V.2 render-determinism contract on the same axis the \
4534 fragment-`#`, query-`?`, backslash-`\\`, template-`{` / \
4535 `}`, shell-redirection-`<` / `>`, backtick-`` ` ``, shell-\
4536 pipe-`|`, shell-command-separator-`;`, shell-background-\
4537 `&`, shell-variable-expansion-`$`, shell-glob-`*`, shell-\
4538 subshell-grouping-`(` / `)`, shell-double-quote-`\"`, \
4539 shell-single-quote-`'`, shell-history-`!`, and list-\
4540 separator-`,` arms close. The peer `:fonte :tag` / \
4541 `:fonte :branch` axes (`is_git_ref_name`) deliberately \
4542 admit `=` (git's `check-ref-format` accepts it as a \
4543 printable byte and the equals carries no refname-grammar \
4544 meaning); the `:entrada :paths` axis \
4545 (`is_gateway_api_http_path`) similarly admits it (K8s \
4546 Gateway API HTTPPathMatch.value OpenAPI regex accepts \
4547 it). `:repo` is substrate-internal and strictly narrower \
4548 than its upstream grammar by design, so the divergence is \
4549 intentional: the URL-query / shell-env-var-assignment / \
4550 git-config-ini key-value-separator footgun is real on the \
4551 typed `:fonte :repo` axis (every `git clone <repo>` \
4552 invocation crosses a shell boundary at the caixa-\
4553 resolver subprocess layer, and the lacre's per-dep \
4554 content-address must be byte-identical to the wire form) \
4555 in a way it isn't on the refname / HTTP-path axes whose \
4556 grammars admit the byte without confusion. Drop the `=` — \
4557 strip the env-var / config-key prefix from the value \
4558 before the URL, or author the bare alphanumeric / `-` / \
4559 `_` slug)"
4560 .to_string());
4561 }
4562 if b == b'%' {
4563 return Err(
4564 "must not contain `%` (RFC 3986 §2.1 reserves the percent byte \
4565 as the URL percent-encoding escape — `%HH` is the \
4566 mandatory encoding mechanism for every byte outside the \
4567 `unreserved` alphanumeric / `-` / `.` / `_` / `~` set, \
4568 and `%` itself must be percent-encoded as `%25` to appear \
4569 literally inside a URL value, peer with the immediately \
4570 prior `=` / `,` / `!` / `*` / `(` / `)` / `'` 'sub-delims' \
4571 arms on the same RFC. No documented `:fonte :repo` shape \
4572 admits the byte: the `github:org/repo` shorthand carries \
4573 an alphanumeric / `-` / `_` / `/` alphabet, every \
4574 `https://` / `ssh://` / `git://` / `file://` URL scheme \
4575 keeps host / path bodies inside the RFC 3986 `unreserved` \
4576 set that excludes the byte and every percent-encoded \
4577 byte (alphanumeric / `-` / `.` / `_` / `~` — no member \
4578 needs percent-encoding), and the `git@host:path` scp-\
4579 style SSH shape names a POSIX path component that \
4580 carries no percent-encoded bytes (every forge — GitHub / \
4581 GitLab / Bitbucket / Codeberg / sourcehut — refuses `%` \
4582 in repo slugs at admission time, and IDN host labels \
4583 must be pre-encoded as Punycode `xn--…` rather than as \
4584 percent-encoded UTF-8 bytes). Beyond the URL-grammar \
4585 question, the percent byte is the canonical render-\
4586 determinism axis-of-non-determinism the typed `:repo` \
4587 slot must close at the manifest layer. First, the \
4588 paste-from-browser-address-bar percent-encoded-space \
4589 footgun: an author copies \
4590 `https://github.com/p/x%20test` from a browser address \
4591 bar or a percent-encoded README hyperlink, intending \
4592 the `%20` as the URL encoding of a literal space; \
4593 libcurl's URL parser (the layer `git clone <https-url>` \
4594 invokes) re-percent-encodes the `%` byte to `%25` on \
4595 the wire (since `%` is reserved as the escape sequence \
4596 lead-in and must itself be encoded for a literal byte), \
4597 so the wire request becomes \
4598 `https://github.com/p/x%2520test` — a different path \
4599 than the lacre's content-address records, defeating \
4600 the THEORY.md §V.2 render-determinism contract \
4601 directly on the encoding-mechanism axis itself (the \
4602 most direct violation of every prior render-\
4603 determinism arm — `#`, `?`, `\\`, `{`/`}`, `<`/`>`, \
4604 `` ` ``, `|`, `;`, `&`, `$`, `*`, `(`/`)`, `\"`, `'`, \
4605 `!`, `,`, `=` — since `%` is the very encoding step \
4606 those arms reason about). Second, the lone-percent \
4607 malformed-escape footgun: an author writes `:repo \
4608 \"https://github.com/p/x%foo\"` (the `%` not followed \
4609 by two hex digits) — every WHATWG-conformant URL \
4610 parser rejects the value at parse time per RFC 3986 \
4611 §2.1 (`%HH` requires exactly two hex digits to follow), \
4612 but the byte rides into the lacre's per-dep content-\
4613 address (`conteudo: format!(\"git:{repo}\")` peer of \
4614 the path-axis embedding at caixa-resolver/src/resolve.\
4615 rs) before the resolver subprocess fails far from the \
4616 source caixa.lisp. Third, the over-encoded path \
4617 footgun: an author writes `:repo \
4618 \"https://github.com/p%2Fx\"` intending the `%2F` as \
4619 the URL encoding of `/`; the GitHub Smart-HTTP \
4620 transport rejects percent-encoded path-separator bytes \
4621 in repo URLs (the URL's path-segment grammar is \
4622 resolved before the percent-decoding pass), but the \
4623 byte rides verbatim into the lacre and locks a \
4624 `git:https://github.com/p%2Fx` closure that diverges \
4625 from the byte-identical `https://github.com/p/x` form \
4626 every other author authored — two authors whose \
4627 `:repo` values differ only in their `/` vs `%2F` \
4628 presence resolve to the byte-identical upstream `git \
4629 clone` but lock to two distinct BLAKE3 closures, the \
4630 canonical render-determinism violation. The peer \
4631 `:fonte :tag` / `:fonte :branch` axes \
4632 (`is_git_ref_name`) deliberately admit `%` (git's \
4633 `check-ref-format` accepts it as a printable byte and \
4634 the percent carries no refname-grammar meaning); the \
4635 `:entrada :paths` axis (`is_gateway_api_http_path`) \
4636 similarly admits it (K8s Gateway API \
4637 HTTPPathMatch.value OpenAPI regex accepts it as a \
4638 path-segment byte). `:repo` is substrate-internal and \
4639 strictly narrower than its upstream grammar by design, \
4640 so the divergence is intentional: the percent-encoding \
4641 axis is the load-bearing render-determinism axis on \
4642 the typed `:fonte :repo` slot (every byte the wire \
4643 differs from the lacre by even a single `%`-escape \
4644 round-trip violates the substrate's content-addressed-\
4645 closure contract) in a way it isn't on the refname / \
4646 HTTP-path axes whose grammars admit the byte without \
4647 confusion. Drop the `%` — substitute the literal byte \
4648 directly (the typed slot admits the same `unreserved` \
4649 byte-set the URL grammar's percent-decoding pass \
4650 produces, so the percent-encoded form is structurally \
4651 redundant), or split the encoded value into the typed \
4652 slot it belongs in (e.g., a host with non-ASCII bytes \
4653 must be pre-encoded as Punycode `xn--…` rather than \
4654 percent-encoded UTF-8))"
4655 .to_string(),
4656 );
4657 }
4658 if b == b'^' {
4659 return Err(
4660 "must not contain `^` (RFC 3986 §2 lists the circumflex byte \
4661 in the 'unwise' set every URL parser is required to refuse \
4662 or percent-encode at the path-segment boundary, peer with \
4663 the `{` / `}` URI Template, `<` / `>` shell-redirection, \
4664 `` ` `` shell-command-substitution, and `|` shell-pipe arms \
4665 on the same paragraph of the same RFC — the 'unwise' \
4666 four-byte subset (`{`, `}`, `|`, `\\`, `^`) is the strictest \
4667 of the §2 reserved classes, every member structurally \
4668 incompatible with every URL grammar at every position. No \
4669 git URL grammar admits the byte: the `github:org/repo` \
4670 shorthand carries an alphanumeric / `-` / `_` / `/` \
4671 alphabet, every `https://` / `ssh://` / `git://` / \
4672 `file://` URL scheme percent-encodes `^` to `%5E` on the \
4673 wire (the WHATWG URL spec's 'fragment percent-encode set' \
4674 canonical mapping every conformant URL parser applies), \
4675 and the `git@host:path` scp-style SSH shape names a POSIX \
4676 path component that carries no shell-metachar bytes. \
4677 Beyond the URL-grammar violation, every interactive POSIX \
4678 shell with history enabled (bash / ksh / zsh's \
4679 `bashcompat` mode) lexes `^old^new^` as the quick history-\
4680 substitution shorthand — `^foo^bar` re-runs the most \
4681 recent history entry with the first `foo` substituted by \
4682 `bar`, the canonical RCE-class injection vector when a \
4683 string lands in a shell context with `set -o histexpand` \
4684 (bash's default for interactive sessions, peer with the \
4685 `!` history-expansion arm). csh / tcsh lex `^` as the \
4686 history-substitution prefix (`^old^new` substitutes `old` \
4687 with `new` in the prior command's first occurrence). Beyond \
4688 shell history, every regular-expression engine (POSIX BRE \
4689 / ERE, PCRE, RE2, the rust `regex` crate, JavaScript's \
4690 `RegExp`) lexes `^` two ways: leading-position `^` anchors \
4691 the match to the start of the line (the canonical `^foo` \
4692 anchored-prefix idiom every grep / sed / awk one-liner \
4693 carries), and inside-class `[^abc]` negates the character \
4694 class (the canonical exclusion idiom every regex carries). \
4695 PowerShell (Windows / cross-platform) lexes `^` as the \
4696 escape character — `cmd ^> file` escapes the redirection \
4697 operator into a literal byte, the canonical paste-from-\
4698 PowerShell-prompt footgun on a cross-platform caixa.lisp. \
4699 A `:repo \"https://github.com/p/x^old^new\"` (the \
4700 canonical paste-from-shell-history footgun where the \
4701 author copies a `git clone <url>` line followed by a \
4702 `^typo^fix` quick-edit-and-rerun shell-history shorthand \
4703 and forgot to trim the `^...^...` tail) or `:repo \
4704 \"github:p/^archived\"` (the symmetric regex-anchor / \
4705 negation paste idiom every doc-quick-start grep-pipeline \
4706 footnotes) is the canonical paste-from-shell-prompt \
4707 footgun the typed slot's accepted set must exclude. The \
4708 byte rides verbatim into the lacre's per-dep content-\
4709 address (`conteudo: format!(\"git:{repo}\")` peer of the \
4710 path-axis embedding at caixa-resolver/src/resolve.rs) and \
4711 into the resolver's `git clone <repo>` \
4712 (caixa-resolver/src/git.rs) subprocess invocation, where \
4713 libcurl's URL parser percent-encodes the byte on the wire \
4714 — so two authors whose `:repo` values differ only in \
4715 their caret presence (one paste-trimmed the history-\
4716 substitution shorthand, the other didn't) resolve to the \
4717 byte-identical upstream `git clone` but lock to two \
4718 distinct BLAKE3 closures, defeating the THEORY.md §V.2 \
4719 render-determinism contract on the same axis the \
4720 fragment-`#`, query-`?`, backslash-`\\`, template-`{` / \
4721 `}`, shell-redirection-`<` / `>`, backtick-`` ` ``, \
4722 shell-pipe-`|`, shell-command-separator-`;`, shell-\
4723 background-`&`, shell-variable-expansion-`$`, shell-glob-\
4724 `*`, subshell-grouping-`(` / `)`, shell-double-quote-`\"`, \
4725 shell-single-quote-`'`, history-expansion-`!`, list-\
4726 separator-`,`, env-var-assignment-`=`, and percent-\
4727 encoding-`%` arms close. Drop the `^...^...` tail — \
4728 substitute the literal value at author time, or use \
4729 `:fonte (:tipo path :caminho \"<local-path>\")` for a \
4730 local workspace dep)"
4731 .to_string(),
4732 );
4733 }
4734 }
4735 if s.starts_with(':') {
4736 return Err(
4737 "must not start with `:` (the canonical empty-scheme footgun — \
4738 `:foo` parses as a zero-length scheme that no git porcelain \
4739 entry-point accepts; use a non-empty scheme prefix like \
4740 `github:`, `https://`, `ssh://`, `git://`, `file://`, or the \
4741 `git@host:path` scp-style SSH form)"
4742 .to_string(),
4743 );
4744 }
4745 if !s.contains(':') {
4746 return Err(
4747 "must contain a `:` separator (every documented `:fonte :repo` \
4748 shape carries one: `github:org/repo` shorthand, `https://…` / \
4749 `ssh://…` / `git://…` / `file://…` URL schemes, or \
4750 `git@host:path` scp-style SSH; a bare `org/repo` form is \
4751 ambiguous — `git clone` reads it as a relative filesystem path \
4752 rather than the GitHub-shorthand expansion the author probably \
4753 intended — so prefix it with `github:` for the registry-\
4754 shorthand resolver convention)"
4755 .to_string(),
4756 );
4757 }
4758 Ok(())
4759}
4760
4761/// Practical cap on a `:caracteristicas` (Cargo-feature-name-shaped)
4762/// entry, in bytes. Cargo itself enforces no length cap on feature
4763/// names — its `restricted_names::validate_feature_name` accepts any
4764/// length — but every realistic feature in the Cargo ecosystem is
4765/// well under this bound (`derive` 6, `serde_json` 10, the
4766/// `__private_…` doubled-underscore convention rarely exceeds 32).
4767/// 64 bytes is the substrate's catch-the-paste-from-binary cap on the
4768/// peer trajectory `is_dns_1123_label` (63), `is_wit_world_ref` (128),
4769/// `is_nats_subject` (256), `is_wasi_keyvalue_slot` (512),
4770/// `is_git_ref_name` (255), `is_git_oid` (40/64),
4771/// `is_git_repo_url` (2048) carry: an axis-appropriate ceiling above
4772/// every legitimate authoring shape, tight enough to surface the
4773/// "paste-from-binary" / "multi-line blob landed in a single-token
4774/// slot" footgun at validate time.
4775pub const CARGO_FEATURE_NAME_MAX_LEN: usize = 64;
4776
4777/// Predicate: assert that `s` is a valid Cargo feature name. The
4778/// contract — modeled on Cargo's
4779/// `restricted_names::validate_feature_name` grammar (the parser the
4780/// Cargo resolver routes every `[dependencies.<dep>.features]` entry
4781/// through at `cargo metadata` time), narrowed to the strict ASCII
4782/// subset every realistic feature in the Cargo ecosystem uses:
4783///
4784/// - 1..=[`CARGO_FEATURE_NAME_MAX_LEN`] (64) bytes;
4785/// - first byte: ASCII alphanumeric or `_` (Cargo's parser admits
4786/// Unicode XID-start characters too; pleme-io narrows to the
4787/// ASCII subset for the same reason every peer value-shape
4788/// predicate above narrows — drift between NFC-vs-NFD
4789/// normalization across filesystems silently rewrites the
4790/// feature-key, breaking the lacre's content-addressing
4791/// invariant). Leading `-` / `+` / `.` are explicitly named —
4792/// each is the canonical "I copy-pasted the
4793/// `+optional-feature` enablement form from a Cargo doc" /
4794/// "I confused the dotted-form with feature-name shape"
4795/// footgun the predicate's diagnostic remediation points at;
4796/// - remaining bytes: ASCII alphanumeric, `_`, `-`, `+`, or `.`
4797/// (the Cargo-accepted continuation set). Whitespace, control
4798/// characters, non-ASCII bytes, `/` / `?` / `#` / `,` /
4799/// other punctuation are each surfaced with a self-locating
4800/// reason naming the canonical authoring footgun (multi-token
4801/// blob, CR/LF paste-from-doc, `/` segment-separator confusion
4802/// with namespaced-dep features the predicate's call site
4803/// explicitly does not enable, list-separator-belongs-to-list-
4804/// grammar miscomprehension).
4805///
4806/// Returns the parser-shaped reason on rejection (without wrapping in
4807/// any error variant) so each per-axis caller — [`crate::Dep::validate`]
4808/// for the `:deps`/`:deps-dev :caracteristicas` axis at validate time,
4809/// every future per-feature axis (M4 caixa-resolver's `lacre.lisp`
4810/// resolved-feature-set materializer, the future per-WitContract
4811/// `:caracteristicas`-shaped capability-set axis if WIT worlds grow a
4812/// typed feature toggle, the future per-`UpgradeInstruction` per-
4813/// capability set axis the §V.2 mes-build extension would carry) —
4814/// wraps the same reason in its own typed `*Invalid { <axis>, reason }`
4815/// variant. The reason wording is axis-agnostic ("Cargo feature names
4816/// reject leading `-`") so every call site reading the same diagnostic
4817/// points at the same rule; drift between any two axes' rule
4818/// enforcement is a build error visible at this predicate, not a
4819/// per-renderer "this passed validate but Cargo rejected at metadata
4820/// time" surprise.
4821///
4822/// Empty input is rejected here (defensively) and at each call site
4823/// via the narrower [`crate::DepError::CaracteristicaEmpty`] variant —
4824/// the same empty-first cascade [`is_dns_1123_label`],
4825/// [`is_gateway_api_http_path`], [`is_wit_world_ref`],
4826/// [`is_nats_subject`], [`is_wasi_keyvalue_slot`], [`is_git_ref_name`],
4827/// [`is_git_oid`], and [`is_git_repo_url`] all carry.
4828///
4829/// Lifted as a typed substrate-side primitive on the same trajectory
4830/// the peer value-shape predicates already follow — the typed slot's
4831/// valid set matches the downstream consumer's accepted set (here,
4832/// Cargo's TOML-feature-name parser at `cargo metadata` time),
4833/// structurally. The ninth value-shape primitive to land in
4834/// [`crate::render`], closing the typed `:deps`/`:deps-dev` surface
4835/// value-shape trajectory on its last unsealed axis (`:caracteristicas`
4836/// entries; the per-entry `:nome` / `:versao` / `:fonte` axes are
4837/// already routed through their respective shape predicates).
4838///
4839/// # Errors
4840///
4841/// Returns the parser-shaped reason naming the specific violation
4842/// (length / first-byte-class / continuation-byte-class / whitespace /
4843/// control-char / non-ASCII / `/`-segment-separator-confusion /
4844/// `,`-list-separator-confusion), without wrapping in any error
4845/// variant — every caller maps the same `String` into its own typed
4846/// `*Invalid { <axis>, reason }` enum variant.
4847pub fn is_cargo_feature_name(s: &str) -> Result<(), String> {
4848 if s.is_empty() {
4849 return Err("must not be empty".to_string());
4850 }
4851 if s.len() > CARGO_FEATURE_NAME_MAX_LEN {
4852 return Err(format!(
4853 "exceeds Cargo feature name max length of {CARGO_FEATURE_NAME_MAX_LEN} bytes \
4854 (got {} bytes; legitimate Cargo feature names rarely exceed ~24 bytes — \
4855 this length suggests a paste-from-binary or multi-token blob landed in \
4856 the `:caracteristicas` slot)",
4857 s.len()
4858 ));
4859 }
4860 let bytes = s.as_bytes();
4861 let first = bytes[0];
4862 if !(first.is_ascii_alphanumeric() || first == b'_') {
4863 let msg = if first == b'+' {
4864 "must not start with `+` (Cargo's feature-name grammar reserves a leading \
4865 `+` for the activation-syntax inside a `[dependencies.<dep>.features]` \
4866 list — `:caracteristicas` entries name the feature itself, not its \
4867 enablement form; drop the leading `+` and author the bare feature name, \
4868 e.g. `\"http\"` not `\"+http\"`)"
4869 .to_string()
4870 } else if first == b'-' {
4871 "must not start with `-` (Cargo's feature-name grammar rejects a leading \
4872 hyphen — `-` is a legitimate continuation character between alphanumeric \
4873 segments but the canonical CLI-argument-injection / kebab-leak footgun at \
4874 the start; drop the leading `-`, e.g. `\"json\"` not `\"-json\"`)"
4875 .to_string()
4876 } else if first == b'.' {
4877 "must not start with `.` (Cargo's feature-name grammar rejects a leading \
4878 dot; `.` is a legitimate continuation character but the canonical \
4879 leading-dot-as-version-suffix / hidden-file footgun at the start. Drop \
4880 the leading `.`)"
4881 .to_string()
4882 } else if first == b' ' || first == b'\t' {
4883 "must not start with whitespace (Cargo's feature-name grammar rejects \
4884 whitespace anywhere; the leading-whitespace arm is the canonical \
4885 paste-from-aligned-doc footgun)"
4886 .to_string()
4887 } else if first < 0x20 || first == 0x7F {
4888 format!(
4889 "must not start with control character 0x{first:02x} (Cargo's feature-name \
4890 grammar rejects ASCII control characters; the CR/LF arm is the canonical \
4891 paste-from-multiline-doc footgun)"
4892 )
4893 } else if first >= 0x80 {
4894 format!(
4895 "must not start with non-ASCII byte 0x{first:02x} (Cargo accepts Unicode \
4896 XID-start characters but pleme-io narrows to the strict ASCII subset every \
4897 realistic feature name uses; legitimate features are kebab-case ASCII \
4898 identifiers like `\"http\"`, `\"json\"`, `\"derive\"`)"
4899 )
4900 } else {
4901 format!(
4902 "must start with an ASCII alphanumeric character or `_`, got {ch:?} \
4903 (Cargo's `restricted_names::validate_feature_name` rejects feature names \
4904 whose first character is outside the XID-start + `_` + digit set; \
4905 pleme-io narrows to the strict ASCII alphanumeric + `_` subset)",
4906 ch = first as char
4907 )
4908 };
4909 return Err(msg);
4910 }
4911 for &b in &bytes[1..] {
4912 let valid = b.is_ascii_alphanumeric() || b == b'_' || b == b'-' || b == b'+' || b == b'.';
4913 if !valid {
4914 let msg = if b == b' ' || b == b'\t' {
4915 format!(
4916 "must not contain whitespace character {ch:?} (Cargo's feature-name \
4917 grammar rejects whitespace; feature names are single-token identifiers \
4918 — use `-` or `_` to separate kebab-case / snake-case segments instead)",
4919 ch = b as char
4920 )
4921 } else if b == b',' {
4922 "must not contain `,` (the comma separator belongs to the \
4923 `:caracteristicas` list grammar between entries, not to the feature-name \
4924 grammar within an entry — split the value into two separate list entries)"
4925 .to_string()
4926 } else if b == b'/' {
4927 "must not contain `/` (Cargo's `dep/feat` syntax for namespaced-dep \
4928 features applies inside `[dependencies.<dep>.features]` list entries that \
4929 already name the parent dep — `:caracteristicas` entries are per-dep \
4930 already, so the segment separator within a feature name must be `-`, \
4931 `_`, `+`, or `.`)"
4932 .to_string()
4933 } else if b == b'?' {
4934 "must not contain `?` (Cargo's feature-name grammar rejects URL-reserved \
4935 punctuation; use `-`, `_`, `+`, or `.` as a segment separator instead)"
4936 .to_string()
4937 } else if b == b'#' {
4938 "must not contain `#` (Cargo's feature-name grammar rejects URL-reserved \
4939 punctuation; use `-`, `_`, `+`, or `.` as a segment separator instead)"
4940 .to_string()
4941 } else if b < 0x20 || b == 0x7F {
4942 format!(
4943 "must not contain control character 0x{b:02x} (Cargo's feature-name \
4944 grammar rejects ASCII control characters; the CR/LF arm is the \
4945 canonical paste-from-multiline-doc footgun)"
4946 )
4947 } else if b >= 0x80 {
4948 format!(
4949 "must not contain non-ASCII byte 0x{b:02x} (Cargo accepts Unicode \
4950 XID-continue characters but pleme-io narrows to the strict ASCII \
4951 subset every realistic feature name uses; raw non-ASCII silently \
4952 round-trips inconsistently across NFC/NFD normalization on APFS / \
4953 case-folding filesystems, breaking the lacre's content-addressing \
4954 invariant)"
4955 )
4956 } else {
4957 format!(
4958 "contains invalid character {ch:?} (Cargo's feature-name grammar \
4959 allows only `[A-Za-z0-9_+\\-.]` after the first character)",
4960 ch = b as char
4961 )
4962 };
4963 return Err(msg);
4964 }
4965 }
4966 Ok(())
4967}
4968
4969/// Practical cap on a `:licenca` (SPDX-expression-shaped) value, in
4970/// bytes. The SPDX specification places no length cap on expressions
4971/// — the grammar admits arbitrarily-nested composite expressions —
4972/// but every realistic pleme-io fixture stays well under this bound
4973/// (`MIT` 3, `Apache-2.0` 10, `Apache-2.0 OR MIT` 17, the longest
4974/// SPDX dual-license-with-exception shape `Apache-2.0 WITH
4975/// LLVM-exception` 31; a `(MIT OR Apache-2.0) AND BSD-3-Clause AND
4976/// ISC` composite caps near 50). 256 bytes is the substrate's
4977/// catch-the-paste-from-binary cap on the peer trajectory
4978/// `is_dns_1123_label` (63), `is_cargo_feature_name` (64),
4979/// `is_wit_world_ref` (128), `is_nats_subject` (256),
4980/// `is_wasi_keyvalue_slot` (512), `is_git_ref_name` (255),
4981/// `is_git_oid` (40/64), `is_git_repo_url` (2048) carry: an
4982/// axis-appropriate ceiling above every legitimate authoring shape,
4983/// tight enough to surface the "paste-from-license-text" /
4984/// "multi-line license blob landed in the `:licenca` slot" footgun
4985/// at validate time.
4986pub const SPDX_EXPRESSION_MAX_LEN: usize = 256;
4987
4988/// Predicate: assert that `s` is a valid SPDX-expression shape. The
4989/// contract — modeled on the SPDX 2.1 expression grammar
4990/// (`compound-expression = simple-expression | "(" compound-expression
4991/// ")" | compound-expression "WITH" exception-id | compound-expression
4992/// "AND" compound-expression | compound-expression "OR"
4993/// compound-expression`; `simple-expression = license-id | license-id
4994/// "+" | "LicenseRef-" idstring | "DocumentRef-" idstring ":"
4995/// "LicenseRef-" idstring`; `idstring = 1*(ALPHA / DIGIT / "-" /
4996/// ".")`), narrowed to the structural alphabet floor every realistic
4997/// SPDX expression in the wild uses:
4998///
4999/// - 1..=[`SPDX_EXPRESSION_MAX_LEN`] (256) bytes;
5000/// - no leading whitespace (paste-from-aligned-doc footgun);
5001/// - no trailing whitespace (paste-from-doc footgun — every
5002/// downstream SPDX parser splits on exact token boundaries and
5003/// a trailing space breaks the `WITH` / `AND` / `OR` keyword
5004/// match);
5005/// - every byte in the SPDX expression alphabet: ASCII alphanumeric
5006/// plus `.`, `-`, `+`, `(`, `)`, `:` (the `DocumentRef-…:LicenseRef-…`
5007/// separator), and a single ASCII space (token separator). Tabs,
5008/// control characters, non-ASCII bytes, `_` (not in `idstring`),
5009/// `,` (SPDX uses `AND` / `OR` keywords, not comma), `/` (the
5010/// `dual-license/A` colloquial idiom is non-SPDX), and every other
5011/// punctuation byte are each surfaced with a self-locating reason
5012/// naming the canonical authoring footgun.
5013///
5014/// The predicate is a *structural* floor — it enforces the alphabet +
5015/// length the SPDX grammar's character class admits, not the full
5016/// expression-parse (compound-expression nesting, `AND`/`OR`/`WITH`
5017/// keyword placement, parenthesis balance, idstring well-formedness
5018/// per simple-expression production). A future tightening on the
5019/// `:licenca` axis can extend past this shape predicate into a full
5020/// SPDX parser + license-id allowlist (peer with how
5021/// [`is_git_repo_url`] is the structural floor on `:repositorio` and
5022/// a future flake-resolver might tighten the per-URL-scheme arm into
5023/// scheme-specific shape predicates). This gate closes the
5024/// `_`/`,`/`/`/tab/CR/LF/non-ASCII/multi-line-blob footguns
5025/// structurally at the manifest layer; the parser-shape arms remain
5026/// for a follow-up routine once a real SPDX-parser dep is justified.
5027///
5028/// Returns the parser-shaped reason on rejection (without wrapping in
5029/// any error variant) so each per-axis caller —
5030/// [`crate::Caixa::validate_licenca`] for the universal `:licenca`
5031/// axis at validate time, every future per-license axis (a future
5032/// `:fonte :license` per-dep license-pin axis, a future
5033/// per-`UpgradeInstruction` per-component license-compatibility axis,
5034/// a future `Lacre` per-resolved-dep license-closure axis) — wraps the
5035/// same reason in its own typed `*Invalid { <axis>, reason }` variant.
5036/// The reason wording is axis-agnostic ("SPDX expressions reject
5037/// leading whitespace") so every call site reading the same diagnostic
5038/// points at the same rule; drift between any two axes' rule
5039/// enforcement is a build error visible at this predicate, not a
5040/// per-renderer "this passed validate but `helm lint` rejected the
5041/// `Chart.yaml license:` value" surprise.
5042///
5043/// Empty input is rejected here (defensively) and at each call site
5044/// via the narrower [`crate::ManifestError::LicencaEmpty`] variant —
5045/// the same empty-first cascade [`is_dns_1123_label`],
5046/// [`is_gateway_api_http_path`], [`is_wit_world_ref`],
5047/// [`is_nats_subject`], [`is_wasi_keyvalue_slot`], [`is_git_ref_name`],
5048/// [`is_git_oid`], [`is_git_repo_url`], and [`is_cargo_feature_name`]
5049/// all carry.
5050///
5051/// Lifted as a typed substrate-side primitive on the same trajectory
5052/// the peer value-shape predicates already follow — the typed slot's
5053/// valid set matches the downstream consumer's accepted set (here,
5054/// the `caixa-helm` chart `README.md` `## License` section + a
5055/// future SPDX-aware Chart.yaml `license:` emitter + the future
5056/// per-resolved-dep license-closure axis a forthcoming `Lacre`
5057/// extension would carry), structurally.
5058///
5059/// # Errors
5060///
5061/// Returns the parser-shaped reason naming the specific violation
5062/// (length / leading-whitespace / trailing-whitespace /
5063/// alphabet-class / tab / control-char / non-ASCII / `_` /
5064/// `,`-list-separator-confusion / `/`-dual-license-idiom), without
5065/// wrapping in any error variant — every caller maps the same
5066/// `String` into its own typed `*Invalid { <axis>, reason }` enum
5067/// variant.
5068pub fn is_spdx_expression_shape(s: &str) -> Result<(), String> {
5069 if s.is_empty() {
5070 return Err("must not be empty".to_string());
5071 }
5072 if s.len() > SPDX_EXPRESSION_MAX_LEN {
5073 return Err(format!(
5074 "exceeds SPDX expression max length of {SPDX_EXPRESSION_MAX_LEN} bytes \
5075 (got {} bytes; realistic SPDX expressions like `\"Apache-2.0 WITH \
5076 LLVM-exception\"` rarely exceed ~64 bytes — this length suggests a \
5077 paste-from-license-text or multi-line blob landed in the `:licenca` \
5078 slot)",
5079 s.len()
5080 ));
5081 }
5082 let bytes = s.as_bytes();
5083 if bytes[0] == b' ' {
5084 return Err(
5085 "must not start with whitespace (SPDX expressions are single tokens \
5086 or token sequences separated by *internal* single ASCII spaces; a \
5087 leading space is the canonical paste-from-aligned-doc footgun and \
5088 breaks every downstream SPDX parser that splits on exact token \
5089 boundaries)"
5090 .to_string(),
5091 );
5092 }
5093 if *bytes.last().expect("non-empty checked above") == b' ' {
5094 return Err(
5095 "must not end with whitespace (SPDX expressions don't terminate with \
5096 trailing whitespace; the trailing-space arm is the canonical \
5097 paste-from-doc footgun that breaks downstream parsers which split \
5098 on exact `AND` / `OR` / `WITH` keyword boundaries)"
5099 .to_string(),
5100 );
5101 }
5102 for &b in bytes {
5103 let valid = b.is_ascii_alphanumeric()
5104 || b == b'.'
5105 || b == b'-'
5106 || b == b'+'
5107 || b == b'('
5108 || b == b')'
5109 || b == b':'
5110 || b == b' ';
5111 if !valid {
5112 let msg = if b == b'\t' {
5113 "must not contain tab character (SPDX expressions use a single \
5114 ASCII space between tokens — tabs are the canonical \
5115 paste-from-aligned-doc footgun and break downstream parsers \
5116 that split on exact `\" \"` boundaries)"
5117 .to_string()
5118 } else if b < 0x20 || b == 0x7F {
5119 format!(
5120 "must not contain control character 0x{b:02x} (SPDX \
5121 expressions are printable ASCII; the CR/LF arm is the \
5122 canonical paste-from-multiline-doc footgun and lands as a \
5123 malformed line in the rendered chart `README.md` `## \
5124 License` section)"
5125 )
5126 } else if b >= 0x80 {
5127 format!(
5128 "must not contain non-ASCII byte 0x{b:02x} (SPDX identifiers \
5129 are ASCII per the `idstring = 1*(ALPHA / DIGIT / \"-\" / \
5130 \".\")` production; raw non-ASCII silently round-trips \
5131 inconsistently across NFC/NFD normalization on APFS / \
5132 case-folding filesystems and breaks at every downstream \
5133 SPDX-aware tool)"
5134 )
5135 } else if b == b'_' {
5136 "must not contain `_` (SPDX `idstring` grammar — license-id, \
5137 LicenseRef, exception-id — is `1*(ALPHA / DIGIT / \"-\" / \
5138 \".\")`; `_` is not in the SPDX alphabet, use `-` as the \
5139 segment separator instead, e.g. `\"Apache-2.0\"` not \
5140 `\"Apache_2.0\"`)"
5141 .to_string()
5142 } else if b == b',' {
5143 "must not contain `,` (SPDX expressions compose multiple \
5144 licenses via the `AND` / `OR` keywords, not the comma \
5145 separator; e.g. `\"MIT OR Apache-2.0\"` not `\"MIT, \
5146 Apache-2.0\"`)"
5147 .to_string()
5148 } else if b == b'/' {
5149 "must not contain `/` (the `dual-license/A` slash form is a \
5150 non-SPDX colloquial idiom; SPDX uses the `OR` keyword to \
5151 compose: `\"MIT OR Apache-2.0\"` not `\"MIT/Apache-2.0\"`)"
5152 .to_string()
5153 } else if b == b';' {
5154 "must not contain `;` (SPDX expressions compose multiple \
5155 licenses via the `AND` / `OR` keywords, not the semicolon \
5156 separator; e.g. `\"MIT AND Apache-2.0\"` not `\"MIT; \
5157 Apache-2.0\"`)"
5158 .to_string()
5159 } else {
5160 format!(
5161 "contains invalid character {ch:?} (the SPDX expression \
5162 alphabet is `[A-Za-z0-9.+\\-():]` plus single ASCII space; \
5163 license IDs / exception IDs are `idstring` `1*(ALPHA / \
5164 DIGIT / \"-\" / \".\")`, composition uses `AND` / `OR` / \
5165 `WITH` keywords + `(`/`)` grouping)",
5166 ch = b as char
5167 )
5168 };
5169 return Err(msg);
5170 }
5171 }
5172 Ok(())
5173}
5174
5175/// Maximum byte length of a chart-description-shaped string. The
5176/// 512-byte cap is the axis-appropriate ceiling for the free-form
5177/// prose summary the `:descricao` axis carries: every realistic
5178/// chart description in the wild (`"Canonical Rust→wasm32-wasip2
5179/// caixa Servico."`, `"Checkout flow."`, `"AWS provider caixa for
5180/// tatara-lisp"`) sits well under 256 bytes, and the 512-byte cap
5181/// surfaces the "paste-from-doc multi-paragraph blob landed in the
5182/// `:descricao` slot" footgun at validate time. Peer with
5183/// [`WASI_KV_SLOT_MAX_LEN`] (512) on the sibling longer-than-
5184/// identifier axis; tighter than [`GIT_REPO_URL_MAX_LEN`] (2048)
5185/// which carries a different axis-class ceiling, and looser than
5186/// [`SPDX_EXPRESSION_MAX_LEN`] (256) which is the canonical
5187/// short-identifier-class axis.
5188pub const CHART_DESCRIPTION_MAX_LEN: usize = 512;
5189
5190/// Scan `s` for the Unicode bidirectional-override / isolate format
5191/// codepoints UAX #9 names as the structural prerequisite of the
5192/// "Trojan Source" attack class (CVE-2021-42574 / Boucher & Anderson
5193/// 2021): nine codepoints in two contiguous blocks that flip the
5194/// rendered visual order of every following character until a
5195/// matching pop, so a string visible to a human reader and the same
5196/// string consumed by a parser/renderer can disagree on the order of
5197/// its content bytes.
5198///
5199/// The accepted set (rejection list):
5200///
5201/// - U+202A `LRE` LEFT-TO-RIGHT EMBEDDING
5202/// - U+202B `RLE` RIGHT-TO-LEFT EMBEDDING
5203/// - U+202C `PDF` POP DIRECTIONAL FORMATTING
5204/// - U+202D `LRO` LEFT-TO-RIGHT OVERRIDE
5205/// - U+202E `RLO` RIGHT-TO-LEFT OVERRIDE
5206/// - U+2066 `LRI` LEFT-TO-RIGHT ISOLATE
5207/// - U+2067 `RLI` RIGHT-TO-LEFT ISOLATE
5208/// - U+2068 `FSI` FIRST STRONG ISOLATE
5209/// - U+2069 `PDI` POP DIRECTIONAL ISOLATE
5210///
5211/// Returns the first offending codepoint in document order, or
5212/// `None` when `s` carries none of them. Iterates `chars()` once
5213/// (single UTF-8 decode pass, peer of every other UTF-8-aware
5214/// predicate in this module) — the per-predicate caller folds the
5215/// `Some(c)` into its axis-specific reason wording with the
5216/// offending codepoint named verbatim as `U+XXXX`.
5217///
5218/// Lifted as a shared helper rather than inlined into each per-axis
5219/// predicate (the PRIME DIRECTIVE duplication-budget rule —
5220/// THEORY.md §I.3.5: "every recurring shape becomes a generator
5221/// before it becomes a pattern; every pattern becomes a library
5222/// before it becomes duplicated code. The duplication budget is
5223/// zero.") because two predicates ([`is_chart_description_shape`],
5224/// [`is_chart_maintainer_name_shape`]) carry the same UTF-8
5225/// free-form-prose accepted set and would otherwise inline the same
5226/// nine-codepoint match arm verbatim. The third caller — every
5227/// future per-axis free-form-prose surface (a future Aplicacao-
5228/// level `:descricao` summary axis, a future per-`:contratos` edge
5229/// `:descricao` annotation, the future per-`:autores`-email-suffix
5230/// shape gate) — lands as a thin `if let Some(c) =
5231/// find_unicode_bidi_override(s) { … }` wrapper rather than
5232/// re-inlining the same codepoint match.
5233///
5234/// The arm is structurally distinct from the per-byte control-char
5235/// arm `[is_chart_description_shape]` already carries: ASCII control
5236/// bytes (`0x00..=0x1F` plus `0x7F`) are caught at the per-byte
5237/// pass; the bidi codepoints all decode to non-ASCII three-byte
5238/// UTF-8 sequences (`E2 80 AA..=E2 80 AE` for U+202A..=U+202E,
5239/// `E2 81 A6..=E2 81 A9` for U+2066..=U+2069) — every byte ≥ 0x80
5240/// per UTF-8 grammar — that the per-byte non-ASCII pass deliberately
5241/// accepts (Unicode letters, em-dash, arrows are canonical
5242/// `:descricao` shapes). Only the typed codepoint scan catches them.
5243fn find_unicode_bidi_override(s: &str) -> Option<char> {
5244 s.chars().find(|c| {
5245 matches!(
5246 *c,
5247 '\u{202A}'
5248 | '\u{202B}'
5249 | '\u{202C}'
5250 | '\u{202D}'
5251 | '\u{202E}'
5252 | '\u{2066}'
5253 | '\u{2067}'
5254 | '\u{2068}'
5255 | '\u{2069}'
5256 )
5257 })
5258}
5259
5260/// Scan `s` for any of the three non-ASCII Unicode line-break
5261/// codepoints UAX #14 (Unicode Line Breaking Algorithm) and the
5262/// YAML 1.1 §4.1 b-char production both treat as line terminators
5263/// outside the two single-byte ASCII shapes (`\n` LF / `\r` CR) the
5264/// per-byte arm on the calling predicate already closes:
5265///
5266/// - U+0085 `NEL` NEXT LINE
5267/// - U+2028 `LS` LINE SEPARATOR
5268/// - U+2029 `PS` PARAGRAPH SEPARATOR
5269///
5270/// YAML 1.2 §5.4 ("Line Break Characters") explicitly retired these
5271/// three from the YAML line-break set per the UTR #20 recommendation,
5272/// so a YAML 1.2-strict parser (the `serde_yaml` / `yaml-rust2` family)
5273/// preserves them as literal codepoints inside the rendered Chart.yaml
5274/// scalar — but YAML 1.1 parsers (go-yaml v2 which Helm v3 / kubectl /
5275/// every Kubernetes client library transitively links, and `ruamel.yaml`
5276/// in compat mode) still treat them as line terminators per the YAML 1.1
5277/// b-char production, so the same `:descricao` / `:autores` value
5278/// authored with an embedded U+2028 parses as a single-line plain-style
5279/// scalar through one downstream consumer and a multi-line block scalar
5280/// through another. The cross-parser line-break disagreement breaks the
5281/// THEORY.md §V.2 render-determinism contract every typed slot carries
5282/// on the same axis the per-byte `\n` / `\r` arms close for ASCII; the
5283/// substrate refuses the three codepoints at validate time so the
5284/// rendered Chart.yaml carries the single-line shape every conformant
5285/// YAML parser agrees on. Independently, every UAX #14 conformant text
5286/// consumer (editors, terminals, web UIs like `helm list` /
5287/// `helm search` / Artifact Hub) breaks the visual line at these
5288/// codepoints regardless of YAML version, so the author's editor view
5289/// of `caixa.lisp` disagrees with the chart-consumer's rendered view
5290/// even when both YAML parsers agree on the byte-level shape.
5291///
5292/// Returns the first offending codepoint in document order, or `None`
5293/// when `s` carries none of them. Iterates `chars()` once (single
5294/// UTF-8 decode pass, peer of [`find_unicode_bidi_override`] and every
5295/// other UTF-8-aware predicate in this module) — the per-predicate
5296/// caller folds the `Some(c)` into its axis-specific reason wording
5297/// with the offending codepoint named verbatim as `U+XXXX`.
5298///
5299/// Lifted as a shared helper rather than inlined into each per-axis
5300/// predicate (the PRIME DIRECTIVE duplication-budget rule —
5301/// THEORY.md §I.3.5: "every recurring shape becomes a generator
5302/// before it becomes a pattern; every pattern becomes a library
5303/// before it becomes duplicated code. The duplication budget is
5304/// zero.") because two predicates ([`is_chart_description_shape`],
5305/// [`is_chart_maintainer_name_shape`]) carry the same UTF-8
5306/// free-form-prose accepted set and would otherwise inline the same
5307/// three-codepoint match arm verbatim — sibling lift to the
5308/// [`find_unicode_bidi_override`] helper one trajectory earlier on
5309/// the same two predicates. The third caller — every future
5310/// per-axis free-form-prose surface (a future Aplicacao-level
5311/// `:descricao` summary axis, a future per-`:contratos` edge
5312/// `:descricao` annotation, the future per-`:autores`-email-suffix
5313/// shape gate) — lands as a thin `if let Some(c) =
5314/// find_unicode_line_break(s) { … }` wrapper rather than re-inlining
5315/// the same codepoint match.
5316///
5317/// The arm is structurally distinct from the per-byte control-char
5318/// arm `[is_chart_description_shape]` already carries: the ASCII
5319/// line-break bytes `\n` (`0x0A`) and `\r` (`0x0D`) are caught at the
5320/// per-byte pass; the three non-ASCII line-break codepoints all
5321/// decode to multi-byte UTF-8 sequences (`C2 85` for U+0085, `E2 80
5322/// A8` for U+2028, `E2 80 A9` for U+2029) — every byte ≥ 0x80 per
5323/// UTF-8 grammar — that the per-byte non-ASCII pass deliberately
5324/// accepts (Unicode letters, em-dash, arrows are canonical
5325/// `:descricao` shapes). Only the typed codepoint scan catches them.
5326fn find_unicode_line_break(s: &str) -> Option<char> {
5327 s.chars()
5328 .find(|c| matches!(*c, '\u{0085}' | '\u{2028}' | '\u{2029}'))
5329}
5330
5331/// Scan `s` for any of the eight BMP Unicode invisible-format
5332/// codepoints — the Cf-category zero-width codepoints that have no
5333/// visible glyph in any conforming font yet ride verbatim through
5334/// string equality and parser lookup:
5335///
5336/// - U+00AD `SHY` SOFT HYPHEN
5337/// - U+200B `ZWSP` ZERO WIDTH SPACE
5338/// - U+2060 `WJ` WORD JOINER
5339/// - U+2061 `FA` FUNCTION APPLICATION
5340/// - U+2062 `IT` INVISIBLE TIMES
5341/// - U+2063 `IS` INVISIBLE SEPARATOR
5342/// - U+2064 `IP` INVISIBLE PLUS
5343/// - U+FEFF `ZWNBSP` ZERO WIDTH NO-BREAK SPACE (BOM)
5344///
5345/// These codepoints break the THEORY.md §V.2 render-determinism
5346/// contract on a third axis from the visual-order class the sibling
5347/// [`find_unicode_bidi_override`] helper closes (the nine UAX #9
5348/// explicit-direction codepoints flip the rendered visual order) and
5349/// the single-line/multi-line class the sibling
5350/// [`find_unicode_line_break`] helper closes (the three UAX #14
5351/// non-ASCII line-break codepoints split a YAML 1.1 scalar): the
5352/// *invisible-identity* divergence. The author's editor view of
5353/// `caixa.lisp`, the chart-consumer's `helm list` / `helm search` /
5354/// Artifact Hub maintainer column, and every conformant terminal /
5355/// browser / editor agree on the visible glyph sequence (the
5356/// codepoint renders as nothing, so `"alice"` and
5357/// `"alice\u{200B}"` look identical end-to-end) — but the byte
5358/// sequence the YAML-plain-style-scalar carries verbatim differs
5359/// from the byte sequence the same author intends to read back, so
5360/// every byte-level grep / diff / equality comparison over the
5361/// rendered Chart.yaml disagrees with the visible-glyph match, the
5362/// Artifact Hub maintainer / description search index lookup misses
5363/// the authored identity entry because the byte sequence carries
5364/// invisible codepoints between letters, and a future per-author
5365/// CLA-signer lookup matches a visually-identical-but-byte-distinct
5366/// identity (the canonical "invisible-codepoint homograph" footgun).
5367/// The canonical authoring shapes that introduce these codepoints:
5368/// paste-from-Microsoft-Word (SHY auto-inserted at every hyphenation
5369/// candidate), paste-from-text-editor-saved-as-UTF-8-with-BOM (BOM
5370/// leading byte from Notepad / older VS Code defaults / Excel CSV
5371/// export), paste-from-typesetting-doc (ZWSP / WJ invisible word-
5372/// break hints from InDesign / LaTeX-rendered PDF copy-paste).
5373///
5374/// Returns the first offending codepoint in document order, or
5375/// `None` when `s` carries none of them. Iterates `chars()` once
5376/// (single UTF-8 decode pass, peer of [`find_unicode_bidi_override`]
5377/// and [`find_unicode_line_break`]) — the per-predicate caller folds
5378/// the `Some(c)` into its axis-specific reason wording with the
5379/// offending codepoint named verbatim as `U+XXXX`.
5380///
5381/// Excluded from the rejected set, on purpose:
5382///
5383/// - U+200C `ZWNJ` ZERO WIDTH NON-JOINER and U+200D `ZWJ` ZERO
5384/// WIDTH JOINER — both carry semantic compositional load in
5385/// Devanagari / Bengali / Persian script clusters (the
5386/// canonical "Persian name authoring" shape relies on ZWNJ to
5387/// break inappropriate ligatures) and in modern emoji ZWJ
5388/// sequences (👨💻 is `MAN` + U+200D `ZWJ` + `LAPTOP`); the
5389/// `:autores` / `:descricao` axes admit Unicode prose where
5390/// such sequences are the canonical authoring shape and a ban
5391/// would regress legitimate maintainer-name fixtures.
5392/// - U+200E `LRM` LEFT-TO-RIGHT MARK and U+200F `RLM`
5393/// RIGHT-TO-LEFT MARK — both are legitimate single-character
5394/// direction *hints* (not overrides) in mixed-script prose
5395/// (the canonical "Arabic name with embedded ASCII email"
5396/// shape relies on RLM to render the visual order reliably
5397/// across YAML / HTML consumers); the visible-order risk on
5398/// these axes is closed by the bidi-*override* helper (the 9
5399/// codepoints UAX #9 names as the Trojan Source vector), not
5400/// by the bidi-*marks*, so LRM/RLM remain accepted natively.
5401/// - Codepoints outside the BMP — Variation Selectors
5402/// Supplement (U+E0100..U+E01EF), Tag characters
5403/// (U+E0001..U+E007F) — sit outside the BMP and rarely
5404/// surface in realistic Helm chart metadata pasted from
5405/// editors; the BMP-restricted set captures the canonical
5406/// paste-from-Word / paste-from-BOM-editor / paste-from-
5407/// typesetting-doc / paste-from-math-formula class without
5408/// committing to a full Unicode `Default_Ignorable_Code_Point`
5409/// table.
5410///
5411/// Lifted as a shared helper rather than inlined into each per-axis
5412/// predicate (the PRIME DIRECTIVE duplication-budget rule —
5413/// THEORY.md §I.3.5: "every recurring shape becomes a generator
5414/// before it becomes a pattern; every pattern becomes a library
5415/// before it becomes duplicated code. The duplication budget is
5416/// zero.") because two predicates ([`is_chart_description_shape`],
5417/// [`is_chart_maintainer_name_shape`]) carry the same UTF-8
5418/// free-form-prose accepted set and would otherwise inline the same
5419/// eight-codepoint match arm verbatim — third lift in the UAX-driven
5420/// render-determinism trio (peer of [`find_unicode_bidi_override`]
5421/// on the visual-order axis and [`find_unicode_line_break`] on the
5422/// single-line/multi-line axis). The third caller — every future
5423/// per-axis free-form-prose surface (a future Aplicacao-level
5424/// `:descricao` summary axis, a future per-`:contratos` edge
5425/// `:descricao` annotation, the future per-`:autores`-email-suffix
5426/// shape gate) — lands as a thin `if let Some(c) =
5427/// find_unicode_invisible_format(s) { … }` wrapper rather than
5428/// re-inlining the same codepoint match.
5429///
5430/// The arm is structurally distinct from every prior arm on the
5431/// calling predicates: the per-byte control-char arm catches ASCII
5432/// `0x00..=0x1F` plus `0x7F` DEL; the per-byte non-ASCII pass
5433/// admits multi-byte UTF-8 sequences (Unicode letters, em-dash,
5434/// arrows are canonical shapes); the bidi-override helper catches
5435/// the 9 visual-order codepoints; the line-break helper catches
5436/// the 3 single-line-vs-multi-line codepoints. None overlap the
5437/// eight invisible-format codepoints here — each decodes to a
5438/// distinct multi-byte UTF-8 sequence (`C2 AD` for U+00AD,
5439/// `E2 80 8B` for U+200B, `E2 81 A0` for U+2060, `E2 81 A1` for
5440/// U+2061, `E2 81 A2` for U+2062, `E2 81 A3` for U+2063, `E2 81
5441/// A4` for U+2064, `EF BB BF` for U+FEFF) the per-byte non-ASCII
5442/// pass deliberately accepts; only the typed codepoint scan catches
5443/// them.
5444///
5445/// The four math-invisible operators U+2061..=U+2064 carry their
5446/// semantic load only inside mathematical typesetting (MathML
5447/// `<mo>` invisible operators, LaTeX `\,\,` thin-space-as-invisible-
5448/// times) — no realistic Helm chart `:descricao` or `:autores`
5449/// value is a math formula. The canonical authoring footgun is the
5450/// paste-from-MathJax-rendered-doc / paste-from-LaTeX-equation /
5451/// paste-from-InDesign-math-equation shape where MathJax /
5452/// LaTeX2RTF / InDesign export an invisible-operator codepoint
5453/// between adjacent symbols to preserve the semantic operator
5454/// reading for screen readers, and the codepoint silently rides
5455/// into the YAML scalar — same invisible-identity divergence class
5456/// the BMP four (SHY / ZWSP / WJ / BOM) close on the paste-from-
5457/// Word / paste-from-BOM-editor / paste-from-typesetting-doc class.
5458fn find_unicode_invisible_format(s: &str) -> Option<char> {
5459 s.chars().find(|c| {
5460 matches!(
5461 *c,
5462 '\u{00AD}'
5463 | '\u{200B}'
5464 | '\u{2060}'
5465 | '\u{2061}'
5466 | '\u{2062}'
5467 | '\u{2063}'
5468 | '\u{2064}'
5469 | '\u{FEFF}'
5470 )
5471 })
5472}
5473
5474/// Predicate: assert that `s` is a valid chart-description shape.
5475/// The `:descricao` axis is a free-form prose summary that lands in
5476/// the rendered `lareira-<nome>` Helm chart's `Chart.yaml`
5477/// `description:` field (a YAML scalar consumed by `helm list`,
5478/// `helm search`, Artifact Hub, and every chart-aware UI) and in
5479/// the chart's `README.md` header paragraph
5480/// (`caixa-helm/src/lib.rs:232`, `caixa-helm/src/lib.rs:333`).
5481/// The contract — modeled on the YAML 1.2 plain-style scalar
5482/// grammar and the Helm chart spec's expectation that
5483/// `description:` is a one-line summary:
5484///
5485/// - 1..=[`CHART_DESCRIPTION_MAX_LEN`] (512) bytes;
5486/// - no leading whitespace (paste-from-aligned-doc footgun —
5487/// YAML plain-style scalars round-trip trim-and-restore on
5488/// leading whitespace, so an authored `" foo"` lands as `"foo"`
5489/// in the rendered Chart.yaml and the round-trip back through
5490/// `caixa.lisp` silently drops the space);
5491/// - no trailing whitespace (paste-from-doc footgun — every YAML
5492/// dumper trims trailing whitespace from plain-style scalars,
5493/// so an authored `"foo "` round-trips inconsistently);
5494/// - no ASCII control characters anywhere (`0x00..=0x1F` plus
5495/// `0x7F` DEL) — tabs, newlines, carriage returns, and every
5496/// other control byte break the single-line YAML scalar shape
5497/// and the README header paragraph. The newline / CR arms are
5498/// the canonical paste-from-multiline-doc footgun; the tab arm
5499/// is the canonical paste-from-aligned-doc footgun; the
5500/// other-control-byte arm catches every more-exotic
5501/// paste-from-binary-blob shape (`0x00` NUL, `0x07` BEL,
5502/// `0x1B` ESC) that would silently land in the rendered
5503/// `Chart.yaml` as a YAML-illegal byte sequence and fail at
5504/// `helm lint` time far from the source caixa.lisp;
5505/// - non-ASCII bytes (UTF-8 continuation sequences) are
5506/// accepted — the canonical author shapes (`"Canonical
5507/// Rust→wasm32-wasip2 caixa Servico."`, `"FIXME — describe
5508/// this caixa"`) carry `→` (U+2192) and `—` (U+2014) and every
5509/// downstream consumer (YAML 1.2, Helm v3, every chart-aware
5510/// UI) round-trips Unicode losslessly;
5511/// - no Unicode bidirectional-override / isolate format
5512/// codepoints (U+202A `LRE`, U+202B `RLE`, U+202C `PDF`,
5513/// U+202D `LRO`, U+202E `RLO`, U+2066 `LRI`, U+2067 `RLI`,
5514/// U+2068 `FSI`, U+2069 `PDI`) — the nine codepoints UAX #9
5515/// names as the structural prerequisite of the "Trojan Source"
5516/// attack class (CVE-2021-42574 / Boucher & Anderson 2021)
5517/// that flip the rendered visual order of every following
5518/// character until a matching pop. Routed through the lifted
5519/// [`find_unicode_bidi_override`] helper so the same
5520/// nine-codepoint accepted set is shared with
5521/// [`is_chart_maintainer_name_shape`] on the sibling
5522/// YAML-plain-style-scalar surface, structurally consistent.
5523/// The non-ASCII byte arm above admits Unicode letters /
5524/// em-dash / arrows because YAML 1.2 + Helm v3 + every
5525/// chart-aware UI round-trip them losslessly; the bidi-override
5526/// codepoints break that round-trip discipline by class
5527/// (the byte sequence rides verbatim into the rendered
5528/// `Chart.yaml`'s `description:` value but renders differently
5529/// in `helm show chart` / Artifact Hub / `helm list` vs the
5530/// author's editor view of `caixa.lisp`), defeating the
5531/// THEORY.md §V.2 render-determinism contract every typed
5532/// slot carries on the same axis the per-byte CR/LF/control
5533/// arms above close for ASCII.
5534/// - no non-ASCII Unicode line-break codepoints (U+0085 `NEL`,
5535/// U+2028 `LS`, U+2029 `PS`) — the three codepoints UAX #14
5536/// (Unicode Line Breaking Algorithm) and the YAML 1.1 §4.1
5537/// b-char production both treat as line terminators outside
5538/// the ASCII `\n` / `\r` arms above. YAML 1.2 §5.4 retired
5539/// them per UTR #20, so YAML 1.2-strict parsers preserve them
5540/// verbatim while YAML 1.1 parsers (go-yaml v2 which Helm v3 /
5541/// kubectl link, `ruamel.yaml` in compat mode) split the
5542/// scalar on them — the same `:descricao` value parses as
5543/// single-line through one consumer and multi-line through
5544/// another, breaking cross-parser determinism on the same
5545/// axis the per-byte `\n` / `\r` arms close for ASCII.
5546/// Independently, every UAX #14 conformant text consumer
5547/// (editors, terminals, `helm list` / Artifact Hub web UIs)
5548/// breaks the visual line at these codepoints regardless of
5549/// YAML version, so the author's editor view of `caixa.lisp`
5550/// and the chart-consumer's rendered view diverge even when
5551/// both YAML parsers agree on the byte-level shape. Routed
5552/// through the lifted [`find_unicode_line_break`] helper so
5553/// the same three-codepoint accepted set is shared with
5554/// [`is_chart_maintainer_name_shape`], peer of the
5555/// [`find_unicode_bidi_override`] lift on the same two
5556/// predicates one trajectory earlier.
5557/// - no Unicode invisible-format codepoints (U+00AD `SHY`,
5558/// U+200B `ZWSP`, U+2060 `WJ`, U+2061 `FA` FUNCTION
5559/// APPLICATION, U+2062 `IT` INVISIBLE TIMES, U+2063 `IS`
5560/// INVISIBLE SEPARATOR, U+2064 `IP` INVISIBLE PLUS, U+FEFF
5561/// `ZWNBSP` / BOM) — the eight BMP Cf-category zero-width
5562/// codepoints with no visible glyph in any conforming font.
5563/// The author's editor view of `caixa.lisp` and the chart-
5564/// consumer's `helm list` / Artifact Hub description column
5565/// agree on the visible glyph sequence (`"Canonical Servico"`
5566/// and `"Canonical\u{200B}Servico"` render identically), but
5567/// the byte sequence the YAML-plain-style-scalar carries
5568/// verbatim differs — every byte-level grep / diff / equality
5569/// comparison and the Artifact Hub description-search index
5570/// lookup disagree silently with the visible-glyph match.
5571/// Closes the canonical paste-from-Microsoft-Word (SHY auto-
5572/// inserted at hyphenation candidates), paste-from-text-
5573/// editor-saved-as-UTF-8-with-BOM (leading BOM byte),
5574/// paste-from-typesetting-doc (ZWSP / WJ invisible word-break
5575/// hints), and paste-from-MathJax/LaTeX-rendered-formula
5576/// (FUNCTION APPLICATION / INVISIBLE TIMES / INVISIBLE
5577/// SEPARATOR / INVISIBLE PLUS — the four math-formula
5578/// invisible operators MathJax / LaTeX export between
5579/// adjacent symbols for screen-reader operator semantics)
5580/// footguns. Routed through the lifted
5581/// [`find_unicode_invisible_format`] helper so the same
5582/// eight-codepoint accepted set is shared with
5583/// [`is_chart_maintainer_name_shape`], third lift in the
5584/// UAX-driven render-determinism trio (peer of
5585/// [`find_unicode_bidi_override`] on the visual-order axis
5586/// and [`find_unicode_line_break`] on the single-line/multi-
5587/// line axis). The eight-codepoint set excludes U+200C
5588/// `ZWNJ` / U+200D `ZWJ` (legitimate compositional load in
5589/// Indic / Persian scripts and emoji ZWJ sequences) and
5590/// U+200E `LRM` / U+200F `RLM` (legitimate single-character
5591/// direction hints in mixed-script prose); the visible-order
5592/// risk on bidi overrides — not marks — is closed by the
5593/// prior helper.
5594///
5595/// The predicate is a *structural* floor — it enforces the
5596/// single-line printable-UTF-8 shape every realistic chart
5597/// description carries, not a per-byte alphabet check (which would
5598/// regress every non-ASCII canonical fixture). Same trajectory as
5599/// [`is_spdx_expression_shape`] (the ASCII-alphabet floor on the
5600/// `:licenca` axis) and [`is_git_repo_url`] (the URL-shape floor on
5601/// the `:repositorio` axis): the typed validator refuses the
5602/// downstream consumer's would-also-refuse shapes at the source
5603/// caixa.lisp boundary with the offending value named verbatim.
5604///
5605/// Returns the parser-shaped reason on rejection (without wrapping
5606/// in any error variant) so each per-axis caller —
5607/// [`crate::Caixa::validate_descricao`] for the universal
5608/// `:descricao` axis at validate time, every future per-description
5609/// axis (a future Aplicacao-level `:descricao` summary axis on
5610/// `mesh.pleme.io/v1alpha1/Caixa` CRs, a future Servico-level
5611/// per-`:contratos` edge `:descricao` annotation) — wraps the same
5612/// reason in its own typed `*Invalid { <axis>, reason }` variant.
5613/// The reason wording is axis-agnostic ("chart descriptions reject
5614/// leading whitespace") so every call site reading the same
5615/// diagnostic points at the same rule; drift between any two axes'
5616/// rule enforcement is a build error visible at this predicate, not
5617/// a per-renderer "this passed validate but `helm lint` rejected
5618/// the Chart.yaml `description:` value" surprise.
5619///
5620/// Empty input is rejected here (defensively) and at each call
5621/// site via the narrower [`crate::ManifestError::DescricaoEmpty`]
5622/// variant — the same empty-first cascade [`is_dns_1123_label`],
5623/// [`is_gateway_api_http_path`], [`is_wit_world_ref`],
5624/// [`is_nats_subject`], [`is_wasi_keyvalue_slot`],
5625/// [`is_git_ref_name`], [`is_git_oid`], [`is_git_repo_url`],
5626/// [`is_cargo_feature_name`], and [`is_spdx_expression_shape`] all
5627/// carry.
5628///
5629/// # Errors
5630///
5631/// Returns the parser-shaped reason naming the specific violation
5632/// (length / leading-whitespace / trailing-whitespace /
5633/// tab / newline / carriage-return / other-control-byte /
5634/// Unicode-bidi-override-codepoint / Unicode-line-break-codepoint),
5635/// without wrapping in any error variant — every caller maps the
5636/// same `String` into its own typed `*Invalid { <axis>, reason }`
5637/// enum variant.
5638pub fn is_chart_description_shape(s: &str) -> Result<(), String> {
5639 if s.is_empty() {
5640 return Err("must not be empty".to_string());
5641 }
5642 if s.len() > CHART_DESCRIPTION_MAX_LEN {
5643 return Err(format!(
5644 "exceeds chart description max length of {CHART_DESCRIPTION_MAX_LEN} bytes \
5645 (got {} bytes; realistic chart descriptions like `\"Canonical \
5646 Rust→wasm32-wasip2 caixa Servico.\"` rarely exceed ~64 bytes — this \
5647 length suggests a paste-from-doc multi-paragraph blob landed in the \
5648 `:descricao` slot)",
5649 s.len()
5650 ));
5651 }
5652 let bytes = s.as_bytes();
5653 if bytes[0] == b' ' {
5654 return Err(
5655 "must not start with whitespace (chart descriptions are single-line YAML \
5656 plain-style scalars; a leading space is the canonical \
5657 paste-from-aligned-doc footgun and round-trips inconsistently — every \
5658 YAML dumper trims leading whitespace from plain-style scalars, so the \
5659 authored space silently drops in the rendered Chart.yaml)"
5660 .to_string(),
5661 );
5662 }
5663 if *bytes.last().expect("non-empty checked above") == b' ' {
5664 return Err(
5665 "must not end with whitespace (chart descriptions don't terminate with \
5666 trailing whitespace; every YAML dumper trims trailing whitespace from \
5667 plain-style scalars, so the authored space round-trips inconsistently \
5668 back through `caixa.lisp`)"
5669 .to_string(),
5670 );
5671 }
5672 for &b in bytes {
5673 if b == b'\t' {
5674 return Err(
5675 "must not contain tab character (chart descriptions are single-line \
5676 YAML plain-style scalars; tabs are the canonical \
5677 paste-from-aligned-doc footgun and break the single-line scalar \
5678 shape — every downstream YAML 1.2 parser is forbidden from \
5679 emitting indentation tabs and tabs in plain-style scalars are \
5680 implementation-defined)"
5681 .to_string(),
5682 );
5683 }
5684 if b == b'\n' {
5685 return Err(
5686 "must not contain newline (chart descriptions are single-line YAML \
5687 plain-style scalars; an embedded newline is the canonical \
5688 paste-from-multiline-doc footgun and lands as a multi-line YAML \
5689 block scalar in the rendered Chart.yaml — every chart-aware UI \
5690 (`helm list`, `helm search`, Artifact Hub) renders the description \
5691 in a single-line column, so the embedded newline is silently \
5692 dropped at every downstream consumer)"
5693 .to_string(),
5694 );
5695 }
5696 if b == b'\r' {
5697 return Err("must not contain carriage return (chart descriptions are \
5698 single-line YAML plain-style scalars; a `\\r` byte is the canonical \
5699 paste-from-Windows-CRLF-doc footgun and lands as a literal CR in \
5700 the rendered Chart.yaml — every YAML 1.2 parser treats CR as a \
5701 line terminator equivalent to LF, so the embedded CR is silently \
5702 normalized to a newline at every downstream consumer)"
5703 .to_string());
5704 }
5705 if b < 0x20 || b == 0x7F {
5706 return Err(format!(
5707 "must not contain control character 0x{b:02x} (chart descriptions \
5708 are printable UTF-8 single-line scalars; the control-byte arm \
5709 catches paste-from-binary-blob footguns like `0x00` NUL, `0x07` \
5710 BEL, `0x1b` ESC that would silently land in the rendered \
5711 Chart.yaml as a YAML-illegal byte sequence and fail at `helm lint` \
5712 time far from the source caixa.lisp)"
5713 ));
5714 }
5715 }
5716 if let Some(c) = find_unicode_bidi_override(s) {
5717 return Err(format!(
5718 "must not contain Unicode bidirectional-override codepoint U+{cp:04X} \
5719 (the nine codepoints UAX #9 names as the structural prerequisite of \
5720 the \"Trojan Source\" attack class — CVE-2021-42574 / Boucher & \
5721 Anderson 2021: U+202A `LRE`, U+202B `RLE`, U+202C `PDF`, U+202D `LRO`, \
5722 U+202E `RLO`, U+2066 `LRI`, U+2067 `RLI`, U+2068 `FSI`, U+2069 `PDI` \
5723 — flip the rendered visual order of every following character until a \
5724 matching pop, so a `:descricao` string visible to a human reading \
5725 `caixa.lisp` and the same string consumed by `helm show chart` / \
5726 `helm list` / Artifact Hub / every chart-aware UI disagree on the \
5727 order of the displayed content bytes. The byte sequence \
5728 ({utf8_seq}) rides verbatim into the rendered Chart.yaml's \
5729 `description:` value at the same axis the per-byte CR/LF/control \
5730 arms close for ASCII, but renders differently across consumers, \
5731 defeating the THEORY.md §V.2 render-determinism contract every typed \
5732 slot carries. The non-ASCII byte arm above admits Unicode letters / \
5733 em-dash / arrows because YAML 1.2 + Helm v3 round-trip them \
5734 losslessly; this codepoint breaks that round-trip discipline by \
5735 class. Drop the bidi-override codepoint; pure visual right-to-left \
5736 text (Hebrew, Arabic) is accepted natively without explicit \
5737 direction marks)",
5738 cp = c as u32,
5739 utf8_seq = c
5740 .encode_utf8(&mut [0u8; 4])
5741 .bytes()
5742 .map(|b| format!("0x{b:02X}"))
5743 .collect::<Vec<_>>()
5744 .join(" "),
5745 ));
5746 }
5747 if let Some(c) = find_unicode_line_break(s) {
5748 return Err(format!(
5749 "must not contain Unicode line-break codepoint U+{cp:04X} (the three \
5750 codepoints UAX #14 / YAML 1.1 §4.1 name as line terminators outside \
5751 the ASCII `\\n` / `\\r` arms above: U+0085 `NEL` NEXT LINE, U+2028 \
5752 `LS` LINE SEPARATOR, U+2029 `PS` PARAGRAPH SEPARATOR. YAML 1.2 §5.4 \
5753 retired them per UTR #20 so YAML 1.2-strict parsers preserve them \
5754 verbatim, but YAML 1.1 parsers (go-yaml v2 which Helm v3 / kubectl / \
5755 every Kubernetes client library transitively links, `ruamel.yaml` in \
5756 compat mode) still split scalars on them — the same `:descricao` \
5757 value parses as a single-line plain-style scalar through one \
5758 downstream consumer and a multi-line block scalar through another, \
5759 breaking cross-parser determinism on the same axis the per-byte \
5760 `\\n` / `\\r` arms close for ASCII. Independently, every UAX #14 \
5761 conformant text consumer (editors, terminals, `helm list` / \
5762 `helm search` / Artifact Hub web UIs) breaks the visual line at \
5763 these codepoints regardless of YAML version, so the author's editor \
5764 view of `caixa.lisp` and the chart-consumer's rendered view of the \
5765 `description:` field diverge even when both YAML parsers agree on \
5766 the byte-level shape, defeating the THEORY.md §V.2 render-\
5767 determinism contract every typed slot carries. The byte sequence \
5768 ({utf8_seq}) rides verbatim into the rendered Chart.yaml at the \
5769 same axis the per-byte `\\n` / `\\r` arms close for ASCII. Routed \
5770 through the shared [`find_unicode_line_break`] helper so the same \
5771 three-codepoint accepted set lives in exactly one place across the \
5772 [`is_chart_maintainer_name_shape`] sibling YAML-plain-style-scalar \
5773 surface, peer of the [`find_unicode_bidi_override`] lift on the \
5774 same two predicates one trajectory earlier. Drop the non-ASCII \
5775 line-break codepoint; split the value into separate logical lines \
5776 at the source if a multi-line summary is intended (the \
5777 `:descricao` axis is single-line by contract — the multi-paragraph \
5778 shape belongs in the chart `README.md` body, not the YAML \
5779 `description:` scalar))",
5780 cp = c as u32,
5781 utf8_seq = c
5782 .encode_utf8(&mut [0u8; 4])
5783 .bytes()
5784 .map(|b| format!("0x{b:02X}"))
5785 .collect::<Vec<_>>()
5786 .join(" "),
5787 ));
5788 }
5789 if let Some(c) = find_unicode_invisible_format(s) {
5790 return Err(format!(
5791 "must not contain Unicode invisible-format codepoint U+{cp:04X} (the \
5792 eight BMP Cf-category zero-width codepoints with no visible glyph in \
5793 any conforming font: U+00AD `SHY` SOFT HYPHEN, U+200B `ZWSP` ZERO \
5794 WIDTH SPACE, U+2060 `WJ` WORD JOINER, U+2061 `FA` FUNCTION \
5795 APPLICATION, U+2062 `IT` INVISIBLE TIMES, U+2063 `IS` INVISIBLE \
5796 SEPARATOR, U+2064 `IP` INVISIBLE PLUS, U+FEFF `ZWNBSP` ZERO WIDTH \
5797 NO-BREAK SPACE / BOM. The invisible-identity divergence: the \
5798 author's editor view of `caixa.lisp`, the chart-consumer's \
5799 `helm list` / `helm search` / Artifact Hub description column, \
5800 and every conformant terminal / browser / editor agree on the \
5801 visible glyph sequence (the codepoint renders as nothing, so \
5802 `\"Canonical Servico\"` and `\"Canonical\\u{{200B}}Servico\"` look \
5803 identical end-to-end), but the byte sequence the YAML-plain-style-\
5804 scalar carries verbatim differs — every byte-level grep / diff / \
5805 equality comparison over the rendered Chart.yaml `description:` \
5806 value disagrees with the visible-glyph match, and the Artifact Hub \
5807 description-search index lookup misses the authored entry because \
5808 the byte sequence carries an extra invisible codepoint between \
5809 letters. The canonical authoring shapes that silently introduce \
5810 these codepoints: paste-from-Microsoft-Word (SHY auto-inserted at \
5811 every hyphenation candidate), paste-from-text-editor-saved-as-UTF-8-\
5812 with-BOM (BOM leading byte from Notepad / older VS Code defaults), \
5813 paste-from-typesetting-doc (ZWSP / WJ invisible word-break hints \
5814 from InDesign / LaTeX-rendered PDF copy-paste), and paste-from-\
5815 MathJax/LaTeX-rendered-formula (FUNCTION APPLICATION / INVISIBLE \
5816 TIMES / INVISIBLE SEPARATOR / INVISIBLE PLUS — MathJax / LaTeX2RTF \
5817 / InDesign math-equation export emit one of these between adjacent \
5818 symbols to preserve operator semantics for screen readers, and the \
5819 codepoint silently rides into the YAML scalar with no visible \
5820 trace). The byte sequence ({utf8_seq}) rides verbatim into the \
5821 rendered Chart.yaml at the same axis the per-byte CR/LF/control \
5822 arms close for ASCII, but renders as nothing across consumers, \
5823 defeating the THEORY.md §V.2 render-determinism contract on a \
5824 third axis from the bidi-override (visual-order) and line-break \
5825 (single-line vs multi-line) classes the prior arms close. Routed \
5826 through the shared [`find_unicode_invisible_format`] helper so \
5827 the eight-codepoint accepted set lives in exactly one place \
5828 across the [`is_chart_maintainer_name_shape`] sibling \
5829 YAML-plain-style-scalar surface, third lift in the UAX-driven \
5830 render-determinism trio (peer of [`find_unicode_bidi_override`] \
5831 on the visual-order axis and [`find_unicode_line_break`] on the \
5832 single-line/multi-line axis). Drop the invisible codepoint; emoji \
5833 ZWJ sequences (U+200D for the 👨💻 family) and bidi direction-mark \
5834 codepoints (U+200E `LRM` / U+200F `RLM`) are accepted natively — \
5835 only the eight zero-semantic-content codepoints are rejected)",
5836 cp = c as u32,
5837 utf8_seq = c
5838 .encode_utf8(&mut [0u8; 4])
5839 .bytes()
5840 .map(|b| format!("0x{b:02X}"))
5841 .collect::<Vec<_>>()
5842 .join(" "),
5843 ));
5844 }
5845 Ok(())
5846}
5847
5848/// Maximum byte length of a chart-maintainer-name-shaped string. The
5849/// 128-byte cap is the axis-appropriate ceiling for the per-entry
5850/// identifier the `:autores` Vec axis carries: every realistic Helm
5851/// chart maintainer name in the wild (`"pleme-io"`, `"Pleme
5852/// Contributors"`, `"alice <alice@example.com>"`, `"François
5853/// Dupont"`) sits well under 64 bytes, and the 128-byte cap surfaces
5854/// the "paste-from-doc multi-paragraph blob landed in a single
5855/// `:autores` entry" footgun at validate time. Tighter than
5856/// [`CHART_DESCRIPTION_MAX_LEN`] (512) on the sibling free-form-prose
5857/// axis where multi-sentence summaries are the canonical shape;
5858/// peer with [`WIT_IDENT_MAX_LEN`] (128) on the sibling
5859/// short-identifier-class axis.
5860pub const CHART_MAINTAINER_NAME_MAX_LEN: usize = 128;
5861
5862/// Predicate: assert that `s` is a valid chart-maintainer-name shape.
5863/// The `:autores` axis is a per-entry maintainer identifier that lands
5864/// in the rendered `lareira-<nome>` Helm chart's `Chart.yaml`
5865/// `maintainers: [{name: …, email: null}]` array via
5866/// [`caixa-helm`]'s `build_chart_yaml` (`caixa-helm/src/lib.rs:251`);
5867/// each entry becomes the `name:` value of a single `Maintainer`
5868/// record (a YAML scalar consumed by `helm list`, `helm search`,
5869/// Artifact Hub's maintainer index, and every chart-aware UI). The
5870/// contract — modeled on the same YAML 1.2 plain-style scalar
5871/// grammar [`is_chart_description_shape`] enforces on the sibling
5872/// `:descricao` axis, with a tighter length cap for the per-entry
5873/// identifier class:
5874///
5875/// - 1..=[`CHART_MAINTAINER_NAME_MAX_LEN`] (128) bytes;
5876/// - no leading whitespace (paste-from-aligned-doc footgun —
5877/// YAML plain-style scalars round-trip trim-and-restore on
5878/// leading whitespace, so an authored `" pleme-io"` lands as
5879/// `"pleme-io"` in the rendered Chart.yaml and the round-trip
5880/// back through `caixa.lisp` silently drops the space);
5881/// - no trailing whitespace (paste-from-doc footgun — every YAML
5882/// dumper trims trailing whitespace from plain-style scalars,
5883/// so an authored `"pleme-io "` round-trips inconsistently);
5884/// - no ASCII control characters anywhere (`0x00..=0x1F` plus
5885/// `0x7F` DEL) — tabs, newlines, carriage returns, and every
5886/// other control byte break the single-line YAML scalar shape
5887/// and the `helm list` / `helm search` / Artifact Hub
5888/// maintainer-column rendering. The newline / CR arms are the
5889/// canonical paste-from-multiline-doc footgun (the author
5890/// pasted a multi-line block of author records into one
5891/// `:autores` entry instead of splitting them into one entry
5892/// per author); the tab arm is the canonical
5893/// paste-from-aligned-doc footgun; the other-control-byte
5894/// arm catches every more-exotic paste-from-binary-blob shape;
5895/// - non-ASCII bytes (UTF-8 continuation sequences) are accepted
5896/// — realistic maintainer names carry Unicode (`"François"`,
5897/// `"日本語"`, `"naïve"`) and every downstream consumer
5898/// (YAML 1.2, Helm v3, every chart-aware UI) round-trips
5899/// Unicode losslessly;
5900/// - no Unicode bidirectional-override / isolate format
5901/// codepoints (U+202A `LRE`, U+202B `RLE`, U+202C `PDF`,
5902/// U+202D `LRO`, U+202E `RLO`, U+2066 `LRI`, U+2067 `RLI`,
5903/// U+2068 `FSI`, U+2069 `PDI`) — the nine codepoints UAX #9
5904/// names as the structural prerequisite of the "Trojan Source"
5905/// attack class (CVE-2021-42574). A maintainer-name with an
5906/// embedded `RLO` flips the visual order of every trailing
5907/// byte, so an `:autores "alice\u{202E}example.com<bob@"` (the
5908/// paste-from-attacker-crafted-doc footgun) renders in
5909/// `helm list`'s maintainer column / Artifact Hub as
5910/// `alice<@bob>moc.elpmaxe` but rides verbatim into the
5911/// rendered Chart.yaml `maintainers:` array — same Trojan
5912/// Source class [`is_chart_description_shape`] closes on the
5913/// sibling `:descricao` axis. Routed through the same lifted
5914/// [`find_unicode_bidi_override`] helper so the nine-codepoint
5915/// accepted set is shared, structurally consistent.
5916/// - no non-ASCII Unicode line-break codepoints (U+0085 `NEL`,
5917/// U+2028 `LS`, U+2029 `PS`) — the three codepoints UAX #14
5918/// (Unicode Line Breaking Algorithm) and YAML 1.1 §4.1 b-char
5919/// production both treat as line terminators outside the
5920/// ASCII `\n` / `\r` arms above. YAML 1.2 §5.4 retired them
5921/// per UTR #20 so the cross-parser line-break disagreement
5922/// (go-yaml v2 / YAML 1.1 still splits; YAML 1.2-strict
5923/// parsers preserve) breaks the THEORY.md §V.2 render-
5924/// determinism contract on the same axis the per-byte `\n` /
5925/// `\r` arms close for ASCII. A maintainer-name with an
5926/// embedded U+2028 parses as one entry through a YAML 1.2
5927/// parser and as two `maintainers:` array entries through a
5928/// YAML 1.1 parser — same paste-from-multiline-doc class the
5929/// `\n` arm above closes, extended to the non-ASCII line-break
5930/// codepoints the per-byte non-ASCII pass deliberately
5931/// admits for Unicode letters. Routed through the same lifted
5932/// [`find_unicode_line_break`] helper so the three-codepoint
5933/// accepted set is shared with [`is_chart_description_shape`]
5934/// on the sibling YAML-plain-style-scalar surface,
5935/// structurally consistent.
5936/// - no Unicode invisible-format codepoints (U+00AD `SHY`,
5937/// U+200B `ZWSP`, U+2060 `WJ`, U+2061 `FA` FUNCTION
5938/// APPLICATION, U+2062 `IT` INVISIBLE TIMES, U+2063 `IS`
5939/// INVISIBLE SEPARATOR, U+2064 `IP` INVISIBLE PLUS, U+FEFF
5940/// `ZWNBSP` / BOM) — the eight BMP Cf-category zero-width
5941/// codepoints with no visible glyph. A maintainer-name with
5942/// an embedded U+200B (`"alice\u{200B}"`) renders identically
5943/// to `"alice"` in `helm list` / Artifact Hub's maintainer
5944/// column, yet the byte sequence is distinct — the Artifact
5945/// Hub maintainer-index lookup misses the authored `"alice"`
5946/// entry, and a future CLA-signer lookup matches a visually-
5947/// identical-but-byte-distinct identity (the canonical
5948/// invisible-codepoint homograph footgun on the maintainer-
5949/// identity axis). Closes the canonical paste-from-Microsoft-
5950/// Word (SHY), paste-from-text-editor-saved-as-UTF-8-with-BOM
5951/// (BOM), paste-from-typesetting-doc (ZWSP / WJ), and
5952/// paste-from-MathJax/LaTeX-rendered-formula (FUNCTION
5953/// APPLICATION / INVISIBLE TIMES / INVISIBLE SEPARATOR /
5954/// INVISIBLE PLUS — math-formula invisible operators
5955/// MathJax / LaTeX2RTF / InDesign emit between symbols for
5956/// screen-reader operator semantics) footguns. Routed through
5957/// the same lifted [`find_unicode_invisible_format`] helper
5958/// so the eight-codepoint accepted set is shared with
5959/// [`is_chart_description_shape`], third lift in the UAX-
5960/// driven render-determinism trio (peer of
5961/// [`find_unicode_bidi_override`] on the visual-order axis
5962/// and [`find_unicode_line_break`] on the single-line/multi-
5963/// line axis). The eight-codepoint set excludes U+200C
5964/// `ZWNJ` / U+200D `ZWJ` (emoji ZWJ sequences are canonical
5965/// for modern maintainer-display names) and U+200E `LRM` /
5966/// U+200F `RLM` (mixed-script direction hints are canonical
5967/// for "Arabic name with embedded ASCII email" shapes).
5968///
5969/// Same structural single-line printable-UTF-8 floor as
5970/// [`is_chart_description_shape`] — both `:descricao` and `:autores`
5971/// land as YAML plain-style scalars in the same `Chart.yaml` and
5972/// share every paste-from-doc footgun the YAML 1.2 grammar refuses
5973/// at parse time. The two predicates differ only on the byte
5974/// length cap: 512 bytes for `:descricao` (multi-sentence prose
5975/// shape) vs 128 bytes for `:autores` entries (short-identifier
5976/// shape). Returns the parser-shaped reason on rejection (without
5977/// wrapping in any error variant) so each per-axis caller —
5978/// [`crate::Caixa::validate_autores`] for the universal `:autores`
5979/// axis at validate time, every future per-maintainer-name axis (a
5980/// future caixa-registry maintainer-index entry, a future
5981/// chart-author CLA-signer lookup) — wraps the same reason in its
5982/// own typed `*Invalid { <axis>, reason }` variant.
5983///
5984/// Empty input is rejected here (defensively) and at each call
5985/// site via the narrower [`crate::ManifestError::AutorEmpty`]
5986/// variant — the same empty-first cascade [`is_dns_1123_label`],
5987/// [`is_gateway_api_http_path`], [`is_wit_world_ref`],
5988/// [`is_nats_subject`], [`is_wasi_keyvalue_slot`],
5989/// [`is_git_ref_name`], [`is_git_oid`], [`is_git_repo_url`],
5990/// [`is_cargo_feature_name`], [`is_spdx_expression_shape`], and
5991/// [`is_chart_description_shape`] all carry.
5992///
5993/// # Errors
5994///
5995/// Returns the parser-shaped reason naming the specific violation
5996/// (length / leading-whitespace / trailing-whitespace / tab /
5997/// newline / carriage-return / other-control-byte /
5998/// Unicode-bidi-override-codepoint), without wrapping in any error
5999/// variant — every caller maps the same `String` into its own typed
6000/// `*Invalid { <axis>, reason }` enum variant.
6001pub fn is_chart_maintainer_name_shape(s: &str) -> Result<(), String> {
6002 if s.is_empty() {
6003 return Err("must not be empty".to_string());
6004 }
6005 if s.len() > CHART_MAINTAINER_NAME_MAX_LEN {
6006 return Err(format!(
6007 "exceeds chart maintainer name max length of \
6008 {CHART_MAINTAINER_NAME_MAX_LEN} bytes (got {} bytes; realistic chart \
6009 maintainer names like `\"pleme-io\"`, `\"Pleme Contributors\"`, \
6010 `\"alice <alice@example.com>\"` rarely exceed ~64 bytes — this \
6011 length suggests a paste-from-doc multi-paragraph blob landed in a \
6012 single `:autores` entry instead of being split into one entry per \
6013 author)",
6014 s.len()
6015 ));
6016 }
6017 let bytes = s.as_bytes();
6018 if bytes[0] == b' ' {
6019 return Err(
6020 "must not start with whitespace (chart maintainer names are \
6021 single-line YAML plain-style scalars; a leading space is the \
6022 canonical paste-from-aligned-doc footgun and round-trips \
6023 inconsistently — every YAML dumper trims leading whitespace from \
6024 plain-style scalars, so the authored space silently drops in the \
6025 rendered Chart.yaml)"
6026 .to_string(),
6027 );
6028 }
6029 if *bytes.last().expect("non-empty checked above") == b' ' {
6030 return Err(
6031 "must not end with whitespace (chart maintainer names don't \
6032 terminate with trailing whitespace; every YAML dumper trims \
6033 trailing whitespace from plain-style scalars, so the authored \
6034 space round-trips inconsistently back through `caixa.lisp`)"
6035 .to_string(),
6036 );
6037 }
6038 for &b in bytes {
6039 if b == b'\t' {
6040 return Err(
6041 "must not contain tab character (chart maintainer names are \
6042 single-line YAML plain-style scalars; tabs are the canonical \
6043 paste-from-aligned-doc footgun and break the single-line \
6044 scalar shape — every downstream YAML 1.2 parser is forbidden \
6045 from emitting indentation tabs and tabs in plain-style scalars \
6046 are implementation-defined)"
6047 .to_string(),
6048 );
6049 }
6050 if b == b'\n' {
6051 return Err("must not contain newline (chart maintainer names are \
6052 single-line YAML plain-style scalars; an embedded newline is \
6053 the canonical paste-from-multiline-doc footgun — the author \
6054 pasted a multi-line block of author records into one \
6055 `:autores` entry instead of splitting them into one entry per \
6056 author, and the result lands as a multi-line YAML block scalar \
6057 in the rendered Chart.yaml `maintainers:` array)"
6058 .to_string());
6059 }
6060 if b == b'\r' {
6061 return Err("must not contain carriage return (chart maintainer \
6062 names are single-line YAML plain-style scalars; a `\\r` byte \
6063 is the canonical paste-from-Windows-CRLF-doc footgun and \
6064 lands as a literal CR in the rendered Chart.yaml — every YAML \
6065 1.2 parser treats CR as a line terminator equivalent to LF, \
6066 so the embedded CR is silently normalized to a newline at \
6067 every downstream consumer)"
6068 .to_string());
6069 }
6070 if b < 0x20 || b == 0x7F {
6071 return Err(format!(
6072 "must not contain control character 0x{b:02x} (chart \
6073 maintainer names are printable UTF-8 single-line scalars; the \
6074 control-byte arm catches paste-from-binary-blob footguns like \
6075 `0x00` NUL, `0x07` BEL, `0x1b` ESC that would silently land \
6076 in the rendered Chart.yaml as a YAML-illegal byte sequence \
6077 and fail at `helm lint` time far from the source caixa.lisp)"
6078 ));
6079 }
6080 }
6081 if let Some(c) = find_unicode_bidi_override(s) {
6082 return Err(format!(
6083 "must not contain Unicode bidirectional-override codepoint U+{cp:04X} \
6084 (the nine codepoints UAX #9 names as the structural prerequisite of \
6085 the \"Trojan Source\" attack class — CVE-2021-42574 / Boucher & \
6086 Anderson 2021: U+202A `LRE`, U+202B `RLE`, U+202C `PDF`, U+202D `LRO`, \
6087 U+202E `RLO`, U+2066 `LRI`, U+2067 `RLI`, U+2068 `FSI`, U+2069 `PDI` \
6088 — flip the rendered visual order of every following character until a \
6089 matching pop, so an `:autores` entry visible to a human reading \
6090 `caixa.lisp` and the same entry consumed by `helm list` / Artifact \
6091 Hub's maintainer column disagree on the order of the displayed \
6092 content bytes. The byte sequence ({utf8_seq}) rides verbatim into \
6093 the rendered Chart.yaml `maintainers:` array at the same axis the \
6094 per-byte CR/LF/control arms close for ASCII, but renders \
6095 differently across consumers, defeating the THEORY.md §V.2 \
6096 render-determinism contract every typed slot carries. Routed through \
6097 the shared [`find_unicode_bidi_override`] helper so the same \
6098 nine-codepoint accepted set lives in exactly one place across the \
6099 [`is_chart_description_shape`] sibling YAML-plain-style-scalar \
6100 surface, structurally consistent. Drop the bidi-override codepoint; \
6101 pure visual right-to-left maintainer names (Hebrew, Arabic) are \
6102 accepted natively without explicit direction marks)",
6103 cp = c as u32,
6104 utf8_seq = c
6105 .encode_utf8(&mut [0u8; 4])
6106 .bytes()
6107 .map(|b| format!("0x{b:02X}"))
6108 .collect::<Vec<_>>()
6109 .join(" "),
6110 ));
6111 }
6112 if let Some(c) = find_unicode_line_break(s) {
6113 return Err(format!(
6114 "must not contain Unicode line-break codepoint U+{cp:04X} (the three \
6115 codepoints UAX #14 / YAML 1.1 §4.1 name as line terminators outside \
6116 the ASCII `\\n` / `\\r` arms above: U+0085 `NEL` NEXT LINE, U+2028 \
6117 `LS` LINE SEPARATOR, U+2029 `PS` PARAGRAPH SEPARATOR. YAML 1.2 §5.4 \
6118 retired them per UTR #20 so YAML 1.2-strict parsers preserve them \
6119 verbatim, but YAML 1.1 parsers (go-yaml v2 which Helm v3 / kubectl / \
6120 every Kubernetes client library transitively links, `ruamel.yaml` in \
6121 compat mode) still split scalars on them — an `:autores` entry with \
6122 an embedded U+2028 parses as one `maintainers:` array entry through \
6123 a YAML 1.2 parser and as two entries through a YAML 1.1 parser, \
6124 breaking cross-parser determinism on the same axis the per-byte \
6125 `\\n` / `\\r` arms close for ASCII. Independently, every UAX #14 \
6126 conformant text consumer (editors, terminals, `helm list` / \
6127 Artifact Hub's maintainer column) breaks the visual line at these \
6128 codepoints regardless of YAML version, so the author's editor view \
6129 of `caixa.lisp` and the chart-consumer's rendered view of the \
6130 `maintainers:` entry diverge even when both YAML parsers agree on \
6131 the byte-level shape, defeating the THEORY.md §V.2 render-\
6132 determinism contract every typed slot carries. The byte sequence \
6133 ({utf8_seq}) rides verbatim into the rendered Chart.yaml at the \
6134 same axis the per-byte `\\n` / `\\r` arms close for ASCII. Routed \
6135 through the shared [`find_unicode_line_break`] helper so the same \
6136 three-codepoint accepted set lives in exactly one place across the \
6137 [`is_chart_description_shape`] sibling YAML-plain-style-scalar \
6138 surface, peer of the [`find_unicode_bidi_override`] lift on the \
6139 same two predicates one trajectory earlier. Drop the non-ASCII \
6140 line-break codepoint; split the value into separate `:autores` \
6141 list entries at the source — the per-entry shape is single-line by \
6142 contract)",
6143 cp = c as u32,
6144 utf8_seq = c
6145 .encode_utf8(&mut [0u8; 4])
6146 .bytes()
6147 .map(|b| format!("0x{b:02X}"))
6148 .collect::<Vec<_>>()
6149 .join(" "),
6150 ));
6151 }
6152 if let Some(c) = find_unicode_invisible_format(s) {
6153 return Err(format!(
6154 "must not contain Unicode invisible-format codepoint U+{cp:04X} (the \
6155 eight BMP Cf-category zero-width codepoints with no visible glyph: \
6156 U+00AD `SHY` SOFT HYPHEN, U+200B `ZWSP` ZERO WIDTH SPACE, U+2060 \
6157 `WJ` WORD JOINER, U+2061 `FA` FUNCTION APPLICATION, U+2062 `IT` \
6158 INVISIBLE TIMES, U+2063 `IS` INVISIBLE SEPARATOR, U+2064 `IP` \
6159 INVISIBLE PLUS, U+FEFF `ZWNBSP` ZERO WIDTH NO-BREAK SPACE / BOM. \
6160 The maintainer-identity divergence: the author's editor view of \
6161 `caixa.lisp` and the `helm list` / Artifact Hub maintainer column \
6162 agree on the visible glyph sequence (`\"alice\"` and \
6163 `\"alice\\u{{200B}}\"` render identically as `alice`), but the byte \
6164 sequence the YAML-plain-style-scalar carries verbatim differs — \
6165 the Artifact Hub maintainer-index lookup misses the authored \
6166 `\"alice\"` entry because the byte sequence carries an extra \
6167 invisible codepoint, a future per-maintainer CLA-signer lookup \
6168 matches a visually-identical-but-byte-distinct identity (the \
6169 canonical invisible-codepoint homograph footgun), and every \
6170 byte-level diff / grep / equality comparison over the Chart.yaml \
6171 `maintainers:` array disagrees with the visible-glyph match. The \
6172 canonical authoring shapes that silently introduce these \
6173 codepoints: paste-from-Microsoft-Word (SHY auto-inserted at \
6174 every hyphenation candidate), paste-from-text-editor-saved-as-\
6175 UTF-8-with-BOM (BOM leading byte from Notepad / older VS Code \
6176 defaults / Excel CSV export), paste-from-typesetting-doc (ZWSP / \
6177 WJ invisible word-break hints from InDesign / LaTeX-rendered PDF \
6178 copy-paste), and paste-from-MathJax/LaTeX-rendered-formula \
6179 (FUNCTION APPLICATION / INVISIBLE TIMES / INVISIBLE SEPARATOR / \
6180 INVISIBLE PLUS — MathJax / LaTeX2RTF / InDesign math-equation \
6181 export emit one of these between adjacent symbols to preserve \
6182 operator semantics for screen readers, and the codepoint silently \
6183 rides into the YAML scalar with no visible trace). The byte \
6184 sequence ({utf8_seq}) rides verbatim into the rendered \
6185 Chart.yaml, but renders as nothing across consumers, defeating \
6186 the THEORY.md §V.2 render-determinism contract on a third axis \
6187 from the bidi-override (visual-order) and line-break (single-\
6188 line vs multi-line) classes the prior arms close. Routed through \
6189 the shared [`find_unicode_invisible_format`] helper so the \
6190 eight-codepoint accepted set is shared with \
6191 [`is_chart_description_shape`], third lift in the UAX-driven \
6192 render-determinism trio (peer of [`find_unicode_bidi_override`] \
6193 on the visual-order axis and [`find_unicode_line_break`] on the \
6194 single-line/multi-line axis). Drop the invisible codepoint; emoji \
6195 ZWJ sequences (U+200D for the 👨💻 family) and bidi direction-mark \
6196 codepoints (U+200E `LRM` / U+200F `RLM`) are accepted natively \
6197 for mixed-script maintainer names — only the eight zero-semantic-\
6198 content codepoints are rejected)",
6199 cp = c as u32,
6200 utf8_seq = c
6201 .encode_utf8(&mut [0u8; 4])
6202 .bytes()
6203 .map(|b| format!("0x{b:02X}"))
6204 .collect::<Vec<_>>()
6205 .join(" "),
6206 ));
6207 }
6208 Ok(())
6209}
6210
6211/// Maximum byte length of a chart-keyword-shaped string. The 20-byte
6212/// cap matches Cargo's `[package] keywords` rule
6213/// (<https://doc.rust-lang.org/cargo/reference/manifest.html#the-keywords-field>:
6214/// "Each keyword should be ASCII text, start with a letter, and only
6215/// contain letters, numbers, _ or -. Keywords are case-insensitive and
6216/// limited to a maximum length of 20 characters.") — the same parser
6217/// crates.io routes its `keywords:` array entries through at publish
6218/// time. Tighter than every peer length cap on the typed Caixa surface
6219/// ([`CHART_MAINTAINER_NAME_MAX_LEN`] 128 on the sibling chart-metadata
6220/// `Vec<String>` axis, [`CARGO_FEATURE_NAME_MAX_LEN`] 64 on the sibling
6221/// `:caracteristicas` per-entry axis, [`CHART_DESCRIPTION_MAX_LEN`] 512
6222/// on the free-form-prose axis); the search-tag class is the tightest
6223/// short-identifier shape on the typed surface — every realistic
6224/// `:etiquetas` entry in the wild (`"iac"`, `"aws"`, `"pangea"`,
6225/// `"hello-world"`, `"tatara-lisp"`, `"caixa-servico"`,
6226/// `"infrastructure"`, `"pangea-native"`) sits well under 20 bytes,
6227/// and the 20-byte cap surfaces the "paste-from-doc multi-tag blob
6228/// landed in a single `:etiquetas` entry" footgun (`"web-service web
6229/// app"`, `"mesh,http,grpc"`) at validate time.
6230pub const CHART_KEYWORD_MAX_LEN: usize = 20;
6231
6232/// Predicate: assert that `s` is a valid chart-keyword shape. The
6233/// `:etiquetas` axis is a per-entry registry-search-tag identifier
6234/// that lands in the rendered `lareira-<nome>` Helm chart's
6235/// `Chart.yaml` `keywords:` array via [`caixa-helm`]'s
6236/// `build_chart_yaml` (folded through a [`std::collections::BTreeSet`]
6237/// alongside the four substrate-fixed tags `lareira` / `wasm` /
6238/// `tatara-lisp` / `caixa-servico`) and indexes the chart through
6239/// Artifact Hub's keyword-search axis + the future caixa-registry's
6240/// keyword index. The contract — modeled on Cargo's crates.io
6241/// `[package] keywords` grammar (the parser the crates.io publish API
6242/// routes every `keywords:` entry through at publish time), narrowed
6243/// to the strict ASCII subset every realistic search tag uses:
6244///
6245/// - 1..=[`CHART_KEYWORD_MAX_LEN`] (20) bytes;
6246/// - first byte: ASCII letter (`A-Z` or `a-z`). Leading digit, `-`,
6247/// `_`, whitespace, control, and non-ASCII are each surfaced with
6248/// a self-locating reason naming the canonical authoring footgun
6249/// (paste-from-numbered-list `"1foo"`, kebab-leak `"-foo"`,
6250/// snake-leak `"_foo"`, paste-from-aligned-doc whitespace,
6251/// paste-from-Unicode-doc non-ASCII);
6252/// - remaining bytes: ASCII alphanumeric, `_`, or `-` (Cargo's
6253/// crates.io-accepted continuation set; tighter than
6254/// [`is_cargo_feature_name`]'s `_`/`-`/`+`/`.` continuation set —
6255/// `+` and `.` are not part of the keyword grammar). Whitespace,
6256/// `,` / `/` / `;` / `.` list-separator confusions, control bytes,
6257/// and non-ASCII bytes are each surfaced with a self-locating
6258/// reason naming the canonical authoring footgun (multi-tag blob
6259/// in one entry, CSV-list-belongs-to-list-grammar miscomprehension,
6260/// CR/LF paste-from-doc, NFC/NFD normalization drift).
6261///
6262/// Returns the parser-shaped reason on rejection (without wrapping in
6263/// any error variant) so each per-axis caller —
6264/// [`crate::Caixa::validate_etiquetas`] for the universal `:etiquetas`
6265/// axis at validate time, every future per-keyword axis (a future
6266/// caixa-registry keyword-index lookup, a future Artifact Hub-keyword
6267/// scraper validator, a future per-Aplicacao aggregated keyword set)
6268/// — wraps the same reason in its own typed `*Invalid { <axis>, reason }`
6269/// variant.
6270///
6271/// Empty input is rejected here (defensively) and at each call site
6272/// via the narrower [`crate::ManifestError::EtiquetaEmpty`] variant —
6273/// the same empty-first cascade [`is_dns_1123_label`],
6274/// [`is_gateway_api_http_path`], [`is_wit_world_ref`],
6275/// [`is_nats_subject`], [`is_wasi_keyvalue_slot`], [`is_git_ref_name`],
6276/// [`is_git_oid`], [`is_git_repo_url`], [`is_cargo_feature_name`],
6277/// [`is_spdx_expression_shape`], [`is_chart_description_shape`], and
6278/// [`is_chart_maintainer_name_shape`] all carry at their call sites.
6279///
6280/// # Errors
6281///
6282/// Returns the parser-shaped reason naming the specific violation
6283/// (length / first-byte-class / continuation-byte-class / whitespace /
6284/// control-char / non-ASCII / `,`-list-separator-confusion /
6285/// `/`-path-separator-confusion / `;`-list-separator-confusion /
6286/// `.`-namespace-confusion), without wrapping in any error variant —
6287/// every caller maps the same `String` into its own typed
6288/// `*Invalid { <axis>, reason }` enum variant.
6289pub fn is_chart_keyword_shape(s: &str) -> Result<(), String> {
6290 if s.is_empty() {
6291 return Err("must not be empty".to_string());
6292 }
6293 if s.len() > CHART_KEYWORD_MAX_LEN {
6294 return Err(format!(
6295 "exceeds chart keyword max length of {CHART_KEYWORD_MAX_LEN} bytes (got \
6296 {} bytes; legitimate `:etiquetas` search tags rarely exceed ~12 bytes — \
6297 this length suggests a paste-from-doc multi-tag blob landed in a single \
6298 `:etiquetas` entry instead of being split into one entry per tag, e.g. \
6299 `(\"mesh\" \"http\" \"grpc\")` not `(\"mesh-http-grpc-rpc-wasm\")`. \
6300 Cargo's crates.io publish API enforces the same 20-byte cap on its \
6301 `keywords:` array at publish time)",
6302 s.len()
6303 ));
6304 }
6305 let bytes = s.as_bytes();
6306 let first = bytes[0];
6307 if !first.is_ascii_alphabetic() {
6308 let msg = if first == b' ' || first == b'\t' {
6309 "must not start with whitespace (chart keywords are single-token \
6310 search-tag identifiers; the leading-whitespace arm is the canonical \
6311 paste-from-aligned-doc footgun and round-trips inconsistently — every \
6312 YAML 1.2 dumper trims leading whitespace from plain-style scalars, so \
6313 the authored space silently drops in the rendered Chart.yaml \
6314 `keywords:` array)"
6315 .to_string()
6316 } else if first == b'-' {
6317 "must not start with `-` (Cargo's crates.io keyword grammar rejects a \
6318 leading hyphen — `-` is a legitimate continuation character between \
6319 alphanumeric segments but the canonical CLI-argument-injection / \
6320 kebab-leak footgun at the start; drop the leading `-`, e.g. \
6321 `\"tatara-lisp\"` not `\"-tatara-lisp\"`)"
6322 .to_string()
6323 } else if first == b'_' {
6324 "must not start with `_` (Cargo's crates.io keyword grammar requires the \
6325 first character be an ASCII letter — `_` is a legitimate continuation \
6326 character between alphanumeric segments but the canonical \
6327 snake-leak / hidden-identifier footgun at the start; drop the leading \
6328 `_`, e.g. `\"caixa-servico\"` not `\"_caixa_servico\"`)"
6329 .to_string()
6330 } else if first.is_ascii_digit() {
6331 format!(
6332 "must not start with digit {ch:?} (Cargo's crates.io keyword grammar \
6333 requires the first character be an ASCII letter — a digit at the \
6334 start is the canonical paste-from-numbered-list footgun, e.g. the \
6335 author copied `1. mesh` from a numbered doc and the `1` leaked \
6336 into the tag; drop the leading digit, e.g. `\"v2\"` not `\"2v\"`)",
6337 ch = first as char
6338 )
6339 } else if first < 0x20 || first == 0x7F {
6340 format!(
6341 "must not start with control character 0x{first:02x} (Cargo's \
6342 crates.io keyword grammar rejects ASCII control characters; the \
6343 CR/LF arm is the canonical paste-from-multiline-doc footgun)"
6344 )
6345 } else if first >= 0x80 {
6346 format!(
6347 "must not start with non-ASCII byte 0x{first:02x} (Cargo's \
6348 crates.io keyword grammar is strict ASCII; the non-ASCII arm \
6349 catches the canonical paste-from-Unicode-doc footgun — every \
6350 legitimate search tag is a kebab-case ASCII identifier like \
6351 `\"mesh\"`, `\"wasm\"`, `\"tatara-lisp\"`. Raw non-ASCII silently \
6352 round-trips inconsistently across NFC/NFD normalization on APFS / \
6353 case-folding filesystems and breaks the Artifact Hub keyword \
6354 search index lookup)"
6355 )
6356 } else {
6357 format!(
6358 "must start with an ASCII letter, got {ch:?} (Cargo's crates.io \
6359 keyword grammar rejects every non-letter first character — the \
6360 canonical search tags are kebab-case ASCII identifiers starting \
6361 with a letter, like `\"mesh\"`, `\"wasm\"`, `\"hello-world\"`)",
6362 ch = first as char
6363 )
6364 };
6365 return Err(msg);
6366 }
6367 for &b in &bytes[1..] {
6368 let valid = b.is_ascii_alphanumeric() || b == b'_' || b == b'-';
6369 if !valid {
6370 let msg = if b == b' ' || b == b'\t' {
6371 format!(
6372 "must not contain whitespace character {ch:?} (Cargo's \
6373 crates.io keyword grammar rejects whitespace; search tags are \
6374 single-token identifiers — use `-` or `_` to separate \
6375 kebab-case / snake-case segments instead, or split into \
6376 separate `:etiquetas` entries: `(\"web\" \"service\")` not \
6377 `(\"web service\")`)",
6378 ch = b as char
6379 )
6380 } else if b == b',' {
6381 "must not contain `,` (the comma separator belongs to the \
6382 `:etiquetas` list grammar between entries, not to the keyword \
6383 grammar within an entry — split the value into separate list \
6384 entries: `(\"mesh\" \"http\" \"grpc\")` not `(\"mesh,http,grpc\")`. \
6385 The author confused the CSV-style list-separator convention with \
6386 the list grammar)"
6387 .to_string()
6388 } else if b == b'/' {
6389 "must not contain `/` (Cargo's crates.io keyword grammar rejects \
6390 path-style separators within a tag; the segment separator within \
6391 a search tag is `-` or `_`, and multi-segment paths belong as \
6392 separate `:etiquetas` entries: `(\"caixa\" \"servico\")` not \
6393 `(\"caixa/servico\")`)"
6394 .to_string()
6395 } else if b == b';' {
6396 "must not contain `;` (the semicolon separator is not part of the \
6397 `:etiquetas` list grammar — split the value into separate list \
6398 entries: `(\"mesh\" \"http\")` not `(\"mesh;http\")`. The author \
6399 confused another lisp-list-style separator with the list \
6400 grammar)"
6401 .to_string()
6402 } else if b == b'.' {
6403 "must not contain `.` (Cargo's crates.io keyword grammar excludes \
6404 `.` from the continuation set — the canonical \
6405 namespace-confusion / version-suffix footgun, e.g. `\"http.1\"` \
6406 / `\"v1.0\"`; use `-` instead, e.g. `\"http-1\"` / `\"v1-0\"`)"
6407 .to_string()
6408 } else if b == b'\n' {
6409 "must not contain newline (chart keywords are single-line \
6410 single-token identifiers; an embedded newline is the canonical \
6411 paste-from-multiline-doc footgun — the author pasted a multi-tag \
6412 block into one `:etiquetas` entry instead of splitting into one \
6413 entry per tag)"
6414 .to_string()
6415 } else if b == b'\r' {
6416 "must not contain carriage return (chart keywords are single-line \
6417 single-token identifiers; a `\\r` byte is the canonical \
6418 paste-from-Windows-CRLF-doc footgun and lands as a literal CR in \
6419 the rendered Chart.yaml `keywords:` array)"
6420 .to_string()
6421 } else if b < 0x20 || b == 0x7F {
6422 format!(
6423 "must not contain control character 0x{b:02x} (Cargo's \
6424 crates.io keyword grammar rejects ASCII control characters; \
6425 the control-byte arm catches paste-from-binary-blob footguns \
6426 like `0x00` NUL, `0x07` BEL, `0x1b` ESC, `0x7f` DEL that \
6427 would silently land in the rendered Chart.yaml \
6428 `keywords:` array as a YAML-illegal byte sequence)"
6429 )
6430 } else if b >= 0x80 {
6431 format!(
6432 "must not contain non-ASCII byte 0x{b:02x} (Cargo's crates.io \
6433 keyword grammar is strict ASCII; the non-ASCII arm catches \
6434 the canonical paste-from-Unicode-doc footgun — raw non-ASCII \
6435 silently round-trips inconsistently across NFC/NFD \
6436 normalization on APFS / case-folding filesystems and breaks \
6437 the Artifact Hub keyword search index lookup)"
6438 )
6439 } else {
6440 format!(
6441 "contains invalid character {ch:?} (Cargo's crates.io keyword \
6442 grammar allows only `[A-Za-z0-9_-]` after the first \
6443 character)",
6444 ch = b as char
6445 )
6446 };
6447 return Err(msg);
6448 }
6449 }
6450 Ok(())
6451}
6452
6453/// Tagged reason a caixa-author-supplied path can fail the
6454/// sandboxed-relative shape gate every callback / script path must
6455/// pass for the layout checker's `root.join(p)` to stay inside the
6456/// caixa root.
6457///
6458/// Returned by [`is_sandboxed_relative_path`] so each per-axis caller
6459/// — [`crate::BehaviorSpec::validate`] on `:behavior :on-*` paths
6460/// (b0c8389), [`crate::UpgradeInstruction::validate`]'s `StateChange`
6461/// arm on `:upgrade-from :state-change :script` (26da2c7), every
6462/// future axis admitting a user-supplied path — match-and-wraps the
6463/// tag into its own typed `*Invalid { slot, path }` enum variant so
6464/// the diagnostic still names *which slot* carried the malformed
6465/// value. The tag is axis-agnostic; the wrapping per-axis variant
6466/// carries the slot identity.
6467///
6468/// Sibling discriminator-style of the per-arm reason substrings every
6469/// value-shape predicate already exposes (`is_dns_1123_label`,
6470/// `is_gateway_api_http_path`, …) — but typed rather than string-
6471/// shaped, because the per-axis variants for path violations were
6472/// already split three ways (`EmptyPath` / `AbsolutePath` /
6473/// `ParentEscape` in `BehaviorError`; `EmptyScript` / `AbsoluteScript`
6474/// / `ParentEscapeScript` in `UpgradeError`), so collapsing them to a
6475/// single `*PathInvalid { reason }` variant would *regress* the
6476/// diagnostic shape rather than preserve it.
6477#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, gen_platform::IsVariant)]
6478pub enum PathShapeViolation {
6479 /// The path string is empty — `PathBuf::new()` or the
6480 /// canonical "I declared the slot but left the value blank"
6481 /// authoring footgun. `root.join(PathBuf::new())` resolves to
6482 /// `root` itself, silently pointing the runtime's `LisleLoader`
6483 /// at the project root rather than a file.
6484 Empty,
6485 /// The path is absolute — `Path::join` *replaces* the base
6486 /// with an absolute right-hand side, so `root.join("/etc/passwd")`
6487 /// resolves to `"/etc/passwd"` and escapes the project sandbox
6488 /// entirely. The Lunatic-style sandbox discipline
6489 /// ([`theory/INSPIRATIONS.md` §III.1][i31]) requires every
6490 /// author-supplied path to live under the caixa root.
6491 ///
6492 /// [i31]: https://github.com/pleme-io/theory/blob/main/INSPIRATIONS.md
6493 Absolute,
6494 /// The path contains a [`Component::ParentDir`] component anywhere
6495 /// — `root.join("../sibling/x")` traverses above the caixa root,
6496 /// the same sandbox-escape vector via parent-directory traversal.
6497 /// Caught regardless of where the `..` component sits (leading,
6498 /// mid-path, trailing) so a future relaxation that only checks
6499 /// one position surfaces at this one predicate.
6500 ParentEscape,
6501}
6502
6503impl PathShapeViolation {
6504 /// Exhaustive iteration surface for every consumer that walks the
6505 /// closed three-arm [`PathShapeViolation`] discriminator set — the
6506 /// paired byte-parity pin on the [`gen_platform::IsVariant`]-derived
6507 /// per-arm `is_*` predicate family, a future `feira lint
6508 /// --explain-path-shape=<axis>` per-arm listing of the accepted
6509 /// violation kinds, a future `mesh.pleme.io/v1alpha1/Caixa` CR
6510 /// materializer's per-path admission-webhook rejection body naming
6511 /// the accepted-violation-tag set, any future property-test harness
6512 /// that sweeps every arm to compute per-arm diagnostic coverage.
6513 /// A future variant addition (a `Symlink` arm the future
6514 /// symlink-escape gate would carry once `Path::is_symlink` becomes
6515 /// part of the sandbox contract, a `TrailingSpace` arm a future
6516 /// authoring-side whitespace-hygiene gate would raise for
6517 /// `"lib/init.lisp "` shapes) extends this slice as one edit and
6518 /// every consumer picks up the new entry by construction; the
6519 /// compiler-checked exhaustiveness on the sibling `match` arms in
6520 /// [`is_sandboxed_relative_path`] and [`require_sandboxed_lisp_path`]
6521 /// is the build-time guarantee that no arm forgets to grow.
6522 ///
6523 /// Peer of the sibling closed-set fieldless typed enums'
6524 /// [`crate::CaixaKind::ALL`] (6b1f4fb) /
6525 /// [`crate::CaixaDialeto::ALL`] (dd4f541) /
6526 /// [`crate::aplicacao::PlacementStrategy::ALL`] (18c7342) /
6527 /// [`crate::aplicacao::RateLimitUnit::ALL`] (6bce03d) /
6528 /// [`crate::dep::DepList::ALL`] (45ee563) /
6529 /// [`crate::supervisor::RestartStrategy::ALL`] (4eec29c) /
6530 /// [`crate::supervisor::RestartPolicy::ALL`] (dd32ccf)
6531 /// exhaustive-iteration surfaces — the tenth closed-set typed
6532 /// enum on the caixa surface to converge onto the same
6533 /// one-canonical-arm-list-per-enum discipline, and the first
6534 /// render-side path-shape-diagnostic axis (as distinct from an
6535 /// OTP-shape M2 slot or an M3 mesh slot) to reach it. Order matches
6536 /// variant declaration order verbatim (`Empty` → `Absolute` →
6537 /// `ParentEscape`) so the slice is the canonical ordering every
6538 /// exhaustive dispatch site (the `Empty → Absolute → ParentEscape`
6539 /// arm-ordering [`is_sandboxed_relative_path`] and every per-axis
6540 /// caller in [`crate::manifest::ManifestError`] preserve for
6541 /// diagnostic-precedence continuity) defers to.
6542 pub const ALL: &'static [Self] = &[Self::Empty, Self::Absolute, Self::ParentEscape];
6543}
6544
6545/// Predicate: assert that `path` is a *sandboxed-relative* path —
6546/// the shape every caixa-author-supplied callback / script path must
6547/// take so the layout checker's `root.join(p)` resolves inside the
6548/// caixa root sandbox. The contract:
6549///
6550/// - non-empty (`PathBuf::new()` → `Empty`);
6551/// - relative (absolute paths replace the base under
6552/// [`Path::join`] semantics → `Absolute`);
6553/// - no [`Component::ParentDir`] components anywhere (traversal
6554/// above the caixa root → `ParentEscape`).
6555///
6556/// Returns [`PathShapeViolation`] tagging the specific failure;
6557/// each per-axis caller match-and-wraps the variant in its own
6558/// typed `*Invalid { slot, path }` enum variant so the diagnostic
6559/// still names *which slot* carried the malformed value. The
6560/// arm-ordering is the same `Empty → Absolute → ParentEscape`
6561/// every prior inlined copy followed (b0c8389 [`crate::BehaviorSpec`],
6562/// 26da2c7 [`crate::UpgradeInstruction::StateChange`]), so any
6563/// caller migrating to the lifted predicate preserves its existing
6564/// per-slot diagnostic precedence by construction.
6565///
6566/// Lifted from `caixa-core::behavior` and `caixa-core::upgrade`
6567/// where the same three-step gate was inlined verbatim across two
6568/// call sites — the PRIME DIRECTIVE duplication-budget rule
6569/// (THEORY.md §I.3.5: "every recurring shape becomes a generator
6570/// before it becomes a pattern; every pattern becomes a library
6571/// before it becomes duplicated code. The duplication budget is
6572/// zero.") promotes the gate to a typed substrate-side predicate
6573/// on the same trajectory the M2-overlay and label-selector helpers
6574/// (9e3a057, 9d09cfb, 9dbeafd, 31455a7, 07a4544, 8b4db42) already
6575/// follow. The third caller — the future M3/M4 axis admitting a
6576/// user-supplied path (the future `:entrada :tls-cert` /
6577/// `:entrada :tls-key` PEM-file axes, the future
6578/// `mesh.pleme.io/v1alpha1/Caixa` CR materializer's per-path
6579/// validator, the future per-Servico pre-warm script axis) — lands
6580/// as a thin five-line wrapper rather than re-inlining the same
6581/// three checks.
6582///
6583/// Pairs with the per-axis empty / absolute / parent-escape variants
6584/// on [`crate::BehaviorError`] and [`crate::UpgradeError`] — those
6585/// remain the typed surface authors see; this predicate is the
6586/// single-source-of-truth gate the caixa-build pipeline consults to
6587/// produce them.
6588///
6589/// # Errors
6590///
6591/// Returns the [`PathShapeViolation`] tag identifying the specific
6592/// violation ([`PathShapeViolation::Empty`] / [`PathShapeViolation::Absolute`]
6593/// / [`PathShapeViolation::ParentEscape`]) so each per-axis caller
6594/// match-and-wraps it into its own typed `*Path` / `*Script` enum
6595/// variant (preserving the per-slot diagnostic granularity the inline
6596/// pre-lift gates already produced).
6597pub fn is_sandboxed_relative_path(path: &Path) -> Result<(), PathShapeViolation> {
6598 if path.as_os_str().is_empty() {
6599 return Err(PathShapeViolation::Empty);
6600 }
6601 if path.is_absolute() {
6602 return Err(PathShapeViolation::Absolute);
6603 }
6604 if path.components().any(|c| matches!(c, Component::ParentDir)) {
6605 return Err(PathShapeViolation::ParentEscape);
6606 }
6607 Ok(())
6608}
6609
6610/// The canonical tatara-lisp source-file extension every M2 typed
6611/// path-slot the M2.5 wasm-engine instantiator reads through
6612/// `tatara_lisp::read` at instance-start time must terminate in.
6613///
6614/// Strict lowercase: the byte-size / duration codecs and every other
6615/// shape-gate predicate in this module are case-sensitive on unit /
6616/// scheme / label boundaries, so a strict `lisp` shape matches the
6617/// downstream accepted set without case-folding drift (an uppercase
6618/// `.LISP` / `.Lisp` shape that a case-insensitive volume's existence
6619/// check would match the on-disk file would still mismatch the
6620/// canonical form the codec emits, breaking the THEORY.md §V.2.7
6621/// render-determinism contract every typed slot carries).
6622pub const LISP_SOURCE_EXTENSION: &str = "lisp";
6623
6624/// Predicate: assert that `path` terminates in the canonical
6625/// [`LISP_SOURCE_EXTENSION`] (lowercase `.lisp`) — the file-type
6626/// shape every M2 typed path-slot the wasm-engine instantiator reads
6627/// as tatara-lisp source must take. The contract:
6628///
6629/// - the path has an extension component (no-extension paths like
6630/// `"lib/init"` or `"a"` fail);
6631/// - the extension's UTF-8 string form is exactly `"lisp"` —
6632/// lowercase, no trailing residue, no double-extension shadow
6633/// like `".lisp.bak"`.
6634///
6635/// Returns `true` on accept, `false` on reject. Each per-axis caller
6636/// — [`crate::BehaviorSpec::validate`] on `:behavior :on-*` paths
6637/// (c97815a), [`crate::UpgradeInstruction::StateChange::validate`]
6638/// on `:upgrade-from :state-change :script` (this commit), every
6639/// future axis admitting a tatara-lisp source path — wraps the
6640/// boolean into its own typed `*NonLispExtension { slot, path }` /
6641/// `*NonLispExtensionScript { script }` enum variant so the
6642/// diagnostic still names *which slot* carried the non-`.lisp`
6643/// value. The predicate is axis-agnostic; the wrapping per-axis
6644/// variant carries the slot identity.
6645///
6646/// Lifted from `caixa-core::behavior` where the same single-line
6647/// gate (`path.extension().and_then(|ext| ext.to_str()) ==
6648/// Some("lisp")`) was inlined verbatim across the first call site
6649/// (`BehaviorSpec::validate_callback_path`) — the PRIME DIRECTIVE
6650/// duplication-budget rule (THEORY.md §I.3.5: "every recurring shape
6651/// becomes a generator before it becomes a pattern; every pattern
6652/// becomes a library before it becomes duplicated code. The
6653/// duplication budget is zero.") promotes the gate to a typed
6654/// substrate-side predicate on the same trajectory the path-shape
6655/// gate [`is_sandboxed_relative_path`] already follows (lifted from
6656/// the same two call sites once the second consumer appeared). The
6657/// third caller — the future `:bibliotecas` per-entry tatara-lisp
6658/// source-file axis (the `feira build` loop reads each through the
6659/// same `tatara_lisp::read` reader at parse time), the future `:exe`
6660/// `:kind Binario` entry-point axis (the nix-built binary's entry
6661/// point loads as Lisp source), the future M2.5 wasm-engine
6662/// pre-warm hook axis — lands as a thin two-line wrapper rather
6663/// than re-inlining the same extension check.
6664///
6665/// Pairs with the per-axis `*NonLispExtension` / `*NonLispExtensionScript`
6666/// variants on [`crate::BehaviorError`] and [`crate::UpgradeError`]
6667/// — those remain the typed surface authors see; this predicate is
6668/// the single-source-of-truth gate the caixa-build pipeline consults
6669/// to produce them.
6670#[must_use]
6671pub fn is_lisp_extension(path: &Path) -> bool {
6672 path.extension().and_then(|ext| ext.to_str()) == Some(LISP_SOURCE_EXTENSION)
6673}
6674
6675/// The canonical compound suffix every `:servicos` entry — the
6676/// ComputeUnit-CR axis the M2 typed-substrate caixa-helm /
6677/// caixa-flux renderers consume via `serde_yaml::from_str` — must
6678/// terminate in. Two-segment shape (`.computeunit.yaml`) rather than
6679/// a single `.yaml` extension: the `.computeunit` segment routes
6680/// authoring-time to the typed `ComputeUnit` CR shape the
6681/// `pleme-computeunit` library chart resolves, distinguishing the
6682/// slot's accepted set from the open `.yaml` universe (Helm
6683/// `values.yaml`, FluxCD `Kustomization.yaml`, the generic K8s
6684/// manifest YAML every operator emits) — same axis-discipline the
6685/// peer [`LISP_SOURCE_EXTENSION`] sibling carries on the tatara-lisp-
6686/// source axis but with a compound suffix because
6687/// [`Path::extension`] only returns the post-last-`.` segment
6688/// (`"yaml"` for `foo.computeunit.yaml`), so the predicate routes
6689/// through [`Path::file_name`] and a string `ends_with` check on the
6690/// full suffix instead.
6691///
6692/// Strict lowercase: every other shape-gate predicate in this module
6693/// is case-sensitive on unit / scheme / label boundaries, so a strict
6694/// `.computeunit.yaml` shape matches the downstream accepted set
6695/// without case-folding drift (an uppercase `.COMPUTEUNIT.YAML` shape
6696/// that a case-insensitive volume's existence check would match the
6697/// on-disk file would still mismatch the canonical form every in-tree
6698/// `:servicos` fixture and the `Caixa::template` scaffold emit,
6699/// breaking the THEORY.md §V.2.7 render-determinism contract every
6700/// typed slot carries).
6701pub const COMPUTEUNIT_YAML_SUFFIX: &str = ".computeunit.yaml";
6702
6703/// Predicate: assert that `path` terminates in the canonical
6704/// [`COMPUTEUNIT_YAML_SUFFIX`] (lowercase `.computeunit.yaml`) — the
6705/// file-type shape every `:servicos` entry, the ComputeUnit-CR axis
6706/// the M2 typed-substrate caixa-helm / caixa-flux renderers consume
6707/// via `serde_yaml::from_str`, must take. The contract:
6708///
6709/// - the path has a final file-name component (paths ending in `/`
6710/// fail);
6711/// - the file name's UTF-8 string form ends in
6712/// `.computeunit.yaml` — lowercase, no case-folding;
6713/// - at least one byte precedes the suffix (the degenerate hidden-
6714/// file `.computeunit.yaml` shape — file name exactly equal to
6715/// the suffix — fails: the substrate identifies each ComputeUnit
6716/// by the file-stem segment that precedes `.computeunit.yaml`,
6717/// so an empty stem is structurally an unidentified Servico).
6718///
6719/// Returns `true` on accept, `false` on reject. The per-axis caller
6720/// — [`crate::Caixa::validate_code_paths`] on the `:servicos` axis —
6721/// wraps the boolean into its own typed
6722/// `ManifestError::CodePathNonComputeUnitYamlExtension { slot, path }`
6723/// variant so the diagnostic still names the offending slot and the
6724/// offending path verbatim. Peer of [`is_lisp_extension`] on the
6725/// tatara-lisp-source axis (`:bibliotecas` 64772a9); same axis-
6726/// agnostic predicate discipline, here on the compound-suffix axis
6727/// [`Path::extension`] can't express on its own. The third caller —
6728/// the future M2.5 caixa-operator `:servicos` admission webhook
6729/// keying off the same accepted set, the M4
6730/// `mesh.pleme.io/v1alpha1/ComputeUnit` CR materializer's per-
6731/// `:servicos` shape gate, the future `feira fmt`'s `:servicos`
6732/// canonical-form normalizer — lands as a thin wrapper rather than
6733/// re-inlining the same compound-suffix check.
6734///
6735/// Pairs with the per-axis
6736/// [`crate::ManifestError::CodePathNonComputeUnitYamlExtension`]
6737/// variant — that remains the typed surface authors see; this
6738/// predicate is the single-source-of-truth gate the caixa-build
6739/// pipeline consults to produce it.
6740#[must_use]
6741pub fn is_computeunit_yaml_extension(path: &Path) -> bool {
6742 path.file_name()
6743 .and_then(|n| n.to_str())
6744 .is_some_and(|name| {
6745 name.len() > COMPUTEUNIT_YAML_SUFFIX.len() && name.ends_with(COMPUTEUNIT_YAML_SUFFIX)
6746 })
6747}
6748
6749/// Canonical camelCase YAML key for the `:limits` slot's overlay.
6750pub const M2_KEY_LIMITS: &str = "limits";
6751/// Canonical camelCase YAML key for the `:behavior` slot's overlay.
6752pub const M2_KEY_BEHAVIOR: &str = "behavior";
6753/// Canonical camelCase YAML key for the `:upgrade-from` slot's overlay.
6754pub const M2_KEY_UPGRADE_FROM: &str = "upgradeFrom";
6755
6756/// Canonical JSON/YAML top-level key for [`crate::Caixa`]'s runtime
6757/// `deps` axis — the runtime-closure dependency list every build the
6758/// caixa participates in reaches (peer of the dev-only `:deps-dev`
6759/// list [`CAIXA_KEY_DEPS_DEV`] pins). The Rust field is single-word
6760/// `deps`; the `#[serde(rename_all = "camelCase")]` attribute on
6761/// [`crate::Caixa`] is a no-op on this axis (no `_` to transform), so
6762/// the emitted JSON key equals the source-side field name byte-for-byte
6763/// and equals this constant's value.
6764///
6765/// [`crate::Caixa::to_lisp`] threads the manifest through
6766/// `serde_json::to_value(self) → tatara_lisp::domain::json_to_sexp`, so
6767/// the emitted JSON key is the load-bearing byte-string the round-trip
6768/// consumes on its way back to the kebab-case `:deps` author surface.
6769/// Until this lift landed the byte-string `"deps"` was structurally
6770/// implicit in the [`crate::Caixa::deps`] field name at
6771/// [`crate::Caixa`] with no compile-time link to any downstream
6772/// `.get(<key>)` consumer or drift-detection pin — a future
6773/// [`crate::Caixa`] field rename (`deps` → `dependencies` matching
6774/// Cargo's verbatim `[dependencies]` axis, `deps` → `runtime_deps`
6775/// matching a hypothetical per-runtime-target vocabulary flip) OR an
6776/// added `#[serde(rename = "…")]` explicit attribute override (either
6777/// of which would silently break every [`crate::Caixa::to_lisp`]
6778/// round-trip and the future M4 operator-side manifest ingest that
6779/// reaches for `deps` via `Value::get(...)`) would surface at consumer
6780/// parse time as a silently-absent JSON key defaulting to
6781/// [`Vec::new()`], far from the rename's commit and with no field
6782/// naming the drift.
6783///
6784/// Peer of [`CAIXA_KEY_DEPS_DEV`] on the two-list dep-graph
6785/// serialized-key axis: this const names the runtime-closure dep-list
6786/// wire key, [`CAIXA_KEY_DEPS_DEV`] names the dev-only dep-list wire
6787/// key. Byte-identical to the peer [`DEP_AUTHOR_KEY_DEPS`] author-facing
6788/// kebab-case label modulo the leading `:` — the two consts split on
6789/// the axis every dep-graph slot carries (author-facing kebab-case
6790/// label vs. renderer-side wire key). Same "one canonical byte-string
6791/// per typed axis" discipline every peer [`M2_KEY_*`] /
6792/// [`M3_KEY_PLACEMENT`] / [`SUPERVISOR_KEY_*`] const carries.
6793pub const CAIXA_KEY_DEPS: &str = "deps";
6794
6795/// Canonical camelCase JSON/YAML top-level key for [`crate::Caixa`]'s
6796/// `deps_dev` axis — the dev-only dependency list that the M0 base
6797/// package model already exposes (peer of the runtime `:deps` list, but
6798/// excluded from published lacres and consumer builds). The Rust field
6799/// is `snake_case` `deps_dev`; the `#[serde(rename_all = "camelCase")]`
6800/// attribute on [`crate::Caixa`] maps it to the camelCase JSON key
6801/// `"depsDev"` this constant pins.
6802///
6803/// [`crate::Caixa::to_lisp`] threads the manifest through
6804/// `serde_json::to_value(self) → tatara_lisp::domain::json_to_sexp`, so
6805/// the emitted JSON key is the load-bearing byte-string the round-trip
6806/// consumes on its way back to the kebab-case `:deps-dev` author
6807/// surface. Until this lift landed the byte-string `"depsDev"` was
6808/// structurally implicit in the `#[serde(rename_all = "camelCase")]`
6809/// derive attribute at [`crate::Caixa`] with no compile-time link to any
6810/// downstream `.get(<key>)` consumer or drift-detection pin — a future
6811/// [`crate::Caixa`] field rename (`deps_dev` → `dev_deps` matching
6812/// Cargo's verbatim `dev-dependencies` axis, `deps_dev` → `deps_test`
6813/// matching a hypothetical per-test-target vocabulary flip) OR a
6814/// `#[serde(rename_all = "…")]` attribute flip (any of which would
6815/// silently break every `Caixa::to_lisp` round-trip and the future M4
6816/// operator-side manifest ingest that reaches for `depsDev` via
6817/// `Value::get(...)`) would surface at consumer parse time as a
6818/// silently-absent JSON key defaulting to `Vec::new()`, far from the
6819/// rename's commit and with no field naming the drift.
6820///
6821/// Peer of [`M2_KEY_UPGRADE_FROM`] on the sibling top-level
6822/// [`crate::Caixa`] multi-word camelCase-renamed serialized-key axis —
6823/// both are `snake_case → camelCase` renames the `rename_all` derive
6824/// produces on the M0 [`crate::Caixa`] surface. Alongside
6825/// [`SUPERVISOR_KEY_MAX_RESTARTS`] (`"maxRestarts"`, 40cc4e5) and
6826/// [`SUPERVISOR_KEY_RESTART_WINDOW`] (`"restartWindow"`, 40cc4e5) —
6827/// which pin the two supervisor-tree top-level multi-word keys the
6828/// [`crate::Caixa`] surface flattens up — this const closes the last of
6829/// the four multi-word top-level [`crate::Caixa`] serde-derived JSON
6830/// keys still lacking a lifted `&'static str` peer. Same "one canonical
6831/// byte-string per typed serialized-key axis" discipline every peer
6832/// [`M2_KEY_*`] / [`M3_KEY_PLACEMENT`] / [`SUPERVISOR_KEY_*`] const
6833/// carries.
6834pub const CAIXA_KEY_DEPS_DEV: &str = "depsDev";
6835
6836/// Canonical author-facing kebab-case `(defcaixa … :limits (…))` top-level
6837/// slot label the M2 per-Servico Lunatic sandbox `:limits` slot surfaces
6838/// under. Peer of [`M2_KEY_LIMITS`] on the dual-axis pair every M2
6839/// top-level slot carries: the camelCase [`M2_KEY_*`] const names the
6840/// *renderer-side* overlay-container wire key the serde-derive-emitted
6841/// programs.yaml / values.yaml block carries under (`"limits"`, load-bearing
6842/// per the `#[serde(rename_all = "camelCase")]` attribute on the emit-side
6843/// [`servico_m2_overlay`] shape), the kebab-case [`M2_AUTHOR_KEY_*`] const
6844/// names the *author-facing* label the [`crate::Caixa::declared_servico_slots`]
6845/// tagger threads through as one of the `&'static str` entries in the
6846/// canonical-declaration-order slot list every kind-coherence gate consults
6847/// ([`crate::LayoutError::ServicoSlotsOnNonServico`] joins them into the
6848/// space-separated `slots:` diagnostic naming which of the three M2 slots
6849/// the offending caixa declared on a non-Servico kind).
6850///
6851/// Until this lift landed the three kebab-case labels sat once each in
6852/// [`crate::Caixa::declared_servico_slots`] as three-arm inline
6853/// `":limits"` / `":behavior"` / `":upgrade-from"` byte-strings the tagger
6854/// pushed onto its return `Vec`, plus a handful of test-side probe
6855/// literals asserting the diagnostic's `slots:` field carries the
6856/// expected per-arm value verbatim — with no compile-time link between
6857/// the tagger's arms and the tests' expected values. A future rebrand
6858/// (a hypothetical `:limits` → `:sandbox` matching the Lunatic
6859/// terminology INSPIRATIONS §III.1 documents at the per-process level,
6860/// `:behavior` → `:gen-server` matching Erlang's verbatim
6861/// `gen_server` name, `:upgrade-from` → `:appup` matching Erlang's
6862/// verbatim appup terminology, or a per-consumer disambiguation as the
6863/// `defcaixa` macro stabilizes) would silently desynchronize the
6864/// production [`crate::Caixa::declared_servico_slots`] tagger from the
6865/// tests until a downstream consumer surfaced the drift at build time as
6866/// a matches-arm miss far from the rename's commit. This lift closes
6867/// that gap by routing both halves (production tagger + tests) through
6868/// three peer consts declared adjacent to the renderer-side
6869/// [`M2_KEY_*`] peers, so the "one canonical declaration per arm, next
6870/// to the axis" discipline the peer [`M2_BEHAVIOR_AUTHOR_KEY_ON_*`]
6871/// sub-slot author-label consts (889dc18) established for the M2
6872/// `:behavior` sub-slot's per-callback kebab-case labels extends onto
6873/// the M2 top-level slot axis. Same "one canonical byte-string per
6874/// typed axis" discipline every peer M2 / M3 renderer-wire-key axis
6875/// carries ([`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] /
6876/// [`M2_KEY_UPGRADE_FROM`], [`M2_LIMITS_KEY_MEMORY`] /
6877/// [`M2_LIMITS_KEY_FUEL`] / [`M2_LIMITS_KEY_WALL_CLOCK`] /
6878/// [`M2_LIMITS_KEY_CPU`] (d8b8b4f), [`M2_BEHAVIOR_KEY_ON_INIT`] etc.
6879/// (21fe462), [`M2_UPGRADE_FROM_KEY_FROM`] /
6880/// [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`] (36ffe65)).
6881pub const M2_AUTHOR_KEY_LIMITS: &str = ":limits";
6882/// Canonical author-facing kebab-case `(defcaixa … :behavior (…))`
6883/// top-level slot label the M2 per-Servico OTP-shaped `:behavior`
6884/// gen_server-callback-set slot surfaces under. Peer of
6885/// [`M2_AUTHOR_KEY_LIMITS`] on the sibling M2 top-level slot dual axis;
6886/// see [`M2_AUTHOR_KEY_LIMITS`] for the full lift rationale.
6887pub const M2_AUTHOR_KEY_BEHAVIOR: &str = ":behavior";
6888/// Canonical author-facing kebab-case `(defcaixa … :upgrade-from (…))`
6889/// top-level slot label the M2 per-Servico OTP-appup `:upgrade-from`
6890/// hot-code-reload table slot surfaces under. Peer of
6891/// [`M2_AUTHOR_KEY_LIMITS`] on the sibling M2 top-level slot dual axis;
6892/// see [`M2_AUTHOR_KEY_LIMITS`] for the full lift rationale.
6893pub const M2_AUTHOR_KEY_UPGRADE_FROM: &str = ":upgrade-from";
6894
6895/// Canonical camelCase YAML sub-key the `:limits :memory` per-Servico
6896/// linear-memory-cap scalar-axis lands under inside the [`M2_KEY_LIMITS`]
6897/// overlay block. Peer of [`M2_KEY_LIMITS`] on the sibling `:limits`
6898/// sub-slot axis: `M2_KEY_LIMITS` names the overlay-container's
6899/// top-level key ("limits"), the four `M2_LIMITS_KEY_*` consts name the
6900/// four typed sub-keys ([`LIMITS_MEMORY_WASM32_MAX_BYTES`]-bounded
6901/// memory cap, [`crate::LIMITS_FUEL_MAX`]-bounded fuel budget,
6902/// [`crate::LIMITS_WALL_CLOCK_MAX`]-bounded wall-clock cap,
6903/// [`crate::LIMITS_CPU_MILLICORES_MAX`]-bounded soft cgroup CPU share)
6904/// that the emit-side [`servico_m2_overlay`] serializes through serde
6905/// (`LimitsSpec` carries `#[serde(rename_all = "camelCase")]`) and every
6906/// substrate-side test-side navigator probes to pin the round-trip
6907/// through the rendered `programs.yaml` per-Servico entry / lareira
6908/// chart `values.yaml` per-`pleme-computeunit` block. The lower-camel
6909/// shape (`"memory"` / `"fuel"` / `"wallClock"` / `"cpu"`) is
6910/// load-bearing: the serde-derive on [`crate::LimitsSpec`] emits under
6911/// the same shape and the drift-detection pin in `limits.rs::tests`
6912/// (`limits_spec_serde_keys_match_lifted_m2_limits_key_consts`)
6913/// serializes a fully-populated [`crate::LimitsSpec`] and asserts each
6914/// canonical `M2_LIMITS_KEY_*` byte-sequence appears in the JSON — so a
6915/// hypothetical future `rename_all = "snake_case"` / `"kebab-case"`
6916/// accident at the derive attribute surfaces as a build-time test
6917/// failure at `limits.rs` rather than as a silent test-side
6918/// `.get(<stale-camelCase-const>)` returning `None` far from the
6919/// derive-attr drift's commit. Same "one canonical byte-string per
6920/// typed axis" discipline every peer M2 / M3 wire-key axis carries
6921/// ([`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] / [`M2_KEY_UPGRADE_FROM`],
6922/// [`M3_PLACEMENT_KEY_ESTRATEGIA`] etc.).
6923pub const M2_LIMITS_KEY_MEMORY: &str = "memory";
6924/// Canonical camelCase YAML sub-key the `:limits :fuel` per-Servico
6925/// wasm-instruction-budget scalar-axis lands under inside the
6926/// [`M2_KEY_LIMITS`] overlay block. Peer of [`M2_LIMITS_KEY_MEMORY`] on
6927/// the sibling `:limits` sub-slot axis.
6928pub const M2_LIMITS_KEY_FUEL: &str = "fuel";
6929/// Canonical camelCase YAML sub-key the `:limits :wall-clock` per-Servico
6930/// wall-clock-cap scalar-axis lands under inside the [`M2_KEY_LIMITS`]
6931/// overlay block. Peer of [`M2_LIMITS_KEY_MEMORY`] on the sibling
6932/// `:limits` sub-slot axis; the camelCase shape (`"wallClock"`, not
6933/// `"wall_clock"`) is load-bearing per the serde-derive attribute on
6934/// [`crate::LimitsSpec`].
6935pub const M2_LIMITS_KEY_WALL_CLOCK: &str = "wallClock";
6936/// Canonical camelCase YAML sub-key the `:limits :cpu` per-Servico
6937/// soft-cgroup-CPU-share millicores scalar-axis lands under inside the
6938/// [`M2_KEY_LIMITS`] overlay block. Peer of [`M2_LIMITS_KEY_MEMORY`] on
6939/// the sibling `:limits` sub-slot axis.
6940pub const M2_LIMITS_KEY_CPU: &str = "cpu";
6941
6942/// Canonical camelCase YAML sub-key the `:behavior :on-init` per-Servico
6943/// OTP-shaped instance-init-callback path scalar-axis lands under inside
6944/// the [`M2_KEY_BEHAVIOR`] overlay block. Peer of [`M2_KEY_BEHAVIOR`] on
6945/// the sibling `:behavior` sub-slot axis: [`M2_KEY_BEHAVIOR`] names the
6946/// overlay-container's top-level key ("behavior"), the six
6947/// `M2_BEHAVIOR_KEY_ON_*` consts name the six typed sub-keys the M2
6948/// [`crate::BehaviorSpec`] struct's OTP-shaped callback fields
6949/// (`on_init` / `on_call` / `on_cast` / `on_info` / `on_state_change` /
6950/// `on_terminate`, analogs of `gen_server:init/1` / `handle_call/3` /
6951/// `handle_cast/2` / `handle_info/2` / `code_change/3` / `terminate/2`
6952/// per `theory/INSPIRATIONS.md` §II.3) serialize as under the
6953/// `#[serde(rename_all = "camelCase")]` derive attribute
6954/// (`"onInit"` / `"onCall"` / `"onCast"` / `"onInfo"` / `"onStateChange"`
6955/// / `"onTerminate"`). Emitted by [`servico_m2_overlay`] as sub-keys of
6956/// the [`M2_KEY_BEHAVIOR`] overlay block and consumed by every
6957/// substrate-side test-side navigator that reaches into the rendered
6958/// `programs.yaml` per-Servico entry / lareira chart `values.yaml`
6959/// per-`pleme-computeunit` block to pin the per-callback round-trip.
6960/// The lower-camel shape is load-bearing: the serde-derive on
6961/// [`crate::BehaviorSpec`] emits under the same shape and the
6962/// drift-detection pin in `behavior.rs::tests`
6963/// (`behavior_spec_serde_keys_match_lifted_m2_behavior_key_consts`)
6964/// serializes a fully-populated [`crate::BehaviorSpec`] and asserts each
6965/// canonical `M2_BEHAVIOR_KEY_ON_*` byte-sequence appears in the JSON —
6966/// so a hypothetical future `rename_all = "snake_case"` / `"kebab-case"`
6967/// accident at the derive attribute or an OTP-lineage per-callback
6968/// rebrand (`:on-init` → `:on-start` matching Akka's per-actor
6969/// preStart naming, `:on-call` → `:on-request` matching a hypothetical
6970/// wasi:http/incoming-handler terminology flip, `:on-state-change` →
6971/// `:on-code-change` matching Erlang's verbatim `code_change/3` name)
6972/// coordinated at the type's derive attribute surfaces as a build-time
6973/// test failure at `behavior.rs` rather than as a silent test-side
6974/// `.get(<stale-camelCase-const>)` returning `None` far from the
6975/// derive-attr drift's commit. Same "one canonical byte-string per typed
6976/// axis" discipline every peer M2 / M3 wire-key axis carries
6977/// ([`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] / [`M2_KEY_UPGRADE_FROM`],
6978/// [`M2_LIMITS_KEY_MEMORY`] / [`M2_LIMITS_KEY_FUEL`] /
6979/// [`M2_LIMITS_KEY_WALL_CLOCK`] / [`M2_LIMITS_KEY_CPU`] (d8b8b4f),
6980/// [`M3_PLACEMENT_KEY_ESTRATEGIA`] etc.).
6981pub const M2_BEHAVIOR_KEY_ON_INIT: &str = "onInit";
6982/// Canonical camelCase YAML sub-key the `:behavior :on-call` per-Servico
6983/// OTP-shaped sync-request-handler path scalar-axis lands under inside
6984/// the [`M2_KEY_BEHAVIOR`] overlay block. Peer of
6985/// [`M2_BEHAVIOR_KEY_ON_INIT`] on the sibling `:behavior` sub-slot axis.
6986pub const M2_BEHAVIOR_KEY_ON_CALL: &str = "onCall";
6987/// Canonical camelCase YAML sub-key the `:behavior :on-cast` per-Servico
6988/// OTP-shaped async-fire-and-forget-handler path scalar-axis lands under
6989/// inside the [`M2_KEY_BEHAVIOR`] overlay block. Peer of
6990/// [`M2_BEHAVIOR_KEY_ON_INIT`] on the sibling `:behavior` sub-slot axis.
6991pub const M2_BEHAVIOR_KEY_ON_CAST: &str = "onCast";
6992/// Canonical camelCase YAML sub-key the `:behavior :on-info` per-Servico
6993/// OTP-shaped out-of-band-message-handler path scalar-axis lands under
6994/// inside the [`M2_KEY_BEHAVIOR`] overlay block. Peer of
6995/// [`M2_BEHAVIOR_KEY_ON_INIT`] on the sibling `:behavior` sub-slot axis.
6996pub const M2_BEHAVIOR_KEY_ON_INFO: &str = "onInfo";
6997/// Canonical camelCase YAML sub-key the `:behavior :on-state-change`
6998/// per-Servico OTP-shaped hot-upgrade state-migration path scalar-axis
6999/// lands under inside the [`M2_KEY_BEHAVIOR`] overlay block. Peer of
7000/// [`M2_BEHAVIOR_KEY_ON_INIT`] on the sibling `:behavior` sub-slot axis;
7001/// the camelCase shape (`"onStateChange"`, not `"on_state_change"`) is
7002/// load-bearing per the serde-derive attribute on
7003/// [`crate::BehaviorSpec`].
7004pub const M2_BEHAVIOR_KEY_ON_STATE_CHANGE: &str = "onStateChange";
7005/// Canonical camelCase YAML sub-key the `:behavior :on-terminate`
7006/// per-Servico OTP-shaped graceful-shutdown-callback path scalar-axis
7007/// lands under inside the [`M2_KEY_BEHAVIOR`] overlay block. Peer of
7008/// [`M2_BEHAVIOR_KEY_ON_INIT`] on the sibling `:behavior` sub-slot axis.
7009pub const M2_BEHAVIOR_KEY_ON_TERMINATE: &str = "onTerminate";
7010
7011/// Canonical author-facing kebab-case `(defcaixa … :behavior (:on-init …))`
7012/// slot label the `:behavior :on-init` per-Servico OTP-shaped instance-init
7013/// callback axis surfaces under. Peer of [`M2_BEHAVIOR_KEY_ON_INIT`] on the
7014/// dual-axis pair every M2 `:behavior` sub-slot carries: the camelCase
7015/// [`M2_BEHAVIOR_KEY_ON_*`] const names the *renderer-side* wire key the
7016/// serde-derive-emitted [`M2_KEY_BEHAVIOR`] overlay carries under
7017/// (`"onInit"` etc, load-bearing per the `#[serde(rename_all = "camelCase")]`
7018/// attribute on [`crate::BehaviorSpec`]), the kebab-case
7019/// [`M2_BEHAVIOR_AUTHOR_KEY_ON_*`] const names the *author-facing* label the
7020/// [`crate::BehaviorSpec::declared_slots`] tagger threads through as the
7021/// `slot: &'static str` field on every [`crate::BehaviorError`] variant
7022/// (`":on-init"` etc, the exact byte-string authors see in the
7023/// per-slot value-shape diagnostic naming which of the six typed callback
7024/// slots the offending path landed on).
7025///
7026/// Until this lift landed the six kebab-case labels sat once each in
7027/// [`crate::BehaviorSpec::declared_slots`] as the six-arm inline
7028/// `":on-init"` / `":on-call"` / `":on-cast"` / `":on-info"` /
7029/// `":on-state-change"` / `":on-terminate"` byte-strings the tagger
7030/// iterated over, plus roughly two dozen test-side probe literals
7031/// asserting the diagnostic's `slot:` field carries the expected
7032/// per-arm value verbatim — with no compile-time link between the
7033/// tagger's arms and the tests' expected values. A future OTP-lineage
7034/// per-callback rebrand (`:on-init` → `:on-start` matching Akka's
7035/// per-actor preStart naming, `:on-call` → `:on-request` matching a
7036/// hypothetical wasi:http/incoming-handler terminology flip,
7037/// `:on-state-change` → `:on-code-change` matching Erlang's verbatim
7038/// `code_change/3` name, `:on-terminate` → `:on-shutdown` matching a
7039/// generic-lifecycle rebrand) or a per-consumer disambiguation (a
7040/// vocabulary shift on the author surface as the `defcaixa` macro
7041/// stabilizes) would silently desynchronize the production
7042/// [`crate::BehaviorSpec::declared_slots`] tagger from the tests until
7043/// a downstream consumer surfaced the drift at build time as a
7044/// matches-arm miss. This lift closes that gap by routing both halves
7045/// (production tagger + tests) through six peer consts declared
7046/// adjacent to the renderer-side [`M2_BEHAVIOR_KEY_ON_*`] peers, so
7047/// the "one canonical declaration per arm, next to the axis" discipline
7048/// the [`WitTarget::HTTP_FIELD_NAME`] / [`WitTarget::PUBSUB_FIELD_NAME`]
7049/// / [`WitTarget::STORE_FIELD_NAME`] payload-arm peer consts (174e96a)
7050/// already established for the [`crate::WitContract::target`]'s per-arm
7051/// diagnostic-scalar axis extends onto the M2 `:behavior` sub-slot
7052/// author-facing-label axis. Same "one canonical byte-string per typed
7053/// axis" discipline every peer M2 / M3 wire-key axis carries
7054/// ([`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] / [`M2_KEY_UPGRADE_FROM`],
7055/// [`M2_LIMITS_KEY_MEMORY`] / [`M2_LIMITS_KEY_FUEL`] /
7056/// [`M2_LIMITS_KEY_WALL_CLOCK`] / [`M2_LIMITS_KEY_CPU`] (d8b8b4f),
7057/// [`M2_BEHAVIOR_KEY_ON_INIT`] / [`M2_BEHAVIOR_KEY_ON_CALL`] /
7058/// [`M2_BEHAVIOR_KEY_ON_CAST`] / [`M2_BEHAVIOR_KEY_ON_INFO`] /
7059/// [`M2_BEHAVIOR_KEY_ON_STATE_CHANGE`] / [`M2_BEHAVIOR_KEY_ON_TERMINATE`]
7060/// (21fe462), [`M2_UPGRADE_FROM_KEY_FROM`] /
7061/// [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`] (36ffe65),
7062/// [`M3_PLACEMENT_KEY_ESTRATEGIA`] etc.), extended here to close the
7063/// M2 `:behavior` sub-slot's *author-facing-label* axis so the same
7064/// discipline the renderer-side wire-key axis carries applies to the
7065/// author-facing side.
7066pub const M2_BEHAVIOR_AUTHOR_KEY_ON_INIT: &str = ":on-init";
7067/// Canonical author-facing kebab-case `(defcaixa … :behavior (:on-call …))`
7068/// slot label for the `:behavior :on-call` per-Servico OTP-shaped
7069/// synchronous request/response handler axis. Peer of
7070/// [`M2_BEHAVIOR_AUTHOR_KEY_ON_INIT`] on the sibling `:behavior` sub-slot
7071/// author-facing-label axis.
7072pub const M2_BEHAVIOR_AUTHOR_KEY_ON_CALL: &str = ":on-call";
7073/// Canonical author-facing kebab-case `(defcaixa … :behavior (:on-cast …))`
7074/// slot label for the `:behavior :on-cast` per-Servico OTP-shaped
7075/// asynchronous fire-and-forget handler axis. Peer of
7076/// [`M2_BEHAVIOR_AUTHOR_KEY_ON_INIT`] on the sibling `:behavior` sub-slot
7077/// author-facing-label axis.
7078pub const M2_BEHAVIOR_AUTHOR_KEY_ON_CAST: &str = ":on-cast";
7079/// Canonical author-facing kebab-case `(defcaixa … :behavior (:on-info …))`
7080/// slot label for the `:behavior :on-info` per-Servico OTP-shaped
7081/// out-of-band message handler axis. Peer of
7082/// [`M2_BEHAVIOR_AUTHOR_KEY_ON_INIT`] on the sibling `:behavior` sub-slot
7083/// author-facing-label axis.
7084pub const M2_BEHAVIOR_AUTHOR_KEY_ON_INFO: &str = ":on-info";
7085/// Canonical author-facing kebab-case
7086/// `(defcaixa … :behavior (:on-state-change …))` slot label for the
7087/// `:behavior :on-state-change` per-Servico OTP-shaped hot-upgrade
7088/// state-migration axis. Peer of [`M2_BEHAVIOR_AUTHOR_KEY_ON_INIT`] on the
7089/// sibling `:behavior` sub-slot author-facing-label axis; the kebab-case
7090/// shape (`":on-state-change"`, not `":on-statechange"` /
7091/// `":on_state_change"`) is load-bearing per the author-facing
7092/// `(defcaixa …)` macro's canonical form and the exact byte-string the
7093/// per-slot [`crate::BehaviorError`] diagnostic threads through.
7094pub const M2_BEHAVIOR_AUTHOR_KEY_ON_STATE_CHANGE: &str = ":on-state-change";
7095/// Canonical author-facing kebab-case
7096/// `(defcaixa … :behavior (:on-terminate …))` slot label for the
7097/// `:behavior :on-terminate` per-Servico OTP-shaped graceful-shutdown
7098/// callback axis. Peer of [`M2_BEHAVIOR_AUTHOR_KEY_ON_INIT`] on the sibling
7099/// `:behavior` sub-slot author-facing-label axis.
7100pub const M2_BEHAVIOR_AUTHOR_KEY_ON_TERMINATE: &str = ":on-terminate";
7101
7102/// Canonical camelCase YAML sub-key the `:upgrade-from :from` per-entry
7103/// OTP-appup-shaped prior-`:versao` semver-string scalar-axis lands under
7104/// inside each element of the [`M2_KEY_UPGRADE_FROM`] overlay sequence.
7105/// Peer of [`M2_KEY_UPGRADE_FROM`] on the sibling `:upgrade-from` sub-slot
7106/// axis: [`M2_KEY_UPGRADE_FROM`] names the overlay-container's top-level
7107/// key ("upgradeFrom"), the two `M2_UPGRADE_FROM_KEY_*` consts name the
7108/// two typed sub-keys the M2 [`crate::UpgradeFromEntry`] struct's
7109/// OTP-appup-shaped per-entry fields (`from` semver-of-the-prior-`:versao`
7110/// / `instructions` typed [`crate::UpgradeInstruction`] list, analogs of
7111/// the OTP `.appup` file's `{FromVsn, [Instruction, …]}` per-entry tuple
7112/// per `theory/INSPIRATIONS.md` §II.4) serialize as under the
7113/// `#[serde(rename_all = "camelCase")]` derive attribute (`"from"` /
7114/// `"instructions"`). Emitted by [`servico_m2_overlay`] as sub-keys of
7115/// each element of the [`M2_KEY_UPGRADE_FROM`] overlay sequence and
7116/// consumed by every substrate-side test-side navigator that reaches into
7117/// the rendered `programs.yaml` per-Servico entry / lareira chart
7118/// `values.yaml` per-`pleme-computeunit` block to pin the per-entry
7119/// round-trip. The lower-camel shape (`"from"` / `"instructions"`) is
7120/// load-bearing: the serde-derive on [`crate::UpgradeFromEntry`] emits
7121/// under the same shape and the drift-detection pin in
7122/// `upgrade.rs::tests`
7123/// (`upgrade_from_entry_serde_keys_match_lifted_m2_upgrade_from_key_consts`)
7124/// serializes a fully-populated [`crate::UpgradeFromEntry`] and asserts
7125/// each canonical `M2_UPGRADE_FROM_KEY_*` byte-sequence appears in the
7126/// JSON — so a hypothetical future `rename_all = "snake_case"` /
7127/// `"kebab-case"` accident at the derive attribute or an OTP-lineage
7128/// per-entry-key rebrand (`:from` → `:prior-versao` matching a hypothetical
7129/// verbatim-Erlang `FromVsn` collapse, `:instructions` → `:steps` matching
7130/// a hypothetical Akka appup-shape rebrand) coordinated at the type's
7131/// derive attribute surfaces as a build-time test failure at `upgrade.rs`
7132/// rather than as a silent test-side `.get(<stale-camelCase-const>)`
7133/// returning `None` far from the derive-attr drift's commit. Same "one
7134/// canonical byte-string per typed axis" discipline every peer M2 / M3
7135/// wire-key axis carries ([`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] /
7136/// [`M2_KEY_UPGRADE_FROM`], [`M2_LIMITS_KEY_MEMORY`] /
7137/// [`M2_LIMITS_KEY_FUEL`] / [`M2_LIMITS_KEY_WALL_CLOCK`] /
7138/// [`M2_LIMITS_KEY_CPU`] (d8b8b4f), [`M2_BEHAVIOR_KEY_ON_INIT`] /
7139/// [`M2_BEHAVIOR_KEY_ON_CALL`] / [`M2_BEHAVIOR_KEY_ON_CAST`] /
7140/// [`M2_BEHAVIOR_KEY_ON_INFO`] / [`M2_BEHAVIOR_KEY_ON_STATE_CHANGE`] /
7141/// [`M2_BEHAVIOR_KEY_ON_TERMINATE`] (21fe462),
7142/// [`M3_PLACEMENT_KEY_ESTRATEGIA`] etc.). Closes the M2 sub-slot camelCase
7143/// key axis: with this lift the three M2 typed slots (`:limits` /
7144/// `:behavior` / `:upgrade-from`) all have their canonical camelCase
7145/// sub-slot key constants pinned into caixa-core.
7146pub const M2_UPGRADE_FROM_KEY_FROM: &str = "from";
7147/// Canonical camelCase YAML sub-key the `:upgrade-from :instructions`
7148/// per-entry OTP-appup-shaped typed [`crate::UpgradeInstruction`] list
7149/// axis lands under inside each element of the [`M2_KEY_UPGRADE_FROM`]
7150/// overlay sequence. Peer of [`M2_UPGRADE_FROM_KEY_FROM`] on the sibling
7151/// `:upgrade-from` sub-slot axis.
7152pub const M2_UPGRADE_FROM_KEY_INSTRUCTIONS: &str = "instructions";
7153
7154/// Canonical `#[serde(tag = "…")]` discriminator-key byte-sequence the
7155/// M2 `:upgrade-from :instructions` per-entry OTP-appup
7156/// [`crate::UpgradeInstruction`] enum surfaces its variant tag under
7157/// on serde emission — the internally-tagged wire key downstream
7158/// consumers navigate to (`serde_json::to_value(&instr).get("kind")`
7159/// / `serde_yaml::Value::Mapping.get("kind")` / hand-authored `{"kind":
7160/// "load-module", "module": "…"}` JSON) to disambiguate which of the
7161/// five OTP-shaped variants they hold. The `#[serde(tag = "kind",
7162/// rename_all = "kebab-case")]` attribute on
7163/// [`crate::UpgradeInstruction`] emits exactly this byte-sequence as
7164/// the tag-slot key, and this const names the same byte-string one
7165/// altitude above the derive attribute so every downstream consumer
7166/// that reaches for the tag (the reflection-vs-serde round-trip check
7167/// in [`caixa-core/tests/dispatcher_registration.rs`] that probes
7168/// `v.get("kind")` against every variant's expected kebab-case tag,
7169/// the future M4 `mesh.pleme.io/v1alpha1/Caixa` CR materializer's
7170/// upgrade-instruction admission webhook, any wasm-operator dispatch
7171/// step that navigates the serialized instruction blob to route by
7172/// variant) routes through one canonical `&'static str` rather than
7173/// re-inlining the literal.
7174///
7175/// Lifted as a typed `pub const` (rather than an inline literal at
7176/// the `#[serde(tag = "…")]` attribute site + every consumer probe)
7177/// so the tag-key axis has exactly one source of truth — a future
7178/// serde-shape rebrand (`tag = "kind"` → `tag = "type"` matching a
7179/// JSON-Schema `discriminator` convention, `tag = "kind"` → `tag = "op"`
7180/// matching a hypothetical OTP-abbreviation collapse, `tag = "kind"`
7181/// → `tag = "instruction"` matching a hypothetical author-surface
7182/// self-description flip as the `defcaixa` macro stabilizes) lands as
7183/// an edit to exactly one const, and every consumer that reaches for
7184/// the tag picks it up at build time rather than at runtime as a
7185/// silent `.get(<stale-tag-key>)` returning `None` far from the
7186/// derive-attr drift's commit. Same "one canonical byte-string per
7187/// typed axis" discipline every peer M2 sub-slot wire-key axis
7188/// carries ([`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] /
7189/// [`M2_KEY_UPGRADE_FROM`], [`M2_LIMITS_KEY_MEMORY`] etc. (d8b8b4f),
7190/// [`M2_BEHAVIOR_KEY_ON_INIT`] etc. (21fe462),
7191/// [`M2_UPGRADE_FROM_KEY_FROM`] / [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`]
7192/// (36ffe65)), now extending the lift onto the last remaining
7193/// un-lifted wire-key axis on the M2 `:upgrade-from :instructions`
7194/// typed slot: the internally-tagged variant-discriminator key that
7195/// pairs with the [`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`] etc.
7196/// (56120ef) per-variant kebab-case *values* the same
7197/// `#[serde(tag = "kind", rename_all = "kebab-case")]` attribute
7198/// emits. With this lift the `:upgrade-from :instructions` axis has
7199/// its dual (`key = "kind"` + five variant-value tags) fully lifted
7200/// into caixa-core.
7201pub const M2_UPGRADE_INSTRUCTION_KEY_KIND: &str = "kind";
7202
7203/// Canonical per-variant data-field JSON key the M2 `:upgrade-from
7204/// :instructions` per-entry OTP-appup
7205/// [`crate::UpgradeInstruction::LoadModule`] / [`crate::UpgradeInstruction::SoftPurge`]
7206/// / [`crate::UpgradeInstruction::Purge`] variants surface their
7207/// module-name payload under on serde emission — the internally-tagged
7208/// per-variant field byte-string every downstream consumer reading the
7209/// module string reaches for
7210/// (`serde_json::to_value(&instr).get("module")` /
7211/// `serde_yaml::Value::Mapping.get("module")` / hand-authored
7212/// `{"kind": "load-module", "module": "hello-rio"}` JSON blobs the
7213/// wasm-operator's upgrade-dispatch step consumes to route the
7214/// per-module load / soft-purge / purge action). The three variants
7215/// carrying a `module: String` field
7216/// ([`crate::UpgradeInstruction::LoadModule`], [`crate::UpgradeInstruction::SoftPurge`],
7217/// [`crate::UpgradeInstruction::Purge`]) all emit this exact
7218/// byte-sequence as the data-field JSON key alongside the
7219/// [`M2_UPGRADE_INSTRUCTION_KEY_KIND`] tag-key on the same instruction
7220/// blob — the `#[serde(tag = "kind", rename_all = "kebab-case")]`
7221/// attribute on [`crate::UpgradeInstruction`] promotes each variant's
7222/// struct-field name to a sibling JSON key at the same nesting level as
7223/// the tag, so a `LoadModule { module: "hello-rio" }` serializes to
7224/// `{"kind": "load-module", "module": "hello-rio"}` — one tag axis, one
7225/// data-field axis, both live on the same JSON object and both must be
7226/// pinned into caixa-core so a future rebrand at either axis surfaces
7227/// as a build-time test failure rather than an apply-time
7228/// `.get(<stale-field-key>)` returning `None` far from the field-name
7229/// drift's commit.
7230///
7231/// Lifted as a typed `pub const` (rather than an inline literal at every
7232/// consumer probe) so the per-variant module-field axis has exactly one
7233/// source of truth — a future struct-field rebrand (`module: String` →
7234/// `component: String` matching a hypothetical WASI component-model
7235/// naming pass, `module: String` → `name: String` matching the
7236/// canonical `KUBE_KEY_NAME` axis, `module: String` → `target: String`
7237/// matching the sibling `:contratos :para` axis) lands as an edit to
7238/// exactly one const, and every consumer that probes the module-field
7239/// key picks it up at build time. Same "one canonical byte-string per
7240/// typed axis" discipline the sibling
7241/// [`M2_UPGRADE_INSTRUCTION_KEY_KIND`] (6a203d7) lift established on
7242/// the peer tag-slot key axis on the same
7243/// [`crate::UpgradeInstruction`] enum: `KEY_KIND` names the tag axis,
7244/// `FIELD_KEY_MODULE` names the module-payload axis, and the two must
7245/// be disjoint by construction (an internally-tagged serialization
7246/// where the tag key collides with a data-field key silently corrupts
7247/// every serialized blob — same failure mode
7248/// `m2_upgrade_instruction_key_kind_const_disjoint_from_variant_data_keys`
7249/// pins on the sibling axis).
7250///
7251/// With this lift and its [`M2_UPGRADE_INSTRUCTION_FIELD_KEY_SCRIPT`]
7252/// peer, the whole `:upgrade-from :instructions` variant-JSON dual is
7253/// lifted into caixa-core: the tag *key*
7254/// ([`M2_UPGRADE_INSTRUCTION_KEY_KIND`]), the five tag *values*
7255/// ([`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`] etc.), and the two
7256/// data-field *keys* (this const and `SCRIPT`) all sit as
7257/// single-source-of-truth `&'static str`s. Any future serde-shape
7258/// rebrand touching either axis (tag key rename, per-variant field
7259/// rename, `rename_all` regime flip) surfaces at build time.
7260pub const M2_UPGRADE_INSTRUCTION_FIELD_KEY_MODULE: &str = "module";
7261
7262/// Canonical per-variant data-field JSON key the M2 `:upgrade-from
7263/// :instructions` per-entry [`crate::UpgradeInstruction::StateChange`]
7264/// variant surfaces its script-path payload under on serde emission —
7265/// the internally-tagged per-variant field byte-string every downstream
7266/// consumer reading the migration-script path reaches for
7267/// (`serde_json::to_value(&instr).get("script")` /
7268/// `serde_yaml::Value::Mapping.get("script")` / hand-authored
7269/// `{"kind": "state-change", "script": "lib/migrations/v01-to-v02.lisp"}`
7270/// JSON blobs the wasm-operator's upgrade-dispatch step consumes to
7271/// route the per-`gen_server` `code_change/3` migration action). Peer
7272/// of [`M2_UPGRADE_INSTRUCTION_FIELD_KEY_MODULE`] on the sibling
7273/// module-payload axis; see [`M2_UPGRADE_INSTRUCTION_FIELD_KEY_MODULE`]
7274/// for the full lift rationale.
7275///
7276/// The [`crate::UpgradeInstruction::StateChange`] variant is the only
7277/// one carrying a `script: PathBuf` field — the two module-bearing
7278/// variants ([`crate::UpgradeInstruction::LoadModule`],
7279/// [`crate::UpgradeInstruction::SoftPurge`],
7280/// [`crate::UpgradeInstruction::Purge`]) route through the sibling
7281/// [`M2_UPGRADE_INSTRUCTION_FIELD_KEY_MODULE`] const, and
7282/// [`crate::UpgradeInstruction::Restart`] carries no data field at all.
7283/// Same one-const-per-typed-axis discipline as the sibling.
7284pub const M2_UPGRADE_INSTRUCTION_FIELD_KEY_SCRIPT: &str = "script";
7285
7286/// Canonical author-facing kebab-case tag the M2 `:upgrade-from
7287/// :instructions` per-entry OTP-appup [`crate::UpgradeInstruction::LoadModule`]
7288/// variant surfaces under — the `:kind` field the
7289/// [`crate::UpgradeError::ModuleEmpty`] / [`crate::UpgradeError::ModuleInvalid`]
7290/// / [`crate::UpgradeError::DuplicateCleanup`] / [`crate::UpgradeError::PurgeWithoutPriorLoad`]
7291/// diagnostics carry so the author can grep their caixa.lisp for
7292/// `(:load-module …)` and fix it in one edit. The
7293/// [`crate::UpgradeInstruction::lisp_form`] production dispatch and every
7294/// test-side probe that pins a `kind:` / `kinds:` / `other_kinds:` /
7295/// `prior_cleanup_kind:` field routes through this const, so a future
7296/// per-variant kebab-case rebrand (`:load-module` → `:load` matching a
7297/// hypothetical Erlang `code:load_module` collapse, `:load-module` →
7298/// `:reload` matching a hypothetical Elixir/Phoenix hot-reload rebrand,
7299/// or a per-consumer disambiguation as the `defcaixa` macro stabilizes)
7300/// lands at one const-edit per arm and reaches both surfaces
7301/// (production dispatch + tests) by construction. Peer of
7302/// [`M2_UPGRADE_FROM_KEY_FROM`] / [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`]
7303/// on the sibling `:upgrade-from` sub-slot renderer-wire-key axis
7304/// (36ffe65) — this const family extends the same "one canonical
7305/// byte-string per typed axis" discipline onto the *author-facing*
7306/// per-instruction-variant tag axis one altitude below the
7307/// `:instructions` container. Same "one canonical declaration per arm,
7308/// next to the axis" discipline the peer [`M2_BEHAVIOR_AUTHOR_KEY_ON_INIT`]
7309/// etc. (889dc18) established for the M2 `:behavior` sub-slot's
7310/// per-callback kebab-case labels, [`CONTRATO_AUTHOR_KEY_DE`] /
7311/// [`CONTRATO_AUTHOR_KEY_PARA`] (f50c875) for the M3 `:contratos`
7312/// per-entry endpoint labels, and every top-level [`M2_AUTHOR_KEY_LIMITS`]
7313/// (f49c8b0) / [`M3_AUTHOR_KEY_MEMBROS`] (882f498) /
7314/// [`SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] (be40492) family established.
7315pub const M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE: &str = ":load-module";
7316/// Canonical author-facing kebab-case tag the M2 `:upgrade-from
7317/// :instructions` per-entry [`crate::UpgradeInstruction::StateChange`]
7318/// variant surfaces under. Peer of [`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`]
7319/// on the sibling per-instruction-variant tag axis; see
7320/// [`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`] for the full lift rationale.
7321pub const M2_UPGRADE_INSTRUCTION_KIND_STATE_CHANGE: &str = ":state-change";
7322/// Canonical author-facing kebab-case tag the M2 `:upgrade-from
7323/// :instructions` per-entry [`crate::UpgradeInstruction::SoftPurge`]
7324/// variant surfaces under. Peer of [`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`]
7325/// on the sibling per-instruction-variant tag axis; see
7326/// [`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`] for the full lift rationale.
7327pub const M2_UPGRADE_INSTRUCTION_KIND_SOFT_PURGE: &str = ":soft-purge";
7328/// Canonical author-facing kebab-case tag the M2 `:upgrade-from
7329/// :instructions` per-entry [`crate::UpgradeInstruction::Purge`]
7330/// variant surfaces under. Peer of [`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`]
7331/// on the sibling per-instruction-variant tag axis; see
7332/// [`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`] for the full lift rationale.
7333pub const M2_UPGRADE_INSTRUCTION_KIND_PURGE: &str = ":purge";
7334/// Canonical author-facing kebab-case tag the M2 `:upgrade-from
7335/// :instructions` per-entry [`crate::UpgradeInstruction::Restart`]
7336/// variant surfaces under. Peer of [`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`]
7337/// on the sibling per-instruction-variant tag axis; see
7338/// [`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`] for the full lift rationale.
7339pub const M2_UPGRADE_INSTRUCTION_KIND_RESTART: &str = ":restart";
7340
7341/// Canonical lowercase JSON/YAML discriminator-key the
7342/// [`crate::dep::DepSource`] enum's `#[serde(tag = "tipo", rename_all
7343/// = "lowercase")]` derive emits as the tag axis at each serialized
7344/// `Dep.fonte` block — the load-bearing byte-string every downstream
7345/// consumer reading a Dep source (the [`caixa_resolver`] per-`:deps`
7346/// git-clone dispatcher, the future `feira lock` / `feira resolve`
7347/// `lacre.lisp` closure writer, every test payload that reaches
7348/// `Value::get(DEP_SOURCE_KEY_TIPO)` to pin the variant discriminator)
7349/// must probe on. Peer of the two variant tag consts
7350/// [`DEP_SOURCE_TIPO_GIT`] and [`DEP_SOURCE_TIPO_PATH`] the sibling
7351/// `rename_all = "lowercase"` axis lifts on the same discriminator
7352/// block: the [`DEP_SOURCE_KEY_TIPO`] const names the outer tag *key*
7353/// (`"tipo":`) the `tag = "tipo"` attribute pins, the two
7354/// `DEP_SOURCE_TIPO_*` consts name the two admitted tag *values*
7355/// (`"git"` / `"path"`) the `rename_all = "lowercase"` attribute pins
7356/// as the discriminator's closed-set arms.
7357///
7358/// Until this lift landed the two load-bearing bytes at both altitudes
7359/// (`"tipo"` at the tag key, `"git"` / `"path"` at the two variant
7360/// tags) sat only as inline literals — at the `#[serde(tag = "tipo",
7361/// rename_all = "lowercase")]` attribute (dep.rs:59) and at one
7362/// round-trip test payload (`git_source_json_round_trip` pinning
7363/// `"tipo":"git"` inline, dep.rs:13563) — with no compile-time link
7364/// between the load-bearing serde-derive attribute and the downstream
7365/// consumers that probe the emit-side discriminator via
7366/// `Value::get(...)`. A future accidental `tag = "type"` /
7367/// `tag = "source_type"` typo at the attribute (English-uniformity
7368/// rebrand as the substrate publishes its typed manifest schema
7369/// outside pleme-io, verbatim-Cargo `"type"` alignment matching a
7370/// hypothetical Zig-store convergence, or per-consumer disambiguation
7371/// as the `defcaixa` macro stabilizes) — or a `rename_all` rebrand
7372/// (`"UPPERCASE"` / `"snake_case"` / `"kebab-case"`) — would silently
7373/// break the resolver's `Dep.fonte` dispatch and every downstream
7374/// `lacre.lisp` closure consumer, with the drift surfacing at fetch
7375/// time far from the derive-attr commit as an unknown-variant deserialize
7376/// failure. Pinning the three canonical byte-sequences to `&'static str`
7377/// consts + running the serialize-and-check drift-detection pins on
7378/// both variants closes the drift structurally at caixa-core build time.
7379///
7380/// Same "one canonical byte-string per typed serialized-key axis"
7381/// discipline the peer [`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`] etc.
7382/// (56120ef), [`M3_PLACEMENT_ESTRATEGIA_SINGLE_NODE`] etc., and
7383/// [`HELM_CHART_TYPE_APPLICATION`] / [`HELM_CHART_TYPE_LIBRARY`]
7384/// (1c5eb9d) closed-set variant-tag lifts carry — extended here to the
7385/// [`crate::dep::DepSource`] `:deps :fonte` typed slot's discriminator
7386/// axis at both altitudes (discriminator key + closed-set variant tags),
7387/// the last `#[serde(tag = ..., rename_all = ...)]` discriminator
7388/// family in caixa-core lacking a lifted peer.
7389pub const DEP_SOURCE_KEY_TIPO: &str = "tipo";
7390/// Canonical lowercase JSON/YAML discriminator-value the
7391/// [`crate::dep::DepSource::Git`] variant surfaces under — the
7392/// `"git"` scalar the `#[serde(tag = "tipo", rename_all =
7393/// "lowercase")]` derive emits at the [`DEP_SOURCE_KEY_TIPO`] axis
7394/// for the Git arm. Peer of [`DEP_SOURCE_TIPO_PATH`] on the sibling
7395/// closed-set variant-tag axis; see [`DEP_SOURCE_KEY_TIPO`] for the
7396/// full lift rationale. The scalar is derived from the Rust variant
7397/// name `Git` by the `rename_all = "lowercase"` derive; ASCII-lowercase
7398/// of `Git` is `git`.
7399pub const DEP_SOURCE_TIPO_GIT: &str = "git";
7400/// Canonical lowercase JSON/YAML discriminator-value the
7401/// [`crate::dep::DepSource::Path`] variant surfaces under — the
7402/// `"path"` scalar the `#[serde(tag = "tipo", rename_all =
7403/// "lowercase")]` derive emits at the [`DEP_SOURCE_KEY_TIPO`] axis
7404/// for the Path arm. Peer of [`DEP_SOURCE_TIPO_GIT`] on the sibling
7405/// closed-set variant-tag axis; see [`DEP_SOURCE_KEY_TIPO`] for the
7406/// full lift rationale.
7407///
7408/// Byte-identical to [`CILIUM_KEY_PATH`], [`FLUX_KUSTOMIZATION_KEY_PATH`],
7409/// and [`GATEWAY_API_KEY_PATH`] today — all four resolve to the same
7410/// four-byte `"path"` literal — but semantically distinct: the three
7411/// `*_KEY_PATH` consts name YAML container/leaf-*key* axes on their
7412/// respective K8s CR schemas (Cilium L7 HTTP-rule filesystem-path
7413/// container, Flux Kustomization git-source-subtree container, Gateway
7414/// API URL-path-match container), while this constant names a
7415/// discriminator *value* on the manifest-side [`crate::dep::DepSource`]
7416/// typed enum's closed-set variant tag axis (Path variant vs Git
7417/// variant). Splitting the four lets each axis's future rebrand land
7418/// independently at its canonical const definition without coupling
7419/// the `:deps :fonte` Path-variant discriminator axis to the three
7420/// K8s-CR key axes (or vice versa) — same
7421/// "byte-identical-but-semantically-distinct" discipline the peer
7422/// [`FLEET_PROGRAMS_KEY_NAME`] / [`KUBE_KEY_NAME`] and
7423/// [`FLEET_PROGRAMS_KEY_VERSAO`] / [`MEMBRO_KEY_VERSAO`] splits
7424/// established on the sibling per-entry key axes.
7425pub const DEP_SOURCE_TIPO_PATH: &str = "path";
7426
7427/// Canonical [`crate::LayoutError::MissingEntry`] `kind: &'static str`
7428/// discriminator scalar the M2 `:behavior` typed slot's per-callback
7429/// on-disk-leaf existence gate surfaces under — the byte-string every
7430/// [`crate::LayoutInvariants::verify`] emission carries when a
7431/// `:behavior :on-init` / `:on-call` / `:on-cast` / `:on-info` /
7432/// `:on-state-change` / `:on-terminate` sub-slot's tatara-lisp source
7433/// path fails to resolve against the caixa root's on-disk layout. Names
7434/// the "M2 :behavior sub-slot leaf-kind" axis one altitude below the
7435/// [`M2_AUTHOR_KEY_BEHAVIOR`] (f49c8b0) parent-slot label: the
7436/// top-level [`M2_AUTHOR_KEY_BEHAVIOR`] const names the M2 slot itself
7437/// on the author surface (`(defcaixa … :behavior (…))`), the six
7438/// [`M2_BEHAVIOR_AUTHOR_KEY_ON_*`] consts (889dc18) name the per-
7439/// callback sub-slot labels the author writes (`(:on-init "lib/init.lisp"
7440/// …)`), and this const names the per-slot-family leaf-kind byte-string
7441/// the layout diagnostic emits when the on-disk `lib/init.lisp` file
7442/// doesn't exist ("MissingEntry { kind: \"behavior-callback\", path:
7443/// /root/lib/init.lisp }").
7444///
7445/// Peer of [`LAYOUT_MISSING_ENTRY_KIND_UPGRADE_SCRIPT`] on the sibling
7446/// M2 `:upgrade-from` typed slot's per-entry leaf-kind axis: the two
7447/// consts split the M2 slot-family's on-disk-leaf categorization axis
7448/// into its two per-slot arms, so the `LayoutError::MissingEntry
7449/// { kind: &'static str, .. }` discriminator's accept-set has one
7450/// canonical declaration per arm rather than two inline byte-strings
7451/// scattered across [`crate::layout`]'s per-slot existence gates.
7452///
7453/// Until this lift landed the byte `"behavior-callback"` sat at two
7454/// sites in [`crate::layout`] — once at the [`crate::LayoutInvariants::verify`]
7455/// per-`:behavior :on-*` sub-slot existence gate's `MissingEntry` emit
7456/// (production, layout.rs:902), once at the
7457/// [`crate::layout::tests::behavior_callback_must_exist`]
7458/// (or peer test) `matches!(…, MissingEntry { kind: "behavior-callback",
7459/// .. })` shape probe (layout.rs:3152) — with no compile-time link
7460/// between the two: a future per-consumer rebrand (a hypothetical
7461/// `"behavior-callback"` → `"m2-behavior-callback"` for altitude-explicit
7462/// scoping as the M3+ layout gates grow their own per-slot leaf-kind
7463/// labels, `"behavior-callback"` → `"gen-server-callback"` matching a
7464/// verbatim-OTP rebrand of the [`M2_AUTHOR_KEY_BEHAVIOR`] slot's
7465/// `gen_server`-lineage identity, or a per-diagnostic disambiguation as
7466/// the `defcaixa` macro stabilizes and per-callback shapes diverge)
7467/// would silently desynchronize the production `MissingEntry` emission
7468/// from the test's `matches!` shape probe until build time surfaced the
7469/// drift as a pattern-arm miss far from the rename's commit. This lift
7470/// closes that gap by routing both halves (production emit + test
7471/// probe) through one peer const declared adjacent to the M2 top-level
7472/// slot-label family, so the "one canonical declaration per arm, next
7473/// to the axis" discipline the peer [`M2_AUTHOR_KEY_LIMITS`] /
7474/// [`M2_AUTHOR_KEY_BEHAVIOR`] / [`M2_AUTHOR_KEY_UPGRADE_FROM`]
7475/// (f49c8b0), [`M2_BEHAVIOR_AUTHOR_KEY_ON_INIT`] etc. (889dc18),
7476/// [`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`] etc. (56120ef),
7477/// [`M3_AUTHOR_KEY_MEMBROS`] etc. (882f498), and
7478/// [`SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] etc. (be40492) top-level +
7479/// sub-slot author-facing-label consts established for the sibling
7480/// M2 / M3 / Supervisor slot-family axes extends onto the M2
7481/// layout-check leaf-kind categorization axis.
7482///
7483/// Byte-shape note: unlike the peer author-facing kebab-case slot
7484/// labels (which carry the leading `:` sigil because the tatara-lisp
7485/// reader emits keyword tokens as `:kebab-case` and the author writes
7486/// them verbatim in `caixa.lisp`), this discriminator has no leading
7487/// `:` because the substrate consumer reading the value is the layout
7488/// diagnostic's downstream printer — the operator running `feira build`
7489/// sees `LayoutError::MissingEntry { kind: "behavior-callback", .. }`
7490/// as a categorization label, not as a tatara-lisp keyword to be
7491/// grep'd for in the source `.lisp`. Same shape distinction the peer
7492/// [`crate::WitTarget::HTTP_FIELD_NAME`] (= `"endpoint"`) /
7493/// [`crate::WitTarget::PUBSUB_FIELD_NAME`] (= `"subject"`) /
7494/// [`crate::WitTarget::STORE_FIELD_NAME`] (= `"slot"`) /
7495/// [`crate::WitTarget::CAPABILITY_EXPECTED`] (= `"none"`) consts
7496/// established on the sibling `:contratos` per-entry payload-field-
7497/// name axis: the field-name byte-strings are the downstream
7498/// diagnostic's format-argument scalars, prefixed by the `:` inside
7499/// the error format template (`":{expected}"`) rather than baked into
7500/// the const.
7501pub const LAYOUT_MISSING_ENTRY_KIND_BEHAVIOR_CALLBACK: &str = "behavior-callback";
7502
7503/// Canonical [`crate::LayoutError::MissingEntry`] `kind: &'static str`
7504/// discriminator scalar the M2 `:upgrade-from` typed slot's per-entry
7505/// [`crate::UpgradeInstruction::StateChange`] script-path on-disk-leaf
7506/// existence gate surfaces under — the byte-string every
7507/// [`crate::LayoutInvariants::verify`] emission carries when a
7508/// `(:state-change "<script>.lisp")` instruction's tatara-lisp source
7509/// path fails to resolve against the caixa root's on-disk layout.
7510/// Peer of [`LAYOUT_MISSING_ENTRY_KIND_BEHAVIOR_CALLBACK`] on the sibling
7511/// M2 `:behavior` typed slot's per-callback leaf-kind axis; see
7512/// [`LAYOUT_MISSING_ENTRY_KIND_BEHAVIOR_CALLBACK`] for the full lift
7513/// rationale.
7514pub const LAYOUT_MISSING_ENTRY_KIND_UPGRADE_SCRIPT: &str = "upgrade-script";
7515
7516/// Canonical [`crate::LayoutError::MissingEntry`] `kind: &'static str`
7517/// discriminator scalar the M0 `:kind Biblioteca` typed slot's
7518/// per-`:bibliotecas` entry on-disk-leaf existence gate surfaces under
7519/// — the byte-string every [`crate::LayoutInvariants::verify`]
7520/// emission carries when a `:bibliotecas ("lib/foo.lisp" …)` entry's
7521/// tatara-lisp source path fails to resolve against the caixa root's
7522/// on-disk layout. Peer of [`LAYOUT_MISSING_ENTRY_KIND_EXE`] /
7523/// [`LAYOUT_MISSING_ENTRY_KIND_SERVICO`] on the sibling M0 code-slot
7524/// per-directory leaf-kind axes, and of the M2-tier
7525/// [`LAYOUT_MISSING_ENTRY_KIND_BEHAVIOR_CALLBACK`] /
7526/// [`LAYOUT_MISSING_ENTRY_KIND_UPGRADE_SCRIPT`] (95c9c4c) leaf-kind
7527/// labels on the [`crate::LayoutError::MissingEntry`] `kind:
7528/// &'static str` discriminator's accept-set — completes the
7529/// M0-tier arm of the same per-slot leaf-kind categorization axis
7530/// the M2 lift established.
7531///
7532/// Byte-identical to [`crate::CaixaKind::Biblioteca`]'s
7533/// [`crate::CaixaKind::as_str`] output today (both resolve to the
7534/// same eleven-byte `"biblioteca"` scalar) — the pin test
7535/// `layout_missing_entry_kind_m0_consts_align_with_caixa_kind_as_str`
7536/// makes the coincidence load-bearing rather than accidental so a
7537/// future rename that touches either axis (a per-consumer
7538/// disambiguation as the layout diagnostic vocabulary sharpens, a
7539/// verbatim-Portuguese rebrand of the [`crate::CaixaKind`]'s
7540/// human-readable-form arm) has to reach both sites in lockstep
7541/// or the pin trips at build time.
7542pub const LAYOUT_MISSING_ENTRY_KIND_BIBLIOTECA: &str = "biblioteca";
7543
7544/// Canonical [`crate::LayoutError::MissingEntry`] `kind: &'static str`
7545/// discriminator scalar the M0 `:kind Binario` typed slot's per-`:exe`
7546/// entry on-disk-leaf existence gate surfaces under — the byte-string
7547/// every [`crate::LayoutInvariants::verify`] emission carries when an
7548/// `:exe ("exe/tool.lisp" …)` entry's tatara-lisp source path fails to
7549/// resolve against the caixa root's on-disk layout. Peer of
7550/// [`LAYOUT_MISSING_ENTRY_KIND_BIBLIOTECA`] /
7551/// [`LAYOUT_MISSING_ENTRY_KIND_SERVICO`] on the sibling M0 code-slot
7552/// per-directory leaf-kind axes; see
7553/// [`LAYOUT_MISSING_ENTRY_KIND_BIBLIOTECA`] for the shared lift
7554/// rationale.
7555///
7556/// Semantically distinct from [`crate::CaixaKind::Binario`]'s
7557/// [`crate::CaixaKind::as_str`] output (`"binario"`) — this const
7558/// names the *directory-entry* leaf-kind label (the M0 `:exe`
7559/// per-entry axis carries source files under the `exe/` subtree),
7560/// not the caixa's own [`crate::CaixaKind`] discriminator. The
7561/// [`crate::LayoutError::MissingEntry`] `kind` emission consumer
7562/// (the operator running `feira build`) reads this as a per-directory
7563/// categorization label (`"missing exe/... entry"`), whereas
7564/// [`crate::CaixaKind::as_str`] names the whole caixa's runtime kind
7565/// (`"binario"` = "this caixa produces one or more binaries"). Two
7566/// axes, two lifts — the pin test
7567/// `layout_missing_entry_kind_m0_consts_align_with_caixa_kind_as_str`
7568/// asserts the *inequality* between this const and
7569/// [`crate::CaixaKind::Binario`]'s [`crate::CaixaKind::as_str`]
7570/// output, so a future accidental collapse of the two axes onto a
7571/// single scalar surfaces at build time.
7572pub const LAYOUT_MISSING_ENTRY_KIND_EXE: &str = "exe";
7573
7574/// Canonical [`crate::LayoutError::MissingEntry`] `kind: &'static str`
7575/// discriminator scalar the M0 `:kind Servico` typed slot's
7576/// per-`:servicos` entry on-disk-leaf existence gate surfaces under —
7577/// the byte-string every [`crate::LayoutInvariants::verify`] emission
7578/// carries when a `:servicos ("servicos/foo.computeunit.yaml" …)`
7579/// entry fails to resolve against the caixa root's on-disk layout.
7580/// Peer of [`LAYOUT_MISSING_ENTRY_KIND_BIBLIOTECA`] /
7581/// [`LAYOUT_MISSING_ENTRY_KIND_EXE`] on the sibling M0 code-slot
7582/// per-directory leaf-kind axes; see
7583/// [`LAYOUT_MISSING_ENTRY_KIND_BIBLIOTECA`] for the shared lift
7584/// rationale. Byte-identical to [`crate::CaixaKind::Servico`]'s
7585/// [`crate::CaixaKind::as_str`] output today (both resolve to the
7586/// same seven-byte `"servico"` scalar).
7587pub const LAYOUT_MISSING_ENTRY_KIND_SERVICO: &str = "servico";
7588
7589/// Canonical human-readable label the M0 [`crate::CaixaKind::Biblioteca`]
7590/// arm surfaces under [`crate::CaixaKind::as_str`] and (routed through
7591/// it) [`std::fmt::Display`] — the byte-string every future diagnostic
7592/// / graph / audit consumer that formats a `:kind` variant as
7593/// user-facing text lands on (the future wasm-operator's per-caixa
7594/// startup log line naming the loaded caixa's typed shape, the future
7595/// `feira app graph` per-member kind column, the future M4
7596/// `wasm.pleme.io/v1alpha1/ComputeUnit` / `mesh.pleme.io/v1alpha1/*` CR
7597/// materializer's admission-webhook rejection body naming which typed
7598/// kind the offending manifest carries). Peer of the sibling four
7599/// [`CAIXA_KIND_LABEL_BINARIO`] / [`CAIXA_KIND_LABEL_SERVICO`] /
7600/// [`CAIXA_KIND_LABEL_SUPERVISOR`] / [`CAIXA_KIND_LABEL_APLICACAO`]
7601/// consts on the same closed [`crate::CaixaKind`] enum surface —
7602/// together the pentad names every author-reachable arm of the
7603/// substrate's most fundamental typed axis (what a caixa produces),
7604/// mirroring the closed-enum-scalar-value trajectory the sibling
7605/// OTP-shaped [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ONE`] etc. (09ffb2d) and
7606/// [`SUPERVISOR_CHILD_RESTART_PERMANENT`] etc. (ccdf955) and the M3
7607/// [`M3_PLACEMENT_ESTRATEGIA_SINGLE_NODE`] etc. (3f0e21c) established
7608/// on the sibling closed-set typed-enum discriminator axes.
7609///
7610/// Until this lift landed the five [`crate::CaixaKind::as_str`] arms
7611/// each returned a hand-authored byte-string literal (`"biblioteca"`
7612/// / `"binario"` / `"servico"` / `"supervisor"` / `"aplicacao"`) at
7613/// the source-side match arm with no compile-time link to the peer
7614/// [`LAYOUT_MISSING_ENTRY_KIND_BIBLIOTECA`] /
7615/// [`LAYOUT_MISSING_ENTRY_KIND_SERVICO`] consts on the sibling
7616/// layout-diagnostic axis (whose bytes coincide by design), and no
7617/// [`std::fmt::Display`] surface at all — every consumer reaching for
7618/// a caixa-kind byte-string past the wire format
7619/// (`Serialize` → PascalCase `"Biblioteca"` etc.) had to reach for the
7620/// hand-authored [`crate::CaixaKind::as_str`] arm's literal or roll a
7621/// per-consumer `format!("{v:?}")` `Debug` route, either of which a
7622/// future variant rename would silently desynchronize. Lifting the
7623/// five arms onto peer consts + routing [`std::fmt::Display`] through
7624/// [`crate::CaixaKind::as_str`] closes the drift footgun structurally:
7625/// the human-readable byte-string (`Display` + `as_str`), the wire
7626/// byte-string (`Serialize`, PascalCase — intentionally distinct from
7627/// the human-readable form), and the layout-diagnostic byte-string
7628/// (`LAYOUT_MISSING_ENTRY_KIND_*`) each route through one canonical
7629/// declaration per axis, with pin tests
7630/// (`caixa_kind_as_str_returns_lifted_peer_const`,
7631/// `caixa_kind_display_routes_through_as_str_helper`) making any drift
7632/// a caixa-core-build-time failure.
7633///
7634/// Byte-identical to [`LAYOUT_MISSING_ENTRY_KIND_BIBLIOTECA`] today
7635/// (both resolve to the same eleven-byte `"biblioteca"` scalar) — the
7636/// pin test
7637/// [`layout_missing_entry_kind_m0_consts_align_with_caixa_kind_as_str`]
7638/// (fe2a898) already made the coincidence load-bearing on the sibling
7639/// layout-leaf-kind axis. Semantically distinct: this const names the
7640/// [`crate::CaixaKind`] discriminator's human-readable form (the
7641/// substrate's canonical `:kind` label), while
7642/// [`LAYOUT_MISSING_ENTRY_KIND_BIBLIOTECA`] names the
7643/// [`crate::LayoutError::MissingEntry`] `kind: &'static str`
7644/// leaf-kind discriminator (the per-`:bibliotecas`-entry on-disk-leaf
7645/// existence diagnostic's categorization label). Two axes, two lifts —
7646/// same "byte-identical-but-semantically-distinct" discipline the peer
7647/// [`FLEET_PROGRAMS_KEY_VERSAO`] / [`MEMBRO_KEY_VERSAO`] split (ce80ca0)
7648/// established on the sibling per-entry version-constraint axis.
7649pub const CAIXA_KIND_LABEL_BIBLIOTECA: &str = "biblioteca";
7650
7651/// Canonical human-readable label the M0 [`crate::CaixaKind::Binario`]
7652/// arm surfaces under [`crate::CaixaKind::as_str`] and (routed through
7653/// it) [`std::fmt::Display`]. Peer of [`CAIXA_KIND_LABEL_BIBLIOTECA`] /
7654/// [`CAIXA_KIND_LABEL_SERVICO`] / [`CAIXA_KIND_LABEL_SUPERVISOR`] /
7655/// [`CAIXA_KIND_LABEL_APLICACAO`] on the same closed
7656/// [`crate::CaixaKind`] enum surface; see [`CAIXA_KIND_LABEL_BIBLIOTECA`]
7657/// for the shared lift rationale.
7658///
7659/// Semantically distinct from [`LAYOUT_MISSING_ENTRY_KIND_EXE`]
7660/// (`"exe"`) — the alignment pin
7661/// [`layout_missing_entry_kind_m0_consts_align_with_caixa_kind_as_str`]
7662/// (fe2a898) asserts the *inequality* between the layout-side leaf-kind
7663/// label (which names the `exe/` directory sub-tree) and this
7664/// [`crate::CaixaKind`] discriminator label (which names the caixa's
7665/// whole runtime kind). Two axes, two lifts.
7666pub const CAIXA_KIND_LABEL_BINARIO: &str = "binario";
7667
7668/// Canonical human-readable label the M0 [`crate::CaixaKind::Servico`]
7669/// arm surfaces under [`crate::CaixaKind::as_str`] and (routed through
7670/// it) [`std::fmt::Display`]. Peer of [`CAIXA_KIND_LABEL_BIBLIOTECA`] /
7671/// [`CAIXA_KIND_LABEL_BINARIO`] / [`CAIXA_KIND_LABEL_SUPERVISOR`] /
7672/// [`CAIXA_KIND_LABEL_APLICACAO`] on the same closed
7673/// [`crate::CaixaKind`] enum surface; see [`CAIXA_KIND_LABEL_BIBLIOTECA`]
7674/// for the shared lift rationale.
7675///
7676/// Byte-identical to [`LAYOUT_MISSING_ENTRY_KIND_SERVICO`] today (both
7677/// resolve to the same seven-byte `"servico"` scalar) — the pin test
7678/// [`layout_missing_entry_kind_m0_consts_align_with_caixa_kind_as_str`]
7679/// (fe2a898) already made the coincidence load-bearing on the sibling
7680/// layout-leaf-kind axis. Semantically distinct: this const names the
7681/// [`crate::CaixaKind`] discriminator's human-readable form (the
7682/// substrate's canonical `:kind Servico` label), while
7683/// [`LAYOUT_MISSING_ENTRY_KIND_SERVICO`] names the per-`:servicos`-entry
7684/// on-disk-leaf existence diagnostic's categorization label.
7685pub const CAIXA_KIND_LABEL_SERVICO: &str = "servico";
7686
7687/// Canonical human-readable label the M2 [`crate::CaixaKind::Supervisor`]
7688/// arm surfaces under [`crate::CaixaKind::as_str`] and (routed through
7689/// it) [`std::fmt::Display`]. Peer of [`CAIXA_KIND_LABEL_BIBLIOTECA`] /
7690/// [`CAIXA_KIND_LABEL_BINARIO`] / [`CAIXA_KIND_LABEL_SERVICO`] /
7691/// [`CAIXA_KIND_LABEL_APLICACAO`] on the same closed
7692/// [`crate::CaixaKind`] enum surface; see [`CAIXA_KIND_LABEL_BIBLIOTECA`]
7693/// for the shared lift rationale.
7694///
7695/// No layout-leaf-kind peer today — the `:kind Supervisor` typed slot
7696/// carries no on-disk source-file sub-tree (a supervisor is composed
7697/// entirely of `:children` references to other caixas), so no
7698/// [`crate::LayoutError::MissingEntry`] `kind:` diagnostic reaches for
7699/// this label. The const stands as the sole source of truth for the
7700/// [`crate::CaixaKind::Supervisor`] arm's human-readable form.
7701pub const CAIXA_KIND_LABEL_SUPERVISOR: &str = "supervisor";
7702
7703/// Canonical human-readable label the M3 [`crate::CaixaKind::Aplicacao`]
7704/// arm surfaces under [`crate::CaixaKind::as_str`] and (routed through
7705/// it) [`std::fmt::Display`]. Peer of [`CAIXA_KIND_LABEL_BIBLIOTECA`] /
7706/// [`CAIXA_KIND_LABEL_BINARIO`] / [`CAIXA_KIND_LABEL_SERVICO`] /
7707/// [`CAIXA_KIND_LABEL_SUPERVISOR`] on the same closed
7708/// [`crate::CaixaKind`] enum surface; see [`CAIXA_KIND_LABEL_BIBLIOTECA`]
7709/// for the shared lift rationale.
7710///
7711/// Byte-identical to [`FLEET_PROGRAMS_KEY_APLICACAO`] today (both
7712/// resolve to the same nine-byte `"aplicacao"` scalar) — the coincidence
7713/// is deliberate but semantically distinct: this const names the
7714/// [`crate::CaixaKind::Aplicacao`] discriminator's human-readable form
7715/// (the substrate's canonical `:kind Aplicacao` label), while
7716/// [`FLEET_PROGRAMS_KEY_APLICACAO`] names the per-programs.yaml-entry
7717/// passthrough-annotation YAML key that links a member entry back to
7718/// its parent Aplicacao (MESH-COMPOSITION §III.4). Two axes, two lifts
7719/// — same "byte-identical-but-semantically-distinct" discipline every
7720/// peer split establishes.
7721pub const CAIXA_KIND_LABEL_APLICACAO: &str = "aplicacao";
7722
7723/// Canonical human-readable label the [`crate::CaixaKind::Acao`] arm
7724/// surfaces under [`crate::CaixaKind::as_str`] and (routed through it)
7725/// [`std::fmt::Display`]. Sixth peer of [`CAIXA_KIND_LABEL_BIBLIOTECA`] /
7726/// [`CAIXA_KIND_LABEL_BINARIO`] / [`CAIXA_KIND_LABEL_SERVICO`] /
7727/// [`CAIXA_KIND_LABEL_SUPERVISOR`] / [`CAIXA_KIND_LABEL_APLICACAO`] on
7728/// the same closed [`crate::CaixaKind`] enum surface; see
7729/// [`CAIXA_KIND_LABEL_BIBLIOTECA`] for the shared lift rationale.
7730///
7731/// No layout-leaf-kind peer today (mirroring [`CAIXA_KIND_LABEL_SUPERVISOR`])
7732/// — the `:kind Acao` slot's sole payload is the `:ci` field
7733/// (a `canteiro_types::CiRun`), which is not a code-surface
7734/// path-existence check the way `:bibliotecas`/`:exe`/`:servicos` are.
7735pub const CAIXA_KIND_LABEL_ACAO: &str = "acao";
7736
7737/// Canonical PascalCase wire byte-string the [`crate::CaixaKind::Biblioteca`]
7738/// arm serializes as under the un-`rename`d `#[derive(Serialize)]` on
7739/// [`crate::CaixaKind`] — the exact byte-shape every wire surface that
7740/// carries a Caixa's `:kind` outside the caixa-core boundary consumes
7741/// (the [`caixa_crd::caixa_cr::CaixaSpec`] `kind:` field the K8s
7742/// `Caixa` CR persists between apply and reconcile passes, the
7743/// tatara-lisp author-surface `:kind Biblioteca` symbol the sexp parser
7744/// binds into the typed [`crate::CaixaKind`] enum, the future M4
7745/// `mesh.pleme.io/v1alpha1/Caixa` CR materializer's per-CR admission-
7746/// webhook wire binding).
7747///
7748/// Peer of the sibling [`CAIXA_KIND_LABEL_BIBLIOTECA`] lowercase-Portuguese
7749/// diagnostic-form const on the sibling axis — the two byte-strings are
7750/// *intentionally distinct* by design (see the two-axis-split docstring
7751/// on [`crate::CaixaKind::as_str`] + the load-bearing pin
7752/// [`crate::kind::tests::caixa_kind_display_matches_as_str_and_not_serialize_wire`]
7753/// on the split). This wire const names the substrate's PascalCase
7754/// wire form; the sibling `_LABEL_*` const names the substrate's
7755/// lowercase-Portuguese diagnostic form. Six-arm parallel of the
7756/// same closed [`crate::CaixaKind`] enum surface — same "one canonical
7757/// byte-string per arm, per axis, next to the axis" discipline every
7758/// peer typed-enum const family carries.
7759///
7760/// Prior to this lift, every consumer that needed the PascalCase wire
7761/// byte-shape reached for one of two fragile paths: `format!("{:?}",
7762/// kind)` (couples the wire format to `Debug`'s stability guarantee,
7763/// which is *no guarantee at all* by Rust's own conventions — a
7764/// `#[derive(Debug)]` swap for a hand-rolled `impl Debug` that pretty-
7765/// prints the variant with extra context is a permitted mechanical
7766/// edit whose apply-time symptom would be every downstream K8s CR
7767/// carrying a stale wire byte-string), or `serde_json::to_string(&k)`
7768/// then string-trim of the outer quotes (introduces an allocation +
7769/// error-handling path for a byte-shape the compiler knows verbatim at
7770/// build time). Lifting the six arms onto peer consts routes the
7771/// substrate's wire byte-shape through one canonical declaration per
7772/// arm the paired [`crate::CaixaKind::wire_name`] +
7773/// [`crate::CaixaKind::from_wire`] typed dispatch consumers key off.
7774pub const CAIXA_KIND_WIRE_BIBLIOTECA: &str = "Biblioteca";
7775
7776/// Canonical PascalCase wire byte-string the [`crate::CaixaKind::Binario`]
7777/// arm serializes as under the un-`rename`d `#[derive(Serialize)]` on
7778/// [`crate::CaixaKind`]. Peer of [`CAIXA_KIND_WIRE_BIBLIOTECA`] on the
7779/// same closed [`crate::CaixaKind`] enum surface; see the sibling
7780/// [`CAIXA_KIND_WIRE_BIBLIOTECA`] docstring for the shared lift
7781/// rationale.
7782pub const CAIXA_KIND_WIRE_BINARIO: &str = "Binario";
7783
7784/// Canonical PascalCase wire byte-string the [`crate::CaixaKind::Servico`]
7785/// arm serializes as under the un-`rename`d `#[derive(Serialize)]` on
7786/// [`crate::CaixaKind`]. Peer of [`CAIXA_KIND_WIRE_BIBLIOTECA`] on the
7787/// same closed [`crate::CaixaKind`] enum surface; see the sibling
7788/// [`CAIXA_KIND_WIRE_BIBLIOTECA`] docstring for the shared lift
7789/// rationale.
7790pub const CAIXA_KIND_WIRE_SERVICO: &str = "Servico";
7791
7792/// Canonical PascalCase wire byte-string the [`crate::CaixaKind::Supervisor`]
7793/// arm serializes as under the un-`rename`d `#[derive(Serialize)]` on
7794/// [`crate::CaixaKind`]. Peer of [`CAIXA_KIND_WIRE_BIBLIOTECA`] on the
7795/// same closed [`crate::CaixaKind`] enum surface; see the sibling
7796/// [`CAIXA_KIND_WIRE_BIBLIOTECA`] docstring for the shared lift
7797/// rationale.
7798pub const CAIXA_KIND_WIRE_SUPERVISOR: &str = "Supervisor";
7799
7800/// Canonical PascalCase wire byte-string the [`crate::CaixaKind::Aplicacao`]
7801/// arm serializes as under the un-`rename`d `#[derive(Serialize)]` on
7802/// [`crate::CaixaKind`]. Peer of [`CAIXA_KIND_WIRE_BIBLIOTECA`] on the
7803/// same closed [`crate::CaixaKind`] enum surface; see the sibling
7804/// [`CAIXA_KIND_WIRE_BIBLIOTECA`] docstring for the shared lift
7805/// rationale.
7806pub const CAIXA_KIND_WIRE_APLICACAO: &str = "Aplicacao";
7807
7808/// Canonical PascalCase wire byte-string the [`crate::CaixaKind::Acao`]
7809/// arm serializes as under the un-`rename`d `#[derive(Serialize)]` on
7810/// [`crate::CaixaKind`]. Sixth peer of [`CAIXA_KIND_WIRE_BIBLIOTECA`] /
7811/// [`CAIXA_KIND_WIRE_BINARIO`] / [`CAIXA_KIND_WIRE_SERVICO`] /
7812/// [`CAIXA_KIND_WIRE_SUPERVISOR`] / [`CAIXA_KIND_WIRE_APLICACAO`] on
7813/// the same closed [`crate::CaixaKind`] enum surface; see the sibling
7814/// [`CAIXA_KIND_WIRE_BIBLIOTECA`] docstring for the shared lift
7815/// rationale.
7816pub const CAIXA_KIND_WIRE_ACAO: &str = "Acao";
7817
7818/// Canonical caixa-root-relative directory name housing every
7819/// [`crate::CaixaKind::Biblioteca`] caixa's `lib/<nome>.lisp` entry
7820/// (and every `:bibliotecas ("lib/foo.lisp" …)` per-entry source
7821/// path the M0 `:kind Biblioteca` typed slot admits). The single
7822/// source of truth every consumer that composes a caixa-root-relative
7823/// path pointing at the tatara-lisp library sub-tree reaches for:
7824///
7825/// - [`crate::LayoutInvariants::verify`] joins `root` with this
7826/// const to reconstruct the default `lib/<nome>.lisp` per-caixa
7827/// entry the [`crate::LayoutError::MissingLib`] emission gates on;
7828/// - `feira init`'s new-caixa scaffolder joins `root` with this
7829/// const to seed the empty `lib/` sub-tree the template's
7830/// `lib/<nome>.lisp` starter file lives in;
7831/// - `feira fmt` / `feira lint` enumerate every `.lisp` under
7832/// `root.join(LAYOUT_DIR_LIB)` as their default target set (their
7833/// `--paths`-less invocation walks the library sub-tree the
7834/// substrate's [`crate::LayoutInvariants::verify`] pins);
7835/// - `feira tofu` reads every `.lisp` under `root.join(LAYOUT_DIR_LIB)`
7836/// to concatenate the `(defteia …)` forms the caixa-arch invariants
7837/// bind on.
7838///
7839/// The `lib/` byte-shape is a Cargo-style abbreviation of the M0
7840/// `:kind Biblioteca` discriminator ([`crate::CaixaKind::Biblioteca`]
7841/// / [`LAYOUT_MISSING_ENTRY_KIND_BIBLIOTECA`], both `"biblioteca"`),
7842/// deliberately distinct from the discriminator's byte-shape so the
7843/// on-disk convention stays terse while the diagnostic label stays
7844/// full-form Portuguese. Peer of [`LAYOUT_DIR_EXE`] /
7845/// [`LAYOUT_DIR_SERVICOS`] on the sibling M0 per-`CaixaKind`
7846/// on-disk-directory-name axes — the three consts jointly single-source
7847/// the CSE-invariant layout convention every caixa the substrate accepts
7848/// carries. A future rebrand of the on-disk directory landing convention
7849/// (`"lib"` → `"src"` matching Rust's convention, `"lib"` → `"biblioteca"`
7850/// matching the full-form Portuguese-uniformity a per-kind consumer
7851/// disambiguation would prefer) lands as a one-line const-edit + the
7852/// paired drift-detection pin that guards the two-axis distinctness
7853/// (`layout_dir_bib_is_distinct_from_layout_missing_entry_kind_bib`)
7854/// rather than a coordinated ~40-site sweep across production +
7855/// tests + CI scaffolders.
7856///
7857/// Same "one canonical byte-string per typed axis + a paired
7858/// drift-detection pin at every load-bearing byte-shape coincidence"
7859/// discipline the M0 [`LAYOUT_MISSING_ENTRY_KIND_BIBLIOTECA`] /
7860/// [`LAYOUT_MISSING_ENTRY_KIND_EXE`] / [`LAYOUT_MISSING_ENTRY_KIND_SERVICO`]
7861/// (fe2a898) leaf-kind categorization triad established on the peer
7862/// [`crate::LayoutError::MissingEntry`] `kind:` discriminator axis.
7863pub const LAYOUT_DIR_LIB: &str = "lib";
7864
7865/// Canonical caixa-root-relative directory name housing every
7866/// [`crate::CaixaKind::Binario`] caixa's `exe/<name>` entry (and
7867/// every `:exe ("exe/tool" …)` per-entry source path the M0
7868/// `:kind Binario` typed slot admits). Peer of [`LAYOUT_DIR_LIB`] /
7869/// [`LAYOUT_DIR_SERVICOS`] on the sibling M0 per-`CaixaKind`
7870/// on-disk-directory-name axes; see [`LAYOUT_DIR_LIB`] for the
7871/// shared lift rationale.
7872///
7873/// [`crate::LayoutInvariants::verify`] joins `root` with this const
7874/// to reconstruct the sandbox-root the [`crate::LayoutError::ExeOutsideDir`]
7875/// emission gates every declared `:exe` entry against — a `:exe`
7876/// entry whose resolved path escapes `root.join(LAYOUT_DIR_EXE)`
7877/// surfaces `ExeOutsideDir(<path>)` at `feira build` time rather than
7878/// silently reaching outside the caixa's sandbox at OCI-build /
7879/// nix-build time. Byte-identical (by design) to
7880/// [`LAYOUT_MISSING_ENTRY_KIND_EXE`] — the M0 `:kind Binario`
7881/// on-disk-directory-name and the [`crate::LayoutError::MissingEntry`]
7882/// `kind:` leaf-kind categorization label share the same three-byte
7883/// scalar because both name the same axis (the `exe/` sub-tree), a
7884/// coincidence the pin test
7885/// `layout_dir_exe_matches_layout_missing_entry_kind_exe` makes
7886/// load-bearing so a rebrand touching either axis without the other
7887/// trips at build time rather than surfacing at
7888/// [`crate::LayoutInvariants::verify`] time as a mismatched
7889/// `MissingEntry.kind` diagnostic naming a stale label.
7890pub const LAYOUT_DIR_EXE: &str = "exe";
7891
7892/// Canonical caixa-root-relative directory name housing every
7893/// [`crate::CaixaKind::Servico`] caixa's
7894/// `servicos/<nome>.computeunit.yaml` per-CR `ComputeUnit` descriptor
7895/// (and every `:servicos ("servicos/foo.computeunit.yaml" …)`
7896/// per-entry source path the M0 `:kind Servico` typed slot admits).
7897/// Peer of [`LAYOUT_DIR_LIB`] / [`LAYOUT_DIR_EXE`] on the sibling M0
7898/// per-`CaixaKind` on-disk-directory-name axes; see [`LAYOUT_DIR_LIB`]
7899/// for the shared lift rationale.
7900///
7901/// [`crate::LayoutInvariants::verify`] joins `root` with this const
7902/// to reconstruct the sandbox-root the
7903/// [`crate::LayoutError::ServicoOutsideDir`] emission gates every
7904/// declared `:servicos` entry against — a `:servicos` entry whose
7905/// resolved path escapes `root.join(LAYOUT_DIR_SERVICOS)` surfaces
7906/// `ServicoOutsideDir(<path>)` at `feira build` time rather than
7907/// silently reaching outside the caixa's sandbox at
7908/// [`caixa_helm`][ch] / [`caixa_flux`][cf] render time or at the
7909/// operator's OCI-build step.
7910///
7911/// The `servicos/` byte-shape is the Portuguese *plural* of the M0
7912/// `:kind Servico` discriminator ([`crate::CaixaKind::Servico`] /
7913/// [`LAYOUT_MISSING_ENTRY_KIND_SERVICO`], both `"servico"`, singular)
7914/// — the on-disk directory holds one-or-more `ComputeUnit` YAML
7915/// descriptors per caixa, the discriminator names the caixa's kind.
7916/// The pin test
7917/// `layout_dir_servicos_is_distinct_from_layout_missing_entry_kind_servico`
7918/// makes the singular/plural split load-bearing so a future rebrand
7919/// touching either axis without the other (a per-consumer
7920/// disambiguation collapsing them, a hypothetical English-uniformity
7921/// pass renaming `"servicos"` → `"services"`) trips at build time.
7922///
7923/// [ch]: caixa_helm
7924/// [cf]: caixa_flux
7925pub const LAYOUT_DIR_SERVICOS: &str = "servicos";
7926
7927/// Canonical `wasm.pleme.io/v1alpha1/ComputeUnit` CRD `spec.module`
7928/// per-CR wasm-module-reference sub-block key — the top-level `spec.*`
7929/// child every rendered `ComputeUnit` YAML carries to name the wasm
7930/// component (`module.source: oci://...` for OCI-hosted binaries,
7931/// `module.source: file://...` for locally-mounted wasm bundles) the
7932/// M2.5 wasm-engine instantiator loads at Servico bring-up. The single
7933/// source of truth every downstream consumer that reads or emits the
7934/// per-CR module sub-block reaches for:
7935///
7936/// - [`caixa_flux::programs_yaml_entry`] splices the ComputeUnit
7937/// YAML's `spec.module` verbatim through into the emitted
7938/// `programs[]` entry (the `lareira-fleet-programs` library chart's
7939/// per-entry module-source axis, populated from the ComputeUnit's
7940/// `spec.module` per the docstring on `programs_yaml_entry` above);
7941/// - [`caixa_helm::build_values_yaml`] threads the same
7942/// `spec.module` sub-block into the rendered `values.yaml`'s
7943/// [`DEFAULT_LIBRARY_NAME`]-wrapped block so the `pleme-computeunit`
7944/// library chart's per-Servico module axis binds to the exact
7945/// source the caixa.lisp's `:servicos` fixture pins;
7946/// - every test-fixture navigator in both crates that reaches into
7947/// the rendered `programs[]` entry / `values.yaml` block by the
7948/// module sub-block key to pin the per-Servico module-source axis
7949/// round-trip (six sites across [`caixa_flux`][cf]'s per-entry
7950/// module + module.source drift-detection sweep + [`caixa_helm`][ch]'s
7951/// per-values module drift-detection sweep) resolves the same
7952/// `&'static str` when parsing back the rendered document;
7953/// - every future per-Servico renderer the absorption-roadmap
7954/// acknowledges (the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
7955/// materializer's per-`:membros` module-source resolver, a future
7956/// per-cluster ComputeUnit-CR admission webhook keying off the same
7957/// accepted sub-block set, the future caixa-otel collector-pipeline
7958/// emitter's per-Servico module-scrape reference).
7959///
7960/// Until this lift landed the byte `"module"` lived as six verbatim
7961/// inline literals across [`caixa_flux`][cf] and [`caixa_helm`][ch]'s
7962/// test-fixture navigators (four sites in caixa-flux —
7963/// `programs_yaml_entry_round_trips`'s `entry.get("module")` pair +
7964/// `upsert_helmrelease_replaces_existing`'s `.get("module")` +
7965/// `upsert_into_programs_yaml`'s `.get("module")` — and two sites in
7966/// caixa-helm — `values_yaml_wraps_under_pleme_computeunit_key`'s
7967/// `cu_block.get("module")` + `values_yaml_wrap_key_follows_library_name_override`'s
7968/// peer navigator on the library-name-override axis). A future
7969/// ComputeUnit CRD schema-key rebrand on the per-CR module-reference
7970/// axis (the substrate moving the wasm-component reference to
7971/// `binary:` for parity with OCI OpenContainer Image nomenclature, to
7972/// `component:` for parity with WIT Component Model wire terminology,
7973/// to `spec.wasm.source` for schema-clarity once the ComputeUnit
7974/// CRD grows sibling `spec.native.*` / `spec.container.*` runtime-
7975/// discriminators as the ABSORPTION-ROADMAP.md M4-M5 trajectory names)
7976/// without a coordinated edit across all six sites would silently
7977/// split the schema: the emitter would write under the drifted key
7978/// while every downstream test would still probe `module:` — the
7979/// `lareira-fleet-programs` library chart's per-entry module-source
7980/// axis would silently receive an empty reference, the workload would
7981/// silently come up with no wasm module bound (the M2.5 instantiator
7982/// falls back to the library chart's admission-time default of a
7983/// hello-world stub, or fails the bring-up at wasm-engine parse time
7984/// with a diagnostic far from the caixa.lisp source), and the failure
7985/// would surface as "the Servico's pods are running but they aren't
7986/// running our code" far from the rebrand commit's source. Lifting
7987/// the literal to one `&'static str` closes the drift footgun
7988/// structurally — every consumer reads the same memory, so any
7989/// future rebrand reaches every consumer by construction.
7990///
7991/// Same "the typed constant lives in one place" discipline the peer
7992/// [`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] / [`M2_KEY_UPGRADE_FROM`]
7993/// lifts apply on the sibling caixa.lisp M2 typed-slot canonical-
7994/// camelCase-key surfaces — extends the discipline from the caixa-
7995/// source-side M2 typed-slot overlay-key triple onto the substrate-
7996/// side `wasm.pleme.io/v1alpha1/ComputeUnit` CRD per-`spec.*`
7997/// sub-block axis every rendered ComputeUnit YAML declares as its
7998/// top-level `(module, trigger, capabilities)` triple (the peer
7999/// [`COMPUTEUNIT_SPEC_KEY_TRIGGER`] +
8000/// [`COMPUTEUNIT_SPEC_KEY_CAPABILITIES`] siblings complete the
8001/// substrate-side ComputeUnit-CRD per-`spec.*` sub-block re-export
8002/// triple).
8003///
8004/// [cf]: ../../caixa_flux/index.html
8005/// [ch]: ../../caixa_helm/index.html
8006pub const COMPUTEUNIT_SPEC_KEY_MODULE: &str = "module";
8007
8008/// Canonical `wasm.pleme.io/v1alpha1/ComputeUnit` CRD `spec.trigger`
8009/// per-CR invocation-trigger sub-block key — the top-level `spec.*`
8010/// child every rendered `ComputeUnit` YAML carries to name how the
8011/// wasm component is invoked (`trigger.service.{port, paths}` for
8012/// HTTP-triggered Servicos, `trigger.subscription.{subject}` for the
8013/// future NATS-triggered Servicos the M4 `:contratos` typed-mesh
8014/// pubsub axis will emit). Peer of [`COMPUTEUNIT_SPEC_KEY_MODULE`] on
8015/// the same ComputeUnit CRD per-`spec.*` sub-block surface —
8016/// `COMPUTEUNIT_SPEC_KEY_MODULE` names the per-CR wasm-binary
8017/// reference axis, this constant names the per-CR invocation-shape
8018/// axis every downstream trigger consumer (the `pleme-computeunit`
8019/// library chart's per-Servico `trigger.service.port` /
8020/// `trigger.service.paths` / `trigger.service.breathability` values-
8021/// block routing, the future M4 pubsub-subscription binding, the
8022/// `caixa-mesh` `CiliumNetworkPolicy` L4-port fallback that reads the
8023/// destination Servico's per-`trigger.service.port` axis via a future
8024/// resolver round-trip) reaches for. Same lift trajectory as the
8025/// sibling [`COMPUTEUNIT_SPEC_KEY_MODULE`] axis — three verbatim
8026/// inline test-side literals (one caixa-flux drift-detection navigator
8027/// + two caixa-helm per-values drift-detection navigators, one under
8028/// the canonical wrap-key + one under the library-name-override wrap-
8029/// key) collapsed onto the same `&'static str` so any future rebrand
8030/// (the substrate moving the invocation-shape axis to `invoke:`,
8031/// `entry:`, or splitting into `trigger.http.*` / `trigger.pubsub.*`
8032/// runtime-discriminators as the M4 `:contratos` axis grows) reaches
8033/// every consumer by construction. See [`COMPUTEUNIT_SPEC_KEY_MODULE`]
8034/// for the full lift rationale.
8035pub const COMPUTEUNIT_SPEC_KEY_TRIGGER: &str = "trigger";
8036
8037/// Canonical `wasm.pleme.io/v1alpha1/ComputeUnit` CRD
8038/// `spec.capabilities` per-CR WASI-capability-list sub-block key — the
8039/// top-level `spec.*` child every rendered `ComputeUnit` YAML carries
8040/// to declare the wasm-component-capability tokens the M2.5 wasm-engine
8041/// instantiator binds at Servico bring-up (`http-in:0.0.0.0:8080` for
8042/// the HTTP incoming-handler, `env` for read-only environment access,
8043/// `sock-*` for TCP outbound, and the sibling WASI-preview-2 preview-
8044/// interfaces per the WIT Component Model). Peer of
8045/// [`COMPUTEUNIT_SPEC_KEY_MODULE`] and [`COMPUTEUNIT_SPEC_KEY_TRIGGER`]
8046/// on the same ComputeUnit CRD per-`spec.*` sub-block surface —
8047/// completes the substrate-side ComputeUnit-CRD per-`spec.*` sub-block
8048/// re-export triple every rendered ComputeUnit YAML declares as its
8049/// top-level `(module, trigger, capabilities)` axis. Same lift
8050/// trajectory as the sibling [`COMPUTEUNIT_SPEC_KEY_MODULE`] axis —
8051/// three verbatim inline test-side literals (one caixa-flux drift-
8052/// detection navigator + two caixa-helm per-values drift-detection
8053/// navigators, one under the canonical wrap-key + one under the
8054/// library-name-override wrap-key) collapsed onto the same
8055/// `&'static str` so any future rebrand (the substrate moving the
8056/// capability-list axis to `caps:` for terse-schema parity with the
8057/// WASI-preview-2 upstream naming, splitting into
8058/// `capabilities.wasi.*` / `capabilities.pleme.*` runtime-vs-substrate
8059/// discriminators, or the M4 WIT Component Model materializer moving
8060/// to a typed `imports:` / `exports:` split) reaches every consumer by
8061/// construction. See [`COMPUTEUNIT_SPEC_KEY_MODULE`] for the full lift
8062/// rationale.
8063pub const COMPUTEUNIT_SPEC_KEY_CAPABILITIES: &str = "capabilities";
8064
8065/// Canonical `wasm.pleme.io/v1alpha1/ComputeUnit` CRD
8066/// `spec.module.source` per-CR wasm-component-reference leaf-scalar
8067/// sub-block key — the nested `spec.module.*` child every rendered
8068/// `ComputeUnit` YAML carries to name the exact wasm-component
8069/// artifact the M2.5 wasm-engine instantiator loads at Servico
8070/// bring-up. Peer of the parent [`COMPUTEUNIT_SPEC_KEY_MODULE`] on the
8071/// same ComputeUnit CRD per-`spec.module.*` sub-block surface —
8072/// `COMPUTEUNIT_SPEC_KEY_MODULE` names the top-level per-CR module-
8073/// reference block; this constant names the block's leaf reference-
8074/// value axis. Every rendered `programs[]` entry the
8075/// `lareira-fleet-programs` library chart consumes carries the
8076/// `module.source: oci://ghcr.io/pleme-io/<caixa>:<versao>` (or
8077/// `module.source: file://...` for locally-mounted wasm bundles;
8078/// `module.source: github:<owner>/<repo>` for git-hosted sources) as
8079/// its per-Servico wasm-artifact reference; every `spec.module.source`
8080/// readback across the [`caixa_flux::programs_yaml_entry`] round-trip
8081/// pins + the [`caixa_flux::upsert_into_programs_yaml`] /
8082/// [`caixa_flux::upsert_into_helmrelease_programs`] cross-upsert
8083/// navigators resolves the same `&'static str`.
8084///
8085/// Until this lift landed the byte `"source"` lived as three verbatim
8086/// inline literals across [`caixa_flux`][cf]'s test-fixture navigators
8087/// (`programs_yaml_entry_round_trips`'s
8088/// `entry.get(COMPUTEUNIT_SPEC_KEY_MODULE).and_then(|m| m.get("source"))`
8089/// per-`module.source` present-check +
8090/// `upsert_into_programs_yaml`'s
8091/// `arr[0].get(COMPUTEUNIT_SPEC_KEY_MODULE).get("source")` cross-
8092/// upsert readback + `upsert_into_helmrelease_programs`'s peer
8093/// navigator on the `HelmRelease`-wrapped `spec.values.programs[]`
8094/// path). A future ComputeUnit-CRD schema rebrand on the per-`module`
8095/// leaf-scalar axis (the substrate moving the reference-value axis to
8096/// `ref:` for parity with the OCI Distribution Spec's per-manifest
8097/// content-reference nomenclature, to `uri:` for parity with the WIT
8098/// Component Model's per-import content-reference field, to
8099/// `module.oci.ref` / `module.file.path` / `module.git.rev` sibling-
8100/// discriminator split once the ComputeUnit CRD grows typed sub-block
8101/// discriminators as the ABSORPTION-ROADMAP.md M4-M5 trajectory names)
8102/// without a coordinated three-site edit would silently split the
8103/// schema: the emitter would write under the drifted leaf-key while
8104/// every downstream navigator would still probe `source:` — the
8105/// `lareira-fleet-programs` library chart's per-entry module-source
8106/// axis would silently receive an empty reference, the workload would
8107/// silently come up with no wasm module bound (the M2.5 instantiator
8108/// falls back to the library chart's admission-time hello-world stub,
8109/// or fails the bring-up at wasm-engine parse time with a diagnostic
8110/// far from the caixa.lisp source), and the failure would surface as
8111/// "the Servico's pods are running but they aren't running our code"
8112/// far from the rebrand commit's source. Lifting the literal to one
8113/// `&'static str` closes the drift footgun structurally — every
8114/// consumer reads the same memory, so any future rebrand reaches every
8115/// consumer by construction.
8116///
8117/// Same "the typed constant lives in one place" discipline the peer
8118/// [`COMPUTEUNIT_SPEC_KEY_MODULE`] / [`COMPUTEUNIT_SPEC_KEY_TRIGGER`] /
8119/// [`COMPUTEUNIT_SPEC_KEY_CAPABILITIES`] lifts apply on the sibling
8120/// substrate-side ComputeUnit-CRD per-`spec.*` sub-block axis —
8121/// extends the discipline one level deeper from the top-level `spec.*`
8122/// container-axis surface onto the nested `spec.module.*` leaf-scalar-
8123/// axis every rendered ComputeUnit YAML declares under its per-CR
8124/// module-reference block.
8125///
8126/// [cf]: ../../caixa_flux/index.html
8127pub const COMPUTEUNIT_MODULE_KEY_SOURCE: &str = "source";
8128
8129/// Canonical YAML key for the M3 `:placement` slot's overlay on a
8130/// rendered programs.yaml entry. The lareira-fleet-programs aggregator
8131/// (and the future `app-operator` per-Aplicacao reconciler) both key
8132/// off this exact spelling to filter entries by `placement.clusters`
8133/// for cross-cluster fanout (MESH-COMPOSITION §III.4) and to dispatch
8134/// on `placement.estrategia` for distributed-app takeover semantics
8135/// (§II.1, §V cross-cluster federation). Lifted as a const alongside
8136/// the M2 keys so the Aplicacao-side renderer
8137/// ([`crate::aplicacao::Placement`] → caixa-mesh
8138/// `programs_for_aplicacao`) and every consumer (the M4 cluster-fanout
8139/// renderer, the future `mesh.pleme.io/v1alpha1/Aplicacao` CR
8140/// materializer, the `app-operator`'s placement-strategy dispatcher)
8141/// spell the same key exactly the same way — drift here = a
8142/// programs.yaml entry whose placement is silently dropped at the
8143/// aggregator's filter step (visible only as "the workload doesn't
8144/// land where the typed slot said it should").
8145pub const M3_KEY_PLACEMENT: &str = "placement";
8146
8147/// Canonical author-facing kebab-case `(defcaixa … :membros (…))`
8148/// top-level mesh slot label the M3 Aplicacao's constituent-Servico set
8149/// surfaces under. Peer of the four sibling M3 top-level mesh-slot
8150/// labels ([`M3_AUTHOR_KEY_CONTRATOS`], [`M3_AUTHOR_KEY_POLITICAS`],
8151/// [`M3_AUTHOR_KEY_PLACEMENT`], [`M3_AUTHOR_KEY_ENTRADA`]) on the
8152/// dual-axis pair every M3 top-level mesh slot carries: the
8153/// author-facing kebab-case `[M3_AUTHOR_KEY_*]` const names the label
8154/// the [`crate::Caixa::declared_mesh_slots`] tagger threads through as
8155/// one of the `&'static str` entries in the canonical-declaration-order
8156/// slot list the kind-coherence gate
8157/// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]) joins into the
8158/// space-separated `slots:` diagnostic naming which of the five mesh
8159/// slots the offending caixa declared on a non-Aplicacao kind. Peer of
8160/// the [`M3_KEY_PLACEMENT`] renderer-side wire-key const declared
8161/// immediately above on the sole M3 mesh slot the renderer surfaces as
8162/// a per-entry overlay-container key (`:membros` / `:contratos` /
8163/// `:politicas` / `:entrada` render as per-arm derived artifacts —
8164/// programs.yaml fan-out, CiliumNetworkPolicies, per-edge overlays,
8165/// Gateway/HTTPRoute — not as a single overlay-container key).
8166///
8167/// Until this lift landed the five kebab-case labels sat once each in
8168/// [`crate::Caixa::declared_mesh_slots`] as five-arm inline
8169/// `":membros"` / `":contratos"` / `":politicas"` / `":placement"` /
8170/// `":entrada"` byte-strings the tagger pushed onto its return `Vec`,
8171/// plus three test-side probe literals across `layout.rs` and
8172/// `manifest.rs::tests` — with no compile-time link between the
8173/// tagger's arms and the tests' expected values. A future rebrand
8174/// (a hypothetical `:membros` → `:members` matching English-uniformity
8175/// as the substrate's per-slot vocabulary stabilizes, `:contratos` →
8176/// `:contracts` matching the same, `:politicas` → `:policies`
8177/// matching the same, `:placement` → `:distribution` matching
8178/// MESH-COMPOSITION §II.1 vocabulary, `:entrada` → `:ingress` matching
8179/// K8s Gateway API's ingress-side vocabulary, or a per-consumer
8180/// disambiguation as the `defcaixa` macro stabilizes) would silently
8181/// desynchronize the production
8182/// [`crate::Caixa::declared_mesh_slots`] tagger from the tests until a
8183/// downstream consumer surfaced the drift at build time as a
8184/// matches-arm miss far from the rename's commit. This lift closes
8185/// that gap by routing both halves (production tagger + tests) through
8186/// five peer consts declared adjacent to the renderer-side
8187/// [`M3_KEY_PLACEMENT`] peer, so the "one canonical declaration per
8188/// arm, next to the axis" discipline the peer
8189/// [`M2_AUTHOR_KEY_LIMITS`] / [`M2_AUTHOR_KEY_BEHAVIOR`] /
8190/// [`M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot consts
8191/// (f49c8b0) established for the sibling per-Servico M2 slot axis
8192/// extends onto the M3 top-level mesh slot axis so both altitudes
8193/// of the typed-slot algebra (per-Servico M2 + per-Aplicacao M3)
8194/// route through peer author-label consts.
8195pub const M3_AUTHOR_KEY_MEMBROS: &str = ":membros";
8196
8197/// Canonical author-facing kebab-case `(defcaixa … :contratos (…))`
8198/// top-level mesh slot label the M3 Aplicacao's WIT-typed inter-Servico
8199/// edge set surfaces under. Peer of [`M3_AUTHOR_KEY_MEMBROS`] on the
8200/// sibling M3 top-level mesh-slot dual axis; see
8201/// [`M3_AUTHOR_KEY_MEMBROS`] for the full lift rationale.
8202pub const M3_AUTHOR_KEY_CONTRATOS: &str = ":contratos";
8203
8204/// Canonical author-facing kebab-case `(defcaixa … :politicas (…))`
8205/// top-level mesh slot label the M3 Aplicacao's mesh-level policy
8206/// overlay ([`crate::aplicacao::MeshPolicy`]: `:timeout`, `:retries`,
8207/// `:circuit-breaker`, `:mtls-required`, `:rate-limit`) surfaces under.
8208/// Peer of [`M3_AUTHOR_KEY_MEMBROS`] on the sibling M3 top-level
8209/// mesh-slot dual axis; see [`M3_AUTHOR_KEY_MEMBROS`] for the full lift
8210/// rationale.
8211pub const M3_AUTHOR_KEY_POLITICAS: &str = ":politicas";
8212
8213/// Canonical author-facing kebab-case `(defcaixa … :placement (…))`
8214/// top-level mesh slot label the M3 Aplicacao's cross-cluster
8215/// distribution strategy ([`crate::aplicacao::Placement`]:
8216/// `:estrategia` + `:clusters` + `:shard-key` / `:affinity`) surfaces
8217/// under. Peer of [`M3_AUTHOR_KEY_MEMBROS`] on the sibling M3
8218/// top-level mesh-slot dual axis; see [`M3_AUTHOR_KEY_MEMBROS`] for
8219/// the full lift rationale. Byte-identical to the peer
8220/// [`M3_KEY_PLACEMENT`] renderer-side wire key modulo the leading `:`
8221/// — the two consts split on the axis every M3 top-level slot carries
8222/// (author-facing kebab-case label vs. renderer-side camelCase overlay
8223/// key), the same split the [`M2_AUTHOR_KEY_LIMITS`] / [`M2_KEY_LIMITS`]
8224/// peer pair established on the sibling M2 axis.
8225pub const M3_AUTHOR_KEY_PLACEMENT: &str = ":placement";
8226
8227/// Canonical author-facing kebab-case `(defcaixa … :entrada (…))`
8228/// top-level mesh slot label the M3 Aplicacao's external-ingress
8229/// gateway surface ([`crate::aplicacao::Entrada`]: `:host`, `:para`,
8230/// `:paths`, `:port`) surfaces under. Peer of [`M3_AUTHOR_KEY_MEMBROS`]
8231/// on the sibling M3 top-level mesh-slot dual axis; see
8232/// [`M3_AUTHOR_KEY_MEMBROS`] for the full lift rationale.
8233pub const M3_AUTHOR_KEY_ENTRADA: &str = ":entrada";
8234
8235/// Canonical author-facing kebab-case `(:de "<caixa>")` per-`:contratos`
8236/// entry source-endpoint sub-slot label the M3 Aplicacao's WIT-typed
8237/// inter-Servico edge set surfaces under. Names the "edge tail" —
8238/// which member `:contratos` entry `n` originates from — per
8239/// MESH-COMPOSITION §IV table row "`:contratos` | typed inter-Servico
8240/// edges | each :de + :para must be in :membros; :wit must reference a
8241/// registered WIT world".
8242///
8243/// Peer of [`M3_AUTHOR_KEY_CONTRATOS`] on the `:contratos` sub-slot
8244/// author-facing-label dual axis: the top-level [`M3_AUTHOR_KEY_CONTRATOS`]
8245/// const (882f498) names the M3 slot itself, the two
8246/// `CONTRATO_AUTHOR_KEY_{DE,PARA}` consts name the per-entry endpoint
8247/// axes the parser reads (`(:de "cart" :para "catalog" …)`).
8248///
8249/// Until this lift landed the two kebab-case labels sat once each in
8250/// [`crate::aplicacao::AplicacaoSpec::validate`]'s per-`:contratos`
8251/// entry endpoint-shape gate as two two-arm inline `":de"` / `":para"`
8252/// byte-strings passed as the `slot: &'static str` argument to
8253/// [`validate_contrato_caixa`], plus a family of test-side probe
8254/// literals asserting the [`crate::aplicacao::AplicacaoError::ContratoCaixaEmpty`]
8255/// / [`crate::aplicacao::AplicacaoError::ContratoCaixaInvalid`]
8256/// diagnostic's `slot:` field carries the expected per-arm value
8257/// verbatim — with no compile-time link between the validator's arms
8258/// and the tests' expected values. A future rebrand (a hypothetical
8259/// `:de` → `:from` for English uniformity matching the OTP `appup`
8260/// `M2_UPGRADE_FROM_KEY_FROM` (36ffe65) sibling, `:para` → `:to`
8261/// matching the same, `:de`/`:para` → `:source`/`:target` matching
8262/// the WIT world's `import`/`export` half-vocabulary, or a per-consumer
8263/// disambiguation as the `defcaixa` macro stabilizes) would silently
8264/// desynchronize the production per-entry endpoint-shape gate from the
8265/// tests until a downstream consumer surfaced the drift at build time
8266/// as a matches-arm miss far from the rename's commit. This lift closes
8267/// that gap by routing both halves (production endpoint-shape gate +
8268/// tests) through two peer consts declared adjacent to the
8269/// [`M3_AUTHOR_KEY_CONTRATOS`] parent-slot label, so the "one
8270/// canonical declaration per arm, next to the axis" discipline the
8271/// peer [`M2_AUTHOR_KEY_LIMITS`] / [`M2_AUTHOR_KEY_BEHAVIOR`] /
8272/// [`M2_AUTHOR_KEY_UPGRADE_FROM`] (f49c8b0), [`M3_AUTHOR_KEY_MEMBROS`]
8273/// / [`M3_AUTHOR_KEY_CONTRATOS`] / [`M3_AUTHOR_KEY_POLITICAS`] /
8274/// [`M3_AUTHOR_KEY_PLACEMENT`] / [`M3_AUTHOR_KEY_ENTRADA`] (882f498),
8275/// and [`SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] etc. (be40492) top-level
8276/// slot consts established for the sibling M2 / M3 / Supervisor
8277/// top-level slot axes extends onto the `:contratos` sub-slot
8278/// endpoint axis.
8279pub const CONTRATO_AUTHOR_KEY_DE: &str = ":de";
8280
8281/// Canonical author-facing kebab-case `(:para "<caixa>")` per-`:contratos`
8282/// entry target-endpoint sub-slot label the M3 Aplicacao's WIT-typed
8283/// inter-Servico edge set surfaces under. Names the "edge head" —
8284/// which member `:contratos` entry `n` terminates at — per
8285/// MESH-COMPOSITION §IV table row "`:contratos` | typed inter-Servico
8286/// edges | each :de + :para must be in :membros". Peer of
8287/// [`CONTRATO_AUTHOR_KEY_DE`] on the sibling `:contratos` per-entry
8288/// endpoint-shape axis; see [`CONTRATO_AUTHOR_KEY_DE`] for the full
8289/// lift rationale.
8290pub const CONTRATO_AUTHOR_KEY_PARA: &str = ":para";
8291
8292/// Canonical author-facing kebab-case `(defcaixa … :estrategia <s>)`
8293/// top-level supervisor-tree slot label the OTP `:kind Supervisor`
8294/// caixa's [`crate::supervisor::RestartStrategy`] discriminator surfaces
8295/// under. Peer of [`M2_AUTHOR_KEY_LIMITS`] /
8296/// [`M3_AUTHOR_KEY_MEMBROS`] on the third kind-scoped
8297/// typed-slot-family axis: the M2 `M2_AUTHOR_KEY_*` consts (f49c8b0)
8298/// name the Servico-runtime slots, the M3 `M3_AUTHOR_KEY_*` consts
8299/// (882f498) name the Aplicacao mesh slots, and these
8300/// `SUPERVISOR_AUTHOR_KEY_*` consts close the last remaining kind ↔
8301/// slot-family axis — the Supervisor supervision-tree slots
8302/// (`:estrategia`, `:max-restarts`, `:restart-window`, `:children`) that
8303/// [`crate::Caixa::declared_supervisor_slots`] tags for the sibling
8304/// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
8305/// kind-coherence gate.
8306///
8307/// Until this lift landed the four kebab-case labels sat once each in
8308/// [`crate::Caixa::declared_supervisor_slots`] as four-arm inline
8309/// `":estrategia"` / `":max-restarts"` / `":restart-window"` /
8310/// `":children"` byte-strings the tagger pushed onto its return `Vec`,
8311/// plus a handful of test-side probe literals asserting the diagnostic's
8312/// `slots:` field carries the expected per-arm value verbatim — with no
8313/// compile-time link between the tagger's arms and the tests' expected
8314/// values. A future rebrand (a hypothetical `:estrategia` →
8315/// `:strategy` for English uniformity, `:max-restarts` →
8316/// `:max-intensity` matching Erlang/OTP's `MaxIntensity` terminology
8317/// verbatim, `:restart-window` → `:period` matching OTP's `Period` name,
8318/// `:children` → `:workers` matching the Elixir `Supervisor.child_spec`
8319/// idiom, or a per-consumer disambiguation as the `defcaixa` macro
8320/// stabilizes) would silently desynchronize the production
8321/// [`crate::Caixa::declared_supervisor_slots`] tagger from the tests
8322/// until a downstream consumer surfaced the drift at build time as a
8323/// matches-arm miss far from the rename's commit. This lift closes that
8324/// gap by routing both halves (production tagger + tests) through four
8325/// peer consts declared adjacent to the peer M2 / M3 top-level
8326/// author-key consts, so the "one canonical declaration per arm, next
8327/// to the axis" discipline the peer [`M2_AUTHOR_KEY_LIMITS`] /
8328/// [`M2_AUTHOR_KEY_BEHAVIOR`] / [`M2_AUTHOR_KEY_UPGRADE_FROM`] top-level
8329/// M2 slot consts (f49c8b0) and [`M3_AUTHOR_KEY_MEMBROS`] /
8330/// [`M3_AUTHOR_KEY_CONTRATOS`] / [`M3_AUTHOR_KEY_POLITICAS`] /
8331/// [`M3_AUTHOR_KEY_PLACEMENT`] / [`M3_AUTHOR_KEY_ENTRADA`] top-level
8332/// M3 slot consts (882f498) established for the sibling
8333/// per-Servico / per-Aplicacao top-level slot axes extends onto the
8334/// per-Supervisor supervision-tree slot axis, closing the last of the
8335/// three kind-scoped typed-slot-family author-facing-label axes.
8336///
8337/// Same "one canonical byte-string per typed axis" discipline every
8338/// peer M2 / M3 renderer-wire-key axis carries ([`M2_KEY_LIMITS`] /
8339/// [`M2_KEY_BEHAVIOR`] / [`M2_KEY_UPGRADE_FROM`],
8340/// [`M2_LIMITS_KEY_MEMORY`] / [`M2_LIMITS_KEY_FUEL`] /
8341/// [`M2_LIMITS_KEY_WALL_CLOCK`] / [`M2_LIMITS_KEY_CPU`] (d8b8b4f),
8342/// [`M2_BEHAVIOR_KEY_ON_INIT`] etc. (21fe462),
8343/// [`M2_UPGRADE_FROM_KEY_FROM`] / [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`]
8344/// (36ffe65), [`M3_PLACEMENT_KEY_ESTRATEGIA`] etc.).
8345pub const SUPERVISOR_AUTHOR_KEY_ESTRATEGIA: &str = ":estrategia";
8346/// Canonical author-facing kebab-case `(defcaixa … :max-restarts <n>)`
8347/// top-level supervisor-tree slot label the OTP `:kind Supervisor`
8348/// caixa's `MaxIntensity` restart-budget counter surfaces under. Peer of
8349/// [`SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] on the sibling supervision-tree
8350/// slot axis; see [`SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] for the full lift
8351/// rationale.
8352pub const SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS: &str = ":max-restarts";
8353/// Canonical author-facing kebab-case
8354/// `(defcaixa … :restart-window "<duration>")` top-level supervisor-tree
8355/// slot label the OTP `:kind Supervisor` caixa's `Period` rolling-window
8356/// counter surfaces under. Peer of [`SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`]
8357/// on the sibling supervision-tree slot axis; see
8358/// [`SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] for the full lift rationale.
8359pub const SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW: &str = ":restart-window";
8360/// Canonical author-facing kebab-case `(defcaixa … :children (…))`
8361/// top-level supervisor-tree slot label the OTP `:kind Supervisor`
8362/// caixa's static child-spec list ([`crate::supervisor::ChildSpec`])
8363/// surfaces under. Peer of [`SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] on the
8364/// sibling supervision-tree slot axis; see
8365/// [`SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] for the full lift rationale.
8366pub const SUPERVISOR_AUTHOR_KEY_CHILDREN: &str = ":children";
8367
8368/// Canonical author-facing kebab-case `(defcaixa … :deps ((…)))` top-
8369/// level dep-list slot label the two-list dependency-graph slot family
8370/// surfaces under. Peer of [`DEP_AUTHOR_KEY_DEPS_DEV`] on the two-list
8371/// dep-graph slot axis: `:deps` names the runtime-closure dep-list
8372/// (every `Cargo.toml [dependencies]` equivalent — reached by every
8373/// build the caixa participates in), the sibling `:deps-dev` names the
8374/// dev-only dep-list (every `Cargo.toml [dev-dependencies]` equivalent
8375/// — reached only by test / dev-shim builds).
8376///
8377/// Threaded verbatim as the `list: &'static str` field on both
8378/// [`crate::DepError::DuplicateNome`] (359fba5) and
8379/// [`crate::DepError::DepIsSelf`] so a `feira lint` diagnostic ("`:deps`
8380/// entry `caixa-teia` is duplicated" / "`:deps-dev` entry `dev-shim` is
8381/// a self-reference") self-locates the offending block in the author's
8382/// `caixa.lisp` without the linter re-deriving the list from context.
8383///
8384/// Until this lift landed the two kebab-case labels sat once each on
8385/// the [`crate::Caixa::validate_deps`] per-list duplicate walk (`list:
8386/// ":deps"` / `list: ":deps-dev"` in `manifest.rs`) and the paired
8387/// [`crate::dep::validate_no_self_dep`] per-list self-edge walk (`list:
8388/// ":deps"` / `list: ":deps-dev"` in `dep.rs`), plus a handful of
8389/// test-side probe literals asserting the `list:` field of a
8390/// `DepError::DuplicateNome` / `DepError::DepIsSelf` carries the
8391/// expected per-list value verbatim — with no compile-time link
8392/// between the two producers and the tests' expected values. A future
8393/// rebrand (a hypothetical `:deps` → `:dependencies` matching Cargo's
8394/// verbatim key, `:deps-dev` → `:dev-dependencies` matching the same,
8395/// `:deps` / `:deps-dev` → `:runtime-deps` / `:dev-deps` for
8396/// symmetry, or a per-consumer disambiguation as the `defcaixa` macro
8397/// stabilizes) would silently desynchronize the two producers from
8398/// each other and from the tests until a downstream consumer surfaced
8399/// the drift at build time as a matches-arm miss far from the
8400/// rename's commit. This lift closes that gap by routing all halves
8401/// (both production walkers + tests) through two peer consts declared
8402/// adjacent to the peer M2 / M3 / Supervisor top-level author-key
8403/// consts, so the "one canonical declaration per arm, next to the
8404/// axis" discipline the peer [`M2_AUTHOR_KEY_LIMITS`] /
8405/// [`M2_AUTHOR_KEY_BEHAVIOR`] / [`M2_AUTHOR_KEY_UPGRADE_FROM`]
8406/// (f49c8b0), [`M3_AUTHOR_KEY_MEMBROS`] / [`M3_AUTHOR_KEY_CONTRATOS`] /
8407/// [`M3_AUTHOR_KEY_POLITICAS`] / [`M3_AUTHOR_KEY_PLACEMENT`] /
8408/// [`M3_AUTHOR_KEY_ENTRADA`] (882f498), [`SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`]
8409/// etc. (be40492), and [`CONTRATO_AUTHOR_KEY_DE`] /
8410/// [`CONTRATO_AUTHOR_KEY_PARA`] (f50c875) top-level slot / per-entry
8411/// endpoint consts established for the sibling M2 / M3 / Supervisor
8412/// slot axes extends onto the two-list dep-graph slot axis.
8413///
8414/// Byte-identical to the peer [`CAIXA_KEY_DEPS`] renderer-side wire-key
8415/// serde-key axis modulo the leading `:` — the two consts split on the
8416/// axis every dep-graph slot carries (author-facing kebab-case label vs.
8417/// renderer-side wire key). Same "one canonical byte-string per typed
8418/// axis" discipline every peer M2 / M3 renderer-wire-key axis carries.
8419pub const DEP_AUTHOR_KEY_DEPS: &str = ":deps";
8420
8421/// Canonical author-facing kebab-case `(defcaixa … :deps-dev ((…)))`
8422/// top-level dep-list slot label the dev-only two-list dependency-graph
8423/// slot family surfaces under. Peer of [`DEP_AUTHOR_KEY_DEPS`] on the
8424/// two-list dep-graph slot axis; see [`DEP_AUTHOR_KEY_DEPS`] for the
8425/// full lift rationale.
8426pub const DEP_AUTHOR_KEY_DEPS_DEV: &str = ":deps-dev";
8427
8428/// Canonical camelCase JSON/YAML top-level key for
8429/// [`crate::supervisor::SupervisorSpec`]'s `estrategia` restart-strategy
8430/// discriminator — the exact byte-sequence the type's
8431/// `#[serde(rename_all = "camelCase")]` derive emits, and the scalar every
8432/// downstream JSON/YAML consumer that reaches into a serialized
8433/// `SupervisorSpec` (via `Value::get(...)`) must probe on.
8434///
8435/// The scalar is derived from the Rust field name `estrategia` by the
8436/// `rename_all = "camelCase"` derive; `estrategia` has no `_`, so the
8437/// serde transform is a no-op on this axis and the emitted key equals the
8438/// source-side field name byte-for-byte. Lifting the byte to one
8439/// `&'static str` closes the drift footgun structurally: a future
8440/// refactor renaming the Rust field OR retaining the field name while
8441/// adding a `#[serde(rename = "…")]` override would silently emit a
8442/// `SupervisorSpec` whose restart-strategy discriminator lands under one
8443/// key while every downstream consumer still probes another — the
8444/// future wasm-operator's supervisor reconcile posture, the M4
8445/// `caixa.pleme.io/v1alpha1/Supervisor` CR materializer's admission
8446/// webhook, the future `feira lint` supervisor-tree cross-check. The
8447/// identity pin (`supervisor_spec_serde_keys_match_lifted_supervisor_key_consts`
8448/// on the source-side type) catches drift at caixa-core build time
8449/// rather than at the reconciler's dispatch step, far from the rebrand
8450/// commit's source.
8451///
8452/// Peer of the sibling author-facing
8453/// [`SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] (`":estrategia"`) on the same
8454/// per-Supervisor supervision-tree slot axis — that constant names the
8455/// kebab-case `(defcaixa … :estrategia …)` author surface's top-level
8456/// slot label, this one names the camelCase JSON/YAML sub-key the
8457/// serialized `SupervisorSpec` carries the same axis under. Byte-distinct
8458/// from (though semantically related to) the peer
8459/// [`M3_PLACEMENT_KEY_ESTRATEGIA`] (also `"estrategia"`) on the M3
8460/// [`crate::aplicacao::Placement`] axis — that axis carries
8461/// [`crate::aplicacao::PlacementStrategy`] cross-cluster distribution
8462/// semantics, this axis carries [`crate::supervisor::RestartStrategy`]
8463/// OTP supervisor semantics; splitting the two lets each schema's
8464/// future rebrand land independently on the same
8465/// "byte-identical-but-semantically-distinct" discipline the peer
8466/// [`FLEET_PROGRAMS_KEY_NAME`] / [`KUBE_KEY_NAME`] split established.
8467///
8468/// Same "one canonical byte-string per typed serialized-key axis"
8469/// discipline every peer camelCase serde-key lift carries
8470/// ([`M2_LIMITS_KEY_MEMORY`] / [`M2_LIMITS_KEY_FUEL`] /
8471/// [`M2_LIMITS_KEY_WALL_CLOCK`] / [`M2_LIMITS_KEY_CPU`] (d8b8b4f),
8472/// [`M2_BEHAVIOR_KEY_ON_INIT`] etc. (21fe462),
8473/// [`M2_UPGRADE_FROM_KEY_FROM`] / [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`]
8474/// (36ffe65), [`M3_PLACEMENT_KEY_ESTRATEGIA`] etc.) — extended here to
8475/// close the last of the four top-level typed-struct
8476/// `#[serde(rename_all = "camelCase")]` axes lacking a lifted peer.
8477pub const SUPERVISOR_KEY_ESTRATEGIA: &str = "estrategia";
8478
8479/// Canonical camelCase JSON/YAML top-level key for
8480/// [`crate::supervisor::SupervisorSpec`]'s `max_restarts` axis. Peer of
8481/// [`SUPERVISOR_KEY_ESTRATEGIA`] on the same sibling
8482/// supervision-tree serialized-key axis; see [`SUPERVISOR_KEY_ESTRATEGIA`]
8483/// for the full lift rationale. The Rust field is `snake_case`
8484/// `max_restarts`; `#[serde(rename_all = "camelCase")]` maps it to the
8485/// camelCase JSON key `"maxRestarts"` this constant pins.
8486pub const SUPERVISOR_KEY_MAX_RESTARTS: &str = "maxRestarts";
8487
8488/// Canonical camelCase JSON/YAML top-level key for
8489/// [`crate::supervisor::SupervisorSpec`]'s `restart_window` axis. Peer of
8490/// [`SUPERVISOR_KEY_ESTRATEGIA`] on the same sibling
8491/// supervision-tree serialized-key axis; see [`SUPERVISOR_KEY_ESTRATEGIA`]
8492/// for the full lift rationale. The Rust field is `snake_case`
8493/// `restart_window`; `#[serde(rename_all = "camelCase")]` maps it to the
8494/// camelCase JSON key `"restartWindow"` this constant pins.
8495pub const SUPERVISOR_KEY_RESTART_WINDOW: &str = "restartWindow";
8496
8497/// Canonical camelCase JSON/YAML top-level key for
8498/// [`crate::supervisor::SupervisorSpec`]'s `children` axis. Peer of
8499/// [`SUPERVISOR_KEY_ESTRATEGIA`] on the same sibling
8500/// supervision-tree serialized-key axis; see [`SUPERVISOR_KEY_ESTRATEGIA`]
8501/// for the full lift rationale. The Rust field is lowercase `children`;
8502/// `#[serde(rename_all = "camelCase")]` is a no-op on this axis and the
8503/// emitted key equals the source-side field name byte-for-byte.
8504pub const SUPERVISOR_KEY_CHILDREN: &str = "children";
8505
8506/// Canonical camelCase JSON/YAML top-level key for the
8507/// [`crate::supervisor::ChildSpec`] struct's `caixa` per-entry-name-of-
8508/// the-child-caixa axis — the `caixa:` field the M2 Supervisor's
8509/// `#[serde(rename_all = "camelCase")]` derive on
8510/// [`crate::supervisor::ChildSpec`] emits at each entry of the
8511/// [`crate::supervisor::SupervisorSpec::children`] list, and the exact
8512/// scalar every downstream consumer reaching for the child caixa's
8513/// [`crate::Caixa::nome`] via `Value::get(...)` (the future wasm-operator's
8514/// per-supervisor-tree child resolver, the M4
8515/// `caixa.pleme.io/v1alpha1/Supervisor` CR materializer's admission
8516/// webhook per-child cross-check, the future `feira` supervisor-tree
8517/// walker's per-child name-lookup, the [`caixa_resolver`] per-child
8518/// git-clone step) must probe on.
8519///
8520/// The scalar is derived from the Rust field name `caixa` by the
8521/// `rename_all = "camelCase"` derive; `caixa` has no `_`, so the serde
8522/// transform is a no-op on this axis and the emitted key equals the
8523/// source-side field name byte-for-byte. Lifting the byte to one
8524/// `&'static str` closes the drift footgun structurally: a future
8525/// refactor renaming the Rust field OR retaining the field name while
8526/// adding a `#[serde(rename = "…")]` override would silently emit a
8527/// `ChildSpec` whose per-entry child-caixa discriminator lands under
8528/// one key while every downstream consumer still probes another. The
8529/// identity pin (`child_spec_serde_keys_match_lifted_supervisor_child_key_consts`
8530/// on the source-side type) catches drift at caixa-core build time
8531/// rather than at the reconciler's dispatch step, far from the rebrand
8532/// commit's source.
8533///
8534/// Peer of [`SUPERVISOR_CHILD_KEY_VERSAO`] / [`SUPERVISOR_CHILD_KEY_RESTART`]
8535/// on the same [`crate::supervisor::ChildSpec`] per-entry serialized-key
8536/// axis. Peer of the sibling [`SUPERVISOR_KEY_ESTRATEGIA`] /
8537/// [`SUPERVISOR_KEY_MAX_RESTARTS`] / [`SUPERVISOR_KEY_RESTART_WINDOW`] /
8538/// [`SUPERVISOR_KEY_CHILDREN`] tetrad (40cc4e5) on the enclosing
8539/// [`crate::supervisor::SupervisorSpec`] top-level serialized-key axis
8540/// — that lift pinned the four camelCase JSON keys the M2
8541/// supervision-tree top-level derive emits, this lift extends the same
8542/// discipline onto the sibling per-entry `ChildSpec` derive so the last
8543/// M2 typed-struct sub-block `#[serde(rename_all = "camelCase")]` axis
8544/// on the Supervisor surface without a lifted serde-key peer joins the
8545/// substrate's "one canonical byte-string per typed serialized-key axis"
8546/// discipline.
8547///
8548/// Byte-identical to (but semantically distinct from) the peer
8549/// [`MEMBRO_KEY_CAIXA`] (ce80ca0) on the sibling M3
8550/// [`crate::aplicacao::Membro`] per-`:membros` entry axis — both axes
8551/// carry per-entry caixa-name discriminators on typed list slots, but
8552/// splitting the two lets each schema's future rebrand land
8553/// independently at its canonical const definition without coupling
8554/// the M2 Supervisor per-child axis to the M3 Aplicacao per-member axis
8555/// (or vice versa) — same "byte-identical-but-semantically-distinct"
8556/// discipline the peer [`FLEET_PROGRAMS_KEY_NAME`] / [`KUBE_KEY_NAME`]
8557/// split established.
8558///
8559/// Same "one canonical byte-string per typed serialized-key axis"
8560/// discipline every peer camelCase serde-key lift carries
8561/// ([`M2_LIMITS_KEY_MEMORY`] etc. (d8b8b4f), [`M2_BEHAVIOR_KEY_ON_INIT`]
8562/// etc. (21fe462), [`M2_UPGRADE_FROM_KEY_FROM`] /
8563/// [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`] (36ffe65),
8564/// [`M3_PLACEMENT_KEY_ESTRATEGIA`] etc., [`SUPERVISOR_KEY_ESTRATEGIA`]
8565/// etc. (40cc4e5), [`MEMBRO_KEY_CAIXA`] / [`MEMBRO_KEY_VERSAO`]
8566/// (ce80ca0), [`CONTRATO_KEY_DE`] / [`CONTRATO_KEY_PARA`] /
8567/// [`CONTRATO_KEY_WIT`] (ca463a4), [`ENTRADA_KEY_HOST`] etc. (a3d6162),
8568/// [`POLITICAS_KEY_TIMEOUT`] etc. (b55cca7), [`CIRCUIT_BREAKER_KEY_MAX_FAILURES`]
8569/// / [`CIRCUIT_BREAKER_KEY_WINDOW`] (468e959)) — extended here to the
8570/// last M2 typed-struct sub-block `#[serde(rename_all = "camelCase")]`
8571/// axis on the Supervisor surface, the per-`:children` entry
8572/// [`crate::supervisor::ChildSpec`] derive.
8573pub const SUPERVISOR_CHILD_KEY_CAIXA: &str = "caixa";
8574
8575/// Canonical camelCase JSON/YAML top-level key for the
8576/// [`crate::supervisor::ChildSpec`] struct's `versao` per-entry-semver-
8577/// constraint-of-the-child axis. Peer of [`SUPERVISOR_CHILD_KEY_CAIXA`]
8578/// on the same [`crate::supervisor::ChildSpec`] per-entry serialized-key
8579/// axis; see [`SUPERVISOR_CHILD_KEY_CAIXA`] for the full lift rationale.
8580/// The Rust field is lowercase `versao`; `#[serde(rename_all = "camelCase")]`
8581/// is a no-op on this axis and the emitted key equals the source-side
8582/// field name byte-for-byte.
8583///
8584/// Byte-identical to (but semantically distinct from) the peer
8585/// [`MEMBRO_KEY_VERSAO`] (ce80ca0) on the sibling M3
8586/// [`crate::aplicacao::Membro`] per-`:membros` entry axis and the peer
8587/// [`FLEET_PROGRAMS_KEY_VERSAO`] on the `lareira-fleet-programs`
8588/// library-chart values-schema axis — same
8589/// "byte-identical-but-semantically-distinct" discipline the peer
8590/// [`FLEET_PROGRAMS_KEY_NAME`] / [`KUBE_KEY_NAME`] /
8591/// [`MEMBRO_KEY_CAIXA`] / [`SUPERVISOR_CHILD_KEY_CAIXA`] splits
8592/// established: each schema's future rebrand lands independently at its
8593/// canonical const definition without coupling one axis to the others.
8594pub const SUPERVISOR_CHILD_KEY_VERSAO: &str = "versao";
8595
8596/// Canonical camelCase JSON/YAML top-level key for the
8597/// [`crate::supervisor::ChildSpec`] struct's `restart` per-entry
8598/// [`crate::supervisor::RestartPolicy`] discriminator axis. Peer of
8599/// [`SUPERVISOR_CHILD_KEY_CAIXA`] on the same
8600/// [`crate::supervisor::ChildSpec`] per-entry serialized-key axis; see
8601/// [`SUPERVISOR_CHILD_KEY_CAIXA`] for the full lift rationale. The Rust
8602/// field is lowercase `restart`; `#[serde(rename_all = "camelCase")]`
8603/// is a no-op on this axis and the emitted key equals the source-side
8604/// field name byte-for-byte.
8605pub const SUPERVISOR_CHILD_KEY_RESTART: &str = "restart";
8606
8607/// Canonical camelCase JSON/YAML top-level key for the
8608/// [`crate::aplicacao::Membro`] struct's `caixa` per-entry-name-of-the-
8609/// member-Servico axis — the `caixa:` field the M3 Aplicacao's
8610/// `#[serde(rename_all = "camelCase")]` derive on [`crate::aplicacao::Membro`]
8611/// emits at each `:membros` entry, and the exact scalar every downstream
8612/// `#[serde(rename_all = "camelCase")]` derive on [`crate::aplicacao::Membro`]
8613/// emits at each `:membros` entry, and the exact scalar every downstream
8614/// consumer reaching for the member's [`crate::Caixa::nome`] via
8615/// `Value::get(...)` (the future wasm-operator's per-`:membros` resolver,
8616/// the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's admission
8617/// webhook, the `feira app graph` verb's per-member name-lookup, the
8618/// [`caixa_resolver`] per-`:membros` git-clone step) must probe on.
8619///
8620/// The scalar is derived from the Rust field name `caixa` by the
8621/// `rename_all = "camelCase"` derive; `caixa` has no `_`, so the
8622/// serde transform is a no-op on this axis and the emitted key equals
8623/// the source-side field name byte-for-byte. Lifting the byte to one
8624/// `&'static str` closes the drift footgun structurally: a future
8625/// refactor renaming the Rust field OR retaining the field name while
8626/// adding a `#[serde(rename = "…")]` override would silently emit a
8627/// `Membro` whose per-entry name discriminator lands under one key while
8628/// every downstream consumer still probes another — the future wasm-
8629/// operator's per-`:membros` resolver, the M4 CR materializer's admission
8630/// webhook, the `feira app graph` verb's per-member name-lookup. The
8631/// identity pin (`membro_serde_keys_match_lifted_membro_key_consts` on
8632/// the source-side type) catches drift at caixa-core build time rather
8633/// than at the reconciler's dispatch step, far from the rebrand commit's
8634/// source.
8635///
8636/// Peer of [`MEMBRO_KEY_VERSAO`] on the same [`crate::aplicacao::Membro`]
8637/// per-entry serialized-key axis. Peer of the sibling
8638/// [`SUPERVISOR_KEY_ESTRATEGIA`] / [`SUPERVISOR_KEY_MAX_RESTARTS`] /
8639/// [`SUPERVISOR_KEY_RESTART_WINDOW`] / [`SUPERVISOR_KEY_CHILDREN`] tetrad
8640/// (40cc4e5) on the sibling `SupervisorSpec` top-level serialized-key
8641/// axis — that lift pinned the four camelCase JSON keys the M2
8642/// supervision-tree top-level derive emits, this lift extends the same
8643/// discipline onto the M3 Aplicacao's per-`:membros` entry derive.
8644///
8645/// Same "one canonical byte-string per typed serialized-key axis"
8646/// discipline every peer camelCase serde-key lift carries
8647/// ([`M2_LIMITS_KEY_MEMORY`] etc. (d8b8b4f), [`M2_BEHAVIOR_KEY_ON_INIT`]
8648/// etc. (21fe462), [`M2_UPGRADE_FROM_KEY_FROM`] /
8649/// [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`] (36ffe65),
8650/// [`M3_PLACEMENT_KEY_ESTRATEGIA`] etc., [`SUPERVISOR_KEY_ESTRATEGIA`]
8651/// etc. (40cc4e5)) — extended here to the M3 [`crate::aplicacao::Membro`]
8652/// per-entry axis, the last top-level typed-struct
8653/// `#[serde(rename_all = "camelCase")]` axis on the M3 mesh-slot family
8654/// lacking a lifted peer.
8655pub const MEMBRO_KEY_CAIXA: &str = "caixa";
8656
8657/// Canonical camelCase JSON/YAML top-level key for the
8658/// [`crate::aplicacao::Membro`] struct's `versao` per-entry-semver-
8659/// constraint-of-the-member axis. Peer of [`MEMBRO_KEY_CAIXA`] on the
8660/// same [`crate::aplicacao::Membro`] per-entry serialized-key axis; see
8661/// [`MEMBRO_KEY_CAIXA`] for the full lift rationale. The Rust field is
8662/// lowercase `versao`; `#[serde(rename_all = "camelCase")]` is a no-op
8663/// on this axis and the emitted key equals the source-side field name
8664/// byte-for-byte.
8665///
8666/// Byte-identical to [`FLEET_PROGRAMS_KEY_VERSAO`] today — both resolve
8667/// to the same six-byte `"versao"` literal — but semantically distinct:
8668/// [`FLEET_PROGRAMS_KEY_VERSAO`] names the `lareira-fleet-programs`
8669/// library chart's per-entry version-constraint schema-axis (spelled
8670/// per the chart's `values.schema.json` — the same schema surface
8671/// [`caixa_mesh::programs_for_aplicacao`] transcribes each `:membros`
8672/// entry's version constraint into), while this constant names the
8673/// [`crate::aplicacao::Membro`] typed struct's derive-emitted `versao`
8674/// field key (spelled per the type's `#[serde(rename_all = "camelCase")]`
8675/// attribute — a separate schema contract on the upstream typed
8676/// manifest). Splitting the two lets each schema's future rebrand land
8677/// independently at its canonical const definition without coupling the
8678/// Membro typed-struct axis to the fleet-programs values-schema axis
8679/// (or vice versa) — same "byte-identical-but-semantically-distinct"
8680/// discipline the peer [`FLEET_PROGRAMS_KEY_NAME`] / [`KUBE_KEY_NAME`]
8681/// split established on the sibling per-entry name-discriminator axis.
8682pub const MEMBRO_KEY_VERSAO: &str = "versao";
8683
8684/// Canonical camelCase JSON/YAML top-level key for the
8685/// [`crate::aplicacao::WitContract`] struct's `de` per-entry
8686/// source-endpoint-of-the-contract axis — the `de:` field the M3
8687/// Aplicacao's `#[serde(rename_all = "camelCase")]` derive on
8688/// [`crate::aplicacao::WitContract`] emits at each `:contratos` entry,
8689/// and the exact scalar every downstream consumer reaching for the
8690/// caller-Servico name via `Value::get(...)` (the future
8691/// wasm-operator's per-`:contratos` edge resolver, the M4
8692/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's admission
8693/// webhook per-edge cross-check, the `feira app graph` verb's per-edge
8694/// tail-label lookup, the future per-`:contratos` `CiliumNetworkPolicy`
8695/// emitter's per-edge `fromEndpoints` selector projection) must probe on.
8696///
8697/// The scalar is derived from the Rust field name `de` by the
8698/// `rename_all = "camelCase"` derive; `de` has no `_`, so the serde
8699/// transform is a no-op on this axis and the emitted key equals the
8700/// source-side field name byte-for-byte. Lifting the byte to one
8701/// `&'static str` closes the drift footgun structurally: a future
8702/// refactor renaming the Rust field OR retaining the field name while
8703/// adding a `#[serde(rename = "…")]` override would silently emit a
8704/// `WitContract` whose per-entry caller-Servico discriminator lands
8705/// under one key while every downstream consumer still probes another —
8706/// the future wasm-operator's per-`:contratos` edge resolver, the M4 CR
8707/// materializer's admission webhook per-edge cross-check, the
8708/// `feira app graph` verb's per-edge tail-label lookup. The identity pin
8709/// (`wit_contract_serde_keys_match_lifted_contrato_key_consts` on the
8710/// source-side type) catches drift at caixa-core build time rather than
8711/// at the reconciler's dispatch step, far from the rebrand commit's
8712/// source.
8713///
8714/// Peer of [`CONTRATO_KEY_PARA`] / [`CONTRATO_KEY_WIT`] on the same
8715/// [`crate::aplicacao::WitContract`] per-entry serialized-key axis. Peer
8716/// of the sibling [`MEMBRO_KEY_CAIXA`] / [`MEMBRO_KEY_VERSAO`] pair
8717/// (ce80ca0) on the sibling M3 [`crate::aplicacao::Membro`] per-entry
8718/// serialized-key axis — that lift pinned the two camelCase JSON keys
8719/// the M3 per-`:membros` derive emits, this lift extends the same
8720/// discipline onto the sibling M3 per-`:contratos` derive so the last
8721/// M3 mesh-slot atom top-level `#[serde(rename_all = "camelCase")]`
8722/// axis on the Aplicacao surface without a lifted peer joins the
8723/// substrate's "one canonical byte-string per typed serialized-key
8724/// axis" discipline.
8725///
8726/// Byte-identical to (but semantically distinct from) the sibling
8727/// author-facing kebab-case [`CONTRATO_AUTHOR_KEY_DE`] (f50c875) modulo
8728/// the leading `:` — the two consts split on the axis every M3 mesh-slot
8729/// atom carries (author-facing kebab-case label vs. renderer-side
8730/// camelCase overlay key), the same split the [`M2_AUTHOR_KEY_LIMITS`] /
8731/// [`M2_KEY_LIMITS`] peer pair established on the sibling M2 axis and
8732/// the [`M3_AUTHOR_KEY_PLACEMENT`] / [`M3_KEY_PLACEMENT`] peer pair
8733/// established on the sibling M3 top-level slot axis.
8734pub const CONTRATO_KEY_DE: &str = "de";
8735
8736/// Canonical camelCase JSON/YAML top-level key for the
8737/// [`crate::aplicacao::WitContract`] struct's `para` per-entry
8738/// target-endpoint-of-the-contract axis. Peer of [`CONTRATO_KEY_DE`] on
8739/// the same [`crate::aplicacao::WitContract`] per-entry serialized-key
8740/// axis; see [`CONTRATO_KEY_DE`] for the full lift rationale. The Rust
8741/// field is lowercase `para`; `#[serde(rename_all = "camelCase")]` is a
8742/// no-op on this axis and the emitted key equals the source-side field
8743/// name byte-for-byte.
8744pub const CONTRATO_KEY_PARA: &str = "para";
8745
8746/// Canonical camelCase JSON/YAML top-level key for the
8747/// [`crate::aplicacao::WitContract`] struct's `wit` per-entry
8748/// WIT-world-reference-of-the-contract axis — the discriminator every
8749/// downstream WIT-shape dispatcher ([`crate::wit_shape_is_http`] /
8750/// [`crate::wit_shape_is_pubsub`] / [`crate::wit_shape_is_store`], the
8751/// future M4 per-edge WIT registry resolver, the future
8752/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's admission-time
8753/// WIT-world classification) keys off. Peer of [`CONTRATO_KEY_DE`] on
8754/// the same [`crate::aplicacao::WitContract`] per-entry serialized-key
8755/// axis; see [`CONTRATO_KEY_DE`] for the full lift rationale. The Rust
8756/// field is lowercase `wit`; `#[serde(rename_all = "camelCase")]` is a
8757/// no-op on this axis and the emitted key equals the source-side field
8758/// name byte-for-byte.
8759pub const CONTRATO_KEY_WIT: &str = "wit";
8760
8761/// Canonical camelCase YAML sub-key for the [`crate::aplicacao::Placement`]
8762/// struct's `estrategia` distribution-strategy discriminator — the
8763/// per-`M3_KEY_PLACEMENT`-block field the M3 [`crate::aplicacao::PlacementStrategy`]
8764/// enum's `Serialize` derive emits, and the exact scalar every downstream
8765/// consumer dispatches on:
8766///
8767/// - the `lareira-fleet-programs` aggregator's per-entry strategy dispatch
8768/// (each `programs[].placement.estrategia` reads `"SingleNode"` /
8769/// `"Replicated"` / `"Sharded"` verbatim to select the takeover
8770/// semantics per MESH-COMPOSITION.md §II.1),
8771/// - the future `app-operator` reconciler's per-Aplicacao strategy branch,
8772/// - the future `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
8773/// admission-time `spec.placement.estrategia` typed-enum bind,
8774/// - and every M3 Adaptive weighting the compression pass reads off
8775/// `placement.estrategia` per MESH-COMPOSITION.md §V.
8776///
8777/// The scalar is derived by [`crate::aplicacao::Placement`]'s
8778/// `#[serde(rename_all = "camelCase")]` from the Rust field name
8779/// `estrategia`; `estrategia` has no `_`, so the serde transform is a
8780/// no-op on this axis and the emitted key equals the source-side field
8781/// name byte-for-byte. Lifting the byte to one `&'static str` closes
8782/// the drift footgun structurally: a future refactor renaming the Rust
8783/// field (`estrategia` → `strategy` for English-uniformity, `distribution`
8784/// for schema-clarity, etc.) OR retaining the field name while adding
8785/// a `#[serde(rename = "…")]` override would silently emit a
8786/// `placement:` block whose distribution-strategy discriminator lands
8787/// under one key while every downstream consumer still probes another —
8788/// the aggregator's dispatch, the operator's reconcile, the CR
8789/// materializer's admission bind would each silently no-op, and the
8790/// workload would silently come up under the strategy's serde-derived
8791/// default rather than the per-Aplicacao override the typed slot set.
8792/// The identity pin + serde round-trip pin the sweep introduces catch
8793/// the drift at caixa-core / caixa-mesh build time rather than at the
8794/// aggregator's filter step or the operator's reconcile posture, far
8795/// from the rebrand commit's source.
8796///
8797/// Peer of [`M3_KEY_PLACEMENT`] on the same programs.yaml per-entry
8798/// axis — that constant names the top-level overlay key the entry
8799/// carries, this one names the per-`placement:` sub-block strategy
8800/// discriminator every consumer dispatches on. Byte-identical to (but
8801/// semantically distinct from) [`crate::supervisor::SupervisorSpec`]'s
8802/// peer `estrategia` field on the M2 supervisor-strategy axis — that
8803/// axis carries [`crate::supervisor::RestartStrategy`] (`OneForOne` /
8804/// `OneForAll` / `RestForOne` / `SimpleOneForOne`, OTP supervisor
8805/// semantics) while this axis carries [`crate::aplicacao::PlacementStrategy`]
8806/// (`SingleNode` / `Replicated` / `Sharded`, cross-cluster distribution
8807/// semantics); splitting the two lets each schema's future rebrand
8808/// land independently on the same byte-identical-but-semantically-
8809/// distinct discipline the [`FLEET_PROGRAMS_KEY_NAME`] / [`KUBE_KEY_NAME`]
8810/// split established.
8811pub const M3_PLACEMENT_KEY_ESTRATEGIA: &str = "estrategia";
8812
8813/// Canonical camelCase YAML sub-key for the [`crate::aplicacao::Placement`]
8814/// struct's `clusters` cluster-pool axis — the per-`M3_KEY_PLACEMENT`-block
8815/// field carrying the validated cluster-list (non-empty + duplicate-free
8816/// per [`crate::aplicacao::AplicacaoSpec::validate_placement`]) that every
8817/// downstream cross-cluster consumer filters off:
8818///
8819/// - the `lareira-fleet-programs` aggregator's per-cluster fanout filter
8820/// (each cluster's aggregator scopes `.Values.programs` by
8821/// `.placement.clusters | contains .Values.cluster`, so a workload's
8822/// `clusters: [rio, mar]` list ends up landing on rio + mar and no other
8823/// cluster per MESH-COMPOSITION.md §III.4),
8824/// - the future `app-operator` reconciler's per-Aplicacao cluster-set
8825/// dispatch,
8826/// - the future `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
8827/// admission-time `spec.placement.clusters` typed-list bind, and
8828/// - the M3 Adaptive compression pass's per-cluster weight lookup per
8829/// MESH-COMPOSITION.md §V.
8830///
8831/// The scalar is derived by [`crate::aplicacao::Placement`]'s
8832/// `#[serde(rename_all = "camelCase")]` from the Rust field name
8833/// `clusters`; `clusters` has no `_`, so the serde transform is a no-op
8834/// on this axis and the emitted key equals the source-side field name
8835/// byte-for-byte. Lifting the byte to one `&'static str` closes the same
8836/// drift footgun the peer [`M3_PLACEMENT_KEY_ESTRATEGIA`] lift closed on
8837/// the sibling distribution-strategy discriminator: a future refactor
8838/// renaming the Rust field (`clusters` → `clusterPool` for schema-clarity,
8839/// `sites` for eventual multi-substrate reach, etc.) OR retaining the
8840/// field name while adding a `#[serde(rename = "…")]` override would
8841/// silently emit a `placement:` block whose cluster-list lands under one
8842/// key while every downstream consumer still probes another — the
8843/// aggregator's per-cluster fanout filter would then see an empty
8844/// `clusters` list on every entry and silently drop every workload from
8845/// every cluster (the failure surfacing as "the newly-deployed Aplicacao
8846/// never spins up anywhere" far from the rebrand commit's source). The
8847/// identity pin + serde-derive round-trip pin the sweep introduces catch
8848/// the drift at caixa-core / caixa-mesh build time rather than at the
8849/// aggregator's fanout step or the operator's reconcile posture.
8850///
8851/// Peer of [`M3_KEY_PLACEMENT`] / [`M3_PLACEMENT_KEY_ESTRATEGIA`] on the
8852/// same programs.yaml per-entry axis — `M3_KEY_PLACEMENT` names the
8853/// top-level overlay key each entry carries, `M3_PLACEMENT_KEY_ESTRATEGIA`
8854/// names the per-sub-block distribution-strategy discriminator every
8855/// dispatch consumer branches on, this constant names the per-sub-block
8856/// cluster-pool list every per-cluster fanout consumer scopes by.
8857pub const M3_PLACEMENT_KEY_CLUSTERS: &str = "clusters";
8858
8859/// Canonical camelCase YAML sub-key for the [`crate::aplicacao::Placement`]
8860/// struct's `affinity` placement-engine-hint axis — the per-`M3_KEY_PLACEMENT`-
8861/// block optional field carrying the validated non-empty affinity hint
8862/// (per [`crate::aplicacao::AplicacaoSpec::validate_placement`]) that every
8863/// downstream placement-hint consumer weights off:
8864///
8865/// - the `lareira-fleet-programs` aggregator's per-entry M3 Adaptive
8866/// compression pass reading `placement.affinity` to weight the emitted
8867/// `ComputeUnit`'s replica-distribution overlay per MESH-COMPOSITION.md §V,
8868/// - the future `app-operator` reconciler's per-Aplicacao pod-affinity /
8869/// node-affinity K8s-primitive materializer keying off the same value as
8870/// an `app.pleme.io/affinity-hint=<value>` label selector,
8871/// - the future `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
8872/// admission-time `spec.placement.affinity` typed-string bind, and
8873/// - the M4 cross-cluster placement engine's per-hint takeover-priority
8874/// dispatch on the same value (`data-locality` / `low-latency` /
8875/// `anti-affinity` per the [`crate::aplicacao::validate_placement_affinity`]
8876/// value-shape gate's documented canonical hint set).
8877///
8878/// The scalar is derived by [`crate::aplicacao::Placement`]'s
8879/// `#[serde(rename_all = "camelCase")]` from the Rust field name
8880/// `affinity`; `affinity` has no `_`, so the serde transform is a no-op on
8881/// this axis and the emitted key equals the source-side field name
8882/// byte-for-byte. Unlike the always-emitted [`M3_PLACEMENT_KEY_ESTRATEGIA`]
8883/// / [`M3_PLACEMENT_KEY_CLUSTERS`] axes, the `affinity` field carries a
8884/// `#[serde(skip_serializing_if = "Option::is_none")]` attribute so the
8885/// key appears in the rendered `placement:` block iff the typed slot
8886/// resolves to `Some(_)` — the omit-when-unset contract the peer typed
8887/// slots ([`crate::aplicacao::MeshPolicy::timeout`],
8888/// [`crate::aplicacao::MeshPolicy::retries`],
8889/// [`crate::aplicacao::MeshPolicy::mtls_required`]) each carry to keep an
8890/// unset typed slot from bloating every rendered programs.yaml entry with
8891/// a nominal-only `affinity: null` value the downstream weighting passes
8892/// would then need to unwrap defensively.
8893///
8894/// Lifting the byte to one `&'static str` closes the same drift footgun
8895/// the peer [`M3_PLACEMENT_KEY_ESTRATEGIA`] / [`M3_PLACEMENT_KEY_CLUSTERS`]
8896/// lifts closed on the sibling always-emitted axes: a future refactor
8897/// renaming the Rust field (`affinity` → `affinityHint` for schema-clarity,
8898/// `placementHint` for symmetry with the future per-cluster affinity
8899/// hierarchy, etc.) OR retaining the field name while adding a
8900/// `#[serde(rename = "…")]` override would silently emit a `placement:`
8901/// block whose affinity hint lands under one key while every downstream
8902/// weighting consumer still probes another — the M3 Adaptive compression
8903/// pass would then see a `None` affinity on every entry and silently fall
8904/// back to the uniform-weight baseline (the workload's typed
8905/// `:affinity "data-locality"` hint would be silently discarded, and the
8906/// failure surfaces as "the newly-deployed Aplicacao's replicas don't
8907/// cluster where the typed slot said they should" far from the rebrand
8908/// commit's source). The identity pin + serde-derive round-trip pin the
8909/// sweep introduces catch the drift at caixa-core / caixa-mesh build time
8910/// rather than at the aggregator's weighting step or the operator's
8911/// reconcile posture.
8912///
8913/// Peer of [`M3_KEY_PLACEMENT`] / [`M3_PLACEMENT_KEY_ESTRATEGIA`] /
8914/// [`M3_PLACEMENT_KEY_CLUSTERS`] on the same programs.yaml per-entry
8915/// axis — `M3_KEY_PLACEMENT` names the top-level overlay key each entry
8916/// carries, `M3_PLACEMENT_KEY_ESTRATEGIA` names the per-sub-block
8917/// distribution-strategy discriminator every dispatch consumer branches
8918/// on, `M3_PLACEMENT_KEY_CLUSTERS` names the per-sub-block cluster-pool
8919/// list every per-cluster fanout consumer scopes by, this constant names
8920/// the per-sub-block optional placement-engine hint every weighting
8921/// consumer reads off.
8922pub const M3_PLACEMENT_KEY_AFFINITY: &str = "affinity";
8923
8924/// Canonical camelCase YAML sub-key for the [`crate::aplicacao::Placement`]
8925/// struct's `shard_key` shard-selection-template axis — the per-`M3_KEY_PLACEMENT`-
8926/// block optional field carrying the validated non-empty shard-key
8927/// template (per [`crate::aplicacao::AplicacaoSpec::validate_placement`]'s
8928/// `ShardedKeyEmpty` arm — the build rejects any `:placement Sharded`
8929/// that omits the slot, and rejects any non-Sharded strategy that
8930/// carries the slot as `ShardKeyOnNonSharded`) that every downstream
8931/// shard-dispatch consumer materializes off:
8932///
8933/// - the `lareira-fleet-programs` aggregator's per-entry M3 shard-pool
8934/// dispatch materializer keying off `placement.shardKey` to hash each
8935/// incoming entity into the per-cluster shard pool the Akka-style
8936/// cluster-sharding reconciler owns (per MESH-COMPOSITION.md §II.4);
8937/// - the future `app-operator` reconciler's per-Aplicacao
8938/// `ShardedResource` CR emitter binding the typed template to the
8939/// K8s-primitive shard-assignment controller's `spec.hashKey`;
8940/// - the future `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
8941/// admission-time `spec.placement.shardKey` typed-string bind, and
8942/// - the M4 Orleans-style virtual-actor runtime's per-grain
8943/// placement dispatch reading the same value as the grain-identity
8944/// hash source (per RUNTIME-PATTERNS.md's virtual-actor pattern
8945/// entry).
8946///
8947/// The scalar is derived by [`crate::aplicacao::Placement`]'s
8948/// `#[serde(rename_all = "camelCase")]` from the Rust field name
8949/// `shard_key`; unlike the peer `affinity` / `clusters` / `estrategia`
8950/// axes (whose field names carry no `_`, so the serde transform is a
8951/// no-op), the `shard_key` field's `snake_case` name is actively
8952/// transformed by the derive to `shardKey` — the emitted key differs
8953/// from the source-side field name and the drift-footgun surface is
8954/// therefore correspondingly larger. Unlike the always-emitted
8955/// [`M3_PLACEMENT_KEY_ESTRATEGIA`] / [`M3_PLACEMENT_KEY_CLUSTERS`]
8956/// axes, the `shard_key` field carries a
8957/// `#[serde(skip_serializing_if = "Option::is_none")]` attribute so the
8958/// key appears in the rendered `placement:` block iff the typed slot
8959/// resolves to `Some(_)` — the omit-when-unset contract the peer typed
8960/// slots ([`M3_PLACEMENT_KEY_AFFINITY`],
8961/// [`crate::aplicacao::MeshPolicy::timeout`],
8962/// [`crate::aplicacao::MeshPolicy::retries`],
8963/// [`crate::aplicacao::MeshPolicy::mtls_required`]) each carry to keep
8964/// an unset typed slot from bloating every rendered programs.yaml
8965/// entry with a nominal-only `shardKey: null` value the downstream
8966/// shard-dispatch passes would then need to unwrap defensively.
8967///
8968/// Lifting the byte to one `&'static str` closes the same drift footgun
8969/// the peer [`M3_PLACEMENT_KEY_ESTRATEGIA`] / [`M3_PLACEMENT_KEY_CLUSTERS`]
8970/// / [`M3_PLACEMENT_KEY_AFFINITY`] lifts closed on the sibling axes:
8971/// a future refactor renaming the Rust field (`shard_key` →
8972/// `partition_key` for Kafka-symmetric naming, `entity_key` for
8973/// Akka/Orleans-symmetric naming, `hash_key` for schema-clarity, etc.)
8974/// OR retaining the field name while adding a `#[serde(rename = "…")]`
8975/// override OR dropping the struct-level `rename_all = "camelCase"`
8976/// attribute would silently emit a `placement:` block whose shard-
8977/// selection template lands under one key while every downstream shard-
8978/// dispatch consumer still probes another — the M3 shard-pool
8979/// dispatch materializer would then see a `None` shard-key on every
8980/// entry and silently fall back to the per-entry random-placement
8981/// baseline (the workload's typed `:shard-key "$tenantId"` template
8982/// would be silently discarded, and per-tenant entities would scatter
8983/// across every cluster in the pool instead of consistently landing on
8984/// one — the failure surfaces as "the newly-deployed sharded Aplicacao
8985/// mysteriously loses its per-tenant locality" far from the rebrand
8986/// commit's source, and Cilium's per-entity trace surfaces the
8987/// symptom only in hubble traces of the actual data-plane skew, not in
8988/// `kubectl describe`). The identity pin + serde-derive round-trip
8989/// pin the sweep introduces catch the drift at caixa-core / caixa-mesh
8990/// build time rather than at the aggregator's shard-dispatch step or
8991/// the operator's reconcile posture. The serde-derive pin is
8992/// particularly load-bearing on this axis (relative to the peer
8993/// `affinity` / `clusters` / `estrategia` pins) because the underlying
8994/// derive transform is *not* a no-op — the emitted `shardKey` key
8995/// differs from the source-side `shard_key` field by construction,
8996/// so any rebrand that touches either endpoint of the transform (the
8997/// field name OR the `rename_all` attribute OR a per-field `rename`
8998/// override) reaches this pin's assertion by construction.
8999///
9000/// Peer of [`M3_KEY_PLACEMENT`] / [`M3_PLACEMENT_KEY_ESTRATEGIA`] /
9001/// [`M3_PLACEMENT_KEY_CLUSTERS`] / [`M3_PLACEMENT_KEY_AFFINITY`] on the
9002/// same programs.yaml per-entry axis — `M3_KEY_PLACEMENT` names the
9003/// top-level overlay key each entry carries, `M3_PLACEMENT_KEY_ESTRATEGIA`
9004/// names the per-sub-block distribution-strategy discriminator every
9005/// dispatch consumer branches on, `M3_PLACEMENT_KEY_CLUSTERS` names the
9006/// per-sub-block cluster-pool list every per-cluster fanout consumer
9007/// scopes by, `M3_PLACEMENT_KEY_AFFINITY` names the per-sub-block
9008/// optional placement-engine hint every weighting consumer reads off,
9009/// this constant names the per-sub-block optional shard-selection
9010/// template every shard-dispatch consumer materializes off. Completes
9011/// the M3 `Placement` sub-key quartet's canonical-key lift alongside
9012/// the sibling always-emitted axes.
9013pub const M3_PLACEMENT_KEY_SHARD_KEY: &str = "shardKey";
9014
9015/// Canonical M3 [`crate::aplicacao::PlacementStrategy::SingleNode`]
9016/// variant discriminator scalar-value — the exact byte-string the
9017/// `Serialize` derive on the un-`rename`d enum emits under
9018/// [`M3_PLACEMENT_KEY_ESTRATEGIA`] whenever the typed slot's
9019/// distribution strategy is the single-cluster-active-at-a-time arm
9020/// (OTP distributed-application takeover, MESH-COMPOSITION.md §II.1).
9021///
9022/// The scalar every downstream cluster-side dispatcher probes verbatim
9023/// to pick the takeover semantics:
9024///
9025/// - the `lareira-fleet-programs` aggregator's per-entry
9026/// `placement.estrategia` strategy dispatch (`if $strat ==
9027/// "SingleNode" { ... }`),
9028/// - the future `app-operator` reconciler's per-Aplicacao
9029/// strategy-branch (`match placement.estrategia { "SingleNode" =>
9030/// … }`),
9031/// - the future `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
9032/// admission-time enum-arm bind, and
9033/// - the M3 Adaptive compression pass's per-strategy weighting per
9034/// MESH-COMPOSITION.md §V.
9035///
9036/// The scalar is derived by `#[derive(Serialize)]` on the
9037/// [`crate::aplicacao::PlacementStrategy`] enum with no
9038/// `#[serde(rename_all = …)]` attribute, so the emitted string is
9039/// byte-for-byte the source-side variant name. Lifting the byte to
9040/// one `&'static str` closes the drift footgun structurally: a future
9041/// refactor renaming the variant (`SingleNode` → `Singleton` for OTP-
9042/// vocabulary parity, `Active` for shorter-form-clarity, etc.) OR
9043/// adding a `#[serde(rename_all = "kebab-case")]` attribute would
9044/// silently emit a `placement.estrategia:` scalar whose distribution
9045/// strategy lands under one spelling while every downstream consumer
9046/// still dispatches on another — the aggregator's strategy branch,
9047/// the operator's reconcile posture, the CR materializer's
9048/// admission-time enum-arm bind would each silently no-op onto the
9049/// enum's `default()` (`Replicated`) and the workload would come up
9050/// on every declared cluster active-active rather than the
9051/// single-cluster-takeover the typed slot named. The serde
9052/// round-trip pin the sweep introduces
9053/// ([`crate::aplicacao::tests::placement_strategy_variants_serialize_to_lifted_scalar_values`])
9054/// catches the drift at caixa-core build time rather than at the
9055/// aggregator's dispatch step or the operator's reconcile posture.
9056///
9057/// Peer of [`M3_PLACEMENT_ESTRATEGIA_REPLICATED`] /
9058/// [`M3_PLACEMENT_ESTRATEGIA_SHARDED`] on the same closed
9059/// PlacementStrategy enum surface — together the three constants
9060/// name every author-reachable arm of the M3 distribution-strategy
9061/// discriminator, mirroring the closed-enum-scalar-value trajectory
9062/// [`CILIUM_AUTH_MODE_REQUIRED`] / [`CILIUM_AUTH_MODE_DISABLED`]
9063/// (8ab119f) established on the sibling Cilium
9064/// `MutualAuthenticationMode` OpenAPI schema enum.
9065pub const M3_PLACEMENT_ESTRATEGIA_SINGLE_NODE: &str = "SingleNode";
9066
9067/// Canonical M3 [`crate::aplicacao::PlacementStrategy::Replicated`]
9068/// variant discriminator scalar-value — the exact byte-string the
9069/// `Serialize` derive on the un-`rename`d enum emits under
9070/// [`M3_PLACEMENT_KEY_ESTRATEGIA`] whenever the typed slot's
9071/// distribution strategy is the every-cluster-active-active arm (the
9072/// enum's `default()` and the canonical happy-path per
9073/// MESH-COMPOSITION.md §II.1).
9074///
9075/// Peer of the sibling [`M3_PLACEMENT_ESTRATEGIA_SINGLE_NODE`] /
9076/// [`M3_PLACEMENT_ESTRATEGIA_SHARDED`] scalars on the same closed
9077/// enum surface — see the sibling doc for the full drift-mode
9078/// analysis. This is the arm the un-`:placement` (`Placement::default()`)
9079/// path serializes as, so drift here silently rebrands the substrate's
9080/// default distribution posture across every Aplicacao that never
9081/// declares the slot explicitly.
9082pub const M3_PLACEMENT_ESTRATEGIA_REPLICATED: &str = "Replicated";
9083
9084/// Canonical M3 [`crate::aplicacao::PlacementStrategy::Sharded`]
9085/// variant discriminator scalar-value — the exact byte-string the
9086/// `Serialize` derive on the un-`rename`d enum emits under
9087/// [`M3_PLACEMENT_KEY_ESTRATEGIA`] whenever the typed slot's
9088/// distribution strategy is the hash-keyed-across-clusters arm (Akka
9089/// cluster sharding, MESH-COMPOSITION.md §II.4). The one arm on which
9090/// the typed [`M3_PLACEMENT_KEY_SHARD_KEY`] sub-block is required —
9091/// `AplicacaoSpec::validate_placement` gates `shard_key.is_some() ==
9092/// matches!(estrategia, Sharded)` as a structural partition of every
9093/// validated [`crate::aplicacao::Placement`].
9094///
9095/// Peer of the sibling [`M3_PLACEMENT_ESTRATEGIA_SINGLE_NODE`] /
9096/// [`M3_PLACEMENT_ESTRATEGIA_REPLICATED`] scalars on the same closed
9097/// enum surface — see the [`M3_PLACEMENT_ESTRATEGIA_SINGLE_NODE`] doc
9098/// for the full drift-mode analysis. This is the arm the future Akka-
9099/// style cluster-sharding reconciler dispatches on before hashing
9100/// `placement.shardKey` across `placement.clusters`, so drift here
9101/// silently collapses the hash-keyed distribution back onto the
9102/// aggregator's default (Replicated) and every sharded workload's
9103/// per-entity routing invariant vanishes at the data plane.
9104pub const M3_PLACEMENT_ESTRATEGIA_SHARDED: &str = "Sharded";
9105
9106/// Canonical M2 [`crate::supervisor::RestartStrategy::OneForOne`] variant
9107/// discriminator scalar-value — the exact byte-string the `Serialize`
9108/// derive on the un-`rename`d enum emits under
9109/// [`SUPERVISOR_KEY_ESTRATEGIA`] whenever the typed `:supervisor
9110/// :estrategia` slot's strategy is the restart-only-the-failed-child arm
9111/// (the enum's `default()` and the canonical happy-path per
9112/// theory/INSPIRATIONS.md §II.2 — Erlang/OTP `one_for_one`).
9113///
9114/// The scalar is the un-`rename`d Rust variant name verbatim; a future
9115/// `#[serde(rename_all = "kebab-case")]` attribute on the enum, or a
9116/// per-variant `#[serde(rename = "…")]` override, or a variant rename in
9117/// the source, would silently emit a `:supervisor :estrategia` scalar
9118/// whose per-failure sibling-restart discipline lands under one spelling
9119/// while every downstream consumer still dispatches on another — the
9120/// future wasm-operator's per-supervisor sibling-restart branch, the
9121/// future M4 `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's
9122/// admission-time enum-arm bind, the `caixa-operator`'s hierarchical
9123/// reconciliation scheduler's per-strategy fan-out would each silently
9124/// no-op onto the enum's `default()` (`OneForOne`) and the tree would
9125/// come up with the wrong sibling-restart posture on every non-default
9126/// arm. The serde round-trip pin the sweep introduces
9127/// ([`crate::supervisor::tests::restart_strategy_variants_serialize_to_lifted_scalar_values`])
9128/// catches the drift at caixa-core build time rather than at the
9129/// operator's reconcile posture.
9130///
9131/// Peer of [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ALL`] /
9132/// [`SUPERVISOR_ESTRATEGIA_REST_FOR_ONE`] /
9133/// [`SUPERVISOR_ESTRATEGIA_SIMPLE_ONE_FOR_ONE`] on the same closed
9134/// [`crate::supervisor::RestartStrategy`] enum surface — together the
9135/// four constants name every author-reachable arm of the OTP-shaped
9136/// per-supervisor sibling-restart discriminator, mirroring the
9137/// closed-enum-scalar-value trajectory [`M3_PLACEMENT_ESTRATEGIA_SINGLE_NODE`]
9138/// / [`M3_PLACEMENT_ESTRATEGIA_REPLICATED`] /
9139/// [`M3_PLACEMENT_ESTRATEGIA_SHARDED`] (3f0e21c) established on the
9140/// sibling M3 `PlacementStrategy` enum on the peer per-Aplicacao
9141/// distribution-strategy axis.
9142pub const SUPERVISOR_ESTRATEGIA_ONE_FOR_ONE: &str = "OneForOne";
9143
9144/// Canonical M2 [`crate::supervisor::RestartStrategy::OneForAll`] variant
9145/// discriminator scalar-value — the exact byte-string the `Serialize`
9146/// derive on the un-`rename`d enum emits under
9147/// [`SUPERVISOR_KEY_ESTRATEGIA`] whenever the typed `:supervisor
9148/// :estrategia` slot's strategy is the restart-every-sibling-on-any-
9149/// failure arm (Erlang/OTP `one_for_all`, used when children share state
9150/// and must be in sync).
9151///
9152/// Peer of the sibling [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ONE`] /
9153/// [`SUPERVISOR_ESTRATEGIA_REST_FOR_ONE`] /
9154/// [`SUPERVISOR_ESTRATEGIA_SIMPLE_ONE_FOR_ONE`] scalars on the same
9155/// closed enum surface — see the sibling
9156/// [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ONE`] doc for the full drift-mode
9157/// analysis.
9158pub const SUPERVISOR_ESTRATEGIA_ONE_FOR_ALL: &str = "OneForAll";
9159
9160/// Canonical M2 [`crate::supervisor::RestartStrategy::RestForOne`]
9161/// variant discriminator scalar-value — the exact byte-string the
9162/// `Serialize` derive on the un-`rename`d enum emits under
9163/// [`SUPERVISOR_KEY_ESTRATEGIA`] whenever the typed `:supervisor
9164/// :estrategia` slot's strategy is the restart-failed-and-later-started-
9165/// siblings arm (Erlang/OTP `rest_for_one`, used when later children
9166/// depend on earlier ones so the startup-order suffix must be
9167/// re-established).
9168///
9169/// Peer of the sibling [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ONE`] /
9170/// [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ALL`] /
9171/// [`SUPERVISOR_ESTRATEGIA_SIMPLE_ONE_FOR_ONE`] scalars on the same
9172/// closed enum surface — see the sibling
9173/// [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ONE`] doc for the full drift-mode
9174/// analysis.
9175pub const SUPERVISOR_ESTRATEGIA_REST_FOR_ONE: &str = "RestForOne";
9176
9177/// Canonical M2 [`crate::supervisor::RestartStrategy::SimpleOneForOne`]
9178/// variant discriminator scalar-value — the exact byte-string the
9179/// `Serialize` derive on the un-`rename`d enum emits under
9180/// [`SUPERVISOR_KEY_ESTRATEGIA`] whenever the typed `:supervisor
9181/// :estrategia` slot's strategy is the dynamic-children-of-one-shape arm
9182/// (Erlang/OTP `simple_one_for_one`, the one arm on which
9183/// [`crate::supervisor::SupervisorSpec::validate`] gates
9184/// `children.is_empty()` as a structural partition — static `:children`
9185/// on a `SimpleOneForOne` supervisor is a build-time rejection).
9186///
9187/// Peer of the sibling [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ONE`] /
9188/// [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ALL`] /
9189/// [`SUPERVISOR_ESTRATEGIA_REST_FOR_ONE`] scalars on the same closed
9190/// enum surface — see the sibling
9191/// [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ONE`] doc for the full drift-mode
9192/// analysis.
9193pub const SUPERVISOR_ESTRATEGIA_SIMPLE_ONE_FOR_ONE: &str = "SimpleOneForOne";
9194
9195/// Canonical M2 [`crate::supervisor::RestartPolicy::Permanent`] variant
9196/// discriminator scalar-value — the exact byte-string the `Serialize`
9197/// derive on the un-`rename`d enum emits under
9198/// [`SUPERVISOR_CHILD_KEY_RESTART`] whenever the typed `:children :restart`
9199/// per-child restart-policy slot is the always-restart-regardless-of-exit
9200/// arm (the enum's `default()` and the canonical happy-path per
9201/// theory/INSPIRATIONS.md §II.2 — Erlang/OTP `permanent`, the
9202/// long-running-service posture where the supervisor must bring the
9203/// child back on every failure mode).
9204///
9205/// The scalar is the un-`rename`d Rust variant name verbatim; a future
9206/// `#[serde(rename_all = "kebab-case")]` attribute on the enum, or a
9207/// per-variant `#[serde(rename = "…")]` override, or a variant rename in
9208/// the source, would silently emit a `:children :restart` scalar
9209/// whose per-exit restart-decision discipline lands under one spelling
9210/// while every downstream consumer still dispatches on another — the
9211/// future wasm-operator's per-child restart-decision branch, the future
9212/// M4 `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's
9213/// admission-time enum-arm bind, the `caixa-operator`'s hierarchical
9214/// reconciliation scheduler's per-child-policy fan-out would each silently
9215/// no-op onto the enum's `default()` (`Permanent`) and children would
9216/// come up with the wrong per-exit restart posture on every non-default
9217/// arm — a `:temporary` `oneShot` child would be restarted on clean
9218/// exit (the successful-completion signal treated as failure), a
9219/// `:transient` child that clean-exited would be restarted (masking the
9220/// clean-completion contract), and the operator's post-exit dispatch
9221/// would silently degrade to the always-restart posture. The serde
9222/// round-trip pin the sweep introduces
9223/// ([`crate::supervisor::tests::restart_policy_variants_serialize_to_lifted_scalar_values`])
9224/// catches the drift at caixa-core build time rather than at the
9225/// operator's reconcile posture.
9226///
9227/// Peer of [`SUPERVISOR_CHILD_RESTART_TEMPORARY`] /
9228/// [`SUPERVISOR_CHILD_RESTART_TRANSIENT`] on the same closed
9229/// [`crate::supervisor::RestartPolicy`] enum surface — together the
9230/// three constants name every author-reachable arm of the OTP-shaped
9231/// per-child restart-decision discriminator, mirroring the
9232/// closed-enum-scalar-value trajectory
9233/// [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ONE`] /
9234/// [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ALL`] /
9235/// [`SUPERVISOR_ESTRATEGIA_REST_FOR_ONE`] /
9236/// [`SUPERVISOR_ESTRATEGIA_SIMPLE_ONE_FOR_ONE`] (09ffb2d) established on
9237/// the sibling `RestartStrategy` enum on the peer per-supervisor
9238/// sibling-restart-strategy axis and [`M3_PLACEMENT_ESTRATEGIA_SINGLE_NODE`]
9239/// / [`M3_PLACEMENT_ESTRATEGIA_REPLICATED`] /
9240/// [`M3_PLACEMENT_ESTRATEGIA_SHARDED`] (3f0e21c) established on the M3
9241/// `PlacementStrategy` enum on the peer per-Aplicacao distribution-strategy
9242/// axis. The three OTP-shaped closed-enum discriminator axes on the
9243/// caixa typed surface (supervisor sibling-restart strategy, per-child
9244/// restart policy, per-Aplicacao placement strategy) now each carry the
9245/// same three-path-convergence (`Serialize` derive → `as_str` helper →
9246/// lifted constant) drift-detection posture.
9247pub const SUPERVISOR_CHILD_RESTART_PERMANENT: &str = "Permanent";
9248
9249/// Canonical M2 [`crate::supervisor::RestartPolicy::Temporary`] variant
9250/// discriminator scalar-value — the exact byte-string the `Serialize`
9251/// derive on the un-`rename`d enum emits under
9252/// [`SUPERVISOR_CHILD_KEY_RESTART`] whenever the typed `:children :restart`
9253/// per-child restart-policy slot is the never-restart arm (Erlang/OTP
9254/// `temporary`, the one-shot posture where the child's completion — clean
9255/// or not — is itself the success signal; the `oneShot`
9256/// [`crate::render::COMPUTEUNIT_SPEC_KEY_TRIGGER`] arm maps here).
9257///
9258/// Peer of the sibling [`SUPERVISOR_CHILD_RESTART_PERMANENT`] /
9259/// [`SUPERVISOR_CHILD_RESTART_TRANSIENT`] scalars on the same
9260/// closed enum surface — see the sibling
9261/// [`SUPERVISOR_CHILD_RESTART_PERMANENT`] doc for the full drift-mode
9262/// analysis.
9263pub const SUPERVISOR_CHILD_RESTART_TEMPORARY: &str = "Temporary";
9264
9265/// Canonical M2 [`crate::supervisor::RestartPolicy::Transient`] variant
9266/// discriminator scalar-value — the exact byte-string the `Serialize`
9267/// derive on the un-`rename`d enum emits under
9268/// [`SUPERVISOR_CHILD_KEY_RESTART`] whenever the typed `:children :restart`
9269/// per-child restart-policy slot is the restart-only-on-abnormal-exit arm
9270/// (Erlang/OTP `transient`, the "restart on non-zero exit or unhandled
9271/// exception; a clean exit completes the child" posture — the third
9272/// canonical OTP per-child restart-decision arm alongside `permanent`
9273/// and `temporary`).
9274///
9275/// Peer of the sibling [`SUPERVISOR_CHILD_RESTART_PERMANENT`] /
9276/// [`SUPERVISOR_CHILD_RESTART_TEMPORARY`] scalars on the same
9277/// closed enum surface — see the sibling
9278/// [`SUPERVISOR_CHILD_RESTART_PERMANENT`] doc for the full drift-mode
9279/// analysis.
9280pub const SUPERVISOR_CHILD_RESTART_TRANSIENT: &str = "Transient";
9281
9282/// Canonical camelCase JSON/YAML top-level key for the
9283/// [`crate::aplicacao::Entrada`] struct's `host` external-hostname axis —
9284/// the `host:` field the M3 Aplicacao's `#[serde(rename_all = "camelCase")]`
9285/// derive on [`crate::aplicacao::Entrada`] emits at the singleton
9286/// `:entrada` block, and the exact scalar every downstream consumer
9287/// reaching for the external hostname via `Value::get(...)` (the
9288/// [`caixa_mesh`] Gateway/HTTPRoute emitter's per-Aplicacao
9289/// `spec.hostnames` projection under [`GATEWAY_API_KEY_HOSTNAME`] /
9290/// [`GATEWAY_API_KEY_HOSTNAMES`], the future `app-operator`
9291/// reconciler's per-Aplicacao ingress-hostname bind, the future
9292/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's admission-time
9293/// hostname cross-check against the cluster's declared
9294/// [`GATEWAY_API_HOSTNAME_MAX_LEN`] discipline) must probe on.
9295///
9296/// The scalar is derived from the Rust field name `host` by the
9297/// `rename_all = "camelCase"` derive; `host` has no `_`, so the serde
9298/// transform is a no-op on this axis and the emitted key equals the
9299/// source-side field name byte-for-byte. Lifting the byte to one
9300/// `&'static str` closes the drift footgun structurally: a future
9301/// refactor renaming the Rust field OR retaining the field name while
9302/// adding a `#[serde(rename = "…")]` override would silently emit an
9303/// `Entrada` whose external-hostname discriminator lands under one key
9304/// while every downstream consumer still probes another — the Gateway
9305/// emitter's per-Aplicacao hostname projection, the operator's ingress
9306/// bind, the CR materializer's admission-time cross-check would each
9307/// silently fall back to no-hostname and the Gateway API would either
9308/// admit an all-hostname listener (breaking the per-Aplicacao
9309/// host-isolation contract MESH-COMPOSITION.md §III.5 promises) or
9310/// reject the resource outright at admission. The identity pin
9311/// (`entrada_serde_keys_match_lifted_entrada_key_consts` on the
9312/// source-side type) catches drift at caixa-core build time rather than
9313/// at the Gateway controller's admission step, far from the rebrand
9314/// commit's source.
9315///
9316/// Peer of [`ENTRADA_KEY_PARA`] / [`ENTRADA_KEY_PATHS`] /
9317/// [`ENTRADA_KEY_PORT`] on the same [`crate::aplicacao::Entrada`]
9318/// singleton serialized-key axis. Peer of the sibling
9319/// [`MEMBRO_KEY_CAIXA`] / [`MEMBRO_KEY_VERSAO`] pair (ce80ca0) and
9320/// [`CONTRATO_KEY_DE`] / [`CONTRATO_KEY_PARA`] / [`CONTRATO_KEY_WIT`]
9321/// triad (ca463a4) on the sibling M3 per-`:membros` and per-`:contratos`
9322/// entry axes — those lifts pinned the M3 collection-slot atom
9323/// camelCase JSON keys, this lift extends the same discipline onto the
9324/// singleton `:entrada` mesh slot so the last M3 typed-struct
9325/// `#[serde(rename_all = "camelCase")]` axis on the Aplicacao surface
9326/// joins the substrate's "one canonical byte-string per typed
9327/// serialized-key axis" discipline. Same discipline every peer
9328/// camelCase serde-key lift carries ([`M2_LIMITS_KEY_MEMORY`] etc.
9329/// (d8b8b4f), [`M2_BEHAVIOR_KEY_ON_INIT`] etc. (21fe462),
9330/// [`M2_UPGRADE_FROM_KEY_FROM`] / [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`]
9331/// (36ffe65), [`M3_PLACEMENT_KEY_ESTRATEGIA`] etc.,
9332/// [`SUPERVISOR_KEY_ESTRATEGIA`] etc. (40cc4e5)).
9333pub const ENTRADA_KEY_HOST: &str = "host";
9334
9335/// Canonical camelCase JSON/YAML top-level key for the
9336/// [`crate::aplicacao::Entrada`] struct's `para` destination-member axis
9337/// — the `para:` field naming which `:membros` entry the external
9338/// Gateway routes to. Peer of [`ENTRADA_KEY_HOST`] on the same
9339/// [`crate::aplicacao::Entrada`] singleton serialized-key axis; see
9340/// [`ENTRADA_KEY_HOST`] for the full lift rationale. The Rust field is
9341/// lowercase `para`; `#[serde(rename_all = "camelCase")]` is a no-op on
9342/// this axis and the emitted key equals the source-side field name
9343/// byte-for-byte.
9344///
9345/// Byte-identical to [`CONTRATO_KEY_PARA`] today — both resolve to the
9346/// same four-byte `"para"` literal — but semantically distinct:
9347/// [`CONTRATO_KEY_PARA`] names the per-`:contratos` edge's callee-Servico
9348/// discriminator on the [`crate::aplicacao::WitContract`] surface, while
9349/// this constant names the singleton `:entrada` block's Gateway-route
9350/// destination-Servico discriminator on the sibling
9351/// [`crate::aplicacao::Entrada`] surface. Splitting the two lets each
9352/// schema's future rebrand land independently on the same
9353/// "byte-identical-but-semantically-distinct" discipline the peer
9354/// [`FLEET_PROGRAMS_KEY_NAME`] / [`KUBE_KEY_NAME`] split established
9355/// (935979a) on the sibling per-entry name-discriminator axis and the
9356/// [`FLEET_PROGRAMS_KEY_VERSAO`] / [`MEMBRO_KEY_VERSAO`] split
9357/// established (ce80ca0) on the sibling per-entry version-constraint
9358/// axis.
9359pub const ENTRADA_KEY_PARA: &str = "para";
9360
9361/// Canonical camelCase JSON/YAML top-level key for the
9362/// [`crate::aplicacao::Entrada`] struct's `paths` per-Aplicacao
9363/// path-filter axis — the `paths:` sequence the M3 Aplicacao's
9364/// `#[serde(rename_all = "camelCase")]` derive emits at the singleton
9365/// `:entrada` block, and the exact scalar every downstream
9366/// per-`:entrada :paths` HTTPRoute-match-projection consumer must probe
9367/// on (the [`caixa_mesh`] HTTPRoute emitter's per-Aplicacao `matches[]`
9368/// projection under [`GATEWAY_API_KEY_MATCHES`], defaulting to
9369/// [`GATEWAY_API_DEFAULT_HTTP_ROUTE_PATH`] when the slot is empty per
9370/// 48e2083). Peer of [`ENTRADA_KEY_HOST`] on the same
9371/// [`crate::aplicacao::Entrada`] singleton serialized-key axis; see
9372/// [`ENTRADA_KEY_HOST`] for the full lift rationale. The Rust field is
9373/// lowercase `paths`; `#[serde(rename_all = "camelCase")]` is a no-op
9374/// on this axis and the emitted key equals the source-side field name
9375/// byte-for-byte.
9376pub const ENTRADA_KEY_PATHS: &str = "paths";
9377
9378/// Canonical camelCase JSON/YAML top-level key for the
9379/// [`crate::aplicacao::Entrada`] struct's `port` destination-Servico
9380/// port axis — the `port:` field the M3 Aplicacao's
9381/// `#[serde(rename_all = "camelCase")]` derive emits at the singleton
9382/// `:entrada` block, defaulting via [`crate::aplicacao::default_port`]
9383/// to [`crate::DEFAULT_SERVICO_PORT`] when the author omits the slot.
9384/// Peer of [`ENTRADA_KEY_HOST`] on the same
9385/// [`crate::aplicacao::Entrada`] singleton serialized-key axis; see
9386/// [`ENTRADA_KEY_HOST`] for the full lift rationale. The Rust field is
9387/// lowercase `port`; `#[serde(rename_all = "camelCase")]` is a no-op on
9388/// this axis and the emitted key equals the source-side field name
9389/// byte-for-byte.
9390///
9391/// Byte-identical to [`KUBE_KEY_PORT`] today — both resolve to the same
9392/// four-byte `"port"` literal — but semantically distinct:
9393/// [`KUBE_KEY_PORT`] names the K8s Service/ContainerPort per-resource
9394/// port-discriminator axis, while this constant names the typed
9395/// [`crate::aplicacao::Entrada`] singleton block's Gateway-route
9396/// destination-Servico port axis on the M3 Aplicacao surface.
9397/// Splitting the two lets each schema's future rebrand land
9398/// independently.
9399pub const ENTRADA_KEY_PORT: &str = "port";
9400
9401/// Canonical camelCase JSON/YAML top-level key for the
9402/// [`crate::aplicacao::MeshPolicy`] struct's `timeout` per-call
9403/// wall-clock cap axis — the `timeout:` field the M3 Aplicacao's
9404/// `#[serde(rename_all = "camelCase")]` derive on
9405/// [`crate::aplicacao::MeshPolicy`] emits at the singleton `:politicas`
9406/// block, and the exact scalar every downstream mesh-timeout consumer
9407/// must probe on (the future M4 per-edge `:politicas` overlay
9408/// projection onto Cilium `L7Rules` / Gateway API `HTTPRoute`
9409/// per-backend `timeouts.backendRequest` axis per
9410/// MESH-COMPOSITION.md §III.3, the future
9411/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's admission-time
9412/// mesh-timeout cross-check, the future `feira lint` per-`:politicas`
9413/// authored-duration bound-check against
9414/// [`crate::POLICY_TIMEOUT_MAX`]).
9415///
9416/// The scalar is derived from the Rust field name `timeout` by the
9417/// `rename_all = "camelCase"` derive; `timeout` has no `_`, so the
9418/// serde transform is a no-op on this axis and the emitted key equals
9419/// the source-side field name byte-for-byte. Lifting the byte to one
9420/// `&'static str` closes the drift footgun structurally: a future
9421/// refactor renaming the Rust field OR retaining the field name while
9422/// adding a `#[serde(rename = "…")]` override would silently emit a
9423/// [`MeshPolicy`][mp] whose per-call timeout discriminator lands under
9424/// one key while every downstream consumer still probes another — the
9425/// M4 per-edge overlay projection, the CR materializer's cross-check,
9426/// the linter's bound-check would each silently fall back to
9427/// no-timeout and every `:contratos`-edge request would silently
9428/// bypass the per-call cap the typed slot set, with the failure
9429/// surfacing as "the mesh no longer enforces the timeout the
9430/// Aplicacao authored" far from the rebrand commit's source. The
9431/// identity pin (`mesh_policy_serde_keys_match_lifted_politicas_key_consts`
9432/// on the source-side type) catches drift at caixa-core build time
9433/// rather than at the mesh controller's reconcile step.
9434///
9435/// [mp]: crate::aplicacao::MeshPolicy
9436///
9437/// Peer of [`POLITICAS_KEY_RETRIES`] / [`POLITICAS_KEY_CIRCUIT_BREAKER`] /
9438/// [`POLITICAS_KEY_MTLS_REQUIRED`] / [`POLITICAS_KEY_RATE_LIMIT`] on the
9439/// same [`crate::aplicacao::MeshPolicy`] singleton serialized-key
9440/// axis. Peer of the sibling [`ENTRADA_KEY_HOST`] etc. tetrad
9441/// (a3d6162), [`M3_PLACEMENT_KEY_ESTRATEGIA`] etc. tetrad,
9442/// [`MEMBRO_KEY_CAIXA`] / [`MEMBRO_KEY_VERSAO`] pair (ce80ca0), and
9443/// [`CONTRATO_KEY_DE`] / [`CONTRATO_KEY_PARA`] / [`CONTRATO_KEY_WIT`]
9444/// triad (ca463a4) on the sibling M3 typed-struct axes — those lifts
9445/// pinned every peer M3 mesh-slot atom, this lift closes the last M3
9446/// typed-struct top-level `#[serde(rename_all = "camelCase")]` axis on
9447/// the Aplicacao surface without a lifted serde-key peer (the
9448/// [`crate::aplicacao::MeshPolicy`] singleton `:politicas` block) so
9449/// the entire M3 typed-struct surface joins the substrate's "one
9450/// canonical byte-string per typed serialized-key axis" discipline.
9451/// Same discipline every peer camelCase serde-key lift carries
9452/// ([`M2_LIMITS_KEY_MEMORY`] etc. (d8b8b4f),
9453/// [`M2_BEHAVIOR_KEY_ON_INIT`] etc. (21fe462),
9454/// [`M2_UPGRADE_FROM_KEY_FROM`] / [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`]
9455/// (36ffe65), [`SUPERVISOR_KEY_ESTRATEGIA`] etc. (40cc4e5)).
9456pub const POLITICAS_KEY_TIMEOUT: &str = "timeout";
9457
9458/// Canonical camelCase JSON/YAML top-level key for the
9459/// [`crate::aplicacao::MeshPolicy`] struct's `retries` transient-failure
9460/// retry-count axis — the `retries:` field the M3 Aplicacao's
9461/// `#[serde(rename_all = "camelCase")]` derive on
9462/// [`crate::aplicacao::MeshPolicy`] emits at the singleton `:politicas`
9463/// block. Peer of [`POLITICAS_KEY_TIMEOUT`] on the same
9464/// [`crate::aplicacao::MeshPolicy`] singleton serialized-key axis; see
9465/// [`POLITICAS_KEY_TIMEOUT`] for the full lift rationale. The Rust
9466/// field is lowercase `retries`; `#[serde(rename_all = "camelCase")]`
9467/// is a no-op on this axis and the emitted key equals the source-side
9468/// field name byte-for-byte.
9469pub const POLITICAS_KEY_RETRIES: &str = "retries";
9470
9471/// Canonical camelCase JSON/YAML top-level key for the
9472/// [`crate::aplicacao::MeshPolicy`] struct's `circuit_breaker`
9473/// circuit-breaker sub-block axis — the `circuitBreaker:` field the M3
9474/// Aplicacao's `#[serde(rename_all = "camelCase")]` derive on
9475/// [`crate::aplicacao::MeshPolicy`] emits at the singleton `:politicas`
9476/// block, and the exact camelCase scalar (Rust field
9477/// `circuit_breaker` → serde-emitted `circuitBreaker`, one of the two
9478/// `MeshPolicy` axes the derive-attribute non-trivially transforms
9479/// alongside [`POLITICAS_KEY_MTLS_REQUIRED`] and
9480/// [`POLITICAS_KEY_RATE_LIMIT`]) every downstream circuit-breaker
9481/// consumer must probe on (the future M4 per-edge `:politicas` overlay
9482/// projection onto the mesh's per-backend failure-counter reset
9483/// window per MESH-COMPOSITION.md §III.3 breaker semantics, the future
9484/// `feira lint` per-`:politicas` breaker-window bound-check against
9485/// [`crate::POLICY_BREAKER_WINDOW_MAX`] and
9486/// [`crate::POLICY_BREAKER_MAX_FAILURES_MAX`]). Peer of
9487/// [`POLITICAS_KEY_TIMEOUT`] on the same
9488/// [`crate::aplicacao::MeshPolicy`] singleton serialized-key axis; see
9489/// [`POLITICAS_KEY_TIMEOUT`] for the full lift rationale.
9490///
9491/// This axis is one of the three non-trivial camelCase transforms
9492/// [`crate::aplicacao::MeshPolicy`]'s derive emits (`circuit_breaker`
9493/// → `circuitBreaker`, `mtls_required` → `mtlsRequired`, `rate_limit`
9494/// → `rateLimit`); a future accidental `rename_all = "snake_case"` /
9495/// `"kebab-case"` / verbatim-field-name flip at the derive would
9496/// silently rebrand the emitted key to `circuit_breaker` /
9497/// `circuit-breaker` / `circuit_breaker` respectively, breaking every
9498/// downstream `Value::get(POLITICAS_KEY_CIRCUIT_BREAKER)` consumer.
9499/// The identity pin (`mesh_policy_serde_keys_match_lifted_politicas_key_consts`
9500/// on the source-side type) catches drift on all three non-trivial
9501/// axes simultaneously.
9502pub const POLITICAS_KEY_CIRCUIT_BREAKER: &str = "circuitBreaker";
9503
9504/// Canonical camelCase JSON/YAML top-level key for the
9505/// [`crate::aplicacao::MeshPolicy`] struct's `mtls_required`
9506/// mTLS-enforcement-toggle axis — the `mtlsRequired:` field the M3
9507/// Aplicacao's `#[serde(rename_all = "camelCase")]` derive on
9508/// [`crate::aplicacao::MeshPolicy`] emits at the singleton `:politicas`
9509/// block, and the exact camelCase scalar (Rust field `mtls_required`
9510/// → serde-emitted `mtlsRequired`) every downstream mesh-identity
9511/// consumer must probe on (the future M4 per-edge `:politicas` overlay
9512/// projection onto Cilium `CiliumNetworkPolicy` per-rule
9513/// [`CILIUM_KEY_AUTHENTICATION`] mode dispatch under the
9514/// [`cilium_auth_mode`] bijection projection (a4dc43c) — the mesh's
9515/// sandboxing-by-default posture MESH-COMPOSITION.md §III.3 promises
9516/// keys off this exact byte-sequence to opt out of mTLS enforcement
9517/// per-edge, so drift here silently reopens the every-edge-mTLS
9518/// invariant the substrate defaults to). Peer of
9519/// [`POLITICAS_KEY_TIMEOUT`] on the same
9520/// [`crate::aplicacao::MeshPolicy`] singleton serialized-key axis; see
9521/// [`POLITICAS_KEY_TIMEOUT`] for the full lift rationale.
9522pub const POLITICAS_KEY_MTLS_REQUIRED: &str = "mtlsRequired";
9523
9524/// Canonical camelCase JSON/YAML top-level key for the
9525/// [`crate::aplicacao::MeshPolicy`] struct's `rate_limit`
9526/// token-bucket-rate-limit axis — the `rateLimit:` field the M3
9527/// Aplicacao's `#[serde(rename_all = "camelCase")]` derive on
9528/// [`crate::aplicacao::MeshPolicy`] emits at the singleton `:politicas`
9529/// block, and the exact camelCase scalar (Rust field `rate_limit` →
9530/// serde-emitted `rateLimit`) every downstream rate-limit consumer
9531/// must probe on (the future M4 per-edge `:politicas` overlay
9532/// projection onto the mesh's per-backend token-bucket `(rate,
9533/// window)` decoder driven by the canonical
9534/// [`crate::aplicacao::rate_limit_codec`] unit-suffix bijection). Peer
9535/// of [`POLITICAS_KEY_TIMEOUT`] on the same
9536/// [`crate::aplicacao::MeshPolicy`] singleton serialized-key axis; see
9537/// [`POLITICAS_KEY_TIMEOUT`] for the full lift rationale.
9538pub const POLITICAS_KEY_RATE_LIMIT: &str = "rateLimit";
9539
9540/// Canonical camelCase JSON/YAML sub-key for the
9541/// [`crate::aplicacao::CircuitBreaker`] struct's `max_failures`
9542/// consecutive-failure-count axis — the `maxFailures:` field the M3
9543/// Aplicacao's `#[serde(rename_all = "camelCase")]` derive on
9544/// [`crate::aplicacao::CircuitBreaker`] emits inside the
9545/// [`POLITICAS_KEY_CIRCUIT_BREAKER`] sub-block, and the exact camelCase
9546/// scalar (Rust field `max_failures` → serde-emitted `maxFailures`,
9547/// the load-bearing non-trivial camelCase transform on this
9548/// [`CircuitBreaker`][cb] axis alongside the no-op
9549/// [`CIRCUIT_BREAKER_KEY_WINDOW`] sibling) every downstream breaker-
9550/// tuning consumer must probe on (the future M4 per-edge `:politicas`
9551/// overlay projection onto the mesh's per-backend
9552/// consecutive-failure-counter tripping threshold per
9553/// MESH-COMPOSITION.md §III.3 breaker semantics, the future
9554/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's admission-time
9555/// breaker cross-check against
9556/// [`crate::POLICY_BREAKER_MAX_FAILURES_MAX`], the future
9557/// `feira lint` per-`:politicas :circuit-breaker` bound-check gate).
9558///
9559/// [cb]: crate::aplicacao::CircuitBreaker
9560///
9561/// Peer of [`CIRCUIT_BREAKER_KEY_WINDOW`] on the same
9562/// [`crate::aplicacao::CircuitBreaker`] serialized-key axis; the two
9563/// consts together close the sub-block's typed-struct axis. Extends
9564/// the [`POLITICAS_KEY_CIRCUIT_BREAKER`] parent-axis lift (b55cca7)
9565/// one level deeper — the parent const names the outer sub-block key
9566/// the derive on [`crate::aplicacao::MeshPolicy`] emits, this pair
9567/// names the inner keys the derive on the payload type emits, so a
9568/// consumer walking `Value::get(POLITICAS_KEY_CIRCUIT_BREAKER)
9569/// .and_then(|v| v.get(CIRCUIT_BREAKER_KEY_MAX_FAILURES))` navigates
9570/// the whole [`crate::aplicacao::MeshPolicy`] breaker-tuning shape
9571/// entirely through lifted canonical byte-sequences with no inline
9572/// string literal at either level.
9573///
9574/// A future accidental `rename_all = "snake_case"` /
9575/// `"kebab-case"` / verbatim-field-name flip at the derive on
9576/// [`crate::aplicacao::CircuitBreaker`] would silently rebrand the
9577/// emitted key to `max_failures` / `max-failures` / `max_failures`
9578/// respectively, breaking every downstream
9579/// `Value::get(CIRCUIT_BREAKER_KEY_MAX_FAILURES)` consumer — with the
9580/// drift surfacing at apply time far from the derive-attr commit. The
9581/// identity pin
9582/// (`circuit_breaker_serde_keys_match_lifted_circuit_breaker_key_consts`
9583/// on the source-side type) catches drift at caixa-core build time.
9584///
9585/// Same discipline every peer camelCase serde-key lift carries
9586/// ([`M2_LIMITS_KEY_MEMORY`] etc. (d8b8b4f),
9587/// [`M2_BEHAVIOR_KEY_ON_INIT`] etc. (21fe462),
9588/// [`M2_UPGRADE_FROM_KEY_FROM`] / [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`]
9589/// (36ffe65), [`SUPERVISOR_KEY_ESTRATEGIA`] etc. (40cc4e5),
9590/// [`POLITICAS_KEY_TIMEOUT`] etc. (b55cca7)).
9591pub const CIRCUIT_BREAKER_KEY_MAX_FAILURES: &str = "maxFailures";
9592
9593/// Canonical camelCase JSON/YAML sub-key for the
9594/// [`crate::aplicacao::CircuitBreaker`] struct's `window`
9595/// failure-counter reset-window axis — the `window:` field the M3
9596/// Aplicacao's `#[serde(rename_all = "camelCase")]` derive on
9597/// [`crate::aplicacao::CircuitBreaker`] emits inside the
9598/// [`POLITICAS_KEY_CIRCUIT_BREAKER`] sub-block. The Rust field is
9599/// lowercase `window`; `#[serde(rename_all = "camelCase")]` is a
9600/// no-op on this axis and the emitted key equals the source-side
9601/// field name byte-for-byte. Peer of
9602/// [`CIRCUIT_BREAKER_KEY_MAX_FAILURES`] on the same
9603/// [`crate::aplicacao::CircuitBreaker`] serialized-key axis; see
9604/// [`CIRCUIT_BREAKER_KEY_MAX_FAILURES`] for the full lift rationale.
9605pub const CIRCUIT_BREAKER_KEY_WINDOW: &str = "window";
9606
9607/// Canonical `lareira-fleet-programs` values-schema key naming the
9608/// per-caixa entry sequence — the exact YAML key the fleet-programs
9609/// library chart's `values.yaml` reads as `programs:` (a sequence of
9610/// per-Servico entries the chart's `range` iterates over to emit one
9611/// `ComputeUnit` CR per entry). Two production consumers in
9612/// [`caixa_flux`] carry this key on the same fleet-programs schema
9613/// axis:
9614///
9615/// 1. [`caixa_flux::upsert_into_helmrelease_programs`] — the writer-
9616/// side upsert path on the aggregator-HelmRelease shape. Walks
9617/// `HelmRelease.spec.values.programs[]` under this exact key to
9618/// match by `metadata.name` and either replace-in-place or append.
9619///
9620/// 2. [`caixa_flux::upsert_into_programs_yaml`] — the writer-side
9621/// upsert path on the bare-values.yaml shape. Walks the
9622/// top-level `programs[]` sequence under the same key.
9623///
9624/// Until this lift landed both consumers carried the bare `"programs"`
9625/// byte inline — `upsert_into_helmrelease_programs`'s
9626/// `values_map.entry(Value::String("programs".into()))` at
9627/// `caixa-flux/src/lib.rs:539` and `upsert_into_programs_yaml`'s
9628/// `let programs_key = Value::String("programs".into());` at
9629/// `caixa-flux/src/lib.rs:591`. A future fleet-programs schema-key
9630/// rebrand (the library chart moving to plural `programas` for
9631/// Brazilian-Portuguese uniformity with the rest of the substrate's
9632/// surface, to a namespaced `pleme.pleme.io/programs` for multi-tenant
9633/// aggregator-values isolation, or to per-kind `servicos` / `aplicacaos`
9634/// splits once the schema grows past the flat sequence — the
9635/// ABSORPTION-ROADMAP.md M4 trajectory) without a coordinated edit
9636/// on both writer-side sites would silently emit an entry under one
9637/// key (e.g. `programas:`) while the peer-side upsert still probes
9638/// the prior key — the aggregator's `range .Values.programs` would
9639/// then iterate an empty sequence and every `ComputeUnit` CR would
9640/// silently vanish from the cluster's fleet, with the failure
9641/// surfacing as "the newly-deployed Servico's pods never spin up" far
9642/// from the rebrand commit's source. Lifting the literal to one
9643/// `&'static str` closes the drift footgun structurally — both
9644/// consumers read from the same memory, so any future rebrand reaches
9645/// both writer sites by construction and a CI build that re-introduces
9646/// a sibling inline `"programs"` literal trips the peer pinning tests
9647/// at the build-time fail-before-deploy posture every prior
9648/// load-bearing-string lift on this surface
9649/// ([`M3_KEY_PLACEMENT`] under the same `programs.yaml` per-entry
9650/// axis, [`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] / [`M2_KEY_UPGRADE_FROM`]
9651/// on the peer M2 overlay-key surfaces, [`DEFAULT_NAMESPACE`]
9652/// / [`DEFAULT_LIBRARY_NAME`] / [`DEFAULT_SERVICO_PORT`] on the peer
9653/// shared-string / port surfaces) establishes.
9654///
9655/// Peer of [`M3_KEY_PLACEMENT`] on the same fleet-programs values
9656/// schema — that constant names the per-entry overlay key, this one
9657/// names the top-level array key both writer verbs upsert into.
9658pub const FLEET_PROGRAMS_KEY_PROGRAMS: &str = "programs";
9659
9660/// Canonical `lareira-fleet-programs` values-schema key naming the
9661/// per-entry name discriminator — the `name:` field the library
9662/// chart's `range .Values.programs` step reads to key each rendered
9663/// `ComputeUnit` CR's `metadata.name` off, and the exact key both
9664/// writer-side upsert paths in [`caixa_flux`] match against to
9665/// replace-in-place-vs-append. Peer of [`FLEET_PROGRAMS_KEY_PROGRAMS`]
9666/// on the same fleet-programs values schema — that constant names
9667/// the top-level array key, this one names the per-entry name-axis
9668/// both writer verbs walk the array by.
9669///
9670/// Two production consumers write this key:
9671///
9672/// 1. [`caixa_flux::programs_yaml_entry`] — the emit-side per-Servico
9673/// entry-builder writes the per-entry name-axis at this exact key
9674/// (seeded from the Caixa's `nome`), at
9675/// `caixa-flux/src/lib.rs`'s `entry.insert("name".into(), …)` call.
9676/// 2. [`caixa_mesh::programs_for_aplicacao`] — the Aplicacao-side
9677/// per-`:membros` entry-builder writes the peer per-entry name-axis
9678/// at the same key (seeded from each `:membros` entry's `:caixa`
9679/// binding), at `caixa-mesh/src/lib.rs`'s per-member
9680/// `entry.insert("name".into(), …)` call.
9681///
9682/// Two production consumers read this key:
9683///
9684/// 3. [`caixa_flux::upsert_into_helmrelease_programs`] — the writer-
9685/// side upsert path on the aggregator-HelmRelease shape reads the
9686/// per-entry key twice (new-entry's `.get("name")` extract +
9687/// per-slot `.get("name")` match-vs-new_name inside
9688/// `HelmRelease.spec.values.programs[]`), plus a
9689/// `Error::MissingField("name")` diagnostic naming the same axis.
9690/// 4. [`caixa_flux::upsert_into_programs_yaml`] — the writer-side
9691/// upsert path on the bare-values.yaml shape reads the same per-
9692/// entry key over the top-level `programs[]` sequence via the
9693/// same three-site (extract + match + `MissingField`) shape.
9694///
9695/// Until this lift landed both writers carried the bare `"name"`
9696/// byte inline at every read + `Error::MissingField("name")`
9697/// diagnostic site, and both emitters carried the same bare byte at
9698/// their `entry.insert("name".into(), …)` call. A future fleet-
9699/// programs schema-key rebrand on the per-entry name-discriminator
9700/// axis (per the same trajectory [`FLEET_PROGRAMS_KEY_PROGRAMS`]'s
9701/// doc-comment names — the `lareira-fleet-programs` library chart
9702/// moving its per-entry name-axis to `nome:` for Brazilian-Portuguese
9703/// uniformity with the rest of the substrate's surface, or to a
9704/// namespaced `pleme.pleme.io/name` for multi-tenant aggregator
9705/// values isolation, or to per-kind `servico-name` / `aplicacao-name`
9706/// splits once the schema grows past the flat sequence — the
9707/// ABSORPTION-ROADMAP.md M4 trajectory) without a coordinated edit
9708/// across all four sites would silently split the schema: one
9709/// emitter would write under `nome:` while the peer-side upsert
9710/// still probed `name:` — the aggregator's `range .Values.programs`
9711/// would then iterate entries whose per-entry name-axis the library
9712/// chart's `metadata.name` templating reads as empty (or match
9713/// against the wrong entry on upsert), and every rendered
9714/// `ComputeUnit` CR would silently collide on empty
9715/// `metadata.name` or vanish at the aggregator's per-entry name-
9716/// keyed reduce step, with the failure surfacing as "the Servico's
9717/// pods never spin up under the expected name" far from the rebrand
9718/// commit's source. Lifting the literal to one `&'static str` closes
9719/// the drift footgun structurally — every consumer reads the same
9720/// memory, so any future rebrand reaches all four sites by
9721/// construction and a CI build that re-introduces a sibling inline
9722/// `"name"` literal trips the peer pinning tests at the build-time
9723/// fail-before-deploy posture every prior load-bearing-string lift
9724/// on this surface ([`FLEET_PROGRAMS_KEY_PROGRAMS`] on the sibling
9725/// fleet-programs top-level array-key axis, [`M3_KEY_PLACEMENT`] /
9726/// [`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] / [`M2_KEY_UPGRADE_FROM`]
9727/// on the peer per-entry overlay-key surfaces) establishes.
9728///
9729/// Byte-identical to [`KUBE_KEY_NAME`] today — both resolve to the
9730/// same three-byte `"name"` literal — but semantically distinct:
9731/// [`KUBE_KEY_NAME`] names the K8s CR canonical `metadata.name` axis
9732/// (every rendered CR's identity discriminator, spelled per the K8s
9733/// apiserver's OpenAPI v3 schema), while this constant names the
9734/// `lareira-fleet-programs` library chart's per-entry name-axis
9735/// (spelled per the chart's `values.schema.json` — a separate schema
9736/// contract). Splitting the two lets each schema's future rebrand
9737/// land independently at its canonical const definition without
9738/// coupling the K8s CR canonical-key axis to the fleet-programs
9739/// values-schema axis (or vice versa).
9740pub const FLEET_PROGRAMS_KEY_NAME: &str = "name";
9741
9742/// Canonical `lareira-fleet-programs` values-schema key naming the
9743/// per-entry parent-Aplicacao-graph discriminator — the `aplicacao:`
9744/// annotation the substrate operator's fleet-aggregator reads to
9745/// group each rendered `programs[]` entry back onto the parent
9746/// Aplicacao its M3 `:membros` list contributed it, and the exact
9747/// key downstream fleet consumers (per-graph observability filters,
9748/// per-Aplicacao Cilium-policy reconciliation, per-graph Gateway/
9749/// `HTTPRoute` attachment) walk to project the flat `programs[]`
9750/// sequence back onto its typed Aplicacao graph.
9751///
9752/// Peer of [`FLEET_PROGRAMS_KEY_NAME`] and [`M3_KEY_PLACEMENT`] on
9753/// the same fleet-programs values schema — `FLEET_PROGRAMS_KEY_NAME`
9754/// carries the per-entry Servico-name discriminator (the `:membros`
9755/// row's own `:caixa` binding), `M3_KEY_PLACEMENT` carries the M3
9756/// placement overlay cloned per entry, and this constant carries the
9757/// per-entry parent-Aplicacao-nome annotation the aggregator uses to
9758/// group entries back into their Aplicacao graph. Together the three
9759/// per-entry keys (plus the top-level [`FLEET_PROGRAMS_KEY_PROGRAMS`]
9760/// array key) name every axis one `programs[]` entry the caixa-mesh
9761/// fan-out emits contributes to the substrate operator's read shape.
9762///
9763/// One production consumer writes this key:
9764/// [`caixa_mesh::programs_for_aplicacao`] — the Aplicacao-side
9765/// per-`:membros` entry-builder writes the parent-Aplicacao-nome
9766/// annotation at this exact key (seeded from the enclosing Caixa's
9767/// `:nome`), at `caixa-mesh/src/lib.rs`'s per-member
9768/// `entry.insert("aplicacao".into(), …)` call. Unlike the peer
9769/// [`FLEET_PROGRAMS_KEY_NAME`] axis (written by both caixa-flux's
9770/// per-Servico entry builder and caixa-mesh's per-`:membros` builder
9771/// — a Servico rendered standalone has no parent-Aplicacao annotation
9772/// to carry), the parent-Aplicacao-nome annotation is emitted only
9773/// by the caixa-mesh Aplicacao-side fan-out — Servicos rendered
9774/// standalone through the caixa-flux path leave the annotation
9775/// absent, which is exactly the discriminator the operator's
9776/// aggregator uses to distinguish Aplicacao-graph-scoped entries
9777/// from stand-alone Servico entries.
9778///
9779/// Until this lift landed the caixa-mesh emitter carried the bare
9780/// `"aplicacao"` byte inline at its `entry.insert("aplicacao".into(),
9781/// …)` call, and the peer in-file test probe (the
9782/// `programs_for_aplicacao_annotates_with_parent_nome` fixture's
9783/// `e.get("aplicacao").and_then(|v| v.as_str())` navigation) carried
9784/// the same bare byte at its readback site. A future fleet-programs
9785/// schema-key rebrand on the per-entry parent-Aplicacao-annotation
9786/// axis (per the same trajectory the sibling [`FLEET_PROGRAMS_KEY_NAME`]
9787/// doc-comment names — the `lareira-fleet-programs` library chart
9788/// moving its per-entry parent-graph-annotation to a namespaced
9789/// `pleme.pleme.io/aplicacao` for multi-tenant aggregator isolation
9790/// once the M4 flat-`programs[]`-per-cluster shape splits into
9791/// per-graph sequences, or to `graph:` for parity with the M3
9792/// `:contratos` graph nomenclature, or to typed `parent:` on the
9793/// ABSORPTION-ROADMAP.md M4 hierarchical-fleet trajectory) without
9794/// a coordinated edit across both sites would silently split the
9795/// schema: the emitter would write under the drifted key while the
9796/// aggregator's per-Aplicacao filter would still read `aplicacao:`
9797/// — every fan-out entry would silently vanish from its parent
9798/// graph's projected view at the aggregator's per-Aplicacao reduce
9799/// step, with the failure surfacing as "the Aplicacao's Servicos
9800/// never appear in per-graph observability filters" far from the
9801/// rebrand commit's source. Lifting the literal to one `&'static
9802/// str` closes the drift footgun structurally — every consumer
9803/// reads the same memory, so any future rebrand reaches both sites
9804/// by construction and a CI build that re-introduces a sibling
9805/// inline `"aplicacao"` literal trips the peer pinning tests at the
9806/// build-time fail-before-deploy posture every prior load-bearing-
9807/// string lift on this surface ([`FLEET_PROGRAMS_KEY_PROGRAMS`] on
9808/// the sibling fleet-programs top-level array-key axis,
9809/// [`FLEET_PROGRAMS_KEY_NAME`] on the peer per-entry name-
9810/// discriminator axis, [`M3_KEY_PLACEMENT`] / [`M2_KEY_LIMITS`] /
9811/// [`M2_KEY_BEHAVIOR`] / [`M2_KEY_UPGRADE_FROM`] on the peer per-
9812/// entry overlay-key surfaces) establishes.
9813///
9814/// Byte-identical to the string form of the
9815/// [`caixa_core::CaixaKind::Aplicacao`] enum variant today — both
9816/// resolve to the same nine-byte `"aplicacao"` literal — but
9817/// semantically distinct: `CaixaKind`'s `Aplicacao` variant names
9818/// the `:kind` enum arm (the typed kind-tag every `defcaixa` selects
9819/// among), while this constant names the `lareira-fleet-programs`
9820/// library chart's per-entry parent-graph-annotation axis (spelled
9821/// per the chart's `values.schema.json` — a separate schema
9822/// contract, one whose future rebrand can land independently of the
9823/// kind-tag axis). Splitting the two lets each schema's future
9824/// rebrand land at its canonical const/variant definition without
9825/// coupling the `:kind` enum-tag axis to the fleet-programs values-
9826/// schema axis (or vice versa) — the same discipline the sibling
9827/// [`FLEET_PROGRAMS_KEY_NAME`] doc-comment establishes vs.
9828/// [`KUBE_KEY_NAME`] on the K8s CR canonical name-axis.
9829pub const FLEET_PROGRAMS_KEY_APLICACAO: &str = "aplicacao";
9830
9831/// Canonical `lareira-fleet-programs` values-schema key naming the
9832/// per-entry version-constraint discriminator — the `versao:` field
9833/// each rendered `programs[]` entry carries so the substrate operator's
9834/// per-`:membros` resolver can resolve each member's caixa.lisp against
9835/// its Aplicacao-declared version-constraint. Every `:membros` row's
9836/// `:versao` (the semver / range constraint the M3 Aplicacao names on
9837/// its `:membros` list) flows through this exact key on the emitted
9838/// per-entry programs.yaml row.
9839///
9840/// Peer of [`FLEET_PROGRAMS_KEY_NAME`], [`FLEET_PROGRAMS_KEY_APLICACAO`],
9841/// and [`M3_KEY_PLACEMENT`] on the same fleet-programs values schema —
9842/// `FLEET_PROGRAMS_KEY_NAME` carries the per-entry Servico-name
9843/// discriminator (each `:membros` row's `:caixa` binding),
9844/// `FLEET_PROGRAMS_KEY_APLICACAO` carries the per-entry parent-graph
9845/// annotation, `M3_KEY_PLACEMENT` carries the M3 placement overlay
9846/// cloned per entry, and this constant carries the per-entry version-
9847/// constraint the operator's resolver reads to fetch the correct
9848/// caixa.lisp release. Together the four per-entry keys (plus the
9849/// top-level [`FLEET_PROGRAMS_KEY_PROGRAMS`] array key) name every axis
9850/// one `programs[]` entry the caixa-mesh fan-out emits contributes to
9851/// the substrate operator's read shape.
9852///
9853/// One production consumer writes this key:
9854/// [`caixa_mesh::programs_for_aplicacao`] — the Aplicacao-side
9855/// per-`:membros` entry-builder writes the per-entry version-
9856/// constraint at this exact key (seeded from each `:membros` row's
9857/// `:versao` binding), at `caixa-mesh/src/lib.rs`'s per-member
9858/// `entry.insert("versao".into(), …)` call. Unlike the peer
9859/// [`FLEET_PROGRAMS_KEY_NAME`] axis (written by both caixa-flux's
9860/// per-Servico entry builder and caixa-mesh's per-`:membros` builder
9861/// — a Servico rendered standalone through the caixa-flux path resolves
9862/// its own `:versao` from its `caixa.lisp` root and hands it to the
9863/// resolver via a distinct path), the per-`:membros` version-constraint
9864/// annotation is emitted only by the caixa-mesh Aplicacao-side fan-out.
9865///
9866/// Until this lift landed the caixa-mesh emitter carried the bare
9867/// `"versao"` byte inline at its `entry.insert("versao".into(), …)`
9868/// call — a partial single-source where three of four per-entry
9869/// fleet-programs axis keys were canonical
9870/// ([`FLEET_PROGRAMS_KEY_NAME`] via 030a63f,
9871/// [`FLEET_PROGRAMS_KEY_APLICACAO`] via cc69ac2, [`M3_KEY_PLACEMENT`])
9872/// and the fourth was scattered. Lifting the fourth key completes the
9873/// fleet-programs values-schema single-sourcing across every per-entry
9874/// axis; every future per-graph aggregator, per-`:membros` resolver,
9875/// per-entry version-constraint consumer inherits the same `&'static
9876/// str` by construction. A future schema-key rebrand on the per-entry
9877/// version-constraint axis (a namespaced `pleme.pleme.io/versao` for
9878/// multi-tenant aggregator isolation, or `version:` for parity with
9879/// upstream conventions, or typed `constraint:` on the ABSORPTION-
9880/// ROADMAP.md M4 typed-resolver trajectory) lands at the one const
9881/// rather than scattered across every future per-emitter/per-resolver
9882/// site.
9883///
9884/// Byte-identical to the `Membro::versao` field name on the M3
9885/// [`AplicacaoSpec`](aplicacao::AplicacaoSpec) today — both resolve to the same six-byte `"versao"`
9886/// literal — but semantically distinct: `Membro::versao` names the
9887/// author-side `:versao` slot on each `:membros` row (the typed
9888/// version-constraint slot every `defcaixa` populates on its
9889/// `:membros` list), while this constant names the
9890/// `lareira-fleet-programs` library chart's per-entry version-
9891/// constraint axis (spelled per the chart's `values.schema.json` — a
9892/// separate schema contract, one whose future rebrand can land
9893/// independently of the author-side slot-name axis). Splitting the two
9894/// lets each schema's future rebrand land at its canonical const /
9895/// field definition without coupling the author-side slot-name axis to
9896/// the fleet-programs values-schema axis (or vice versa) — the same
9897/// discipline the sibling [`FLEET_PROGRAMS_KEY_APLICACAO`] doc-comment
9898/// establishes vs. the [`CaixaKind::Aplicacao`] enum-variant tag.
9899pub const FLEET_PROGRAMS_KEY_VERSAO: &str = "versao";
9900
9901/// Canonical pleme-io label namespace prefix. Every cluster object
9902/// emitted by any caixa-side renderer that needs to carry the
9903/// pleme-io workload identity uses this prefix; runtime label
9904/// injectors (`lareira-fleet-programs` chart's pod template,
9905/// `pleme-computeunit` library chart's identity sidecar, the
9906/// caixa-operator's pod-mutating webhook) and runtime label
9907/// consumers (Cilium identity-based policy, Hubble flow attribution,
9908/// `caixa-mesh`'s policy / Gateway emission, future
9909/// observability/tracing renderers) all spell the same prefix
9910/// exactly the same way — drift between *any* of those = a
9911/// CiliumNetworkPolicy that matches no pods, a Hubble flow that
9912/// can't be correlated to its workload, an OpenTelemetry resource
9913/// attribute that doesn't join to its caixa lacre.
9914///
9915/// Lifted to a const so a future top-level rebrand or multi-tenant
9916/// label-namespace migration is a one-line edit, not a search-and-
9917/// replace across every renderer crate.
9918pub const PLEME_LABEL_PREFIX: &str = "pleme.pleme.io";
9919
9920/// Canonical pleme-io label key naming the **Aplicacao** the workload
9921/// belongs to. Together with [`LABEL_PROGRAM`] this is the load-bearing
9922/// identity tuple every per-Aplicacao mesh renderer (Cilium, Gateway,
9923/// future caixa-otel) keys off — `(LABEL_APLICACAO, LABEL_PROGRAM)` =
9924/// the unique workload selector inside one cluster.
9925pub const LABEL_APLICACAO: &str = "pleme.pleme.io/aplicacao";
9926
9927/// Canonical pleme-io label key naming the **program** (i.e. the
9928/// caixa Servico's `:nome`) a pod runs. `LABEL_APLICACAO` +
9929/// `LABEL_PROGRAM` together pick exactly one workload identity in one
9930/// cluster. Used as the `matchLabels` axis on every Cilium
9931/// `endpointSelector` / `fromEndpoints` rule and on Gateway API
9932/// `backendRefs` selectors emitted by [`crate`]'s downstream
9933/// renderers.
9934pub const LABEL_PROGRAM: &str = "pleme.pleme.io/program";
9935
9936/// Canonical pleme-io label key naming the **contrato** (the M3
9937/// `:contratos` edge: `<de>-to-<para>`) a CiliumNetworkPolicy enforces.
9938/// Carried on the policy's *own* labels (not on workload pods) so
9939/// Hubble + cluster operators can group flows by typed contrato edge,
9940/// not just by source/destination pod identity.
9941pub const LABEL_CONTRATO: &str = "pleme.pleme.io/contrato";
9942
9943/// Canonical M3 `:contratos` edge-direction separator byte-string every
9944/// caixa-mesh emitter that encodes a typed edge as a K8s-name-shaped
9945/// scalar (the [`LABEL_CONTRATO`] label value carried on every
9946/// per-`(:de, :para)` `CiliumNetworkPolicy`'s `metadata.labels`, and
9947/// the per-`(:de, :para)` `CiliumNetworkPolicy`'s `metadata.name`
9948/// itself) inserts between the `:de` and `:para` halves of the typed
9949/// edge tuple. Load-bearing on both the writer half (the CNP renderer)
9950/// and the reader half (Hubble flow grouping by contrato label,
9951/// per-CNP operator filters, `kubectl get cnp -l pleme.pleme.io/contrato=<de>-to-<para>`
9952/// grep-by-label). Until this lift landed the `-to-` byte-string sat
9953/// in two verbatim inline-`format!` sites at the caixa-mesh
9954/// `cilium_network_policies` emitter — one at the
9955/// [`LABEL_CONTRATO`] `labels.insert(...)` call and one at the
9956/// [`kube_resource_skeleton`] `name:` argument — with no compile-time
9957/// link between them. A future edge-encoding rebrand (`-to-` → `->`
9958/// for compactness, `-to-` → `_to_` to reserve `-` for embedded
9959/// DNS-1123-label boundaries, an edge-direction-arrow migration to
9960/// UTF-8 shapes) would have had to be threaded through both sites in
9961/// lockstep or the two would silently split: one CNP's `metadata.name`
9962/// keys off the drifted encoding, its own `metadata.labels.pleme.pleme.io/contrato`
9963/// value keys off the original, and every operator-side grep-by-label
9964/// query (`kubectl get cnp -l pleme.pleme.io/contrato=cart-to-catalog`)
9965/// finds the label but the resulting CNP's `metadata.name` no longer
9966/// matches the queried edge encoding. Every downstream consumer that
9967/// joins the two axes (the M4 mesh-graph audit, the future Hubble-side
9968/// contrato-flow renderer, the operator's per-edge policy inspector)
9969/// silently loses the join. Lifted onto one `&'static str` so a future
9970/// edge-encoding rebrand lands at one const, and every downstream
9971/// consumer picks up the new encoding by construction.
9972pub const CONTRATO_EDGE_LABEL_SEPARATOR: &str = "-to-";
9973
9974/// Canonical M3 `:contratos` edge label value — the `<de>-to-<para>`
9975/// K8s-name-shaped scalar every per-`(:de, :para)` `CiliumNetworkPolicy`
9976/// document carries at its `metadata.labels.pleme.pleme.io/contrato`
9977/// axis (the [`LABEL_CONTRATO`] label key). Composes on the lifted
9978/// [`CONTRATO_EDGE_LABEL_SEPARATOR`] byte-string so a future
9979/// edge-encoding rebrand lands at one canonical composition, and every
9980/// downstream consumer that grep-by-label picks up the new encoding by
9981/// construction.
9982///
9983/// Peer of [`cilium_network_policy_name`] on the sibling per-`(:de,
9984/// :para)` CNP `metadata.name` encoding axis — the CNP name composes
9985/// on this helper's output (the CNP `metadata.name` is
9986/// `format!("{aplicacao}-{contrato_edge_label(de, para)}")`), so a
9987/// future rebrand on either axis reaches both consumers through one
9988/// canonical composition instead of a coordinated two-site rewrite of
9989/// caixa-mesh's `cilium_network_policies` per-`(:de, :para)` group's
9990/// [`LABEL_CONTRATO`] `labels.insert(...)` call and the
9991/// [`kube_resource_skeleton`] `name:` argument.
9992#[must_use]
9993pub fn contrato_edge_label(de: &str, para: &str) -> String {
9994 format!("{de}{CONTRATO_EDGE_LABEL_SEPARATOR}{para}")
9995}
9996
9997/// Canonical per-`(:de, :para)` `CiliumNetworkPolicy` `metadata.name`
9998/// K8s-name-shaped scalar every caixa-mesh `cilium_network_policies`
9999/// emitter mounts its per-edge CNP under. Composes on the lifted
10000/// [`contrato_edge_label`] helper (the CNP name is the parent
10001/// Aplicacao's `:nome` joined to the contrato-edge-label by a
10002/// canonical `-` separator: `format!("{aplicacao}-{edge}")`), so the
10003/// two axes — the CNP `metadata.labels.pleme.pleme.io/contrato` value
10004/// and the CNP `metadata.name` — share one canonical
10005/// edge-encoding source of truth ([`CONTRATO_EDGE_LABEL_SEPARATOR`]).
10006///
10007/// Peer of [`contrato_edge_label`] on the parent-composition axis —
10008/// the two writer-side helpers close the canonical
10009/// `(LABEL_CONTRATO-value, metadata.name)` per-CNP identity pair so a
10010/// future edge-encoding rebrand or a per-emitter typo can't silently
10011/// split the two axes at emit time and orphan every operator-side
10012/// grep-by-label query at apply time far from the source caixa.lisp.
10013///
10014/// The `aplicacao` prefix scopes the emitted CNP to its owning
10015/// Aplicacao (so two Aplicacaos hosting a same-named `(de, para)`
10016/// contrato edge — `checkout-cart-to-catalog` vs
10017/// `orders-cart-to-catalog` — land at distinct CNP `metadata.name`s
10018/// with no `kubectl apply` collision at the shared namespace).
10019#[must_use]
10020pub fn cilium_network_policy_name(aplicacao: &str, de: &str, para: &str) -> String {
10021 let edge = contrato_edge_label(de, para);
10022 format!("{aplicacao}-{edge}")
10023}
10024
10025/// Canonical per-`:entrada` `HTTPRoute` `metadata.name` K8s-name-shaped
10026/// scalar every caixa-mesh `gateway_routes` emitter mounts its
10027/// per-`:entrada` HTTPRoute under. Composes the parent Aplicacao's
10028/// `:nome` and the `:entrada :para` destination Servico's `:nome` on a
10029/// canonical `-` separator (`format!("{aplicacao}-{para}")`), so the
10030/// per-`(:aplicacao, :entrada.para)` HTTPRoute identity axis lives at
10031/// one composer instead of a verbatim inline `format!("{}-{}",
10032/// caixa.nome, entrada.para)` at the [`caixa_mesh::gateway_routes`]
10033/// [`kube_resource_skeleton`] `name:` argument.
10034///
10035/// Peer of [`cilium_network_policy_name`] on the sibling per-Aplicacao
10036/// per-CR K8s-name-shaped-identity-scalar axis: the CNP name composer
10037/// carries the per-`(:de, :para)` L4/L7 policy CR name and this
10038/// composer carries the per-`:entrada` L7 route CR name; both share
10039/// the same "aplicacao-prefixed sub-identity" discipline (a per-CR
10040/// identity scalar keyed off the parent Aplicacao's `:nome` joined to
10041/// the per-CR sub-axis by a canonical `-` separator) so a future
10042/// substrate-side per-Aplicacao Gateway API axis extension
10043/// (`GRPCRoute` on grpc-shaped `:contratos` payloads once the sibling
10044/// [`WitTarget`] variant lands, `TCPRoute` on the sibling l4-only
10045/// tcp-shaped payload axis, per-`:entrada` `HTTPRouteFilter` /
10046/// `BackendTLSPolicy` overlays the Gateway API v1.x per-route policy
10047/// extension surface acknowledges) reaches the shared "aplicacao-prefix
10048/// + sub-axis + canonical `-` separator" naming discipline through
10049/// this composer's peer-shape by construction. Until this lift landed
10050/// the HTTPRoute `metadata.name` axis sat as a verbatim inline
10051/// `format!("{}-{}", caixa.nome, entrada.para)` at the
10052/// [`caixa_mesh::gateway_routes`] emitter (with an in-file test-side
10053/// probe pinning the expected `checkout-cart` shape by verbatim
10054/// literal), and any future name-encoding rebrand on this axis
10055/// (`<aplicacao>-<para>` → `<aplicacao>-httproute-<para>` for
10056/// operator-side per-CR-kind disambiguation once the sibling
10057/// GRPCRoute / TCPRoute lands and their names would otherwise collide,
10058/// `<aplicacao>-<para>` → `<aplicacao>.<para>` on a DNS-1123-subdomain-
10059/// safe axis migration, a per-namespace scoping prefix for
10060/// multi-tenant Aplicacao hosting) would have had to be threaded
10061/// through both sites in lockstep or the HTTPRoute `metadata.name`
10062/// silently split from the operator-side grep-by-name / `kubectl get
10063/// httproute -n tatara-system <aplicacao>-<para>` lookup encoding at
10064/// apply time far from the source caixa.lisp.
10065///
10066/// The `aplicacao` prefix scopes the emitted HTTPRoute to its owning
10067/// Aplicacao (so two Aplicacaos hosting a same-named `:entrada :para`
10068/// destination — `checkout-cart` vs `orders-cart` — land at distinct
10069/// HTTPRoute `metadata.name`s with no `kubectl apply` collision at the
10070/// shared namespace, mirroring the peer CNP `metadata.name` collision
10071/// posture the sibling [`cilium_network_policy_name`] composer's
10072/// docstring names).
10073#[must_use]
10074pub fn gateway_api_http_route_name(aplicacao: &str, para: &str) -> String {
10075 format!("{aplicacao}-{para}")
10076}
10077
10078/// Canonical K8s API key naming the resource's API-version selector
10079/// (e.g. `cilium.io/v2`, `gateway.networking.k8s.io/v1`,
10080/// `wasm.pleme.io/v1alpha1`). Lifted to a const so a future API-server
10081/// rename or a multi-version-skew migration is a one-line edit, not a
10082/// search-and-replace across every per-target renderer.
10083pub const KUBE_KEY_API_VERSION: &str = "apiVersion";
10084/// Canonical K8s API key naming the resource's kind discriminator
10085/// (e.g. `CiliumNetworkPolicy`, `Gateway`, `HTTPRoute`, `ComputeUnit`).
10086pub const KUBE_KEY_KIND: &str = "kind";
10087/// Canonical K8s API key naming the resource's metadata block.
10088pub const KUBE_KEY_METADATA: &str = "metadata";
10089/// Canonical K8s API key naming the resource's name (under metadata).
10090pub const KUBE_KEY_NAME: &str = "name";
10091/// Canonical K8s API key naming the resource's namespace (under metadata).
10092pub const KUBE_KEY_NAMESPACE: &str = "namespace";
10093/// Canonical K8s API key naming the resource's labels (under metadata).
10094pub const KUBE_KEY_LABELS: &str = "labels";
10095/// Canonical K8s API key naming the resource's per-kind body (sibling
10096/// to [`KUBE_KEY_METADATA`] at the K8s CR top level). Every typed
10097/// substrate renderer that materializes a CR populates `spec.*` from
10098/// the source caixa.lisp — caixa-mesh's `cilium_network_policies`
10099/// per-`(:de, :para)` `CiliumNetworkPolicy` emitter (the policy's
10100/// `endpointSelector` / `ingress` block lives under spec),
10101/// caixa-mesh's `gateway_routes` `Gateway` + `HTTPRoute` emitter (the
10102/// listeners / rules / parentRefs block lives under spec),
10103/// caixa-flux's `programs_yaml_entry` + `upsert_into_helmrelease_programs`
10104/// (the fleet `HelmRelease`'s `spec.values.programs[]` axis),
10105/// caixa-helm's `values.yaml` builder (the upstream ComputeUnit YAML's
10106/// `spec.*` axis the rendered `lareira-<nome>` chart re-routes through
10107/// the library alias). Spelled exactly as the K8s apiserver expects
10108/// (the canonical OpenAPI v3 schema property name K8s machinery
10109/// validates against on every CR registration), so the rendered YAML
10110/// round-trips through every K8s schema parser without per-renderer
10111/// string drift. Lifted on the trajectory the peer
10112/// [`KUBE_KEY_API_VERSION`] / [`KUBE_KEY_KIND`] /
10113/// [`KUBE_KEY_METADATA`] / [`KUBE_KEY_NAME`] / [`KUBE_KEY_NAMESPACE`]
10114/// / [`KUBE_KEY_LABELS`] / [`KUBE_KEY_MATCH_LABELS`] canonical-K8s-
10115/// API-key constants establish.
10116pub const KUBE_KEY_SPEC: &str = "spec";
10117/// Canonical K8s API key naming the `matchLabels` axis of a
10118/// [`LabelSelector`][k8s-ls] — the equality-based projection of the
10119/// selector schema (the other axis, `matchExpressions`, is set-based
10120/// and intentionally out-of-scope for the V0 [`label_selector`]
10121/// helper). Spelled exactly as the K8s apiserver expects (camelCase
10122/// `matchLabels`, not `match_labels` / `MatchLabels` / `match-labels`)
10123/// so the rendered YAML round-trips through every K8s schema parser
10124/// (Cilium CRDs, Gateway API, `ComputeUnit`, future
10125/// `mesh.pleme.io/v1alpha1/Aplicacao`) without per-renderer string
10126/// drift.
10127///
10128/// [k8s-ls]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#labelselector-v1-meta
10129pub const KUBE_KEY_MATCH_LABELS: &str = "matchLabels";
10130
10131/// Canonical K8s API key naming the per-CR **`rules` collection** axis —
10132/// the container the apiserver-side OpenAPI schema for every rule-shaped
10133/// CR (Cilium L7 `spec.ingress[].toPorts[].rules`, Gateway API
10134/// `HTTPRoute.spec.rules[]`, RBAC `Role.rules[]` /
10135/// `ClusterRole.rules[]`, and every future rule-list-shaped CR the M4
10136/// `mesh.pleme.io/v1alpha1/Aplicacao` materializer + the per-edge
10137/// `CiliumClusterwideEnvoyConfig` emitter will land on) mounts the
10138/// per-CR list of match/action rules under. Spelled exactly as the K8s
10139/// apiserver expects (lowercase `rules`, not `Rules` / `rule` /
10140/// `ruleset`) so the rendered YAML round-trips through every K8s schema
10141/// parser without per-renderer string drift.
10142///
10143/// Two production-code call sites in this crate's downstream
10144/// [`caixa-mesh`][cm] renderer carry this key on the same
10145/// K8s-rule-list-axis surface (both landing sites lived at inline
10146/// `"rules".into()` before this lift):
10147///
10148/// 1. `cilium_network_policies` — the per-`(:de, :para)`
10149/// `CiliumNetworkPolicy` emitter's per-`toPorts[]` `rules:` mapping
10150/// (the Cilium L7 rule-list container that carries the `http:` /
10151/// `kafka:` / `dns:` per-protocol L7 rules the Cilium data plane
10152/// dispatches on).
10153/// 2. `gateway_routes` — the `HTTPRoute` emitter's top-level
10154/// `spec.rules[]` sequence (the Gateway API rule-list container that
10155/// carries the per-rule `matches[]` + `backendRefs[]` + timeouts /
10156/// retries overlay the gateway-class-controller dispatches on).
10157///
10158/// Five test-side traversal sites in the same renderer navigate the
10159/// rendered mesh bundle's per-CR `rules:` axis to pin per-CR L7-rule /
10160/// Gateway-API-rule presence, absence, and content invariants (the
10161/// `.get("rules")` retrievals under `toPorts[]` on the L7 policy pins
10162/// and under `spec` on the HTTPRoute pins). All seven sites now route
10163/// through this const so a future K8s CRD schema rebrand on the shared
10164/// axis (or the canonical typo footgun `"Rules"` / `"rule"` /
10165/// `"ruleset"`) surfaces at this one const rather than as an admission-
10166/// time silent drop across two distinct CR emitters.
10167///
10168/// Lifted on the trajectory the peer [`KUBE_KEY_API_VERSION`] /
10169/// [`KUBE_KEY_KIND`] / [`KUBE_KEY_METADATA`] / [`KUBE_KEY_NAME`] /
10170/// [`KUBE_KEY_NAMESPACE`] / [`KUBE_KEY_LABELS`] / [`KUBE_KEY_SPEC`] /
10171/// [`KUBE_KEY_MATCH_LABELS`] canonical-K8s-API-key constants establish
10172/// — extends the K8s-CR top-level `(apiVersion, kind, metadata, spec)`
10173/// axis quartet + the nested `metadata.{name, namespace, labels}`
10174/// triplet + the `LabelSelector.matchLabels` selector-projection axis
10175/// onto the load-bearing nested `spec.rules[]` / `toPorts[].rules`
10176/// rule-list container axis every downstream L7-policy /
10177/// HTTPRoute-rule-dispatch consumer of the rendered mesh bundle keys
10178/// off.
10179///
10180/// [cm]: ../../caixa_mesh/index.html
10181pub const KUBE_KEY_RULES: &str = "rules";
10182
10183/// Canonical K8s API key naming the per-CR **L4 port** scalar axis —
10184/// the field the apiserver-side OpenAPI schema for every port-carrying
10185/// CR body-position (Cilium L7 `spec.ingress[].toPorts[].ports[].port`
10186/// per-port-tuple L4 port number, Gateway API
10187/// `Gateway.spec.listeners[].port` per-listener L4 port number,
10188/// Gateway API `HTTPRoute.spec.rules[].backendRefs[].port` per-rule
10189/// per-backend L4 port number, and every future port-shaped CR body-
10190/// position the M4 `mesh.pleme.io/v1alpha1/Aplicacao` materializer +
10191/// the per-edge `CiliumClusterwideEnvoyConfig` emitter will land on)
10192/// mounts the L4 port value under. Spelled exactly as the K8s
10193/// apiserver expects (lowercase `port`, not `Port` / `portNumber` /
10194/// `portValue` / `targetPort` — the L4-port-number axis, distinct
10195/// from the `targetPort` L4-forwarding-destination axis on the K8s
10196/// Service CRD that lives on a sibling field name the port-value
10197/// axis is not) so the rendered YAML round-trips through every K8s
10198/// schema parser without per-renderer string drift.
10199///
10200/// Three production-code call sites in this crate's downstream
10201/// [`caixa-mesh`][cm] renderer carry this key on the same
10202/// K8s-L4-port-scalar-axis surface (all three landing sites lived at
10203/// inline `"port".into()` before this lift):
10204///
10205/// 1. `cilium_network_policies` — the per-`(:de, :para)`
10206/// `CiliumNetworkPolicy` emitter's per-`toPorts[].ports[]` port-
10207/// tuple entry's `port:` scalar (the L4 port number the Cilium
10208/// data plane's per-tuple bpf policy dispatch loop compares
10209/// against the observed TCP/UDP L4 header port value).
10210/// 2. `gateway_routes` — the `Gateway` emitter's per-listener
10211/// `spec.listeners[].port` scalar (the L4 port number the
10212/// gateway-class-controller's per-listener bind loop opens the
10213/// listener socket on).
10214/// 3. `gateway_routes` — the `HTTPRoute` emitter's per-rule
10215/// `spec.rules[].backendRefs[].port` scalar (the L4 port number
10216/// the gateway-class-controller's per-rule backend-dispatch loop
10217/// forwards the matched request to on the resolved Service /
10218/// ExternalName backend).
10219///
10220/// Two test-side traversal sites in the same renderer navigate the
10221/// rendered mesh bundle's per-CR L4-port scalar axis to pin per-CR
10222/// port-value content invariants (the `.get("port")` retrievals under
10223/// `toPorts[].ports[]` on the L7 policy pin threading through
10224/// [`DEFAULT_SERVICO_PORT`] and under `backendRefs[]` on the
10225/// HTTPRoute-backend-port pin). All five sites now route through this
10226/// const so a future K8s CRD schema rebrand on the shared axis (or
10227/// the canonical typo footgun `"Port"` / `"portNumber"` /
10228/// `"portValue"`) surfaces at this one const rather than as an
10229/// admission-time silent drop across three distinct CR emitters.
10230///
10231/// Lifted on the trajectory the peer [`KUBE_KEY_API_VERSION`] /
10232/// [`KUBE_KEY_KIND`] / [`KUBE_KEY_METADATA`] / [`KUBE_KEY_NAME`] /
10233/// [`KUBE_KEY_NAMESPACE`] / [`KUBE_KEY_LABELS`] / [`KUBE_KEY_SPEC`] /
10234/// [`KUBE_KEY_MATCH_LABELS`] / [`KUBE_KEY_RULES`] canonical-K8s-API-
10235/// key constants establish — extends the K8s-CR top-level
10236/// `(apiVersion, kind, metadata, spec)` axis quartet + the nested
10237/// `metadata.{name, namespace, labels}` triplet + the
10238/// `LabelSelector.matchLabels` selector-projection axis + the
10239/// `spec.rules[]` / `toPorts[].rules` rule-list container axis onto
10240/// the load-bearing nested L4-port-scalar axis every downstream
10241/// bpf-policy-dispatch / gateway-listener-bind / gateway-backend-
10242/// dispatch consumer of the rendered mesh bundle keys off.
10243///
10244/// [cm]: ../../caixa_mesh/index.html
10245pub const KUBE_KEY_PORT: &str = "port";
10246
10247/// Canonical K8s API key naming the per-CR **L4/L7 protocol**
10248/// scalar-discriminator axis — the field the apiserver-side `OpenAPI`
10249/// schema for every protocol-carrying CR body-position (Cilium L7
10250/// `spec.ingress[].toPorts[].ports[].protocol` per-port-tuple L4
10251/// transport protocol discriminator picking between `TCP` / `UDP` /
10252/// `SCTP` / `ANY`, Gateway API `Gateway.spec.listeners[].protocol`
10253/// per-listener L7 listener-protocol discriminator picking between
10254/// `HTTP` / `HTTPS` / `TCP` / `TLS` / `UDP`, and every future
10255/// protocol-shaped CR body-position the M4
10256/// `mesh.pleme.io/v1alpha1/Aplicacao` materializer + the per-edge
10257/// `CiliumClusterwideEnvoyConfig` emitter will land on) mounts the
10258/// protocol-value discriminator under. Spelled exactly as the K8s
10259/// apiserver expects (lowercase `protocol`, not `Protocol` /
10260/// `proto` / `transportProtocol` — the singular scalar-key
10261/// convention K8s uses across every protocol-carrying CR family,
10262/// distinct from the `protocols[]` plural-container axis used on a
10263/// few application-layer-protocol CRDs which is not this axis) so
10264/// the rendered YAML round-trips through every K8s schema parser
10265/// without per-renderer string drift.
10266///
10267/// Two production-code call sites in this crate's downstream
10268/// [`caixa-mesh`][cm] renderer carry this key on the same
10269/// K8s-protocol-scalar-axis surface (both landing sites lived at
10270/// inline `"protocol".into()` before this lift):
10271///
10272/// 1. `cilium_network_policies` — the per-`(:de, :para)`
10273/// `CiliumNetworkPolicy` emitter's per-`toPorts[].ports[]` port-
10274/// tuple entry's `protocol:` scalar (the L4 transport protocol
10275/// discriminator the Cilium data plane's per-tuple bpf policy
10276/// dispatch loop compares against the observed L4 header
10277/// protocol before applying the port match — a drifted key here
10278/// makes the per-tuple bpf policy fall back to the CRD default
10279/// `ANY`, silently admitting UDP traffic through a TCP-only
10280/// rule).
10281/// 2. `gateway_routes` — the `Gateway` emitter's per-listener
10282/// `spec.listeners[].protocol` scalar (the L7 listener protocol
10283/// discriminator the gateway-class-controller's per-listener
10284/// bind loop selects the L7 parser + TLS termination strategy
10285/// from — a drifted key here silently fails the listener
10286/// validation, the gateway-class-controller rejects the entire
10287/// `Gateway` object at admission time, no L7 traffic admitted).
10288///
10289/// One test-side traversal site in the same renderer navigates the
10290/// rendered mesh bundle's per-CR protocol scalar axis to pin per-CR
10291/// listener-protocol content invariants (the
10292/// `gateway_emits_gateway_plus_httproute_pair` `.get("protocol")`
10293/// retrieval on the emitted `Gateway`'s first listener pinning the
10294/// canonical `HTTP` listener-protocol value). All three sites now
10295/// route through this const so a future K8s CRD schema rebrand on
10296/// the shared axis (or the canonical typo footgun `"Protocol"` /
10297/// `"proto"` / `"transportProtocol"`) surfaces at this one const
10298/// rather than as an admission-time silent drop across two distinct
10299/// CR emitters.
10300///
10301/// Lifted on the trajectory the peer [`KUBE_KEY_API_VERSION`] /
10302/// [`KUBE_KEY_KIND`] / [`KUBE_KEY_METADATA`] / [`KUBE_KEY_NAME`] /
10303/// [`KUBE_KEY_NAMESPACE`] / [`KUBE_KEY_LABELS`] / [`KUBE_KEY_SPEC`] /
10304/// [`KUBE_KEY_MATCH_LABELS`] / [`KUBE_KEY_RULES`] /
10305/// [`KUBE_KEY_PORT`] canonical-K8s-API-key constants establish —
10306/// extends the K8s-CR top-level `(apiVersion, kind, metadata, spec)`
10307/// axis quartet + the nested `metadata.{name, namespace, labels}`
10308/// triplet + the `LabelSelector.matchLabels` selector-projection
10309/// axis + the `spec.rules[]` / `toPorts[].rules` rule-list container
10310/// axis + the L4-port-scalar axis onto the load-bearing nested
10311/// L4/L7-protocol-scalar-discriminator axis every downstream bpf-
10312/// policy-dispatch / gateway-listener-bind consumer of the rendered
10313/// mesh bundle keys off before it can commit to a port match or a
10314/// listener parser.
10315///
10316/// [cm]: ../../caixa_mesh/index.html
10317pub const KUBE_KEY_PROTOCOL: &str = "protocol";
10318
10319/// Canonical K8s API key naming the per-CR **discriminated-union type**
10320/// scalar-discriminator axis — the field the apiserver-side OpenAPI schema
10321/// for every discriminated-union CR body-position (Gateway API v1
10322/// `HTTPRouteMatch.path.type` per-`HTTPRouteMatch` path-selection-predicate
10323/// discriminator picking between `Exact` / `PathPrefix` /
10324/// `RegularExpression`, K8s core `Condition.type` per-condition kind
10325/// discriminator, K8s core `Volume.<projection>.type` per-projection
10326/// content-source discriminator, and every future discriminated-union CR
10327/// body-position the M4 `mesh.pleme.io/v1alpha1/Aplicacao` materializer
10328/// + the per-edge `CiliumClusterwideEnvoyConfig` emitter's per-listener
10329/// filter-chain type-discriminator + a future per-`:entrada :paths`
10330/// typed slot admitting a per-path `(:predicate <Exact|Prefix|Regex>)`
10331/// axis will land on) mounts the discriminated-union type-value under.
10332/// Spelled exactly as the K8s apiserver expects (lowercase `type`, not
10333/// `Type` / `kind` / `discriminator` — the singular scalar-key
10334/// convention K8s uses across every discriminated-union CR family,
10335/// distinct from the top-level [`KUBE_KEY_KIND`] CRD-registration
10336/// discriminator on the K8s CR top-level which is the CRD-lookup half
10337/// of the `(apiVersion, kind)` tuple the K8s apiserver's `RESTMapper`
10338/// consults and is not this axis) so the rendered YAML round-trips
10339/// through every K8s schema parser without per-renderer string drift.
10340///
10341/// One production-code call site in this crate's downstream
10342/// [`caixa-mesh`][cm] renderer carries this key on the same
10343/// K8s-discriminated-union-type-scalar-axis surface (the landing site
10344/// lived at an inline `"type".into()` before this lift):
10345///
10346/// 1. `gateway_routes` — the `HTTPRoute` emitter's per-rule per-match
10347/// `spec.rules[].matches[].path.type` scalar (the path-selection-
10348/// predicate discriminator the gateway-class-controller's per-rule
10349/// L7 dispatch pass selects the path-match strategy from — a drifted
10350/// key here silently fails the per-match path-selection-predicate
10351/// validation, the Gateway API v1 `PathMatchType` OpenAPI schema
10352/// validator drops the entire `HTTPRoute` object at admission with
10353/// no per-rule L7 URL-path filtering applied, and every external
10354/// `:entrada` path-filtered flow the route was authored to accept
10355/// drops at the gateway-class-controller's admission gate with no
10356/// field naming the discriminator-drift root cause).
10357///
10358/// Pairs with the sibling [`GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX`]
10359/// (530705d) per-`HTTPRouteMatch` path-selection-predicate discriminator
10360/// scalar-VALUE the discriminator scalar-KEY here holds under, closing
10361/// the per-`HTTPRouteMatch` path-selection-predicate `(type key →
10362/// PathPrefix value)` scalar-key/scalar-value discriminator axis pair
10363/// the M3 Aplicacao mesh renderer's external `:entrada` per-path
10364/// L7-filtering ingress contract rests on — the same shape the sibling
10365/// [`KUBE_KEY_PROTOCOL`] (0307950) key + [`KUBE_PROTOCOL_TCP`] (2123047)
10366/// / [`GATEWAY_API_PROTOCOL_HTTP`] (1b57473) value pair already carries
10367/// on the L4/L7-protocol scalar-discriminator surface. A `"Type"` /
10368/// `"kind"` / `"discriminator"` / `"predicate"` typo at the production-
10369/// code call site lands outside the Gateway API v1 `HTTPPathMatch`
10370/// OpenAPI schema's admitted property set, surfacing apply-side as a
10371/// non-self-locating "spec.rules[0].matches[0].path: Unknown field
10372/// \"Type\"" apiserver admission-rejection far from the source
10373/// `caixa.lisp` / the renderer's `path_match.insert(…)` call site.
10374///
10375/// Lifted on the trajectory the peer [`KUBE_KEY_API_VERSION`] /
10376/// [`KUBE_KEY_KIND`] / [`KUBE_KEY_METADATA`] / [`KUBE_KEY_NAME`] /
10377/// [`KUBE_KEY_NAMESPACE`] / [`KUBE_KEY_LABELS`] / [`KUBE_KEY_SPEC`] /
10378/// [`KUBE_KEY_MATCH_LABELS`] / [`KUBE_KEY_RULES`] / [`KUBE_KEY_PORT`] /
10379/// [`KUBE_KEY_PROTOCOL`] canonical-K8s-API-key constants establish —
10380/// extends the K8s-CR top-level `(apiVersion, kind, metadata, spec)`
10381/// axis quartet + the nested `metadata.{name, namespace, labels}`
10382/// triplet + the `LabelSelector.matchLabels` selector-projection axis
10383/// + the `spec.rules[]` / `toPorts[].rules` rule-list container axis +
10384/// the L4-port-scalar axis + the L4/L7-protocol-scalar-discriminator
10385/// axis onto the load-bearing nested discriminated-union-type-scalar-
10386/// discriminator axis every downstream gateway-class-controller /
10387/// apiserver-side OpenAPI-schema-validator consumer of the rendered
10388/// mesh bundle keys off before it can commit to a per-match path-
10389/// selection predicate.
10390///
10391/// [cm]: ../../caixa_mesh/index.html
10392pub const KUBE_KEY_TYPE: &str = "type";
10393
10394/// Default cluster-wide K8s namespace every caixa renderer emits
10395/// objects into when the source caixa doesn't pin its own. The single
10396/// source of truth both [`caixa-flux`][cf]'s programs.yaml /
10397/// GitRepository / HelmRelease / Kustomization emitters and
10398/// [`caixa-mesh`][cm]'s programs fan-out / CiliumNetworkPolicy /
10399/// Gateway / HTTPRoute emitters consult — re-exported by each
10400/// renderer's lib as `pub use caixa_core::DEFAULT_NAMESPACE`, so a
10401/// future per-cluster-namespace rebrand (e.g. moving to `pleme-system`
10402/// once `tatara-system` outlives its scoping intent) is a one-line
10403/// edit here, not a coordinated rewrite across every renderer
10404/// crate's `metadata.namespace` slot.
10405///
10406/// Until this lift landed both renderers carried their own `pub const
10407/// DEFAULT_NAMESPACE: &str = "tatara-system"` declarations
10408/// (caixa-flux/src/lib.rs:77, caixa-mesh/src/lib.rs:172), with the
10409/// `caixa-mesh` site's doc-comment explicitly acknowledging the
10410/// duplication ("Mirrors `caixa_flux::DEFAULT_NAMESPACE`"); a future
10411/// rebrand on either side without a coordinated edit on the other
10412/// would have silently emitted into two distinct namespaces on the
10413/// same cluster's apply — Servicos at programs.yaml's namespace,
10414/// their Aplicacao's NetworkPolicies / Gateways / HTTPRoutes at a
10415/// drifted one — and the CiliumNetworkPolicy's `endpointSelector`
10416/// would match no pods (different namespace), silently dropping every
10417/// L7 contrato flow at apply time with no diagnostic naming the
10418/// namespace-drift root cause.
10419///
10420/// Lifting it to caixa-core's render-constants block alongside the
10421/// peer [`LABEL_APLICACAO`] / [`LABEL_PROGRAM`] / [`LABEL_CONTRATO`]
10422/// label-namespace constants and the canonical [`KUBE_KEY_NAMESPACE`]
10423/// API-key constant makes the namespace-axis discipline structural:
10424/// every renderer that reaches for the default namespace consults the
10425/// same `&'static str`, and every future renderer (the M4
10426/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer, the future
10427/// per-edge `CiliumClusterwideEnvoyConfig` emitter, the future
10428/// caixa-otel collector-pipeline emitter) inherits the same value by
10429/// construction, with no opportunity for per-renderer drift. Same
10430/// "the typed constant lives in one place" discipline the
10431/// [`PLEME_LABEL_PREFIX`] (a8d4d57) and [`KUBE_KEY_API_VERSION`] /
10432/// [`KUBE_KEY_KIND`] / [`KUBE_KEY_METADATA`] lifts apply on the peer
10433/// shared-string axes.
10434///
10435/// [cf]: ../../caixa_flux/index.html
10436/// [cm]: ../../caixa_mesh/index.html
10437pub const DEFAULT_NAMESPACE: &str = "tatara-system";
10438
10439/// Canonical FluxCD installation namespace every `caixa-flux` `Kustomization`
10440/// document apply-targets. The single source of truth both axes of the
10441/// rendered `kustomization.yaml` document reach for:
10442///
10443/// - `metadata.namespace` — the namespace the `Kustomization` resource
10444/// itself lives in (the `FluxCD` `kustomize-controller` watches this
10445/// namespace by default; a drifted value sits outside the controller's
10446/// watch window and is never reconciled);
10447/// - `spec.sourceRef.name` — the `GitRepository` the bootstrap pipeline
10448/// created at `flux bootstrap` time and the per-Servico `Kustomization`
10449/// transitively threads its `path: ./clusters/<cluster>/services/<name>`
10450/// reference through. The canonical FluxCD bootstrap convention names
10451/// this `GitRepository` after the installation namespace (the
10452/// `flux-system` namespace contains a `GitRepository/flux-system`
10453/// pointing at the operator's source-of-truth repo); both axes are the
10454/// same conceptual "Flux installation namespace" load-bearing string
10455/// and must move together on any future rebrand.
10456///
10457/// Until this lift landed both axes carried inline `flux-system` literals
10458/// inside [`cluster_bundle`]'s `kustomization.yaml` format-string template
10459/// (caixa-flux/src/lib.rs:477, 483) — two production-code consumers of the
10460/// same load-bearing FluxCD-installation-namespace convention, drift-prone
10461/// by construction. A future per-cluster Flux installation rebrand (the
10462/// operator moving the bootstrap controllers to a different installation
10463/// namespace, e.g. `flux-pleme` to match the per-tenant scoping convention
10464/// once `flux-system` outlives its scoping intent; or any per-edition
10465/// rebrand the FluxCD upgrade docs name) on one axis without a coordinated
10466/// edit on the other would have silently emitted a `Kustomization` whose
10467/// `metadata.namespace` sat outside the `kustomize-controller` watch
10468/// window (controller-side: never reconciled, every `HelmRelease` /
10469/// `GitRepository` it gates frozen at last-applied state) or whose
10470/// `spec.sourceRef.name` pointed at a `GitRepository` that doesn't exist
10471/// in the rebranded namespace (apply-side: the reference dangles, the
10472/// dependent chart never pulls). The apply-time symptom (the Servico's
10473/// `HelmRelease` is created but never reconciled, or never reaches its
10474/// chart source) is invisible at admission and surfaces only as
10475/// "the cluster says the resources are applied but nothing changed",
10476/// typically far from the rebrand commit's source.
10477///
10478/// Lifting it to caixa-core's render-constants block alongside the peer
10479/// [`DEFAULT_NAMESPACE`] (a085b26, the workload-side
10480/// `tatara-system` namespace every emitted resource lives in) makes the
10481/// installation-namespace axis discipline structural: both kustomization
10482/// axes consult the same `&'static str`, and every future renderer that
10483/// reaches for the canonical Flux installation namespace (the future M4
10484/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
10485/// `Kustomization`, the future per-edge `Kustomization` the operator
10486/// emits for the `CiliumClusterwideEnvoyConfig` pipeline, the future
10487/// `caixa-otel` collector-pipeline `Kustomization`) inherits the same
10488/// value by construction with no opportunity for per-renderer drift.
10489/// Same "the typed constant lives in one place" discipline the
10490/// [`DEFAULT_NAMESPACE`] (a085b26) / [`DEFAULT_LIBRARY_NAME`] (41438dc) /
10491/// [`crate::DEFAULT_SERVICO_PORT`] (1e22add) /
10492/// [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] (0a6a602) lifts apply on the
10493/// peer canonical-load-bearing-string surface.
10494///
10495/// The value is a valid DNS-1123 label (the K8s apiserver-side floor every
10496/// `metadata.namespace` rule enforces): lowercase ASCII alphanumeric with
10497/// `-` separators, no leading / trailing hyphen, length within the
10498/// [`DNS_1123_LABEL_MAX_LEN`] (63-byte) cap. A future rebrand on this lift
10499/// cannot silently land a value the apiserver refuses, by construction:
10500/// the [`default_flux_system_namespace_is_a_valid_dns_1123_label`] pin
10501/// trips at caixa-core build time on any drift past the typed floor.
10502///
10503/// [cf]: ../../caixa_flux/index.html
10504pub const DEFAULT_FLUX_SYSTEM_NAMESPACE: &str = "flux-system";
10505
10506/// Canonical FluxCD `HelmRelease` CRD `apiVersion` every `caixa-flux`
10507/// `helmrelease.yaml` document emits. The Flux v2 `helm-controller` watches
10508/// resources at this exact group/version (`helm.toolkit.fluxcd.io/v2`);
10509/// drift to a stale `v2beta1` / `v2beta2` (the pre-GA Flux v2 betas every
10510/// upstream Flux GA-migration doc names) silently routes the rendered
10511/// `HelmRelease` outside the controller's `Watches` and breaks at apply
10512/// time with a non-self-locating "no kind 'HelmRelease' is registered for
10513/// version 'helm.toolkit.fluxcd.io/v2beta2'" error far from the source
10514/// caixa.lisp / the renderer's format-string template.
10515///
10516/// The single source of truth both axes of the rendered Flux bundle reach
10517/// for:
10518///
10519/// - `helmrelease.yaml` `apiVersion` — the top-level CRD-group/version
10520/// the rendered document declares (caixa-flux/src/lib.rs:455 — the
10521/// `helmrelease` format-string template);
10522/// - `kustomization.yaml` `spec.healthChecks[]` per-entry `apiVersion`
10523/// — the same Flux-v2 `HelmRelease` reference the parent Kustomization
10524/// gates its health-check on (caixa-flux/src/lib.rs:504 — the
10525/// `kustomization` format-string template). The Flux v2 contract pairs
10526/// a `HelmRelease` document with its sibling `Kustomization`'s
10527/// `healthChecks[].apiVersion` axis: both must name the same Flux v2
10528/// `HelmRelease` CRD group/version for the Kustomization's per-resource
10529/// health-gate to bind to the rendered HelmRelease; a future Flux v3
10530/// promotion (the upstream Flux roadmap names a per-CRD-group / per-
10531/// v3 version migration once the Flux v2 LTS branch closes) on one
10532/// axis without a coordinated edit on the other would have silently
10533/// emitted a `Kustomization` whose `healthChecks[].apiVersion` pointed
10534/// at an obsolete CRD group/version (apply-side: the health check
10535/// never resolves, the parent Kustomization sits perpetually in
10536/// `Reconciling`).
10537///
10538/// Until this lift landed both axes carried inline
10539/// `helm.toolkit.fluxcd.io/v2` literals inside [`cluster_bundle`]'s
10540/// `helmrelease.yaml` + `kustomization.yaml` format-string templates and a
10541/// matching pair inside the in-file `upsert_into_helmrelease_programs`
10542/// test fixtures (caixa-flux/src/lib.rs:928, 970) — four occurrences of
10543/// the same load-bearing FluxCD-CRD-group/version convention, drift-prone
10544/// by construction. The PRIME DIRECTIVE duplication-budget rule
10545/// (THEORY.md §I.3.5: "every recurring shape becomes a generator before
10546/// it becomes a pattern; every pattern becomes a library before it
10547/// becomes duplicated code. The duplication budget is zero.") promotes
10548/// the constant to a typed substrate-side `&'static str` on the same
10549/// trajectory the [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) lift
10550/// established on the sibling Flux-installation-namespace axis. The two
10551/// render-side consumers now thread the same `&'static str` through their
10552/// format-string templates so a future Flux v3 promotion lands in one
10553/// place; the test fixtures keep the value as a literal because they
10554/// exercise `serde_yaml::from_str` on a static YAML document — the
10555/// build-time pin [`default_flux_helmrelease_api_version_matches_caixa_flux_test_fixtures`]
10556/// trips if the literals ever drift past the typed const.
10557///
10558/// Same "the typed constant lives in one place" discipline the
10559/// [`DEFAULT_NAMESPACE`] (a085b26) / [`DEFAULT_LIBRARY_NAME`] (41438dc) /
10560/// [`crate::DEFAULT_SERVICO_PORT`] (1e22add) /
10561/// [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] (0a6a602) /
10562/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) lifts apply on the peer
10563/// canonical-load-bearing-string surface.
10564///
10565/// [cf]: ../../caixa_flux/index.html
10566pub const FLUX_HELMRELEASE_API_VERSION: &str = "helm.toolkit.fluxcd.io/v2";
10567
10568/// Canonical FluxCD `GitRepository` CRD `apiVersion` every `caixa-flux`
10569/// `gitrepository.yaml` document emits. The Flux v2 `source-controller`
10570/// watches resources at this exact group/version
10571/// (`source.toolkit.fluxcd.io/v1`); drift to a stale `v1beta1` / `v1beta2`
10572/// (the pre-GA Flux v2 source-controller betas every upstream Flux GA-
10573/// migration doc names) silently routes the rendered `GitRepository`
10574/// outside the controller's `Watches` and breaks at apply time with a
10575/// non-self-locating "no kind 'GitRepository' is registered for version
10576/// 'source.toolkit.fluxcd.io/v1beta2'" error far from the source
10577/// caixa.lisp / the renderer's format-string template.
10578///
10579/// The single source of truth the `gitrepository.yaml` `apiVersion` axis
10580/// reaches for (caixa-flux/src/lib.rs:436 — the `gitrepo` format-string
10581/// template). The Flux v2 source/helm/kustomize controller triple pairs
10582/// each CRD-group/version against its sibling controller's `Watches`
10583/// registration: the rendered `GitRepository` is the chart-source the
10584/// sibling `HelmRelease` document's `spec.chart.spec.sourceRef.kind:
10585/// GitRepository` references, and the parent `Kustomization`'s
10586/// `spec.sourceRef.kind: GitRepository` also points at this same CRD
10587/// group/version. A future Flux v3 promotion on this axis without a
10588/// coordinated edit on the sibling [`FLUX_HELMRELEASE_API_VERSION`] /
10589/// future-`FLUX_KUSTOMIZATION_API_VERSION` axes would silently land the
10590/// rendered `GitRepository` outside the source-controller's `Watches`
10591/// (controller-side: never reconciled, the dependent HelmRelease's
10592/// `chart: sourceRef` dangles, every per-Servico apply silently comes
10593/// up with the prior reconciled state).
10594///
10595/// Until this lift landed the axis carried an inline
10596/// `source.toolkit.fluxcd.io/v1` literal inside [`cluster_bundle`]'s
10597/// `gitrepository.yaml` format-string template — one occurrence today,
10598/// promoted to a typed substrate-side `&'static str` on the same
10599/// trajectory the [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
10600/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) lifts established on the
10601/// sibling Flux-v2-load-bearing-string surface. The render-side consumer
10602/// now threads the same `&'static str` through its format-string
10603/// template so a future Flux v3 promotion lands in one place; every
10604/// future renderer that reaches for the canonical Flux v2 `GitRepository`
10605/// apiVersion (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
10606/// materializer's per-Aplicacao `GitRepository`, a future per-edge
10607/// `GitRepository` the operator emits for the
10608/// `CiliumClusterwideEnvoyConfig` pipeline, a future `caixa-otel`
10609/// collector-pipeline `GitRepository`) inherits the same value by
10610/// construction with no opportunity for per-renderer drift.
10611///
10612/// Same "the typed constant lives in one place" discipline the
10613/// [`DEFAULT_NAMESPACE`] (a085b26) / [`DEFAULT_LIBRARY_NAME`] (41438dc) /
10614/// [`crate::DEFAULT_SERVICO_PORT`] (1e22add) /
10615/// [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] (0a6a602) /
10616/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) /
10617/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) lifts apply on the peer
10618/// canonical-load-bearing-string surface.
10619///
10620/// [cf]: ../../caixa_flux/index.html
10621pub const FLUX_GITREPOSITORY_API_VERSION: &str = "source.toolkit.fluxcd.io/v1";
10622
10623/// Canonical FluxCD `GitRepository` CRD `kind` discriminator every
10624/// `caixa-flux`-emitted document that names a Flux v2 `GitRepository`
10625/// at a [`KUBE_KEY_KIND`]-rooted axis declares. Paired peer to the
10626/// sibling [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) — the K8s
10627/// apiserver-side CRD resolution contract is the `(apiVersion, kind)`
10628/// tuple keyed against the registered `CustomResourceDefinition`, so
10629/// drift on the kind axis is exactly as load-bearing as drift on the
10630/// apiVersion axis it accompanies (the apiserver's `RESTMapper` consults
10631/// both together; a `("source.toolkit.fluxcd.io/v1", "GitRepostiory")`
10632/// typo at any one of the three production-code call sites lands
10633/// outside the registered Flux v2 source-controller CRD's
10634/// `RESTKind` lookup, surfacing apply-side as a non-self-locating
10635/// "no kind 'GitRepostiory' is registered for version
10636/// 'source.toolkit.fluxcd.io/v1'" error far from the source
10637/// caixa.lisp / the renderer's format-string template).
10638///
10639/// The single source of truth the rendered Flux bundle's three
10640/// `GitRepository`-naming axes reach for:
10641///
10642/// - the rendered `gitrepository.yaml` document's top-level
10643/// [`KUBE_KEY_KIND`] axis (caixa-flux/src/lib.rs:505 — the
10644/// `gitrepo` format-string template);
10645/// - the rendered `helmrelease.yaml` document's
10646/// `spec.chart.spec.sourceRef.kind` axis (caixa-flux/src/lib.rs:556 —
10647/// the `helmrelease` format-string template), pointing back at the
10648/// sibling `GitRepository` the chart sources from;
10649/// - the rendered `kustomization.yaml` document's `spec.sourceRef.kind`
10650/// axis (caixa-flux/src/lib.rs:591 — the `kustomization` format-
10651/// string template), pointing back at the cluster's bootstrap
10652/// `GitRepository` (paired with [`DEFAULT_FLUX_SYSTEM_NAMESPACE`]
10653/// on the namespace axis).
10654///
10655/// All three axes name the same K8s CRD discriminator and must move
10656/// together on any future Flux v3 rebrand (e.g. an upstream Flux v3
10657/// rename like `GitSource`). Until this lift landed the three axes
10658/// carried inline `GitRepository` literals across the three production-
10659/// code occurrences in caixa-flux/src/lib.rs:505, 556, 591 (the
10660/// `cluster_bundle` `gitrepo` + `helmrelease` + `kustomization` format-
10661/// string templates) plus a matching set inside the in-file
10662/// `cluster_bundle_*` test fixtures — six occurrences of the same load-
10663/// bearing FluxCD-CRD-`kind`-discriminator convention, drift-prone by
10664/// construction. A drift on the `helmrelease.yaml`
10665/// `spec.chart.spec.sourceRef.kind` site alone — the one apply-side
10666/// failure mode the apiserver can't self-locate — would have silently
10667/// dangled the HelmRelease's chart sourceRef (controller-side: the
10668/// `helm-controller` never resolves a chart for the HelmRelease, the
10669/// rendered Servico chart never reconciles, every per-Servico apply
10670/// silently comes up with the prior reconciled state) with no diagnostic
10671/// naming the kind-drift root cause far from the source caixa.lisp.
10672///
10673/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
10674/// "every recurring shape becomes a generator before it becomes a
10675/// pattern; every pattern becomes a library before it becomes
10676/// duplicated code. The duplication budget is zero.") promotes the
10677/// constant to a typed substrate-side `&'static str` on the same
10678/// trajectory the [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
10679/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
10680/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) lifts established on
10681/// the sibling Flux-v2-load-bearing-string axes — extends the
10682/// discipline from the apiVersion half of the `(apiVersion, kind)`
10683/// CRD-lookup tuple onto the kind half on the same Flux v2
10684/// source-controller CRD. The three render-side consumers now thread
10685/// the same `&'static str` through their format-string templates so a
10686/// future Flux v3 rebrand lands in one place; every future renderer
10687/// that reaches for the canonical Flux v2 `GitRepository` kind (the
10688/// future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
10689/// per-Aplicacao `GitRepository`, a future per-edge `GitRepository`
10690/// the operator emits for the `CiliumClusterwideEnvoyConfig` pipeline,
10691/// a future `caixa-otel` collector-pipeline `GitRepository`) inherits
10692/// the same value by construction with no opportunity for per-renderer
10693/// drift.
10694///
10695/// Same "the typed constant lives in one place" discipline the
10696/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
10697/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
10698/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) /
10699/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) lifts apply on the peer
10700/// canonical-Flux-v2-load-bearing-string surface.
10701///
10702/// [cf]: ../../caixa_flux/index.html
10703pub const FLUX_KIND_GIT_REPOSITORY: &str = "GitRepository";
10704
10705/// Canonical FluxCD `HelmRelease` CRD `kind` discriminator every
10706/// `caixa-flux`-emitted document that names a Flux v2 `HelmRelease`
10707/// at a [`KUBE_KEY_KIND`]-rooted axis declares. Paired peer to the
10708/// sibling [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) — the K8s
10709/// apiserver-side CRD resolution contract is the `(apiVersion, kind)`
10710/// tuple keyed against the registered `CustomResourceDefinition`, so
10711/// drift on the kind axis is exactly as load-bearing as drift on the
10712/// apiVersion axis it accompanies (the apiserver's `RESTMapper`
10713/// consults both together; a `("helm.toolkit.fluxcd.io/v2",
10714/// "HelmRelase")` typo at any one of the two production-code call
10715/// sites lands outside the registered Flux v2 helm-controller CRD's
10716/// `RESTKind` lookup, surfacing apply-side as a non-self-locating
10717/// "no kind 'HelmRelase' is registered for version
10718/// 'helm.toolkit.fluxcd.io/v2'" error far from the source
10719/// caixa.lisp / the renderer's format-string template).
10720///
10721/// The single source of truth the rendered Flux bundle's two
10722/// `HelmRelease`-naming axes reach for:
10723///
10724/// - the rendered `helmrelease.yaml` document's top-level
10725/// [`KUBE_KEY_KIND`] axis (caixa-flux/src/lib.rs:580 — the
10726/// `helmrelease` format-string template);
10727/// - the rendered `kustomization.yaml` document's
10728/// `spec.healthChecks[].kind` axis (caixa-flux/src/lib.rs:631 —
10729/// the `kustomization` format-string template), pointing back at
10730/// the sibling `HelmRelease` the Kustomization pins as a
10731/// health-gate before declaring its own reconcile complete.
10732///
10733/// Both axes name the same K8s CRD discriminator and must move
10734/// together on any future Flux v3 rebrand (e.g. an upstream Flux v3
10735/// rename like `ChartRelease`). Until this lift landed the two axes
10736/// carried inline `HelmRelease` literals across the two production-
10737/// code occurrences in caixa-flux/src/lib.rs:580 (the
10738/// `cluster_bundle` `helmrelease` format-string template) and 631
10739/// (the `kustomization` `spec.healthChecks[]` element). A drift on
10740/// the `kustomization.yaml` `spec.healthChecks[].kind` site alone —
10741/// the one apply-side failure mode the apiserver can't self-locate
10742/// (a healthCheck kind typo doesn't fail apply-parse the way a
10743/// top-level kind typo does; it sits as a dangling unmatched health
10744/// gate the `kustomize-controller` perpetually re-evaluates) —
10745/// would have silently pinned the parent Kustomization at
10746/// `Reconciling` forever with no diagnostic naming the kind-drift
10747/// root cause far from the source caixa.lisp.
10748///
10749/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
10750/// "every recurring shape becomes a generator before it becomes a
10751/// pattern; every pattern becomes a library before it becomes
10752/// duplicated code. The duplication budget is zero.") promotes the
10753/// constant to a typed substrate-side `&'static str` on the same
10754/// trajectory the [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
10755/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
10756/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
10757/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) lifts established on
10758/// the sibling Flux-v2-load-bearing-string axes — extends the
10759/// discipline from the kind axis of the Flux v2 source-controller
10760/// CRD (the [`FLUX_KIND_GIT_REPOSITORY`] lift) onto the kind axis of
10761/// the sibling Flux v2 helm-controller CRD. The two render-side
10762/// consumers now thread the same `&'static str` through their
10763/// format-string templates so a future Flux v3 rebrand lands in one
10764/// place; every future renderer that reaches for the canonical Flux
10765/// v2 `HelmRelease` kind (the future M4
10766/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-
10767/// Aplicacao `HelmRelease`, a future per-edge `HelmRelease` the
10768/// operator emits for the `CiliumClusterwideEnvoyConfig` pipeline,
10769/// a future `caixa-otel` collector-pipeline `HelmRelease`) inherits
10770/// the same value by construction with no opportunity for per-
10771/// renderer drift.
10772///
10773/// Same "the typed constant lives in one place" discipline the
10774/// [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
10775/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
10776/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
10777/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) /
10778/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) lifts apply on the
10779/// peer canonical-Flux-v2-load-bearing-string surface.
10780///
10781/// [cf]: ../../caixa_flux/index.html
10782pub const FLUX_KIND_HELM_RELEASE: &str = "HelmRelease";
10783
10784/// Canonical FluxCD `Kustomization` CRD `apiVersion` every `caixa-flux`
10785/// `kustomization.yaml` document emits. The Flux v2 `kustomize-controller`
10786/// watches resources at this exact group/version
10787/// (`kustomize.toolkit.fluxcd.io/v1`); drift to a stale `v1beta1` /
10788/// `v1beta2` (the pre-GA Flux v2 kustomize-controller betas every
10789/// upstream Flux GA-migration doc names) silently routes the rendered
10790/// `Kustomization` outside the controller's `Watches` and breaks at
10791/// apply time with a non-self-locating "no kind 'Kustomization' is
10792/// registered for version 'kustomize.toolkit.fluxcd.io/v1beta2'" error
10793/// far from the source caixa.lisp / the renderer's format-string
10794/// template.
10795///
10796/// The single source of truth the `kustomization.yaml` `apiVersion`
10797/// axis reaches for (caixa-flux/src/lib.rs:531 — the `kustomization`
10798/// format-string template). Completes the Flux v2 controller triplet
10799/// (source-controller + helm-controller + kustomize-controller) lift
10800/// alongside the sibling [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3)
10801/// and [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) — every per-
10802/// controller CRD-group/version is now a typed substrate-side
10803/// `&'static str` consumed through one `pub use caixa_core::FLUX_*`
10804/// re-export at the renderer site. The three controllers share the
10805/// canonical `.toolkit.fluxcd.io` root (asserted by
10806/// [`tests::flux_controller_triplet_api_versions_share_toolkit_fluxcd_io_root`]),
10807/// so a future Flux v3 promotion that forks any controller out of the
10808/// toolkit group surfaces here as a coordinated cross-axis edit-point
10809/// across all three constants.
10810///
10811/// The rendered `Kustomization`'s `metadata.namespace` (the Flux
10812/// installation namespace, [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] —
10813/// 7197d38) and `spec.sourceRef.kind: GitRepository`
10814/// (referenced through [`FLUX_GITREPOSITORY_API_VERSION`]) and
10815/// `spec.healthChecks[].apiVersion` (the rendered `HelmRelease`'s
10816/// CRD-group/version, [`FLUX_HELMRELEASE_API_VERSION`]) all share
10817/// the cluster-side contract with the upstream Flux v2 controller
10818/// triplet: a coordinated edit on any one of these four constants
10819/// must move alongside the sibling axes, and the lift makes that
10820/// movement a typed substrate-side edit-point rather than a
10821/// distributed-across-format-string-template-literals refactor.
10822///
10823/// Until this lift landed the axis carried an inline
10824/// `kustomize.toolkit.fluxcd.io/v1` literal inside [`cluster_bundle`]'s
10825/// `kustomization.yaml` format-string template — one occurrence today,
10826/// promoted to a typed substrate-side `&'static str` on the same
10827/// trajectory the [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
10828/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
10829/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) lifts established on
10830/// the sibling Flux-v2-load-bearing-string surface. The render-side
10831/// consumer now threads the same `&'static str` through its
10832/// format-string template so a future Flux v3 promotion lands in one
10833/// place; every future renderer that reaches for the canonical Flux
10834/// v2 `Kustomization` apiVersion (the future M4
10835/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
10836/// `Kustomization`, a future per-edge `Kustomization` the operator
10837/// emits for the `CiliumClusterwideEnvoyConfig` pipeline, a future
10838/// `caixa-otel` collector-pipeline `Kustomization`) inherits the
10839/// same value by construction with no opportunity for per-renderer
10840/// drift.
10841///
10842/// Same "the typed constant lives in one place" discipline the
10843/// [`DEFAULT_NAMESPACE`] (a085b26) / [`DEFAULT_LIBRARY_NAME`] (41438dc) /
10844/// [`crate::DEFAULT_SERVICO_PORT`] (1e22add) /
10845/// [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] (0a6a602) /
10846/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) /
10847/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
10848/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) lifts apply on the
10849/// peer canonical-load-bearing-string surface.
10850///
10851/// [cf]: ../../caixa_flux/index.html
10852pub const FLUX_KUSTOMIZATION_API_VERSION: &str = "kustomize.toolkit.fluxcd.io/v1";
10853
10854/// Canonical FluxCD `Kustomization` CRD `kind` discriminator every
10855/// `caixa-flux`-emitted document that names a Flux v2 `Kustomization`
10856/// at a [`KUBE_KEY_KIND`]-rooted axis declares. Paired peer to the
10857/// sibling [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) — the K8s
10858/// apiserver-side CRD resolution contract is the `(apiVersion, kind)`
10859/// tuple keyed against the registered `CustomResourceDefinition`, so
10860/// drift on the kind axis is exactly as load-bearing as drift on the
10861/// apiVersion axis it accompanies (the apiserver's `RESTMapper` consults
10862/// both together; a `("kustomize.toolkit.fluxcd.io/v1", "Kustomizaton")`
10863/// typo at the production-code call site lands outside the registered
10864/// Flux v2 kustomize-controller CRD's `RESTKind` lookup, surfacing
10865/// apply-side as a non-self-locating "no kind 'Kustomizaton' is
10866/// registered for version 'kustomize.toolkit.fluxcd.io/v1'" error far
10867/// from the source caixa.lisp / the renderer's format-string template).
10868///
10869/// The single source of truth the rendered Flux bundle's
10870/// `Kustomization`-naming axis reaches for:
10871///
10872/// - the rendered `kustomization.yaml` document's top-level
10873/// [`KUBE_KEY_KIND`] axis (caixa-flux/src/lib.rs:651 — the
10874/// `kustomization` format-string template).
10875///
10876/// The kind axis names the same K8s CRD discriminator as the sibling
10877/// [`FLUX_KUSTOMIZATION_API_VERSION`] apiVersion axis and must move
10878/// together on any future Flux v3 rebrand. Until this lift landed the
10879/// axis carried an inline `Kustomization` literal across the one
10880/// production-code occurrence in caixa-flux/src/lib.rs:651 (the
10881/// `cluster_bundle` `kustomization` format-string template) plus a
10882/// matching set inside the in-file `cluster_bundle_*` test fixtures —
10883/// occurrences of the same load-bearing FluxCD-CRD-`kind`-discriminator
10884/// convention, drift-prone by construction. A drift on the top-level
10885/// `kustomization.yaml` `kind` axis would have surfaced as a
10886/// non-self-locating "no kind 'Kustomizaton' is registered for version
10887/// 'kustomize.toolkit.fluxcd.io/v1'" error far from the source
10888/// caixa.lisp at apply parse time, with the rendered parent Kustomization
10889/// never reconciling and every downstream per-Servico `dependsOn` chain
10890/// freezing at the kustomize-controller's CRD-lookup boundary.
10891///
10892/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
10893/// "every recurring shape becomes a generator before it becomes a
10894/// pattern; every pattern becomes a library before it becomes
10895/// duplicated code. The duplication budget is zero.") promotes the
10896/// constant to a typed substrate-side `&'static str` on the same
10897/// trajectory the [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
10898/// [`FLUX_KIND_HELM_RELEASE`] (e24ea3c) /
10899/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) /
10900/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
10901/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) lifts established on
10902/// the sibling Flux-v2-load-bearing-string axes — extends the
10903/// discipline from the apiVersion half of the `(apiVersion, kind)`
10904/// CRD-lookup tuple onto the kind half on the same Flux v2
10905/// kustomize-controller CRD. Completes the Flux v2 controller triplet
10906/// kind-axis lift (source-controller + helm-controller +
10907/// kustomize-controller) alongside the sibling
10908/// [`FLUX_KIND_GIT_REPOSITORY`] and [`FLUX_KIND_HELM_RELEASE`] — every
10909/// per-controller CRD `kind` discriminator is now a typed substrate-side
10910/// `&'static str` consumed through one `pub use caixa_core::FLUX_KIND_*`
10911/// re-export at the renderer site. The render-side consumer now threads
10912/// the same `&'static str` through its format-string template so a
10913/// future Flux v3 rebrand lands in one place; every future renderer
10914/// that reaches for the canonical Flux v2 `Kustomization` kind (the
10915/// future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
10916/// per-Aplicacao `Kustomization`, a future per-edge `Kustomization`
10917/// the operator emits for the `CiliumClusterwideEnvoyConfig` pipeline,
10918/// a future `caixa-otel` collector-pipeline `Kustomization`) inherits
10919/// the same value by construction with no opportunity for per-renderer
10920/// drift.
10921///
10922/// Same "the typed constant lives in one place" discipline the
10923/// [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
10924/// [`FLUX_KIND_HELM_RELEASE`] (e24ea3c) /
10925/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
10926/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
10927/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) /
10928/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) lifts apply on the peer
10929/// canonical-Flux-v2-load-bearing-string surface.
10930///
10931/// [cf]: ../../caixa_flux/index.html
10932pub const FLUX_KIND_KUSTOMIZATION: &str = "Kustomization";
10933
10934/// Canonical Flux v2 per-`HelmRelease`/`Kustomization` source-reference
10935/// container-axis key every `caixa-flux`-emitted bundle document mounts its
10936/// per-CR source-of-truth pointer under (`spec.chart.spec.sourceRef` on
10937/// `HelmRelease`, `spec.sourceRef` on `Kustomization`) — the Flux v2 CRD
10938/// schema places the `(kind, name, namespace)` reference triple under this
10939/// single container key, so drift on the container axis is exactly as
10940/// load-bearing as drift on the sibling [`FLUX_KIND_GIT_REPOSITORY`]
10941/// (dbbcf29) kind-discriminator + [`DEFAULT_FLUX_SYSTEM_NAMESPACE`]
10942/// (7197d38) namespace axes the block nests (a `"source_ref"` / `"source"`
10943/// / `"sourceReference"` / `"gitSourceRef"` typo at either the emit-side
10944/// format-string template or a downstream test-fixture probe silently
10945/// dangles the `HelmRelease.spec.chart.spec.sourceRef` chart resolution +
10946/// the `Kustomization.spec.sourceRef` source resolution at the Flux v2
10947/// source-controller's CRD registration; the source-controller's per-CR
10948/// reconcile loop keys off this exact container axis to source the
10949/// `(kind, name, namespace)` reference triple, and a drift silently freezes
10950/// the dependent per-Servico `dependsOn` chain at apply time with no
10951/// field naming the sourceRef-container-drift root cause).
10952///
10953/// The single source of truth the rendered Flux bundle's per-CR
10954/// source-reference-container-axis-naming reaches for:
10955///
10956/// - the rendered `helmrelease.yaml` document's per-`HelmRelease`
10957/// `spec.chart.spec.sourceRef` block (caixa-flux/src/lib.rs — the
10958/// `cluster_bundle` `helmrelease` format-string template's
10959/// `{source_ref_key}:\n` sub-block header, now threaded through
10960/// the lifted const via a `{source_ref_key}` named-arg
10961/// interpolation);
10962/// - the rendered `kustomization.yaml` document's per-`Kustomization`
10963/// `spec.sourceRef` block (caixa-flux/src/lib.rs — the sibling
10964/// `cluster_bundle` `kustomization` format-string template's
10965/// `{source_ref_key}:\n` sub-block header, now threaded through
10966/// the lifted const via the sibling `{source_ref_key}` named-arg
10967/// interpolation);
10968/// - five test-side navigation sites in `mod tests` that probe the
10969/// rendered documents' `.get("sourceRef")` container axis to pin
10970/// the emitted `(kind, name, namespace)` reference triple against
10971/// the sibling lifted [`FLUX_KIND_GIT_REPOSITORY`] +
10972/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] axes.
10973///
10974/// The container-axis key names the same Flux-v2-source-controller-side
10975/// per-CR source-of-truth reference-triple container as the sibling
10976/// per-CRD `kind` discriminator [`FLUX_KIND_GIT_REPOSITORY`] nests inside,
10977/// and must move together on any future Flux v3 rebrand (a hypothetical
10978/// upstream Flux v3 rename of the source-reference container axis from
10979/// `sourceRef` to `source` / `sourceReference` / `sourceOf`, coordinated
10980/// with the upstream fluxcd/flux2 project's per-version deprecation
10981/// cycle, would land at this one const rather than scattered across the
10982/// two per-CR format-string templates + five per-test-fixture probe
10983/// sites).
10984///
10985/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
10986/// "every recurring shape becomes a generator before it becomes a
10987/// pattern; every pattern becomes a library before it becomes
10988/// duplicated code. The duplication budget is zero.") promotes the
10989/// constant to a typed substrate-side `&'static str` on the same
10990/// trajectory the [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
10991/// [`FLUX_KIND_HELM_RELEASE`] (e24ea3c) /
10992/// [`FLUX_KIND_KUSTOMIZATION`] (2d61a6f) /
10993/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
10994/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
10995/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) /
10996/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) lifts established on the
10997/// sibling canonical-Flux-v2-load-bearing-string surfaces — extends the
10998/// per-CRD kind-discriminator + apiVersion + install-namespace lift
10999/// trajectory onto the sibling per-CR source-reference container-axis
11000/// key the `cluster_bundle` `HelmRelease` + `Kustomization` renderers
11001/// both consume under their nested `(kind, name, namespace)` reference
11002/// triple.
11003///
11004/// [cf]: ../../caixa_flux/index.html
11005pub const FLUX_KEY_SOURCE_REF: &str = "sourceRef";
11006
11007/// Canonical Flux v2 per-`HelmRelease` inline-chart-template container-axis
11008/// key every `caixa-flux`-emitted `HelmRelease` document nests its per-CR
11009/// chart-template block under (`spec.chart` on `HelmRelease`) — the Flux v2
11010/// CRD schema places the `HelmChartTemplate` sub-document (whose nested
11011/// `spec.chart` string names the referenced chart, `spec.sourceRef` names
11012/// the source-of-truth `(kind, name, namespace)` triple, and
11013/// `spec.interval` names the per-CR reconcile cadence) under this single
11014/// container key, so drift on the container axis silently dangles the
11015/// whole chart-template block the Flux v2 `helm-controller`'s per-CR
11016/// reconcile loop reads to source the referenced chart at Helm-render time
11017/// (a `"Chart"` / `"chartTemplate"` / `"helmChart"` / `"chartRef"` typo at
11018/// either the emit-side format-string template or a downstream test-
11019/// fixture probe silently dangles the `HelmRelease.spec.chart` chart-
11020/// template resolution at the Flux v2 helm-controller's CRD registration;
11021/// the referenced chart never resolves, and the per-Servico workload
11022/// freezes at apply time with no field naming the container-axis-drift
11023/// root cause).
11024///
11025/// The single source of truth the rendered Flux bundle's per-CR
11026/// chart-template-container-axis-naming reaches for:
11027///
11028/// - the rendered `helmrelease.yaml` document's per-`HelmRelease`
11029/// `spec.chart` block (caixa-flux/src/lib.rs — the `cluster_bundle`
11030/// `helmrelease` format-string template's baked `chart:\n` container
11031/// axis at line 914, sibling to the peer lifted [`FLUX_KEY_SOURCE_REF`]
11032/// source-reference container axis nested inside the same block +
11033/// [`FLUX_KEY_VALUES`] per-cluster-override block-body axis at the
11034/// sibling `spec.values` position);
11035/// - two test-side navigation sites in `mod tests` that probe the
11036/// rendered `helmrelease.yaml` document's `.get("chart")` container
11037/// axis to reach the nested `spec.chart.spec.sourceRef.kind` pin
11038/// against the sibling lifted [`FLUX_KIND_GIT_REPOSITORY`] axis
11039/// (caixa-flux/src/lib.rs:2680, 2774).
11040///
11041/// The container-axis key names the same Flux-v2-helm-controller-side
11042/// per-`HelmRelease` chart-template container as the peer sibling per-CR
11043/// source-reference container-axis [`FLUX_KEY_SOURCE_REF`] nests under,
11044/// and must move together on any future Flux v3 rebrand (a hypothetical
11045/// upstream Flux v3 rename of the per-`HelmRelease` chart-template
11046/// container axis from `chart` to `Chart` / `chartTemplate` / `helmChart`
11047/// / `chartRef`, coordinated with the upstream fluxcd/flux2 project's
11048/// per-version deprecation cycle, would land at this one const rather
11049/// than scattered across the one per-CR format-string template + two
11050/// per-test-fixture probe sites).
11051///
11052/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
11053/// "every recurring shape becomes a generator before it becomes a
11054/// pattern; every pattern becomes a library before it becomes
11055/// duplicated code. The duplication budget is zero.") promotes the
11056/// constant to a typed substrate-side `&'static str` on the same
11057/// trajectory the [`FLUX_KEY_SOURCE_REF`] (e985089) /
11058/// [`FLUX_KEY_VALUES`] (b54dc87) lifts established on the sibling
11059/// canonical-Flux-v2-per-`HelmRelease`-body-key surfaces — completes the
11060/// triplet of Flux v2 per-`HelmRelease` `spec.*` body-key constants
11061/// (`spec.chart` + `spec.chart.spec.sourceRef` + `spec.values`) the
11062/// `cluster_bundle` renderer's `helmrelease.yaml` format-string template
11063/// threads through its per-CR block-body layout.
11064///
11065/// The inner scalar-value axis `spec.chart.spec.chart` (the chart-name
11066/// leaf the `HelmChartTemplate.spec` sub-document mounts under; the same
11067/// spelling `"chart"` at a distinct schema position) is a schematically
11068/// separate leaf-scalar-key axis (the chart-NAME field the helm-controller
11069/// resolves through the sibling [`FLUX_KEY_SOURCE_REF`] triple's source),
11070/// and is not covered by this lift — a rebrand of the container axis
11071/// (`spec.chart` in this const) does not necessarily coincide with a
11072/// rebrand of the leaf-scalar `spec.chart.spec.chart` chart-name field
11073/// key, so the two axes stay decoupled at the substrate.
11074///
11075/// [cf]: ../../caixa_flux/index.html
11076pub const FLUX_KEY_CHART: &str = "chart";
11077
11078/// Canonical Flux v2 `HelmChartTemplate.spec.chart` per-CR chart-NAME-
11079/// reference leaf-scalar-key every `caixa-flux`-emitted `HelmRelease`
11080/// document nests inside the parent `spec.chart.spec` sub-document (the
11081/// `HelmChartTemplate.spec` block the parent [`FLUX_KEY_CHART`] (8467748)
11082/// container-axis key opens; a nested [`KUBE_KEY_SPEC`] axis inside that
11083/// container hosts this leaf plus its sibling [`FLUX_KEY_SOURCE_REF`]
11084/// per-CR source-reference triple).
11085///
11086/// The parent [`FLUX_KEY_CHART`] docstring explicitly names this leaf-
11087/// scalar axis as *not* covered by that container-axis lift ("The inner
11088/// scalar-value axis `spec.chart.spec.chart` … is a schematically
11089/// separate leaf-scalar-key axis (the chart-NAME field the helm-controller
11090/// resolves through the sibling [`FLUX_KEY_SOURCE_REF`] triple's
11091/// source), and is not covered by this lift — a rebrand of the
11092/// container axis … does not necessarily coincide with a rebrand of
11093/// the leaf-scalar `spec.chart.spec.chart` chart-name field key, so
11094/// the two axes stay decoupled at the substrate."). This const closes
11095/// the substrate-side declaration of the sibling leaf-scalar axis the
11096/// parent container-axis lift explicitly left as future work.
11097///
11098/// The Flux v2 `helm-controller`'s reconcile pipeline reads the chart-
11099/// NAME reference from this exact leaf-scalar-axis key on every
11100/// reconcile: the value at `HelmChartTemplate.spec.chart` names the
11101/// chart-artifact the sibling `HelmChartTemplate.spec.sourceRef`
11102/// triple's source-artifact publishes (an OCIRepository's remote OCI
11103/// chart archive by chart-name, a GitRepository's sub-tree path by
11104/// directory-name, a HelmRepository's chart index entry by chart-name).
11105/// A drifted `spec.chart.spec.Chart` / `spec.chart.spec.chartRef` /
11106/// `spec.chart.spec.chartName` at the emission-side key would silently
11107/// land a well-formed but ignored `HelmChartTemplate.spec.*` extra
11108/// property the apiserver's CRD OpenAPI schema permits (arbitrary
11109/// `spec.*` extras) and the helm-controller would fail to resolve any
11110/// chart-artifact through the sibling `sourceRef` triple's source at
11111/// reconcile time (the sibling `sourceRef` still resolves the *source*
11112/// artifact, but the chart-NAME lookup inside the source
11113/// short-circuits at the missing chart-NAME field with a
11114/// non-self-locating "chart 'unknown' not found in <source>" error far
11115/// from the source `caixa.lisp` / the renderer's format-string
11116/// template).
11117///
11118/// The single source of truth the rendered Flux bundle's per-CR
11119/// `HelmChartTemplate.spec.chart` chart-NAME reference leaf-scalar-
11120/// axis key reaches for:
11121///
11122/// - the rendered `helmrelease.yaml` document's per-`HelmChartTemplate`
11123/// `spec.chart` chart-NAME leaf scalar (caixa-flux/src/lib.rs:1814
11124/// — the `cluster_bundle` `helmrelease` format-string template's
11125/// lifted `chart: {chart_path}` interpolation the peer sibling
11126/// [`FLUX_KEY_SOURCE_REF`] source-reference triple's per-CR source-
11127/// artifact publishes).
11128///
11129/// The leaf-scalar-axis key names the same Flux-v2-helm-controller-
11130/// side per-`HelmChartTemplate` chart-NAME field every
11131/// `caixa-flux`-emitted `HelmRelease` document threads the chart
11132/// artifact name through, and must move together on any future Flux
11133/// v3 rebrand (a hypothetical upstream Flux v3 rename of the per-
11134/// `HelmChartTemplate.spec.chart` chart-NAME reference leaf-scalar-
11135/// axis from `chart` to `Chart` / `chartRef` / `chartName`,
11136/// coordinated with the upstream fluxcd/flux2 project's per-version
11137/// deprecation cycle, would land at this one const rather than
11138/// scattered across the one per-CR format-string template site).
11139///
11140/// Deliberate axis-independence discipline with the parent
11141/// [`FLUX_KEY_CHART`] container-axis re-export: both consts spell the
11142/// same underlying `"chart"` string but name distinct schema axes on
11143/// the same CRD group (Flux v2 `HelmRelease.spec.chart` container-
11144/// axis parent vs `HelmRelease.spec.chart.spec.chart` chart-NAME leaf
11145/// grandchild), so the two `pub const` declarations stay sibling
11146/// constants at the rustc symbol-name axis rather than coalescing onto
11147/// one canonical declaration — a future Flux v3 rebrand on the leaf-
11148/// scalar-axis lands independently of the sibling container-axis
11149/// rebrand. Peer to the deliberate [`CILIUM_KEY_PATH`] (ef6114f) /
11150/// [`GATEWAY_API_KEY_PATH`] (9f45aa4) axis-independence discipline the
11151/// two-CRD-groups-sharing-a-string sibling `"path"` re-exports
11152/// established on the peer canonical-axis-independence surface.
11153///
11154/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
11155/// "every recurring shape becomes a generator before it becomes a
11156/// pattern; every pattern becomes a library before it becomes
11157/// duplicated code. The duplication budget is zero.") promotes the
11158/// constant to a typed substrate-side `&'static str` on the same
11159/// trajectory the [`FLUX_KEY_CHART`] (8467748) /
11160/// [`FLUX_KEY_SOURCE_REF`] (e985089) / [`FLUX_KEY_VALUES`] (b54dc87) /
11161/// [`FLUX_KEY_HEALTH_CHECKS`] (6dbff58) lifts established on the
11162/// sibling canonical-Flux-v2-per-`HelmRelease`-body-key surfaces —
11163/// completes the per-`HelmRelease` chart-template `(spec.chart →
11164/// spec.chart.spec.chart + spec.chart.spec.sourceRef)` axis chain by
11165/// declaring the leaf-scalar sibling of the container-axis parent
11166/// the `FLUX_KEY_CHART` lift already anchors.
11167///
11168/// [cf]: ../../caixa_flux/index.html
11169pub const FLUX_HELMCHART_TEMPLATE_KEY_CHART: &str = "chart";
11170
11171/// Canonical Flux v2 per-`HelmRelease` values-override block-body-axis key
11172/// every `caixa-flux`-emitted `HelmRelease` document nests its per-cluster
11173/// value overrides under (`spec.values` on `HelmRelease`) — the Flux v2
11174/// CRD schema places the arbitrary per-cluster-override YAML body under
11175/// this single key, so drift on the block-body-axis silently dangles the
11176/// per-cluster override the `helm-controller`'s per-CR reconcile loop
11177/// merges into the referenced chart's `values.yaml` at Helm-render time
11178/// (a `"Values"` / `"vals"` / `"chartValues"` / `"overrides"` typo at
11179/// either the emit-side format-string template, the `upsert_into_helmrelease_programs`
11180/// upsert-path's `spec.values.programs[]` write, or a downstream
11181/// test-fixture probe silently routes the per-cluster overrides nowhere;
11182/// the workload silently comes up with the referenced chart's admission-
11183/// time defaults, far from the source `caixa.lisp` / the renderer's
11184/// format-string template).
11185///
11186/// The single source of truth every Flux-v2-per-`HelmRelease` values-
11187/// override-block-axis navigation reaches for:
11188///
11189/// - the rendered `helmrelease.yaml` document's per-`HelmRelease`
11190/// `spec.values` block (caixa-flux/src/lib.rs:900 — the
11191/// `cluster_bundle` `helmrelease` format-string template's baked
11192/// `values:\n` key beside the peer sibling lifted
11193/// [`DEFAULT_LIBRARY_NAME`] wrap key + [`HELM_VALUES_KEY_ENABLED`]
11194/// enable-toggle);
11195/// - the `upsert_into_helmrelease_programs` upsert path's
11196/// `spec.values.programs[]` write-side navigation
11197/// (caixa-flux/src/lib.rs:649 — the `lareira-fleet-programs`-
11198/// targeted `HelmRelease` CR's per-Servico entry-list mount);
11199/// - three test-side navigation sites in `mod tests` that probe the
11200/// rendered documents' `.get("values")` block-body axis to pin the
11201/// emitted per-cluster overrides against the sibling lifted
11202/// [`DEFAULT_LIBRARY_NAME`] wrap key + [`HELM_VALUES_KEY_ENABLED`]
11203/// enable-toggle + [`FLEET_PROGRAMS_KEY_PROGRAMS`] entry-list axis.
11204///
11205/// The block-body-axis key names the same Flux-v2-helm-controller-side
11206/// per-`HelmRelease` per-cluster-override block-body every
11207/// `caixa-flux`-emitted `HelmRelease` document threads its per-cluster
11208/// overlays through, and must move together on any future Flux v3
11209/// rebrand (a hypothetical upstream Flux v3 rename of the values-
11210/// override block-body-axis from `values` to `Values` / `chartValues`
11211/// / `overrides`, coordinated with the upstream fluxcd/flux2 project's
11212/// per-version deprecation cycle, would land at this one const rather
11213/// than scattered across the one emit-side format-string template + one
11214/// upsert-side write-side navigation + three per-test-fixture probe
11215/// sites).
11216///
11217/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
11218/// "every recurring shape becomes a generator before it becomes a
11219/// pattern; every pattern becomes a library before it becomes
11220/// duplicated code. The duplication budget is zero.") promotes the
11221/// constant to a typed substrate-side `&'static str` on the same
11222/// trajectory the [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
11223/// [`FLUX_KIND_HELM_RELEASE`] (e24ea3c) /
11224/// [`FLUX_KIND_KUSTOMIZATION`] (2d61a6f) /
11225/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
11226/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
11227/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) /
11228/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) /
11229/// [`FLUX_KEY_SOURCE_REF`] (e985089) lifts established on the sibling
11230/// canonical-Flux-v2-load-bearing-string surfaces — extends the per-CRD
11231/// kind-discriminator + apiVersion + install-namespace + source-
11232/// reference-container lift trajectory onto the sibling per-CR values-
11233/// override-block-body-axis key both `cluster_bundle` +
11234/// `upsert_into_helmrelease_programs` renderers consume under the
11235/// per-cluster override + per-Servico entry-list nesting.
11236///
11237/// [cf]: ../../caixa_flux/index.html
11238pub const FLUX_KEY_VALUES: &str = "values";
11239
11240/// Canonical Flux v2 per-`Kustomization` health-gate reference-list
11241/// container-axis key every `caixa-flux`-emitted `kustomization.yaml`
11242/// document mounts its per-sibling-`HelmRelease` health-probe list under
11243/// (`spec.healthChecks` on `Kustomization`) — the Flux v2 CRD schema places
11244/// the `[]NamespacedObjectKindReference` list under this single container
11245/// key, so drift on the container axis silently dangles the whole per-
11246/// Kustomization health-gate the Flux v2 `kustomize-controller`'s per-CR
11247/// reconcile loop reads to gate `Ready=True` on the referenced sibling
11248/// `HelmRelease` reaching its `HelmReleaseReady=True` condition (a
11249/// `"HealthChecks"` / `"healthchecks"` / `"healthcheck"` /
11250/// `"health_checks"` / `"probes"` typo at either the emit-side format-
11251/// string template or a downstream test-fixture probe silently
11252/// dangles the parent `Kustomization` at `Reconciling` forever at the Flux
11253/// v2 kustomize-controller's health-gate evaluation; the dependent per-
11254/// cluster fleet-programs upsert chain never sees `Ready=True` at apply
11255/// time with no field naming the container-axis-drift root cause).
11256///
11257/// The single source of truth every Flux-v2-per-`Kustomization` health-
11258/// gate-reference-list-container-axis-naming reaches for:
11259///
11260/// - the rendered `kustomization.yaml` document's per-`Kustomization`
11261/// `spec.healthChecks` block (caixa-flux/src/lib.rs — the
11262/// `cluster_bundle` `kustomization` format-string template's baked
11263/// `healthChecks:\n` container-axis key at line 990, threaded together
11264/// with the sibling lifted [`FLUX_HELMRELEASE_API_VERSION`] per-entry
11265/// `apiVersion` axis + [`FLUX_KIND_HELM_RELEASE`] per-entry `kind`
11266/// axis the health-gate references);
11267/// - three test-side navigation sites in `mod tests` that probe the
11268/// rendered `kustomization.yaml` document's
11269/// `.get("healthChecks")` container axis to pin the emitted per-entry
11270/// `apiVersion` + `kind` against the sibling lifted
11271/// [`FLUX_HELMRELEASE_API_VERSION`] + [`FLUX_KIND_HELM_RELEASE`] axes
11272/// (caixa-flux/src/lib.rs:2266, 2952, 3016).
11273///
11274/// The container-axis key names the same Flux-v2-kustomize-controller-side
11275/// per-`Kustomization` health-gate-reference-list the sibling per-entry
11276/// `apiVersion` [`FLUX_HELMRELEASE_API_VERSION`] + per-entry `kind`
11277/// [`FLUX_KIND_HELM_RELEASE`] axes nest under, and must move together on
11278/// any future Flux v3 rebrand (a hypothetical upstream Flux v3 rename of
11279/// the per-`Kustomization` health-gate reference-list container axis from
11280/// `healthChecks` to `HealthChecks` / `healthchecks` / `healthcheck` /
11281/// `health_checks` / `probes`, coordinated with the upstream fluxcd/flux2
11282/// project's per-version deprecation cycle, would land at this one const
11283/// rather than scattered across the one emit-side format-string template +
11284/// three per-test-fixture probe sites).
11285///
11286/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
11287/// "every recurring shape becomes a generator before it becomes a
11288/// pattern; every pattern becomes a library before it becomes
11289/// duplicated code. The duplication budget is zero.") promotes the
11290/// constant to a typed substrate-side `&'static str` on the same
11291/// trajectory the [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
11292/// [`FLUX_KIND_HELM_RELEASE`] (e24ea3c) /
11293/// [`FLUX_KIND_KUSTOMIZATION`] (2d61a6f) /
11294/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
11295/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
11296/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) /
11297/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) /
11298/// [`FLUX_KEY_SOURCE_REF`] (e985089) /
11299/// [`FLUX_KEY_CHART`] (8467748) /
11300/// [`FLUX_KEY_VALUES`] (b54dc87) lifts established on the sibling
11301/// canonical-Flux-v2-load-bearing-string surfaces — extends the per-CRD
11302/// kind-discriminator + apiVersion + install-namespace + source-
11303/// reference-container + chart-template-container + values-override-block
11304/// lift trajectory onto the sibling per-`Kustomization` health-gate-
11305/// reference-list container-axis key the `cluster_bundle` renderer
11306/// consumes under its `kustomization.yaml` format-string template.
11307///
11308/// [cf]: ../../caixa_flux/index.html
11309pub const FLUX_KEY_HEALTH_CHECKS: &str = "healthChecks";
11310
11311/// Canonical Flux v2 per-CR reconcile-poll cadence scalar-axis key every
11312/// `caixa-flux`-emitted Flux document (`GitRepository`, `HelmRelease`,
11313/// `Kustomization`) declares its per-CR `spec.interval` reconcile cadence
11314/// under. Unlike the sibling per-CR body-key axes ([`FLUX_KEY_SOURCE_REF`],
11315/// [`FLUX_KEY_CHART`], [`FLUX_KEY_VALUES`], [`FLUX_KEY_HEALTH_CHECKS`])
11316/// which each land on exactly one of the three Flux v2 controller CRDs,
11317/// the reconcile-poll cadence scalar-axis is the *shared* Flux v2 per-CR
11318/// contract every controller (the `source-controller`, the
11319/// `helm-controller`, the `kustomize-controller`) reads to schedule its
11320/// per-CR reconcile loop off the sibling per-CR CRD registration. Drift on
11321/// the scalar-axis key silently drops the per-CR reconcile schedule from
11322/// the Flux v2 controllers' per-CR watch registrations — a `"Interval"` /
11323/// `"period"` / `"cadence"` / `"pollInterval"` / `"reconcileInterval"`
11324/// typo at any of the three emit-side format-string template sites
11325/// silently drops the per-CR reconcile schedule from the affected Flux v2
11326/// controller's per-CR watch registration; the referenced Git source
11327/// never re-polls / the referenced chart never re-templates / the parent
11328/// Kustomization never re-applies at upstream drift, freezing the whole
11329/// cluster's per-`caixa` per-cluster bundle at the last-applied snapshot
11330/// with no field naming the scalar-axis-drift root cause.
11331///
11332/// The single source of truth every Flux-v2-per-CR-reconcile-poll-cadence-
11333/// scalar-axis-naming reaches for — the three per-CR emit sites the
11334/// [`cluster_bundle`][cf] renderer threads through are all named through
11335/// this one const:
11336///
11337/// - the rendered `gitrepository.yaml` document's per-`GitRepository`
11338/// `spec.interval` scalar (caixa-flux/src/lib.rs — the `cluster_bundle`
11339/// `gitrepo` format-string template's baked `interval:` scalar-axis
11340/// key, nested alongside the sibling lifted
11341/// [`FLUX_GITREPOSITORY_API_VERSION`] top-level `apiVersion` +
11342/// [`FLUX_KIND_GIT_REPOSITORY`] top-level `kind` axes the source-
11343/// controller reads to bind the per-CR poll cycle);
11344/// - the rendered `helmrelease.yaml` document's per-`HelmRelease`
11345/// `spec.interval` scalar (caixa-flux/src/lib.rs — the `cluster_bundle`
11346/// `helmrelease` format-string template's baked `interval:` scalar-
11347/// axis key, nested alongside the sibling lifted
11348/// [`FLUX_HELMRELEASE_API_VERSION`] top-level `apiVersion` +
11349/// [`FLUX_KIND_HELM_RELEASE`] top-level `kind` axes the helm-controller
11350/// reads to bind the per-CR poll cycle);
11351/// - the rendered `kustomization.yaml` document's per-`Kustomization`
11352/// `spec.interval` scalar (caixa-flux/src/lib.rs — the `cluster_bundle`
11353/// `kustomization` format-string template's baked `interval:` scalar-
11354/// axis key, nested alongside the sibling lifted
11355/// [`FLUX_KUSTOMIZATION_API_VERSION`] top-level `apiVersion` +
11356/// [`FLUX_KIND_KUSTOMIZATION`] top-level `kind` axes the kustomize-
11357/// controller reads to bind the per-CR poll cycle).
11358///
11359/// The three sites must move together on any future Flux v3 rebrand (a
11360/// hypothetical upstream fluxcd/flux2 rename from `interval` to `Interval`
11361/// / `period` / `cadence` / `pollInterval` / `reconcileInterval`,
11362/// coordinated with the upstream project's per-version deprecation cycle,
11363/// would land at this one const rather than scattered across the three
11364/// per-CR emit-side format-string template sites). This is a distinct
11365/// duplication shape from the sibling [`FLUX_KEY_SOURCE_REF`] /
11366/// [`FLUX_KEY_CHART`] / [`FLUX_KEY_VALUES`] / [`FLUX_KEY_HEALTH_CHECKS`]
11367/// lifts: those closed *one-CR-body-key* duplication trios (one emit-site
11368/// per CR + several test-side probes); this one closes the sibling
11369/// *three-CR-shared-body-key* triplet the Flux v2 reconcile-poll cadence
11370/// contract shares across all three per-cluster-bundle CRDs.
11371///
11372/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
11373/// "every recurring shape becomes a generator before it becomes a
11374/// pattern; every pattern becomes a library before it becomes
11375/// duplicated code. The duplication budget is zero.") promotes the
11376/// constant to a typed substrate-side `&'static str` on the same
11377/// trajectory the [`FLUX_KEY_SOURCE_REF`] (e985089) /
11378/// [`FLUX_KEY_CHART`] (8467748) /
11379/// [`FLUX_KEY_VALUES`] (b54dc87) /
11380/// [`FLUX_KEY_HEALTH_CHECKS`] (6dbff58) lifts established on the sibling
11381/// canonical-Flux-v2-per-CR-body-key surfaces — extends the per-CR
11382/// body-key lift trajectory onto the sibling *cross-CR-shared* reconcile-
11383/// poll cadence scalar-axis every Flux v2 controller reads to bind its
11384/// per-CR poll cycle.
11385///
11386/// [cf]: ../../caixa_flux/fn.cluster_bundle.html
11387pub const FLUX_KEY_INTERVAL: &str = "interval";
11388
11389/// Canonical Flux v2 per-`GitRepository` `spec.ref.tag` git-tag-selector
11390/// scalar-axis key every `caixa-flux`-emitted `gitrepository.yaml`
11391/// document declares when the per-Servico bundle's `git_ref` is a
11392/// tag-shaped selector. Peer of [`FLUX_GITREPOSITORY_REF_KEY_BRANCH`]
11393/// / [`FLUX_GITREPOSITORY_REF_KEY_COMMIT`] on the sibling per-shape
11394/// arms of the `FluxCD` source-controller `GitRepository.spec.ref`
11395/// ref-selection discriminated-union axis — the three-way sub-selector
11396/// key set the Flux v2 `source-controller` reads to bind the per-CR
11397/// git-source clone `refspec` from the (tag | branch | commit) input
11398/// triple. A drifted value at any of the three keys (`"Tag"` /
11399/// `"gitTag"` / `"tagName"` at this arm, `"Branch"` / `"gitBranch"`
11400/// at the sibling arm, `"Commit"` / `"sha"` / `"revision"` at the
11401/// third arm) silently dangles the whole `spec.ref` sub-block at the
11402/// `FluxCD` `source-controller`'s CRD registration; the per-Servico
11403/// clone never resolves at reconcile time and the sibling
11404/// `HelmRelease.spec.chart.spec.sourceRef` reference dangles at
11405/// admission with no field naming the sub-selector-key-drift root
11406/// cause. Changing this value is a coordinated Flux v3 migration
11407/// alongside the upstream `fluxcd/flux2` deprecation cycle, not an
11408/// incidental edit.
11409///
11410/// The single source of truth every Flux-v2-per-`GitRepository`-
11411/// `spec.ref`-tag-arm-axis-naming reaches for — the two per-render
11412/// consumer sites the [`crate::render`]-side lift closes on the
11413/// [`caixa_flux::GitRefSpec::Tag`] variant are both named through this
11414/// one const via the [`caixa_flux::GitRefSpec::ref_field_name`]
11415/// dispatch:
11416///
11417/// - the rendered `gitrepository.yaml` document's per-`GitRepository`
11418/// `spec.ref.tag` YAML sub-field (caixa-flux's `cluster_bundle`
11419/// `gitref_field` composer, the sole in-tree emission site);
11420/// - the sibling per-render human-readable narrator's `tag <value>`
11421/// prefix (caixa-flux's `cluster_bundle` `tag_human` composer's
11422/// tag-arm branch), the operator-facing per-arm narrator prose
11423/// `feira app graph` / `feira deploy` diagnostics quote.
11424///
11425/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5) —
11426/// promotes the sub-selector-key byte-string to a typed substrate-side
11427/// `&'static str` on the same trajectory the peer per-CR body-key
11428/// [`FLUX_KEY_SOURCE_REF`] (e985089) / [`FLUX_KEY_CHART`] (8467748) /
11429/// [`FLUX_KEY_VALUES`] (b54dc87) / [`FLUX_KEY_HEALTH_CHECKS`] (6dbff58)
11430/// / [`FLUX_KEY_INTERVAL`] (48db6e2) lifts established on the sibling
11431/// canonical-Flux-v2-per-CR-body-key surfaces — pivots the discipline
11432/// from the per-CR body-key axis onto the sibling per-`GitRepository`-
11433/// `spec.ref`-sub-selector-key axis every `cluster_bundle`-rendered
11434/// bundle threads its per-shape ref-selection through, and closes the
11435/// coordinated 2-site duplication (`gitref_field` YAML emit +
11436/// `tag_human` narrator prose) the prior inline `format!(" tag:
11437/// {t:?}")` + `format!("tag {t}")` literals in
11438/// caixa-flux/src/lib.rs carried on the tag-arm of the discriminated-
11439/// union.
11440///
11441/// [cf]: ../../caixa_flux/index.html
11442pub const FLUX_GITREPOSITORY_REF_KEY_TAG: &str = "tag";
11443
11444/// Canonical Flux v2 per-`GitRepository` `spec.ref.branch`
11445/// git-branch-selector scalar-axis key every `caixa-flux`-emitted
11446/// `gitrepository.yaml` document declares when the per-Servico
11447/// bundle's `git_ref` is a branch-shaped selector. Peer of
11448/// [`FLUX_GITREPOSITORY_REF_KEY_TAG`] /
11449/// [`FLUX_GITREPOSITORY_REF_KEY_COMMIT`] on the sibling per-shape arms
11450/// of the `FluxCD` source-controller `GitRepository.spec.ref`
11451/// ref-selection discriminated-union axis; see
11452/// [`FLUX_GITREPOSITORY_REF_KEY_TAG`] for the full lift rationale.
11453///
11454/// [cf]: ../../caixa_flux/index.html
11455pub const FLUX_GITREPOSITORY_REF_KEY_BRANCH: &str = "branch";
11456
11457/// Canonical Flux v2 per-`GitRepository` `spec.ref.commit`
11458/// git-commit-selector scalar-axis key every `caixa-flux`-emitted
11459/// `gitrepository.yaml` document declares when the per-Servico
11460/// bundle's `git_ref` is a commit-shaped selector. Peer of
11461/// [`FLUX_GITREPOSITORY_REF_KEY_TAG`] /
11462/// [`FLUX_GITREPOSITORY_REF_KEY_BRANCH`] on the sibling per-shape arms
11463/// of the `FluxCD` source-controller `GitRepository.spec.ref`
11464/// ref-selection discriminated-union axis; see
11465/// [`FLUX_GITREPOSITORY_REF_KEY_TAG`] for the full lift rationale.
11466///
11467/// [cf]: ../../caixa_flux/index.html
11468pub const FLUX_GITREPOSITORY_REF_KEY_COMMIT: &str = "commit";
11469
11470/// Canonical Flux v2 per-`GitRepository` `spec.ref` ref-selection
11471/// discriminated-union parent container-axis key every `caixa-flux`-
11472/// emitted `gitrepository.yaml` document mounts its per-shape
11473/// `{tag, branch, commit}` sub-selector arm under. Nests one level
11474/// above the sibling [`FLUX_GITREPOSITORY_REF_KEY_TAG`] /
11475/// [`FLUX_GITREPOSITORY_REF_KEY_BRANCH`] /
11476/// [`FLUX_GITREPOSITORY_REF_KEY_COMMIT`] triple it wraps — the K8s
11477/// Flux v2 `source.toolkit.fluxcd.io/v1` `GitRepository` CRD schema
11478/// pins the per-CR ref-selection through this `spec.ref` container-
11479/// axis, and every rendered `spec.ref.{tag,branch,commit}` arm the
11480/// [`caixa_flux::GitRefSpec`] discriminated-union emits nests
11481/// beneath this exact key.
11482///
11483/// The FluxCD `source-controller`'s per-CR `RESTMapper` reads
11484/// `spec.ref` to source the per-Servico git clone refspec (the
11485/// container-axis carrying the three-way `{tag, branch, commit}`
11486/// arm the controller dispatches on), so drift on the container-
11487/// axis KEY is exactly as load-bearing as drift on the sibling per-
11488/// shape sub-selector KEY the arms decode through: a `"Ref"` /
11489/// `"gitRef"` / `"revision"` / `"source"` typo at the writer site
11490/// silently emits a `GitRepository` whose ref-selection container-
11491/// axis the CRD schema validator drops as unknown, and the sibling
11492/// `HelmRelease.spec.chart.spec.sourceRef` reference dangles at
11493/// admission with the per-Servico clone never resolving at reconcile
11494/// time — apply-side: the Flux v2 `source-controller`'s per-CR
11495/// reconcile loop no-ops entirely (no clone, no artifact, no
11496/// checksum), the sibling `HelmRelease`'s per-chart resolve step
11497/// finds the empty artifact, and every rendered `HelmRelease` /
11498/// `Kustomization` bundle document downstream of this `GitRepository`
11499/// silently no-ops at the FluxCD apply chain with no field naming
11500/// the container-axis-drift root cause.
11501///
11502/// The single source of truth every Flux-v2-per-`GitRepository`-
11503/// `spec.ref`-container-axis-naming reaches for — the two per-render
11504/// consumer sites the [`crate::render`]-side lift closes:
11505///
11506/// - the rendered `gitrepository.yaml` document's per-`GitRepository`
11507/// `spec.ref` YAML block-body axis (caixa-flux's `cluster_bundle`
11508/// `gitrepo` template composer's `ref:` sub-block header — the
11509/// sole production emission site the prior inline `"ref:"`
11510/// literal sat at);
11511/// - the peer test-fixture navigation site
11512/// (caixa-flux's `cluster_bundle_gitrepository_ref_*` per-arm
11513/// round-trip pin's `.get("ref")` sub-selector traversal step —
11514/// the sole test-side reader site the prior inline `"ref"`
11515/// literal sat at).
11516///
11517/// Changing this value is a coordinated Flux v3 migration alongside
11518/// the upstream `fluxcd/flux2` deprecation cycle, not an incidental
11519/// edit — pinning it here means the migration lands as one edit at
11520/// the const plus a re-run of the pin tests rather than a per-
11521/// renderer sweep with no single source of truth to consult.
11522///
11523/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5)
11524/// promotes the parent-container-axis byte-string to a typed
11525/// substrate-side `&'static str` on the same trajectory the sibling
11526/// per-shape arm sub-selector-key
11527/// [`FLUX_GITREPOSITORY_REF_KEY_TAG`] (7d40380) /
11528/// [`FLUX_GITREPOSITORY_REF_KEY_BRANCH`] (7d40380) /
11529/// [`FLUX_GITREPOSITORY_REF_KEY_COMMIT`] (7d40380) triple lifts
11530/// established on the sibling per-shape arm surface — nests the
11531/// parent container-axis KEY above the already-lifted per-shape arm
11532/// sub-selector-KEY triple, so the whole per-`GitRepository`
11533/// `spec.ref` sub-schema (parent container-axis KEY + per-shape arm
11534/// sub-selector-KEY triple + per-arm value) now navigates through
11535/// four caixa-core `&'static str`s in coordination, and any future
11536/// Flux v2 sub-schema rebrand (an upstream `fluxcd/flux2` v3
11537/// rename of the ref-selection container-axis from `spec.ref` to
11538/// `spec.gitRef` / `spec.source.ref`) lands at one const edit
11539/// coordinated with the sibling per-shape arm lifts.
11540///
11541/// [cf]: ../../caixa_flux/index.html
11542pub const FLUX_GITREPOSITORY_KEY_REF: &str = "ref";
11543
11544/// Canonical Flux v2 `GitRepository.spec.url` per-CR remote-repo-URL
11545/// leaf-scalar-axis key every [`caixa-flux`][cf]-rendered
11546/// `gitrepository.yaml` document declares. The FluxCD `source-controller`
11547/// reads `spec.url` as the git remote URL it clones per-reconcile — the
11548/// authoritative remote the per-Servico artifact archive is sourced from
11549/// at every reconcile cycle. A drifted key (e.g. `"URL"`, `"gitUrl"`,
11550/// `"repo"`, `"repository"`) at the writer site would silently emit a
11551/// `GitRepository` whose CRD schema validator drops the URL field as
11552/// unknown, and the per-Servico artifact would never populate — the
11553/// downstream `HelmRelease.spec.chart.spec.sourceRef` reference dangles
11554/// with an empty artifact at admission, every rendered `HelmRelease` /
11555/// `Kustomization` bundle document downstream silently no-ops at
11556/// reconcile time with no field naming the URL-key-drift root cause.
11557///
11558/// Sibling to the already-lifted per-`GitRepository`-CR `spec` sub-
11559/// block keys [`FLUX_GITREPOSITORY_KEY_REF`] (84a3c20, the parent
11560/// container-axis for the `spec.ref.{tag,branch,commit}` per-shape arm
11561/// discriminated union) — this constant names the peer per-CR leaf-
11562/// scalar remote-URL axis on the same top-level `spec` position. Both
11563/// axes together completely enumerate the `GitRepository.spec.*` per-
11564/// CR sub-block keys `caixa-flux`'s current `cluster_bundle` gitrepo
11565/// template writes (`spec.interval` reaches through the lifted
11566/// `FLUX_KEY_INTERVAL`, `spec.url` through this constant, `spec.ref`
11567/// through [`FLUX_GITREPOSITORY_KEY_REF`]), so any future Flux v3
11568/// `GitRepository` schema promotion lands as one caixa-core edit
11569/// coordinated across the sibling sub-block key axes.
11570///
11571/// The single source of truth every Flux-v2-per-`GitRepository`-
11572/// `spec.url`-leaf-scalar-axis-naming reaches for — one production
11573/// consumer today:
11574///
11575/// - the rendered `gitrepository.yaml` document's per-`GitRepository`
11576/// `spec.url` leaf-scalar remote-URL axis (caixa-flux's
11577/// `cluster_bundle` `gitrepo` template composer's `url:` sub-key
11578/// — the sole production emission site the prior inline `"url:"`
11579/// literal sat at).
11580///
11581/// Every future per-`GitRepository` renderer (the M4 typed-Aplicacao
11582/// materializer's per-Aplicacao `GitRepository` synthesis for
11583/// per-aggregator-manifest sources, any future `caixa-otel`
11584/// collector-pipeline `GitRepository`, any future per-cluster snapshot
11585/// `GitRepository` the operator emits) inherits the canonical URL
11586/// leaf-scalar key by construction with no opportunity for
11587/// per-renderer drift.
11588///
11589/// [cf]: ../../caixa_flux/index.html
11590pub const FLUX_GITREPOSITORY_KEY_URL: &str = "url";
11591
11592/// Canonical Flux v2 per-cluster-bundle `HelmRelease` document
11593/// filename every [`caixa-flux`][cf]-rendered `cluster_bundle` carries
11594/// at the per-Servico bundle's rendered file collection — the fixed
11595/// filename the sibling `gitrepository.yaml` + `kustomization.yaml`
11596/// bundle documents key against when the cluster-side `FluxCD`
11597/// controllers reconcile the per-Servico release cycle, and the
11598/// exact filename every downstream consumer that reaches into the
11599/// rendered bundle by document name looks up.
11600///
11601/// Two production consumers reach for this filename:
11602///
11603/// - [`caixa-flux`][cf]'s [`cluster_bundle`][cb] `BundleFile`
11604/// assembly's per-file `path` axis for the `HelmRelease`
11605/// document — the sole caixa-flux production emit site the prior
11606/// inline `PathBuf::from("helmrelease.yaml")` literal sat at,
11607/// one of the three canonical per-Servico Flux bundle files the
11608/// renderer emits alongside the sibling `gitrepository.yaml` +
11609/// `kustomization.yaml` documents;
11610/// - the peer test-fixture navigators in this crate reach into the
11611/// rendered `BundleFile` collection by the same filename to
11612/// round-trip-pin each emitted `HelmRelease` axis — a dozen
11613/// `.find(|f| f.path == PathBuf::from("helmrelease.yaml"))` +
11614/// `names.contains(&"helmrelease.yaml".to_string())` fixture
11615/// navigators across every per-CR body-axis sweep, `apiVersion`
11616/// round-trip, `spec.chart` / `spec.values` / `spec.sourceRef`
11617/// nested block existence pin.
11618///
11619/// Until this lift landed the filename `"helmrelease.yaml"` lived as
11620/// thirteen verbatim inline literals (one production
11621/// `PathBuf::from("helmrelease.yaml")` at the `cluster_bundle`
11622/// `BundleFile`-vec construction site + twelve test-side
11623/// `PathBuf::from("helmrelease.yaml")` /
11624/// `names.contains(&"helmrelease.yaml".to_string())` /
11625/// `.expect("helmrelease.yaml present")` fixture navigators). A drift
11626/// on the emit side (a `"HelmRelease.yaml"` / `"helm-release.yaml"` /
11627/// `"helmrelease.yml"` / `"helm_release.yaml"` typo, or an accidental
11628/// per-fork rebrand onto a stale filename any per-edition Flux
11629/// substrate might introduce) at any one site would surface as one of
11630/// two silent failure modes at cluster-side reconcile time:
11631///
11632/// - the `FluxCD` `kustomize-controller` refuses to apply the
11633/// rendered bundle at all — the per-Servico
11634/// `Kustomization.spec.path` opens the bundle directory and its
11635/// `HelmRelease` navigator returns `None`, with the reconcile
11636/// dropping at "no `HelmRelease` document found under this
11637/// bundle" far from the emit-drift commit's source, and the
11638/// per-Servico release cycle drops with no field naming the
11639/// bundle-filename-drift root cause (the operator sees "the
11640/// release never picks up its Helm chart" with no canonical
11641/// anchor to compare the rendered filename against);
11642/// - the sibling `Kustomization` document's per-CR
11643/// `spec.healthChecks[]` references the drifted filename via
11644/// `namespace/name` — the healthCheck stays perpetually `Unknown`
11645/// because the referenced `HelmRelease` never materializes at the
11646/// expected bundle path, and the peer `GitRepository` document's
11647/// every-poll reconcile ticks the bundle-tree hash over the
11648/// drifted filename with the per-Servico release cycle silently
11649/// frozen at "waiting on healthCheck".
11650///
11651/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
11652/// "every recurring shape becomes a generator before it becomes a
11653/// pattern; every pattern becomes a library before it becomes
11654/// duplicated code. The duplication budget is zero.") promotes the
11655/// filename to a typed substrate-side `&'static str` on the same
11656/// trajectory the peer [`HELM_CHART_YAML_FILENAME`] (c2c99b0) /
11657/// [`HELM_VALUES_YAML_FILENAME`] (9a980ba) lifts established on the
11658/// sibling Helm-chart-directory filename axes — pivots the
11659/// canonical-filename single-sourcing discipline from the per-Helm-
11660/// chart-directory metadata / values file surfaces onto the sibling
11661/// per-Flux-v2-bundle `HelmRelease` document filename axis every
11662/// rendered per-Servico bundle declares at its cluster-side reconcile
11663/// tree. Peer of a future sibling lift on the other two per-Servico
11664/// Flux bundle document filenames (`gitrepository.yaml` +
11665/// `kustomization.yaml`) — this const anchors the first coordinate
11666/// of the per-bundle
11667/// `(gitrepository, helmrelease, kustomization)` filename axis triple
11668/// every rendered cluster bundle carries.
11669///
11670/// [cf]: ../../caixa_flux/index.html
11671/// [cb]: ../../caixa_flux/fn.cluster_bundle.html
11672pub const FLUX_HELMRELEASE_YAML_FILENAME: &str = "helmrelease.yaml";
11673
11674/// Canonical Flux v2 per-cluster-bundle `GitRepository` document
11675/// filename every [`caixa-flux`][cf]-rendered [`cluster_bundle`][cb]
11676/// carries at the per-Servico bundle's rendered file collection — the
11677/// fixed filename the sibling `helmrelease.yaml` +
11678/// `kustomization.yaml` documents key against when the cluster-side
11679/// `FluxCD` `source-controller` reconciles the per-Servico Git-source
11680/// poll cycle, and the exact filename every downstream consumer that
11681/// reaches into the rendered bundle by document name looks up.
11682///
11683/// Two production consumers reach for this filename:
11684///
11685/// - [`caixa-flux`][cf]'s [`cluster_bundle`][cb] `BundleFile`
11686/// assembly's per-file `path` axis for the `GitRepository`
11687/// document — the sole caixa-flux production emit site the prior
11688/// inline `PathBuf::from("gitrepository.yaml")` literal sat at,
11689/// one of the three canonical per-Servico Flux bundle files the
11690/// renderer emits alongside the sibling `helmrelease.yaml` +
11691/// `kustomization.yaml` documents (the second coordinate of the
11692/// per-bundle `(gitrepository, helmrelease, kustomization)`
11693/// filename axis triple this const closes);
11694/// - the peer test-fixture navigators in this crate reach into the
11695/// rendered `BundleFile` collection by the same filename to
11696/// round-trip-pin each emitted `GitRepository` axis — every
11697/// `.find(|f| f.path == PathBuf::from("gitrepository.yaml"))` +
11698/// `names.contains(&"gitrepository.yaml".to_string())` fixture
11699/// navigator across the per-CR body-axis sweeps that pin the
11700/// Git-source apiVersion / kind / `spec.url` / `spec.ref`
11701/// round-trips.
11702///
11703/// Until this lift landed the filename `"gitrepository.yaml"` lived
11704/// as nine verbatim inline literals across [`caixa-flux`][cf] (one
11705/// production `PathBuf::from("gitrepository.yaml")` at the
11706/// `cluster_bundle` `BundleFile`-vec construction site + eight
11707/// test-side fixture navigators). A drift on the emit side (a
11708/// `"GitRepository.yaml"` / `"git-repository.yaml"` /
11709/// `"gitrepository.yml"` typo, or an accidental per-fork rebrand)
11710/// would surface at cluster-side reconcile time far from the source:
11711/// the `FluxCD` `source-controller` never registers a `GitRepository`
11712/// document under the expected bundle path, the sibling
11713/// `HelmRelease.spec.chart.spec.sourceRef` reference dangles at
11714/// admission, and the per-Servico release cycle silently freezes at
11715/// last-applied state with no field naming the filename-drift root
11716/// cause.
11717///
11718/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
11719/// "every recurring shape becomes a generator before it becomes a
11720/// pattern; every pattern becomes a library before it becomes
11721/// duplicated code. The duplication budget is zero.") promotes the
11722/// filename to a typed substrate-side `&'static str` on the same
11723/// trajectory the peer [`FLUX_HELMRELEASE_YAML_FILENAME`] (ba7b0b2) /
11724/// [`HELM_CHART_YAML_FILENAME`] (c2c99b0) /
11725/// [`HELM_VALUES_YAML_FILENAME`] (9a980ba) lifts established on the
11726/// sibling per-Flux-v2-bundle / per-Helm-chart-directory filename
11727/// axes — pairs with the sibling
11728/// [`FLUX_KUSTOMIZATION_YAML_FILENAME`] on the third coordinate to
11729/// close the per-bundle `(gitrepository, helmrelease, kustomization)`
11730/// filename axis triple every rendered cluster bundle carries.
11731///
11732/// [cf]: ../../caixa_flux/index.html
11733/// [cb]: ../../caixa_flux/fn.cluster_bundle.html
11734pub const FLUX_GITREPOSITORY_YAML_FILENAME: &str = "gitrepository.yaml";
11735
11736/// Canonical Flux v2 per-cluster-bundle `Kustomization` document
11737/// filename every [`caixa-flux`][cf]-rendered [`cluster_bundle`][cb]
11738/// carries at the per-Servico bundle's rendered file collection — the
11739/// fixed filename the sibling `gitrepository.yaml` +
11740/// `helmrelease.yaml` documents key against when the cluster-side
11741/// `FluxCD` `kustomize-controller` reconciles the per-Servico apply
11742/// cycle, and the exact filename every downstream consumer that
11743/// reaches into the rendered bundle by document name looks up.
11744///
11745/// Two production consumers reach for this filename:
11746///
11747/// - [`caixa-flux`][cf]'s [`cluster_bundle`][cb] `BundleFile`
11748/// assembly's per-file `path` axis for the `Kustomization`
11749/// document — the sole caixa-flux production emit site the prior
11750/// inline `PathBuf::from("kustomization.yaml")` literal sat at,
11751/// one of the three canonical per-Servico Flux bundle files the
11752/// renderer emits alongside the sibling `gitrepository.yaml` +
11753/// `helmrelease.yaml` documents (the third coordinate of the
11754/// per-bundle `(gitrepository, helmrelease, kustomization)`
11755/// filename axis triple this const closes);
11756/// - the peer test-fixture navigators in this crate reach into the
11757/// rendered `BundleFile` collection by the same filename to
11758/// round-trip-pin each emitted `Kustomization` axis — every
11759/// `.find(|f| f.path == PathBuf::from("kustomization.yaml"))` +
11760/// `names.contains(&"kustomization.yaml".to_string())` fixture
11761/// navigator across the per-CR body-axis sweeps that pin the
11762/// Kustomization apiVersion / kind / `spec.sourceRef` /
11763/// `spec.healthChecks` round-trips.
11764///
11765/// Until this lift landed the filename `"kustomization.yaml"` lived
11766/// as sixteen verbatim inline literals across [`caixa-flux`][cf]
11767/// (one production `PathBuf::from("kustomization.yaml")` at the
11768/// `cluster_bundle` `BundleFile`-vec construction site + fifteen
11769/// test-side fixture navigators). A drift on the emit side (a
11770/// `"Kustomization.yaml"` / `"kustomize.yaml"` / `"kustomization.yml"`
11771/// typo, or an accidental per-fork rebrand) would surface at
11772/// cluster-side reconcile time far from the source: the `FluxCD`
11773/// `kustomize-controller` never picks up the parent `Kustomization`
11774/// under the expected bundle path, every per-Servico apply silently
11775/// stops advancing at last-applied state, and the sibling
11776/// `HelmRelease` / `GitRepository` reconciles register with no
11777/// parent Kustomization gating their health, with no field naming
11778/// the filename-drift root cause.
11779///
11780/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
11781/// "every recurring shape becomes a generator before it becomes a
11782/// pattern; every pattern becomes a library before it becomes
11783/// duplicated code. The duplication budget is zero.") promotes the
11784/// filename to a typed substrate-side `&'static str` on the same
11785/// trajectory the peer [`FLUX_HELMRELEASE_YAML_FILENAME`] (ba7b0b2) /
11786/// [`FLUX_GITREPOSITORY_YAML_FILENAME`] (this commit) /
11787/// [`HELM_CHART_YAML_FILENAME`] (c2c99b0) /
11788/// [`HELM_VALUES_YAML_FILENAME`] (9a980ba) lifts established on the
11789/// sibling per-Flux-v2-bundle / per-Helm-chart-directory filename
11790/// axes — closes the per-bundle `(gitrepository, helmrelease,
11791/// kustomization)` filename axis triple every rendered cluster
11792/// bundle carries.
11793///
11794/// [cf]: ../../caixa_flux/index.html
11795/// [cb]: ../../caixa_flux/fn.cluster_bundle.html
11796pub const FLUX_KUSTOMIZATION_YAML_FILENAME: &str = "kustomization.yaml";
11797
11798/// Canonical K8s Gateway API CRD `apiVersion` every `caixa-mesh`-emitted
11799/// `Gateway` / `HTTPRoute` document declares. The K8s apiserver-side
11800/// SIG-Network Gateway API conformance registers the `Gateway` /
11801/// `HTTPRoute` / `GatewayClass` / `TCPRoute` / `TLSRoute` / `GRPCRoute`
11802/// CRDs at this exact group/version (`gateway.networking.k8s.io/v1`);
11803/// drift to a stale `v1beta1` / `v1alpha2` (the pre-GA Gateway API betas
11804/// every upstream conformance doc names) silently routes the rendered
11805/// `Gateway` / `HTTPRoute` outside the apiserver's CRD-version
11806/// registration and breaks at apply time with a non-self-locating "no
11807/// kind 'Gateway' is registered for version
11808/// 'gateway.networking.k8s.io/v1beta1'" error far from the source
11809/// caixa.lisp / the renderer's [`kube_resource_skeleton`] call site.
11810///
11811/// The single source of truth both Gateway-API CRD axes of the rendered
11812/// Aplicacao mesh bundle reach for:
11813///
11814/// - `Gateway` `apiVersion` — the top-level CRD-group/version the
11815/// rendered Gateway document declares (caixa-mesh/src/lib.rs:455 —
11816/// the `gateway_routes` per-Aplicacao Gateway skeleton call);
11817/// - `HTTPRoute` `apiVersion` — the same Gateway API CRD
11818/// group/version every per-`:entrada :paths` HTTPRoute declares
11819/// (caixa-mesh/src/lib.rs:496 — the `gateway_routes` HTTPRoute
11820/// skeleton call). The K8s SIG-Network Gateway API contract bumps
11821/// `Gateway`, `HTTPRoute`, `GatewayClass`, and the rest of the
11822/// per-conformance CRD set as a unit; a future Gateway-API GA
11823/// promotion (the upstream Gateway API SIG roadmap names per-CRD-
11824/// group / per-version migration once the v1 GA branch matures) on
11825/// one axis without a coordinated edit on the other would have
11826/// silently emitted a `Gateway` / `HTTPRoute` pair pointing at
11827/// distinct CRD versions — apply-side: the `Gateway` and
11828/// `HTTPRoute` land in two distinct apiserver-side CRD
11829/// registrations, the per-route attached-policy resolution
11830/// pipeline never binds, every external `:entrada` flow drops at
11831/// the gateway with no field naming the version-drift root cause.
11832///
11833/// Until this lift landed both axes carried inline
11834/// `gateway.networking.k8s.io/v1` literals across two production-code
11835/// occurrences in caixa-mesh/src/lib.rs:455, 496 (the `gateway_routes`
11836/// `Gateway` + `HTTPRoute` skeleton calls) plus a matching pair inside
11837/// the in-file `gateway_carries_canonical_kube_skeleton_without_labels`
11838/// + `httproute_carries_canonical_kube_skeleton_without_labels` test
11839/// fixtures — four occurrences of the same load-bearing Gateway API
11840/// CRD-group/version convention, drift-prone by construction.
11841///
11842/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
11843/// "every recurring shape becomes a generator before it becomes a
11844/// pattern; every pattern becomes a library before it becomes
11845/// duplicated code. The duplication budget is zero.") promotes the
11846/// constant to a typed substrate-side `&'static str` on the same
11847/// trajectory the [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
11848/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
11849/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) /
11850/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) lifts established on
11851/// the peer Flux-v2-controller-triplet canonical-load-bearing-string
11852/// axis — extends the discipline from the cluster-side Flux v2
11853/// reconcile contract (the source/helm/kustomize controllers) onto
11854/// the cluster-side K8s Gateway API ingress contract (the
11855/// Gateway-API-conformant gateway implementation: Cilium, Istio,
11856/// Envoy Gateway, NGINX, et al.). The two render-side consumers now
11857/// thread the same `&'static str` through their `kube_resource_skeleton`
11858/// calls so a future Gateway API CRD-group/version promotion lands in
11859/// one place; every future renderer that reaches for the canonical
11860/// Gateway API CRD apiVersion (the future M4
11861/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
11862/// Gateway + HTTPRoute, a future per-edge `TCPRoute` / `TLSRoute` /
11863/// `GRPCRoute` the caixa-mesh emits for non-HTTP `:entrada` edges,
11864/// a future `GatewayClass` the operator emits for per-cluster
11865/// gateway-class scoping) inherits the same value by construction
11866/// with no opportunity for per-renderer drift.
11867///
11868/// [cm]: ../../caixa_mesh/index.html
11869pub const GATEWAY_API_API_VERSION: &str = "gateway.networking.k8s.io/v1";
11870
11871/// Canonical Cilium CRD `apiVersion` every `caixa-mesh`-emitted
11872/// `CiliumNetworkPolicy` document declares. The Cilium control plane's
11873/// upstream-shipped CRD bundle registers `CiliumNetworkPolicy`,
11874/// `CiliumClusterwideNetworkPolicy`, `CiliumEndpoint`, `CiliumIdentity`,
11875/// `CiliumNode`, `CiliumLocalRedirectPolicy`, and the rest of the
11876/// per-conformance Cilium CRD set at this exact group/version
11877/// (`cilium.io/v2`); drift to a stale `v2alpha1` (the historical
11878/// pre-stable Cilium-CRD-group/version label upstream Cilium-CRD docs
11879/// reference for in-flight per-CRD-version migration) silently routes
11880/// the rendered `CiliumNetworkPolicy` outside the cluster's
11881/// Cilium-operator-side CRD-version registration and breaks at apply
11882/// time with a non-self-locating "no kind 'CiliumNetworkPolicy' is
11883/// registered for version 'cilium.io/v2alpha1'" error far from the
11884/// source caixa.lisp / the renderer's [`kube_resource_skeleton`] call
11885/// site.
11886///
11887/// The single source of truth the rendered Aplicacao Cilium-side
11888/// mesh bundle's CRD-group/version axis reaches for:
11889///
11890/// - `CiliumNetworkPolicy` `apiVersion` — the top-level CRD-group/
11891/// version every emitted CNP document declares
11892/// (caixa-mesh/src/lib.rs:326 — the `cilium_network_policies`
11893/// per-`(:de, :para)` policy skeleton call). Until this lift
11894/// landed both the production-code emit at the per-policy
11895/// skeleton call site and the matching in-file
11896/// `cilium_policy_carries_canonical_kube_skeleton` test fixture
11897/// pin (caixa-mesh/src/lib.rs:1560) carried inline `"cilium.io/v2"`
11898/// string literals — two occurrences of the same load-bearing
11899/// Cilium-CRD-group/version convention, drift-prone by
11900/// construction. The Cilium project bumps the per-conformance
11901/// Cilium-CRD set as a unit; a future Cilium-CRD-group/version
11902/// promotion (the upstream Cilium roadmap names per-CRD-group /
11903/// per-version migration once the `cilium.io/v3` branch lands) on
11904/// one axis without a coordinated edit on the other would have
11905/// silently emitted a `CiliumNetworkPolicy` document whose
11906/// top-level apiVersion drifts off the lifted-test-fixture pin —
11907/// apply-side: the policy lands in a stale CRD-version
11908/// registration the Cilium operator no longer watches, every
11909/// `(:de, :para)` intra-mesh L4 contract drops at the eBPF data
11910/// plane with no field naming the version-drift root cause.
11911///
11912/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
11913/// "every recurring shape becomes a generator before it becomes a
11914/// pattern; every pattern becomes a library before it becomes
11915/// duplicated code. The duplication budget is zero.") promotes the
11916/// constant to a typed substrate-side `&'static str` on the same
11917/// trajectory the [`GATEWAY_API_API_VERSION`] (3c6cfc3) /
11918/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
11919/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
11920/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) /
11921/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) lifts established on
11922/// the peer K8s Gateway API ingress / Flux v2 reconcile canonical-
11923/// load-bearing-string axes — extends the discipline from the
11924/// cluster-side K8s Gateway API ingress + Flux v2 reconcile contracts
11925/// onto the cluster-side Cilium identity-based mesh contract (the
11926/// eBPF-anchored Cilium control plane that materializes every
11927/// per-`(:de, :para)` L4 / L7 contrato as an identity-keyed eBPF
11928/// allow rule). The render-side consumer now threads the same
11929/// `&'static str` through its `kube_resource_skeleton` call so a
11930/// future Cilium-CRD-group/version promotion lands in one place;
11931/// every future renderer that reaches for the canonical
11932/// Cilium-CRD apiVersion (the future M4
11933/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
11934/// per-Aplicacao CiliumNetworkPolicy fan-out, a future
11935/// `CiliumClusterwideNetworkPolicy` the caixa-mesh emits for
11936/// cluster-scoped baseline-allow / baseline-deny rules, a future
11937/// `CiliumLocalRedirectPolicy` the operator emits for per-Servico
11938/// local-redirect coordination) inherits the same value by
11939/// construction with no opportunity for per-renderer drift.
11940///
11941/// [cm]: ../../caixa_mesh/index.html
11942pub const CILIUM_API_VERSION: &str = "cilium.io/v2";
11943
11944/// Canonical Cilium CRD `kind` discriminator the rendered
11945/// `CiliumNetworkPolicy` document declares at its top-level
11946/// [`KUBE_KEY_KIND`] axis. Pairs with the sibling [`CILIUM_API_VERSION`]
11947/// (279d611) — the K8s apiserver-side CRD resolution contract is the
11948/// `(apiVersion, kind)` tuple keyed against the registered
11949/// `CustomResourceDefinition`, so drift on the kind axis is exactly as
11950/// load-bearing as drift on the apiVersion axis it accompanies (the
11951/// apiserver's `RESTMapper` consults both together; a
11952/// `("cilium.io/v2", "CilumNetworkPolicy")` typo at the production-code
11953/// call site lands outside the registered Cilium-operator-side
11954/// `CiliumNetworkPolicy` CRD's `RESTKind` lookup, surfacing apply-side as
11955/// a non-self-locating "no kind 'CilumNetworkPolicy' is registered for
11956/// version 'cilium.io/v2'" error far from the source caixa.lisp / the
11957/// renderer's [`kube_resource_skeleton`] call site).
11958///
11959/// The single source of truth the rendered Aplicacao Cilium-side mesh
11960/// bundle's `CiliumNetworkPolicy`-naming axis reaches for:
11961///
11962/// - the rendered `CiliumNetworkPolicy` document's top-level
11963/// [`KUBE_KEY_KIND`] axis (caixa-mesh/src/lib.rs:382 — the
11964/// `cilium_network_policies` per-`(:de, :para)` policy
11965/// [`kube_resource_skeleton`] call).
11966///
11967/// The kind axis names the same Cilium-operator-side CRD discriminator
11968/// as the sibling [`CILIUM_API_VERSION`] apiVersion axis and must move
11969/// together on any future `cilium.io/v3` rebrand. Until this lift
11970/// landed the axis carried an inline `CiliumNetworkPolicy` literal at
11971/// the one production-code occurrence in caixa-mesh/src/lib.rs:382 (the
11972/// `cilium_network_policies` [`kube_resource_skeleton`] kind argument)
11973/// plus a matching set inside the in-file
11974/// `cilium_policy_carries_canonical_kube_skeleton` /
11975/// `render_all_includes_every_artifact_kind` /
11976/// `cilium_policy_metadata_block_iterates_alphabetically` test fixtures
11977/// — occurrences of the same load-bearing Cilium-CRD-`kind`-discriminator
11978/// convention, drift-prone by construction. A drift on the top-level
11979/// `CiliumNetworkPolicy` `kind` axis would have surfaced as a
11980/// non-self-locating "no kind 'CilumNetworkPolicy' is registered for
11981/// version 'cilium.io/v2'" error far from the source caixa.lisp at
11982/// apply parse time, with the rendered per-`(:de, :para)` CNP never
11983/// landing in the Cilium-operator-side CRD registration and every
11984/// intra-mesh L4/L7 contrato flow dropping at the eBPF data plane with
11985/// no field naming the kind-discriminator-drift root cause.
11986///
11987/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
11988/// "every recurring shape becomes a generator before it becomes a
11989/// pattern; every pattern becomes a library before it becomes
11990/// duplicated code. The duplication budget is zero.") promotes the
11991/// constant to a typed substrate-side `&'static str` on the same
11992/// trajectory the [`FLUX_KIND_KUSTOMIZATION`] (4114773) /
11993/// [`FLUX_KIND_HELM_RELEASE`] (e24ea3c) /
11994/// [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
11995/// [`CILIUM_API_VERSION`] (279d611) /
11996/// [`GATEWAY_API_API_VERSION`] (3c6cfc3) lifts established on the
11997/// sibling cluster-side-CRD-`kind`-discriminator + canonical-CRD-
11998/// group/version axes — extends the discipline from the apiVersion
11999/// half of the `(apiVersion, kind)` CRD-lookup tuple onto the kind
12000/// half on the same Cilium-CRD-axis, completing the per-Cilium-CRD
12001/// kind+apiVersion lift pair the M3 Aplicacao mesh renderer's eBPF
12002/// data-plane contract rests on. The render-side consumer now threads
12003/// the same `&'static str` through its [`kube_resource_skeleton`] call
12004/// so a future `cilium.io/v3` rebrand lands in one place; every future
12005/// renderer that reaches for the canonical Cilium `CiliumNetworkPolicy`
12006/// kind (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
12007/// materializer's per-Aplicacao CiliumNetworkPolicy fan-out, a future
12008/// per-cluster baseline-allow / baseline-deny renderer that emits the
12009/// peer `CiliumClusterwideNetworkPolicy`, a future per-Servico
12010/// local-redirect renderer that emits the peer
12011/// `CiliumLocalRedirectPolicy`) inherits the same value by construction
12012/// with no opportunity for per-renderer drift.
12013///
12014/// Same "the typed constant lives in one place" discipline the
12015/// [`FLUX_KIND_KUSTOMIZATION`] (4114773) /
12016/// [`FLUX_KIND_HELM_RELEASE`] (e24ea3c) /
12017/// [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
12018/// [`CILIUM_API_VERSION`] (279d611) /
12019/// [`GATEWAY_API_API_VERSION`] (3c6cfc3) /
12020/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) lifts apply on the peer
12021/// canonical-cluster-side-CRD-discriminator surface.
12022///
12023/// [cm]: ../../caixa_mesh/index.html
12024pub const CILIUM_KIND_NETWORK_POLICY: &str = "CiliumNetworkPolicy";
12025
12026/// Canonical Cilium `CiliumNetworkPolicy` L4/L7 per-ingress-rule port-set
12027/// container-axis key every `cilium_network_policies`-emitted CNP
12028/// document mounts its per-ingress-rule `[{ports: […], rules: {…}}]`
12029/// list under (`spec.ingress[].toPorts[]`). Pairs with the sibling
12030/// [`KUBE_KEY_RULES`] (a205eb3) — the Cilium L7-dispatch schema nests
12031/// `spec.ingress[].toPorts[].rules.http[]` under the shared
12032/// (`toPorts`, `rules`) container-key pair, so drift on the `toPorts`
12033/// axis is exactly as load-bearing as drift on the `rules` axis it
12034/// wraps (the Cilium-operator-side CRD schema validator drops any
12035/// `spec.ingress[]` entry whose port-set container carries an
12036/// unrecognized key — a `"toports"` / `"toPort"` / `"targetPorts"` typo
12037/// silently emits an ingress rule whose per-port set the Cilium
12038/// operator's per-CNP L4/L7 dispatch pass no-ops entirely: every
12039/// intra-mesh `:contratos` flow the CNP was authored to allow now
12040/// drops at the eBPF data plane's default-deny gate with no field
12041/// naming the port-set-container-drift root cause).
12042///
12043/// The single source of truth the rendered Aplicacao Cilium-side mesh
12044/// bundle's per-CNP port-set-container-naming axis reaches for:
12045///
12046/// - the rendered `CiliumNetworkPolicy` document's
12047/// `spec.ingress[].toPorts[]` axis (caixa-mesh/src/lib.rs:939 —
12048/// the `cilium_network_policies` per-`(:de, :para)` policy's
12049/// `ingress_rule.insert("toPorts", …)` call).
12050///
12051/// The port-set-container axis names the same Cilium-operator-side
12052/// per-ingress-rule dispatch container as the sibling [`KUBE_KEY_RULES`]
12053/// nested L7-dispatch container axis and must move together on any
12054/// future Cilium CRD schema rebrand (an upstream `cilium.io/v3` rename
12055/// of the port-set container from `toPorts` to `ports` / `portSet` /
12056/// `endpoints`, coordinated with the Cilium project's periodic CRD
12057/// schema-migration passes). Until this lift landed the axis carried
12058/// an inline `toPorts` literal at the one production-code occurrence
12059/// in caixa-mesh/src/lib.rs:939 (the `cilium_network_policies`
12060/// `ingress_rule.insert("toPorts", …)` call) plus a matching set
12061/// inside the in-file `cilium_http_contracts_emit_l7_rules` /
12062/// `cilium_pubsub_contracts_skip_l7_rules` /
12063/// `cilium_multiple_edges_same_pair_fold_into_one_policy` /
12064/// `cnp_authentication_carries_mtls_overlay_at_ingress_rule_level` /
12065/// `cnp_l4_fallback_port_routes_through_lifted_default_servico_port`
12066/// test-fixture navigations — six occurrences of the same load-bearing
12067/// Cilium-CRD-`toPorts`-container-key convention, drift-prone by
12068/// construction. A drift on any one production or test-fixture site
12069/// to `"toports"` / `"toPort"` / `"targetPorts"` would have surfaced
12070/// as a Cilium-operator-side schema validator drop at apply time (the
12071/// affected `spec.ingress[]` entry's port-set container the CRD
12072/// schema validator recognizes as unknown), with every intra-mesh
12073/// `:contratos` flow the CNP was authored to allow dropping at the
12074/// eBPF data plane's default-deny gate with no field naming the
12075/// container-drift root cause. A drift on the test-fixture side
12076/// silently masks the emission-side pin (`.get("toPorts")` returns
12077/// `None` under both the drifted-key emitter and the drifted-key
12078/// probe — the `cilium_pubsub_contracts_skip_l7_rules` absence pin's
12079/// downstream `to_ports.get("rules").is_none()` assertion succeeds
12080/// vacuously because `to_ports` is itself `None`).
12081///
12082/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
12083/// "every recurring shape becomes a generator before it becomes a
12084/// pattern; every pattern becomes a library before it becomes
12085/// duplicated code. The duplication budget is zero.") promotes the
12086/// constant to a typed substrate-side `&'static str` on the same
12087/// trajectory the [`KUBE_KEY_RULES`] (a205eb3) /
12088/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
12089/// [`CILIUM_API_VERSION`] (279d611) lifts established on the sibling
12090/// canonical-K8s-CR-rule-list-axis / canonical-Cilium-CRD-`kind` /
12091/// canonical-Cilium-CRD-`apiVersion` surfaces — extends the discipline
12092/// from the outer `(apiVersion, kind, spec)` shell of the Cilium CNP
12093/// down through the load-bearing `spec.ingress[].toPorts[].rules`
12094/// dispatch axis onto the port-set container half of the
12095/// `(toPorts, rules)` L4/L7-dispatch container-key pair, completing
12096/// the per-CNP L4/L7-dispatch-axis lift pair the M3 Aplicacao mesh
12097/// renderer's eBPF data-plane contract rests on. The render-side
12098/// consumer now threads the same `&'static str` through its
12099/// `ingress_rule.insert(…)` call so a future Cilium-CRD rebrand
12100/// on the port-set-container axis (or an upstream Cilium project
12101/// rename to a per-CRD sibling name — unlikely but the same
12102/// coordination point the prior lifts anchor for) lands in one place;
12103/// every future renderer that reaches for the canonical
12104/// per-CNP port-set-container-axis (the future M4
12105/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
12106/// CiliumNetworkPolicy fan-out, a future
12107/// `CiliumClusterwideNetworkPolicy` renderer that emits cluster-scoped
12108/// baseline-allow rules with the same `spec.ingress[].toPorts[]`
12109/// shape, a future `CiliumClusterwideEnvoyConfig` renderer whose
12110/// per-edge Envoy configuration nests under the same port-set
12111/// container-key convention) inherits the same value by construction
12112/// with no opportunity for per-renderer drift.
12113///
12114/// Same "the typed constant lives in one place" discipline the
12115/// [`KUBE_KEY_RULES`] (a205eb3) /
12116/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
12117/// [`CILIUM_API_VERSION`] (279d611) lifts apply on the peer
12118/// canonical-Cilium-CNP-dispatch-axis surface.
12119///
12120/// [cm]: ../../caixa_mesh/index.html
12121pub const CILIUM_KEY_TO_PORTS: &str = "toPorts";
12122
12123/// Canonical Cilium `CiliumNetworkPolicy` destination-identity selector-
12124/// axis key every `cilium_network_policies`-emitted CNP document mounts
12125/// its L3-target `LabelSelector` under (`spec.endpointSelector`). Pairs
12126/// with the sibling [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) — the Cilium CNP
12127/// schema pins the destination workload through the `endpointSelector`
12128/// axis and the admitted L4 port set through the `toPorts` axis, so
12129/// drift on the destination-identity axis is exactly as load-bearing as
12130/// drift on the port-set-container axis it accompanies (the Cilium-
12131/// operator-side CRD schema validator drops any `spec` block whose
12132/// destination-identity axis carries an unrecognized key — an
12133/// `"endpointselector"` / `"endpointSelectors"` / `"endpoints"` typo
12134/// silently emits a CNP whose L3-target selector the Cilium operator's
12135/// per-CNP identity-resolution pass no-ops entirely: the policy binds
12136/// against no destination pods and every intra-mesh `:contratos` flow
12137/// the CNP was authored to allow drops at the eBPF data plane's
12138/// default-deny gate with no field naming the destination-identity-
12139/// axis-drift root cause).
12140///
12141/// The single source of truth the rendered Aplicacao Cilium-side mesh
12142/// bundle's per-CNP destination-identity-axis-naming reaches for:
12143///
12144/// - the rendered `CiliumNetworkPolicy` document's
12145/// `spec.endpointSelector` axis (caixa-mesh/src/lib.rs:990 —
12146/// the `cilium_network_policies` per-`(:de, :para)` policy's
12147/// `policy_spec.insert("endpointSelector", …)` call).
12148///
12149/// The destination-identity axis names the same Cilium-operator-side
12150/// per-CNP L3-target selector as the sibling [`CILIUM_KEY_TO_PORTS`]
12151/// per-ingress-rule port-set-container axis and must move together on
12152/// any future Cilium CRD schema rebrand (an upstream `cilium.io/v3`
12153/// rename of the destination-identity axis from `endpointSelector` to
12154/// `endpoints` / `targetSelector` / `destinationSelector`, coordinated
12155/// with the Cilium project's periodic CRD schema-migration passes).
12156/// Until this lift landed the axis carried an inline `endpointSelector`
12157/// literal at the one production-code occurrence in
12158/// caixa-mesh/src/lib.rs:990 (the `cilium_network_policies`
12159/// `policy_spec.insert("endpointSelector", …)` call) plus a matching
12160/// set inside the in-file
12161/// `cilium_policy_endpoint_selector_targets_destination_program` /
12162/// `cnp_endpoint_selector_carries_program_only_single_axis_shape` test-
12163/// fixture navigations — three occurrences of the same load-bearing
12164/// Cilium-CRD-`endpointSelector`-axis-key convention, drift-prone by
12165/// construction. A drift on any one production or test-fixture site
12166/// to `"endpointselector"` / `"endpointSelectors"` / `"endpoints"` would
12167/// have surfaced as a Cilium-operator-side schema validator drop at
12168/// apply time (the affected `spec` block's destination-identity axis
12169/// the CRD schema validator recognizes as unknown), with every intra-
12170/// mesh `:contratos` flow the CNP was authored to allow dropping at the
12171/// eBPF data plane's default-deny gate with no field naming the
12172/// destination-identity-drift root cause. A drift on the test-fixture
12173/// side silently masks the emission-side pin (`.get("endpointSelector")`
12174/// returns `None` under both the drifted-key emitter and the drifted-key
12175/// probe — the downstream `.and_then(|s| s.get("matchLabels"))` chain
12176/// short-circuits vacuously because the outer selector-lookup is itself
12177/// `None`).
12178///
12179/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
12180/// "every recurring shape becomes a generator before it becomes a
12181/// pattern; every pattern becomes a library before it becomes
12182/// duplicated code. The duplication budget is zero.") promotes the
12183/// constant to a typed substrate-side `&'static str` on the same
12184/// trajectory the [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
12185/// [`KUBE_KEY_RULES`] (a205eb3) /
12186/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
12187/// [`CILIUM_API_VERSION`] (279d611) lifts established on the sibling
12188/// canonical-Cilium-CNP-dispatch-axis / canonical-Cilium-CRD-`kind` /
12189/// canonical-Cilium-CRD-`apiVersion` surfaces — extends the discipline
12190/// from the outer `(apiVersion, kind, spec)` shell of the Cilium CNP
12191/// and the per-ingress-rule `toPorts.rules` L4/L7-dispatch axis onto
12192/// the destination-identity half of the `(endpointSelector, ingress)`
12193/// per-CNP-body key pair, completing the per-CNP L3/L4/L7-triad lift
12194/// set the M3 Aplicacao mesh renderer's eBPF data-plane contract rests
12195/// on. The render-side consumer now threads the same `&'static str`
12196/// through its `policy_spec.insert(…)` call so a future Cilium-CRD
12197/// rebrand on the destination-identity axis (or an upstream Cilium
12198/// project rename to a per-CRD sibling name — unlikely but the same
12199/// coordination point the prior lifts anchor for) lands in one place;
12200/// every future renderer that reaches for the canonical per-CNP
12201/// destination-identity-axis (the future M4
12202/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
12203/// `CiliumNetworkPolicy` fan-out, a future
12204/// `CiliumClusterwideNetworkPolicy` renderer that emits cluster-scoped
12205/// baseline-allow rules with the same `spec.endpointSelector` shape, a
12206/// future `CiliumLocalRedirectPolicy` renderer whose per-Servico local-
12207/// redirect selector nests under the same destination-identity axis
12208/// convention) inherits the same value by construction with no
12209/// opportunity for per-renderer drift.
12210///
12211/// Same "the typed constant lives in one place" discipline the
12212/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
12213/// [`KUBE_KEY_RULES`] (a205eb3) /
12214/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
12215/// [`CILIUM_API_VERSION`] (279d611) lifts apply on the peer
12216/// canonical-Cilium-CNP-body-axis surface.
12217///
12218/// [cm]: ../../caixa_mesh/index.html
12219pub const CILIUM_KEY_ENDPOINT_SELECTOR: &str = "endpointSelector";
12220
12221/// Canonical Cilium `CiliumNetworkPolicy` traffic-direction container-
12222/// axis key every `cilium_network_policies`-emitted CNP document mounts
12223/// its inbound-per-`(:de, :para)` ingress-rule list under (`spec.ingress[]`).
12224/// Pairs with the sibling [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) +
12225/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) — the per-CNP `spec` schema mounts
12226/// the destination workload identity under `endpointSelector`, the
12227/// permitted inbound-per-`(:de, :para)` ingress-rule list under
12228/// `ingress[]`, and each per-ingress-rule port-set under
12229/// `ingress[].toPorts[]`, so drift on the traffic-direction axis is
12230/// exactly as load-bearing as drift on the destination-identity /
12231/// port-set-container axes it accompanies (the Cilium-operator-side CRD
12232/// schema validator drops any `spec` block whose traffic-direction axis
12233/// carries an unrecognized key — an `"Ingress"` / `"ingressRules"` /
12234/// `"inbound"` typo silently emits a CNP whose ingress-rule list the
12235/// Cilium operator's per-CNP L4/L7-dispatch pass no-ops entirely: the
12236/// policy binds against the destination workload but admits no ingress
12237/// traffic, and every intra-mesh `:contratos` flow the CNP was authored
12238/// to allow drops at the eBPF data plane's default-deny gate with no
12239/// field naming the traffic-direction-axis-drift root cause).
12240///
12241/// The single source of truth the rendered Aplicacao Cilium-side mesh
12242/// bundle's per-CNP traffic-direction-axis-naming reaches for:
12243///
12244/// - the rendered `CiliumNetworkPolicy` document's `spec.ingress[]`
12245/// axis (caixa-mesh/src/lib.rs:1036 — the `cilium_network_policies`
12246/// per-`(:de, :para)` policy's `policy_spec.insert("ingress", …)`
12247/// call).
12248///
12249/// The traffic-direction axis names the same Cilium-operator-side per-
12250/// CNP inbound-traffic dispatch container as the sibling
12251/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] destination-identity axis and
12252/// [`CILIUM_KEY_TO_PORTS`] per-ingress-rule port-set container-axis and
12253/// must move together on any future Cilium CRD schema rebrand (an
12254/// upstream `cilium.io/v3` rename of the traffic-direction axis from
12255/// `ingress` to `inbound` / `ingressRules` / `incoming`, coordinated
12256/// with the Cilium project's periodic CRD schema-migration passes, or
12257/// the introduction of a sibling `egress` axis for outbound-traffic
12258/// dispatch under the same per-CNP-body schema). Until this lift landed
12259/// the axis carried an inline `ingress` literal at the one production-
12260/// code occurrence in caixa-mesh/src/lib.rs:1036 (the
12261/// `cilium_network_policies` `policy_spec.insert("ingress", …)` call)
12262/// plus a matching set inside the in-file
12263/// `cilium_http_contracts_emit_l7_rules` /
12264/// `cilium_policies_are_identity_based` /
12265/// `cnp_from_endpoints_carries_program_plus_aplicacao_labels_two_axis_shape`
12266/// / `cilium_multiple_edges_same_pair_fold_into_one_policy` /
12267/// `cilium_pubsub_contracts_skip_l7_rules` /
12268/// `render_multi_doc_contains_expected_kinds` /
12269/// `cnp_authentication_carries_mtls_overlay_at_ingress_rule_level` /
12270/// `cnp_l4_fallback_port_routes_through_lifted_default_servico_port`
12271/// test-fixture navigations — nine occurrences of the same load-bearing
12272/// Cilium-CRD-`ingress`-axis-key convention, drift-prone by
12273/// construction. A drift on any one production or test-fixture site
12274/// to `"Ingress"` / `"ingressRules"` / `"inbound"` would have surfaced
12275/// as a Cilium-operator-side schema validator drop at apply time (the
12276/// affected `spec` block's traffic-direction axis the CRD schema
12277/// validator recognizes as unknown), with every intra-mesh `:contratos`
12278/// flow the CNP was authored to allow dropping at the eBPF data plane's
12279/// default-deny gate with no field naming the traffic-direction-drift
12280/// root cause. A drift on the test-fixture side silently masks the
12281/// emission-side pin (`.get("ingress")` returns `None` under both the
12282/// drifted-key emitter and the drifted-key probe — the downstream
12283/// `.and_then(|i| i.as_sequence())` chain short-circuits vacuously
12284/// because the outer traffic-direction-lookup is itself `None`, and
12285/// every per-CNP downstream navigation — `fromEndpoints`, `toPorts`,
12286/// `authentication` — rides through the same short-circuited outer
12287/// axis-lookup with no field naming the drift root cause).
12288///
12289/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
12290/// "every recurring shape becomes a generator before it becomes a
12291/// pattern; every pattern becomes a library before it becomes
12292/// duplicated code. The duplication budget is zero.") promotes the
12293/// constant to a typed substrate-side `&'static str` on the same
12294/// trajectory the [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
12295/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
12296/// [`KUBE_KEY_RULES`] (a205eb3) /
12297/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
12298/// [`CILIUM_API_VERSION`] (279d611) lifts established on the sibling
12299/// canonical-Cilium-CNP-destination-identity /
12300/// canonical-Cilium-CNP-port-set-container /
12301/// canonical-K8s-CR-rule-list / canonical-Cilium-CRD-`kind` /
12302/// canonical-Cilium-CRD-`apiVersion` surfaces — completes the per-CNP
12303/// L3/L4/L7-triad lift set `(endpointSelector, ingress → toPorts →
12304/// rules)` the M3 Aplicacao mesh renderer's eBPF data-plane contract
12305/// rests on by lifting the traffic-direction axis that structurally
12306/// separates the destination-identity axis from the port-set-container
12307/// axis nested beneath it. The render-side consumer now threads the
12308/// same `&'static str` through its `policy_spec.insert(…)` call so a
12309/// future Cilium-CRD rebrand on the traffic-direction axis (or an
12310/// upstream Cilium project rename to a per-CRD sibling name — unlikely
12311/// on the CRD's stable `cilium.io/v2` slot, but the coordination point
12312/// the prior lifts anchor for) lands in one place; every future
12313/// renderer that reaches for the canonical per-CNP traffic-direction-
12314/// axis (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
12315/// materializer's per-Aplicacao `CiliumNetworkPolicy` fan-out, a future
12316/// `CiliumClusterwideNetworkPolicy` renderer that emits cluster-scoped
12317/// baseline-allow rules with the same `spec.ingress[]` shape, a future
12318/// `CiliumLocalRedirectPolicy` renderer whose per-Servico local-
12319/// redirect ingress-rule list nests under the same traffic-direction
12320/// axis convention) inherits the same value by construction with no
12321/// opportunity for per-renderer drift.
12322///
12323/// Same "the typed constant lives in one place" discipline the
12324/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
12325/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
12326/// [`KUBE_KEY_RULES`] (a205eb3) /
12327/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
12328/// [`CILIUM_API_VERSION`] (279d611) lifts apply on the peer
12329/// canonical-Cilium-CNP-body-axis surface.
12330///
12331/// [cm]: ../../caixa_mesh/index.html
12332pub const CILIUM_KEY_INGRESS: &str = "ingress";
12333
12334/// Canonical Cilium `CiliumNetworkPolicy` per-ingress-rule identity-
12335/// source selector-list axis key every `cilium_network_policies`-emitted
12336/// CNP document mounts its permitted-source `LabelSelector` list under
12337/// (`spec.ingress[].fromEndpoints[]`). Pairs with the sibling
12338/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) — the Cilium CNP schema
12339/// pins the destination workload identity through the per-CNP-body
12340/// `endpointSelector` axis and the admitted source workload identities
12341/// through the per-ingress-rule `fromEndpoints[]` axis, so drift on the
12342/// identity-source axis is exactly as load-bearing as drift on the
12343/// destination-identity axis it accompanies (the Cilium-operator-side
12344/// CRD schema validator drops any per-ingress-rule block whose
12345/// identity-source axis carries an unrecognized key — a
12346/// `"fromendpoints"` / `"fromEndPoint"` / `"sourceEndpoints"` typo
12347/// silently emits a CNP whose per-`(:de, :para)` ingress-rule identity-
12348/// source list the Cilium operator's per-CNP identity-resolution pass
12349/// no-ops entirely: the ingress rule admits no source pods and every
12350/// intra-mesh `:contratos` flow the CNP was authored to allow drops at
12351/// the eBPF data plane's default-deny gate with no field naming the
12352/// identity-source-axis-drift root cause).
12353///
12354/// The single source of truth the rendered Aplicacao Cilium-side mesh
12355/// bundle's per-ingress-rule identity-source-axis-naming reaches for:
12356///
12357/// - the rendered `CiliumNetworkPolicy` document's per-ingress-rule
12358/// `fromEndpoints[]` axis (caixa-mesh/src/lib.rs:991 — the
12359/// `cilium_network_policies` per-`(:de, :para)` policy's
12360/// `ingress_rule.insert("fromEndpoints", …)` call).
12361///
12362/// The identity-source axis names the same Cilium-operator-side per-
12363/// ingress-rule source-workload selector list as the sibling
12364/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] destination-identity axis and must
12365/// move together on any future Cilium CRD schema rebrand (an upstream
12366/// `cilium.io/v3` rename of the identity-source axis from
12367/// `fromEndpoints` to `sourceEndpoints` / `fromWorkloads` /
12368/// `sourceSelectors`, coordinated with the Cilium project's periodic
12369/// CRD schema-migration passes). Until this lift landed the axis
12370/// carried an inline `fromEndpoints` literal at the one production-code
12371/// occurrence in caixa-mesh/src/lib.rs:991 (the `cilium_network_policies`
12372/// `ingress_rule.insert("fromEndpoints", …)` call) plus a matching set
12373/// inside the in-file
12374/// `cnp_from_endpoints_carries_program_plus_aplicacao_labels_two_axis_shape`
12375/// / `cilium_policies_are_identity_based`
12376/// / `cnp_authentication_carries_mtls_overlay_at_ingress_rule_level`
12377/// test-fixture navigations — five occurrences of the same load-bearing
12378/// Cilium-CRD-`fromEndpoints`-axis-key convention, drift-prone by
12379/// construction. A drift on any one production or test-fixture site
12380/// to `"fromendpoints"` / `"fromEndPoint"` / `"sourceEndpoints"` would
12381/// have surfaced as a Cilium-operator-side schema validator drop at
12382/// apply time (the affected per-ingress-rule block's identity-source
12383/// axis the CRD schema validator recognizes as unknown), with every
12384/// intra-mesh `:contratos` flow the CNP was authored to allow dropping
12385/// at the eBPF data plane's default-deny gate with no field naming the
12386/// identity-source-drift root cause. A drift on the test-fixture side
12387/// silently masks the emission-side pin
12388/// (`.get("fromEndpoints")` returns `None` under both the drifted-key
12389/// emitter and the drifted-key probe — the downstream `.and_then(|e|
12390/// e.as_sequence())` / `.and_then(|e| e.get(KUBE_KEY_MATCH_LABELS))`
12391/// chain short-circuits vacuously because the outer identity-source-
12392/// lookup is itself `None`).
12393///
12394/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
12395/// "every recurring shape becomes a generator before it becomes a
12396/// pattern; every pattern becomes a library before it becomes
12397/// duplicated code. The duplication budget is zero.") promotes the
12398/// constant to a typed substrate-side `&'static str` on the same
12399/// trajectory the [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
12400/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
12401/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
12402/// [`KUBE_KEY_RULES`] (a205eb3) /
12403/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
12404/// [`CILIUM_API_VERSION`] (279d611) lifts established on the sibling
12405/// canonical-Cilium-CNP-destination-identity /
12406/// canonical-Cilium-CNP-traffic-direction-container /
12407/// canonical-Cilium-CNP-port-set-container /
12408/// canonical-K8s-CR-rule-list / canonical-Cilium-CRD-`kind` /
12409/// canonical-Cilium-CRD-`apiVersion` surfaces — completes the per-CNP
12410/// identity-pair lift set `(endpointSelector, fromEndpoints)` the M3
12411/// Aplicacao mesh renderer's eBPF data-plane contract rests on by
12412/// lifting the identity-source axis structurally paired with the
12413/// destination-identity axis under the Cilium-operator-side per-CNP
12414/// SPIFFE-identity-bound access-control contract. The render-side
12415/// consumer now threads the same `&'static str` through its
12416/// `ingress_rule.insert(…)` call so a future Cilium-CRD rebrand on the
12417/// identity-source axis (or an upstream Cilium project rename to a
12418/// per-CRD sibling name — unlikely on the CRD's stable `cilium.io/v2`
12419/// slot, but the coordination point the prior lifts anchor for) lands
12420/// in one place; every future renderer that reaches for the canonical
12421/// per-ingress-rule identity-source-axis (the future M4
12422/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
12423/// `CiliumNetworkPolicy` fan-out, a future
12424/// `CiliumClusterwideNetworkPolicy` renderer that emits cluster-scoped
12425/// baseline-allow rules with the same `spec.ingress[].fromEndpoints[]`
12426/// shape, a future `CiliumLocalRedirectPolicy` renderer whose per-
12427/// Servico local-redirect source-workload selector list nests under
12428/// the same identity-source axis convention) inherits the same value
12429/// by construction with no opportunity for per-renderer drift.
12430///
12431/// Same "the typed constant lives in one place" discipline the
12432/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
12433/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
12434/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
12435/// [`KUBE_KEY_RULES`] (a205eb3) /
12436/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
12437/// [`CILIUM_API_VERSION`] (279d611) lifts apply on the peer
12438/// canonical-Cilium-CNP-body-axis surface.
12439///
12440/// [cm]: ../../caixa_mesh/index.html
12441pub const CILIUM_KEY_FROM_ENDPOINTS: &str = "fromEndpoints";
12442
12443/// Canonical Cilium `CiliumNetworkPolicy` per-`toPorts[]`-entry L4
12444/// port-tuple-list-container axis key every `cilium_network_policies`-
12445/// emitted CNP document mounts its per-port-set `[{port, protocol}]` list
12446/// under (`spec.ingress[].toPorts[].ports[]`). Nests inside the sibling
12447/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) — the Cilium CNP schema pins the
12448/// per-ingress-rule port-set-container axis through the `toPorts[]` list
12449/// and the per-port-set L4 port-tuple list through the `ports[]` axis
12450/// beneath each entry, so drift on the L4 port-tuple-list-container axis
12451/// is exactly as load-bearing as drift on the port-set container axis it
12452/// nests inside (the Cilium-operator-side CRD schema validator drops any
12453/// per-`toPorts[]` entry whose port-tuple-list-container axis carries an
12454/// unrecognized key — a `"port"` / `"portList"` / `"L4Ports"` typo
12455/// silently emits a CNP whose per-`(:de, :para)` per-port-set L4
12456/// port-tuple list the Cilium operator's per-CNP L4-allow eBPF-program
12457/// generation pass no-ops entirely: the port-set admits no `(port,
12458/// protocol)` tuple and every intra-mesh `:contratos` flow the CNP was
12459/// authored to allow drops at the eBPF data plane's default-deny gate
12460/// with no field naming the L4-port-tuple-list-container-axis-drift root
12461/// cause).
12462///
12463/// The single source of truth the rendered Aplicacao Cilium-side mesh
12464/// bundle's per-`toPorts[]`-entry L4-port-tuple-list-container-axis-
12465/// naming reaches for:
12466///
12467/// - the rendered `CiliumNetworkPolicy` document's per-`toPorts[]`-
12468/// entry `ports[]` axis (caixa-mesh/src/lib.rs:1081 — the
12469/// `cilium_network_policies` per-`(:de, :para)` policy's
12470/// `to_port.insert("ports", …)` call).
12471///
12472/// The L4 port-tuple-list-container axis names the same Cilium-operator-
12473/// side per-port-set L4-allow eBPF-program-generation source-list as the
12474/// sibling [`CILIUM_KEY_TO_PORTS`] port-set container axis it nests
12475/// inside and must move together on any future Cilium CRD schema rebrand
12476/// (an upstream `cilium.io/v3` rename of the L4 port-tuple-list axis
12477/// from `ports` to `portList` / `l4Ports` / `tuples`, coordinated with
12478/// the Cilium project's periodic CRD schema-migration passes). Until this
12479/// lift landed the axis carried an inline `ports` literal at the one
12480/// production-code occurrence in caixa-mesh/src/lib.rs:1081 (the
12481/// `cilium_network_policies` `to_port.insert("ports", …)` call) plus a
12482/// matching set inside the in-file
12483/// `cilium_pubsub_contracts_skip_l7_rules`
12484/// / `cnp_l4_fallback_port_reflects_default_servico_port`
12485/// test-fixture navigations — three occurrences of the same load-bearing
12486/// Cilium-CRD-`ports`-axis-key convention, drift-prone by construction. A
12487/// drift on any one production or test-fixture site to `"port"` /
12488/// `"portList"` / `"L4Ports"` would have surfaced as a Cilium-operator-
12489/// side schema validator drop at apply time (the affected per-`toPorts[]`
12490/// entry's port-tuple-list-container axis the CRD schema validator
12491/// recognizes as unknown), with every intra-mesh `:contratos` flow the
12492/// CNP was authored to allow dropping at the eBPF data plane's default-
12493/// deny gate with no field naming the L4-port-tuple-list-container-drift
12494/// root cause. A drift on the test-fixture side silently masks the
12495/// emission-side pin (`.get("ports")` returns `None` under both the
12496/// drifted-key emitter and the drifted-key probe — the downstream
12497/// `.and_then(|p| p.as_sequence())` / `.and_then(|s| s.first())` /
12498/// `.and_then(|p| p.get("port"))` chain short-circuits vacuously because
12499/// the outer L4-port-tuple-list-container lookup is itself `None`).
12500///
12501/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
12502/// "every recurring shape becomes a generator before it becomes a
12503/// pattern; every pattern becomes a library before it becomes
12504/// duplicated code. The duplication budget is zero.") promotes the
12505/// constant to a typed substrate-side `&'static str` on the same
12506/// trajectory the [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
12507/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
12508/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
12509/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
12510/// [`KUBE_KEY_RULES`] (a205eb3) /
12511/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
12512/// [`CILIUM_API_VERSION`] (279d611) lifts established on the sibling
12513/// canonical-Cilium-CNP-identity-source /
12514/// canonical-Cilium-CNP-destination-identity /
12515/// canonical-Cilium-CNP-traffic-direction-container /
12516/// canonical-Cilium-CNP-port-set-container /
12517/// canonical-K8s-CR-rule-list / canonical-Cilium-CRD-`kind` /
12518/// canonical-Cilium-CRD-`apiVersion` surfaces — nests the per-port-set
12519/// L4 port-tuple-list-container axis structurally beneath the sibling
12520/// [`CILIUM_KEY_TO_PORTS`] port-set-container axis, extending the per-CNP
12521/// L3/L4/L7-triad `(endpointSelector, ingress → toPorts → ports / rules)`
12522/// lift set with the L4-half's port-tuple-list-container axis the M3
12523/// Aplicacao mesh renderer's eBPF data-plane L4-allow contract rests on.
12524/// The render-side consumer now threads the same `&'static str` through
12525/// its `to_port.insert(…)` call so a future Cilium-CRD rebrand on the
12526/// L4 port-tuple-list-container axis (or an upstream Cilium project
12527/// rename to a per-CRD sibling name — unlikely on the CRD's stable
12528/// `cilium.io/v2` slot, but the coordination point the prior lifts
12529/// anchor for) lands in one place; every future renderer that reaches
12530/// for the canonical per-`toPorts[]`-entry L4-port-tuple-list-container
12531/// axis (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
12532/// materializer's per-Aplicacao `CiliumNetworkPolicy` fan-out, a future
12533/// `CiliumClusterwideNetworkPolicy` renderer that emits cluster-scoped
12534/// baseline-allow rules with the same
12535/// `spec.ingress[].toPorts[].ports[]` shape, a future
12536/// `CiliumLocalRedirectPolicy` renderer whose per-Servico local-redirect
12537/// L4 port-tuple list nests under the same L4-port-tuple-list-container
12538/// axis convention) inherits the same value by construction with no
12539/// opportunity for per-renderer drift.
12540///
12541/// Same "the typed constant lives in one place" discipline the
12542/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
12543/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
12544/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
12545/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
12546/// [`KUBE_KEY_RULES`] (a205eb3) /
12547/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
12548/// [`CILIUM_API_VERSION`] (279d611) lifts apply on the peer
12549/// canonical-Cilium-CNP-body-axis surface.
12550///
12551/// [cm]: ../../caixa_mesh/index.html
12552pub const CILIUM_KEY_PORTS: &str = "ports";
12553
12554/// Canonical Cilium `CiliumNetworkPolicy` per-ingress-rule mutual-auth
12555/// policy body-axis key every `cilium_network_policies`-emitted CNP
12556/// document mounts its per-rule mTLS enforcement block under
12557/// (`spec.ingress[].authentication`). Sibling to
12558/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) +
12559/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) at the per-ingress-rule body
12560/// level — the Cilium CNP schema places the per-rule mutual-auth mode
12561/// (`{mode: required | disabled}`) at the ingress-rule axis alongside
12562/// the identity-source (`fromEndpoints`) and port-set (`toPorts`)
12563/// axes, so drift on the authentication axis is exactly as
12564/// load-bearing as drift on the sibling per-ingress-rule-body axes it
12565/// pairs with (the Cilium-operator-side CRD schema validator drops
12566/// any per-`ingress[]` entry whose mutual-auth axis carries an
12567/// unrecognized key — a `"auth"` / `"mutualAuth"` / `"mtls"` typo
12568/// silently emits a CNP whose per-`(:de, :para)` per-rule mTLS block
12569/// the Cilium operator's per-CNP mutual-auth SPIFFE-handshake
12570/// pipeline no-ops entirely: the ingress rule falls back to the
12571/// cluster-default authentication mode (typically `"disabled"` — no
12572/// mutual-auth enforcement), and every intra-mesh `:contratos` flow
12573/// the CNP was authored to protect with per-edge mTLS silently
12574/// bypasses the SPIFFE-identity-bound mutual-auth handshake with no
12575/// field naming the mutual-auth-axis-drift root cause).
12576///
12577/// The single source of truth the rendered Aplicacao Cilium-side
12578/// mesh bundle's per-ingress-rule mutual-auth-axis naming reaches for:
12579///
12580/// - the rendered `CiliumNetworkPolicy` document's per-`ingress[]`
12581/// entry `authentication` axis (caixa-mesh/src/lib.rs — the
12582/// `cilium_network_policies` per-`(:de, :para)` policy's
12583/// `ingress_rule.insert("authentication", …)` call in the
12584/// `:politicas :mtls-required` overlay emit gate).
12585///
12586/// The mutual-auth axis names the same Cilium-operator-side per-rule
12587/// SPIFFE-identity-handshake enforcement policy as the sibling per-
12588/// ingress-rule identity-source (`fromEndpoints`) and port-set
12589/// (`toPorts`) axes it pairs with, and must move together on any
12590/// future Cilium CRD schema rebrand (an upstream `cilium.io/v3`
12591/// rename of the mutual-auth axis from `authentication` to
12592/// `mutualAuth` / `mtls` / `authPolicy`, coordinated with the Cilium
12593/// project's periodic CRD schema-migration passes). Until this lift
12594/// landed the axis carried an inline `authentication` literal at the
12595/// one production-code emitter site (the `cilium_network_policies`
12596/// per-rule `ingress_rule.insert("authentication", …)` call in the
12597/// `:mtls-required` overlay emit gate) plus a matching set inside
12598/// the in-file `cnp_authentication_renders_every_policy_independently`
12599/// / `cnp_authentication_position_is_rule_level_not_nested` /
12600/// `cnp_authentication_pubsub_contracts_carry_overlay_too` /
12601/// `cnp_authentication_mode_is_a_yaml_string_scalar` /
12602/// `cnp_omits_authentication_when_mtls_required_unset` /
12603/// `cnp_explicit_mtls_required_false_emits_disabled_mode` /
12604/// `cnp_authentication_overlay_when_mtls_required_set` (name approximate)
12605/// test-fixture navigations — ten occurrences of the same
12606/// load-bearing Cilium-CRD-mutual-auth-axis-key convention, drift-
12607/// prone by construction. A drift on any one production or test-
12608/// fixture site to `"auth"` / `"mutualAuth"` / `"mtls"` would surface
12609/// as a Cilium-operator-side schema-validator drop at apply time
12610/// (the affected per-`ingress[]` entry's mutual-auth-axis key the
12611/// CRD schema validator recognizes as unknown), with every intra-
12612/// mesh `:contratos` flow the CNP was authored to protect with per-
12613/// edge SPIFFE-identity-bound mutual-auth silently bypassing the
12614/// mTLS handshake at the Cilium data-plane's default-authentication
12615/// mode with no field naming the mutual-auth-axis-drift root cause.
12616/// A drift on the test-fixture side silently masks the emission-
12617/// side pin (`.get("authentication")` returns `None` under both the
12618/// drifted-key emitter and the drifted-key probe — every downstream
12619/// `.and_then(|a| a.get("mode"))` chain short-circuits vacuously
12620/// because the outer mutual-auth-body-lookup is itself `None`).
12621///
12622/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
12623/// "every recurring shape becomes a generator before it becomes a
12624/// pattern; every pattern becomes a library before it becomes
12625/// duplicated code. The duplication budget is zero.") promotes the
12626/// constant to a typed substrate-side `&'static str` on the same
12627/// trajectory the [`CILIUM_KEY_PORTS`] (1087693) /
12628/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
12629/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
12630/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
12631/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
12632/// [`KUBE_KEY_RULES`] (a205eb3) /
12633/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
12634/// [`CILIUM_API_VERSION`] (279d611) lifts established on the
12635/// sibling canonical-Cilium-CNP-body-axis surfaces — nests the
12636/// per-ingress-rule mutual-auth axis structurally beside the sibling
12637/// [`CILIUM_KEY_FROM_ENDPOINTS`] identity-source and
12638/// [`CILIUM_KEY_TO_PORTS`] port-set-container axes at the per-rule
12639/// body triple `(fromEndpoints, toPorts, authentication)` the M3
12640/// Aplicacao mesh renderer's SPIFFE-identity-bound per-edge mTLS
12641/// contract rests on.
12642///
12643/// [cm]: ../../caixa_mesh/index.html
12644pub const CILIUM_KEY_AUTHENTICATION: &str = "authentication";
12645
12646/// Canonical Cilium `CiliumNetworkPolicy` per-`ingress[].authentication`
12647/// block mTLS-mode-discriminator leaf-scalar-axis key every
12648/// `cilium_network_policies`-emitted CNP document mounts its per-rule
12649/// mutual-auth mode leaf under (`spec.ingress[].authentication.mode`).
12650/// Nests exactly one level beneath the sibling
12651/// [`CILIUM_KEY_AUTHENTICATION`] (db31108) per-ingress-rule mutual-auth
12652/// body-axis it sits inside: the Cilium CNP schema places the mTLS
12653/// enforcement mode discriminator (`"required"` / `"disabled"`) as the
12654/// single leaf-scalar axis of the per-rule authentication block, so
12655/// drift on the mode-discriminator leaf axis is exactly as load-bearing
12656/// as drift on the sibling per-ingress-rule mutual-auth body-axis key
12657/// (`authentication`) it nests inside (the Cilium-operator-side CNP
12658/// schema validator drops any per-`ingress[]` entry whose per-rule
12659/// mutual-auth block carries an unrecognized leaf axis — a `"policy"` /
12660/// `"authMode"` / `"handshakeMode"` typo at either the emit-side single-
12661/// field-overlay call site or a downstream renderer's per-rule authn
12662/// leaf upsert silently emits a per-`ingress[]` mutual-auth block whose
12663/// mode-discriminator leaf the Cilium CRD schema validator rejects as
12664/// unknown; the ingress rule falls back to the cluster-default
12665/// authentication mode (typically `"disabled"` — no mutual-auth
12666/// enforcement) silently bypassing the SPIFFE-identity-bound mTLS
12667/// handshake every intra-mesh `:contratos` flow the CNP was authored to
12668/// protect with per-edge mTLS, and the emit-side/probe-side split
12669/// silently masks the per-rule mutual-auth pin (`.get("mode")` returns
12670/// `None` under both the drifted-key emitter and the drifted-key probe
12671/// — every downstream `.and_then(|v| v.as_str())` chain short-circuits
12672/// vacuously because the outer mode-leaf-lookup is itself `None`).
12673///
12674/// The single source of truth the rendered Aplicacao Cilium-side mesh
12675/// bundle's per-ingress-rule mutual-auth-mode-leaf-axis naming reaches
12676/// for:
12677///
12678/// - the rendered `CiliumNetworkPolicy` document's per-`ingress[]`
12679/// entry `authentication.mode` leaf axis (caixa-mesh/src/lib.rs —
12680/// the `cilium_network_policies` per-`(:de, :para)` policy's
12681/// `single_field_overlay(spec.politicas.mtls_required, "mode", …)`
12682/// call site in the `:politicas :mtls-required` overlay emit gate,
12683/// the exact field the `single_field_overlay` helper writes the
12684/// single leaf under when the tristate `:mtls-required` slot is
12685/// set).
12686///
12687/// The mode-discriminator leaf-axis names the same Cilium-operator-side
12688/// per-rule SPIFFE-identity-handshake enforcement policy as the sibling
12689/// per-ingress-rule mutual-auth-body-axis key (`authentication`) it nests
12690/// inside, and must move together on any future Cilium CRD schema
12691/// rebrand (an upstream `cilium.io/v3` rename of the mutual-auth mode-
12692/// discriminator leaf from `mode` to `policy` / `authMode` /
12693/// `handshakeMode`, coordinated with the Cilium project's periodic CRD
12694/// schema-migration passes). Until this lift landed the axis carried an
12695/// inline `mode` literal at the one production-code emitter site (the
12696/// `cilium_network_policies` per-rule `single_field_overlay(...,
12697/// "mode", ...)` call in the `:mtls-required` overlay emit gate) plus a
12698/// matching set inside the in-file `cnp_carries_politicas_mtls_required_
12699/// on_every_rule` / `cnp_explicit_mtls_required_false_emits_disabled_
12700/// mode` / `cnp_authentication_renders_every_policy_independently` /
12701/// `cnp_authentication_pubsub_contracts_carry_overlay_too` /
12702/// `cnp_authentication_mode_is_a_yaml_string_scalar` test-fixture
12703/// navigations — six occurrences of the same load-bearing Cilium-CRD-
12704/// mutual-auth-mode-discriminator-leaf-axis-key convention, drift-prone
12705/// by construction. A drift on any one production or test-fixture site
12706/// to `"policy"` / `"authMode"` / `"handshakeMode"` would surface as a
12707/// Cilium-operator-side schema-validator drop at apply time (the
12708/// affected per-`ingress[]` entry's per-rule mutual-auth-mode-
12709/// discriminator-leaf-axis key the CRD schema validator recognizes as
12710/// unknown), with every intra-mesh `:contratos` flow the CNP was
12711/// authored to protect with per-edge SPIFFE-identity-bound mutual-auth
12712/// silently bypassing the mTLS handshake at the Cilium data-plane's
12713/// default-authentication mode with no field naming the mutual-auth-
12714/// mode-discriminator-leaf-axis-drift root cause.
12715///
12716/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
12717/// "every recurring shape becomes a generator before it becomes a
12718/// pattern; every pattern becomes a library before it becomes
12719/// duplicated code. The duplication budget is zero.") promotes the
12720/// constant to a typed substrate-side `&'static str` on the same
12721/// trajectory the [`CILIUM_KEY_AUTHENTICATION`] (db31108) /
12722/// [`CILIUM_KEY_PORTS`] (1087693) /
12723/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
12724/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
12725/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
12726/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
12727/// [`KUBE_KEY_RULES`] (a205eb3) /
12728/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
12729/// [`CILIUM_API_VERSION`] (279d611) lifts established on the
12730/// sibling canonical-Cilium-CNP-body-axis surfaces — descends the
12731/// per-ingress-rule mutual-auth mode-discriminator leaf axis one level
12732/// beneath the parent [`CILIUM_KEY_AUTHENTICATION`] body-axis key it
12733/// pairs with, completing the per-rule mutual-auth
12734/// `(authentication → mode)` body/leaf axis pair the M3 Aplicacao mesh
12735/// renderer's SPIFFE-identity-bound per-edge mTLS enforcement contract
12736/// rests on.
12737///
12738/// [cm]: ../../caixa_mesh/index.html
12739pub const CILIUM_KEY_MODE: &str = "mode";
12740
12741/// Canonical Cilium `CiliumNetworkPolicy` per-`ingress[].toPorts[].rules`
12742/// L7-HTTP-rule-list-discriminator container-axis key every
12743/// `cilium_network_policies`-emitted CNP document mounts its per-`toPorts[]`
12744/// entry L7 HTTP-rule list under (`spec.ingress[].toPorts[].rules.http`).
12745/// Nests exactly one level beneath the sibling [`KUBE_KEY_RULES`] (a205eb3)
12746/// per-`toPorts[]` rule-list-container axis it sits inside: the Cilium CNP
12747/// schema places the L7-protocol-selection discriminator (`http` / future
12748/// `kafka` / future `dns`) as the single per-protocol keyed axis of the
12749/// per-`toPorts[]` rules block, so drift on the L7-HTTP-rule-list-
12750/// discriminator axis is exactly as load-bearing as drift on the sibling
12751/// [`KUBE_KEY_RULES`] per-`toPorts[]` rule-list-container axis-key it nests
12752/// inside (the Cilium-operator-side CNP schema validator drops any per-
12753/// `toPorts[]` entry whose per-protocol L7-rule-list-discriminator key it
12754/// recognizes as unknown — a `"HTTP"` / `"Http"` / `"http/1.1"` /
12755/// `"httpRules"` typo at either the emit-side `rules.insert(…)` call site
12756/// or a downstream renderer's per-`toPorts[]` L7-rule-list upsert silently
12757/// emits a per-`toPorts[]` entry whose L7-HTTP-rule-list-discriminator key
12758/// the Cilium CRD schema validator rejects as unknown; the per-`toPorts[]`
12759/// entry falls back to L4-only enforcement — no L7 URL-path predicate is
12760/// applied — silently admitting every HTTP-method / URL-path combination
12761/// the ingress rule was authored to filter to the exact path prefix set
12762/// the typed `:contratos` graph names at the L7 introspection axis, and
12763/// the emit-side/probe-side split silently masks the per-`toPorts[]` L7-
12764/// rule-list pin (`.get("http")` returns `None` under both the drifted-
12765/// key emitter and the drifted-key probe — every downstream
12766/// `.and_then(|h| h.as_sequence())` chain short-circuits vacuously because
12767/// the outer L7-HTTP-rule-list-lookup is itself `None`).
12768///
12769/// The single source of truth the rendered Aplicacao Cilium-CNP-side
12770/// intra-mesh L7-tuple-gating bundle's per-`toPorts[]` L7-HTTP-rule-list-
12771/// discriminator-axis naming reaches for:
12772///
12773/// - the rendered `CiliumNetworkPolicy` document's per-`toPorts[]` entry
12774/// `rules.http` L7-HTTP-rule-list-discriminator axis (caixa-mesh/src/lib.rs —
12775/// the `cilium_network_policies` per-`(:de, :para)` policy's
12776/// `rules.insert("http", …)` call in the `WitTarget::Http` L7-
12777/// introspection emit branch, the exact per-protocol keyed axis of
12778/// the per-`toPorts[]` rules block the L7 URL-path predicate lands
12779/// under).
12780///
12781/// The L7-HTTP-rule-list-discriminator axis names the same Cilium-operator-
12782/// side per-`toPorts[]` L7 URL-path predicate selection as the sibling
12783/// [`KUBE_KEY_RULES`] per-`toPorts[]` rule-list-container axis-key it nests
12784/// inside, and must move together on any future Cilium CRD schema rebrand
12785/// (an upstream `cilium.io/v3` rename of the L7-HTTP-rule-list-
12786/// discriminator from `http` to `httpRules` / `l7Http` / `httpMatch`,
12787/// coordinated with the Cilium project's periodic CRD schema-migration
12788/// passes). Until this lift landed the axis carried an inline `http`
12789/// literal at the one production-code emitter site (the
12790/// `cilium_network_policies` per-`(:de, :para)` `rules.insert("http", …)`
12791/// call in the `WitTarget::Http` L7 introspection emit branch) plus a
12792/// matching set inside the in-file `cilium_l7_rules_fan_in_captures_every_
12793/// http_edge` / `cilium_http_contracts_carry_l7_path` test-fixture
12794/// navigations — three occurrences of the same load-bearing Cilium-CRD-
12795/// L7-HTTP-rule-list-discriminator convention, drift-prone by
12796/// construction. A drift on any one production or test-fixture site to
12797/// `"HTTP"` / `"Http"` / `"httpRules"` would surface as a Cilium-operator-
12798/// side schema-validator drop at apply time (the affected per-
12799/// `toPorts[]` entry's L7-rule-list-discriminator key the CRD schema
12800/// validator recognizes as unknown), with every intra-mesh HTTP-shaped
12801/// `:contratos` flow the CNP was authored to filter to a URL-path prefix
12802/// silently bypassing the L7 path predicate at the Cilium data-plane's
12803/// L4-only fallback dispatch with no field naming the L7-HTTP-rule-list-
12804/// discriminator-drift root cause.
12805///
12806/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
12807/// "every recurring shape becomes a generator before it becomes a
12808/// pattern; every pattern becomes a library before it becomes
12809/// duplicated code. The duplication budget is zero.") promotes the
12810/// constant to a typed substrate-side `&'static str` on the same
12811/// trajectory the [`CILIUM_KEY_MODE`] (4289dfb) /
12812/// [`CILIUM_KEY_AUTHENTICATION`] (db31108) /
12813/// [`CILIUM_KEY_PORTS`] (1087693) /
12814/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
12815/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
12816/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
12817/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
12818/// [`KUBE_KEY_RULES`] (a205eb3) /
12819/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
12820/// [`CILIUM_API_VERSION`] (279d611) lifts established on the
12821/// sibling canonical-Cilium-CNP-body-axis surfaces — descends the per-
12822/// `toPorts[]` L7-HTTP-rule-list-discriminator axis one level beneath the
12823/// parent [`KUBE_KEY_RULES`] per-`toPorts[]` rule-list-container axis-key
12824/// it nests inside, completing the per-`toPorts[]` L7-introspection
12825/// `(rules → http)` container/protocol-discriminator axis pair the M3
12826/// Aplicacao mesh renderer's HTTP-shaped-`:contratos` URL-path-prefix-
12827/// filtering L7-enforcement contract rests on.
12828///
12829/// [cm]: ../../caixa_mesh/index.html
12830pub const CILIUM_KEY_HTTP: &str = "http";
12831
12832/// Canonical Cilium `CiliumNetworkPolicy` per-`ingress[].toPorts[].rules.http[]`
12833/// per-HTTP-rule URL-path-predicate leaf-scalar-axis key every
12834/// `cilium_network_policies`-emitted CNP document mounts its per-HTTP-rule
12835/// URL-path-prefix predicate scalar under
12836/// (`spec.ingress[].toPorts[].rules.http[].path`). Nests exactly one level
12837/// beneath the sibling [`CILIUM_KEY_HTTP`] (ccd81e8) per-`toPorts[]`
12838/// L7-HTTP-rule-list-discriminator container-axis it sits inside: the Cilium
12839/// CNP schema places the per-HTTP-rule URL-path predicate scalar (the exact
12840/// URL-path regex the Cilium L7 dispatch pass matches the observed HTTP
12841/// request line's path segment against) as the single load-bearing leaf-
12842/// scalar axis of the per-`rules.http[]` entry — so drift on the per-HTTP-
12843/// rule URL-path-predicate leaf axis is exactly as load-bearing as drift on
12844/// the sibling [`CILIUM_KEY_HTTP`] per-`toPorts[]` L7-HTTP-rule-list-
12845/// discriminator container-axis key it nests inside (the Cilium-operator-
12846/// side CNP schema validator drops any per-`rules.http[]` entry whose per-
12847/// HTTP-rule URL-path-predicate leaf key it recognizes as unknown — a
12848/// `"Path"` / `"pathPrefix"` / `"regex"` / `"urlPath"` / `"pathMatch"` typo
12849/// at either the emit-side `http_rule.insert(…)` call site or a downstream
12850/// renderer's per-`rules.http[]` URL-path leaf upsert silently emits a per-
12851/// `rules.http[]` entry whose URL-path-predicate leaf-axis key the Cilium
12852/// CRD schema validator rejects as unknown; the per-`rules.http[]` entry
12853/// falls back to a match-any-URL-path predicate — the per-`toPorts[]` L7
12854/// rule admits every URL path on the destination port silently, bypassing
12855/// the URL-path-prefix predicate the typed `:contratos` HTTP-shaped edge's
12856/// `:endpoint` slot names at the L7 introspection axis, and the emit-
12857/// side/probe-side split silently masks the per-`rules.http[]` URL-path
12858/// pin (`.get("path")` returns `None` under both the drifted-key emitter
12859/// and the drifted-key probe — every downstream `.and_then(|v| v.as_str())`
12860/// chain short-circuits vacuously because the outer per-HTTP-rule URL-
12861/// path-lookup is itself `None`).
12862///
12863/// The single source of truth the rendered Aplicacao Cilium-CNP-side
12864/// intra-mesh per-`toPorts[]` L7-URL-path-predicate-gating bundle's per-
12865/// `rules.http[]` URL-path-predicate-leaf-axis naming reaches for:
12866///
12867/// - the rendered `CiliumNetworkPolicy` document's per-`toPorts[]`
12868/// `rules.http[]` entry's `path` URL-path-predicate leaf axis
12869/// (caixa-mesh/src/lib.rs — the `cilium_network_policies` per-`(:de,
12870/// :para)` policy's `http_rule.insert("path", …)` call in the
12871/// `WitTarget::Http` L7 introspection emit branch, the exact per-
12872/// `rules.http[]` leaf axis the per-HTTP-rule URL-path predicate scalar
12873/// lands under, seeded from the typed HTTP-shaped `:contratos` edge's
12874/// `:endpoint` slot).
12875///
12876/// The per-HTTP-rule URL-path-predicate-leaf-axis names the same Cilium-
12877/// operator-side per-`rules.http[]` URL-path predicate selection as the
12878/// sibling [`CILIUM_KEY_HTTP`] per-`toPorts[]` L7-HTTP-rule-list-
12879/// discriminator container-axis key it nests inside, and must move together
12880/// on any future Cilium CRD schema rebrand (an upstream `cilium.io/v3`
12881/// rename of the per-HTTP-rule URL-path-predicate leaf from `path` to
12882/// `urlPath` / `pathPrefix` / `pathMatch`, coordinated with the Cilium
12883/// project's periodic CRD schema-migration passes). Until this lift landed
12884/// the axis carried an inline `path` literal at the one production-code
12885/// emitter site (the `cilium_network_policies` per-`(:de, :para)`
12886/// `http_rule.insert("path", …)` call in the `WitTarget::Http` L7
12887/// introspection emit branch) plus a matching set inside the in-file
12888/// `cilium_http_contracts_emit_l7_rules` test-fixture per-HTTP-rule URL-
12889/// path-predicate presence-and-value pin — two occurrences of the same
12890/// load-bearing Cilium-CRD per-HTTP-rule URL-path-predicate-leaf-axis
12891/// convention, drift-prone by construction. A drift on any one production
12892/// or test-fixture site to `"Path"` / `"pathPrefix"` / `"regex"` /
12893/// `"urlPath"` / `"pathMatch"` would surface as a Cilium-operator-side
12894/// schema-validator drop at apply time (the affected per-`rules.http[]`
12895/// entry's URL-path-predicate leaf-axis key the CRD schema validator
12896/// recognizes as unknown), with every intra-mesh HTTP-shaped `:contratos`
12897/// flow the CNP was authored to filter to a URL-path prefix silently
12898/// bypassing the L7 URL-path predicate at the Cilium data-plane's match-
12899/// any-URL-path fallback with no field naming the URL-path-predicate-
12900/// leaf-axis-drift root cause.
12901///
12902/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
12903/// "every recurring shape becomes a generator before it becomes a
12904/// pattern; every pattern becomes a library before it becomes
12905/// duplicated code. The duplication budget is zero.") promotes the
12906/// constant to a typed substrate-side `&'static str` on the same
12907/// trajectory the [`CILIUM_KEY_HTTP`] (ccd81e8) /
12908/// [`CILIUM_KEY_MODE`] (4289dfb) /
12909/// [`CILIUM_KEY_AUTHENTICATION`] (db31108) /
12910/// [`CILIUM_KEY_PORTS`] (1087693) /
12911/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
12912/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
12913/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
12914/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
12915/// [`KUBE_KEY_RULES`] (a205eb3) /
12916/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
12917/// [`CILIUM_API_VERSION`] (279d611) lifts established on the
12918/// sibling canonical-Cilium-CNP-body-axis surfaces — descends the per-
12919/// `toPorts[]` L7-introspection `(rules → http → path)` container /
12920/// protocol-discriminator / URL-path-predicate axis chain one leaf level
12921/// beneath the parent [`CILIUM_KEY_HTTP`] per-`toPorts[]` L7-HTTP-rule-
12922/// list-discriminator axis-key it nests inside, completing the per-
12923/// `toPorts[]` L7-introspection `(rules → http → path)` container /
12924/// protocol-discriminator / URL-path-predicate axis triple the M3
12925/// Aplicacao mesh renderer's HTTP-shaped-`:contratos` URL-path-prefix-
12926/// filtering L7-enforcement contract rests on.
12927///
12928/// Distinct from the sibling K8s-Gateway-API-side
12929/// [`GATEWAY_API_KEY_PATH`] (9f45aa4) per-`HTTPRouteMatch` path-matcher
12930/// container-axis key: both keys spell the same underlying `"path"`
12931/// string but name distinct schema axes on distinct CRD groups — the
12932/// Cilium-side axis is a per-HTTP-rule URL-path predicate leaf scalar
12933/// on the Cilium `cilium.io/v2` `CiliumNetworkPolicy` CRD's per-
12934/// `toPorts[].rules.http[]` entry, the Gateway-API-side axis is a per-
12935/// `HTTPRouteMatch` path-matcher two-leaf container (`{type, value}`)
12936/// on the K8s Gateway API v1 `HTTPRoute` CRD's `spec.rules[].matches[]`
12937/// entry. Keeping them as sibling `pub const` declarations (rather than
12938/// coalescing onto a single shared constant that happens to carry the
12939/// same string) mirrors the deliberate axis-independence discipline the
12940/// [`CILIUM_KIND_NETWORK_POLICY`] / [`GATEWAY_API_KIND_GATEWAY`] /
12941/// [`GATEWAY_API_KIND_HTTP_ROUTE`] kind-discriminator lifts already
12942/// codified on the sibling per-CRD-kind axes, so a future Cilium-side
12943/// per-HTTP-rule URL-path-predicate rebrand (Cilium `cilium.io/v3` renames
12944/// `path` → `urlPath`) can land independently of the Gateway-API-side
12945/// per-`HTTPRouteMatch` path-matcher container-axis rebrand without any
12946/// cross-CRD coordination footgun where a shared constant would force a
12947/// coupled edit against schema evolutions the two CRD projects run on
12948/// independent cadences. Note: Rust's `&'static str` interner coalesces
12949/// identical byte-sequences onto one storage allocation at codegen time,
12950/// so at runtime a `.as_ptr()` comparison between the two constants can't
12951/// distinguish "sibling `pub const` declarations carrying identical
12952/// bytes" from "coalesced canonical declaration" — the axis-independence
12953/// discipline lives at the rustc symbol-name axis (the two `pub const
12954/// CILIUM_KEY_PATH` / `pub const GATEWAY_API_KEY_PATH` symbols a future
12955/// rebrand of one leaves the other structurally untouched under) rather
12956/// than the runtime-address axis, and the per-axis re-export identity
12957/// pins in the consuming renderer crates (each pinning the local re-
12958/// export against its own canonical declaration on its own axis) remain
12959/// the load-bearing "no sibling local `pub const` drift" gate for the
12960/// pair.
12961///
12962/// [cm]: ../../caixa_mesh/index.html
12963pub const CILIUM_KEY_PATH: &str = "path";
12964
12965/// Canonical K8s Gateway API CRD `kind` discriminator the rendered
12966/// `Gateway` document declares at its top-level [`KUBE_KEY_KIND`] axis.
12967/// Pairs with the sibling [`GATEWAY_API_API_VERSION`] (3c6cfc3) — the
12968/// K8s apiserver-side CRD resolution contract is the
12969/// `(apiVersion, kind)` tuple keyed against the registered
12970/// `CustomResourceDefinition`, so drift on the kind axis is exactly as
12971/// load-bearing as drift on the apiVersion axis it accompanies (the
12972/// apiserver's `RESTMapper` consults both together; a
12973/// `("gateway.networking.k8s.io/v1", "Gatway")` typo at the production-
12974/// code call site lands outside the registered Gateway-API-conformant
12975/// `Gateway` CRD's `RESTKind` lookup, surfacing apply-side as a
12976/// non-self-locating "no kind 'Gatway' is registered for version
12977/// 'gateway.networking.k8s.io/v1'" error far from the source caixa.lisp
12978/// / the renderer's [`kube_resource_skeleton`] call site).
12979///
12980/// The single source of truth the rendered Aplicacao Gateway-API-side
12981/// ingress bundle's `Gateway`-naming axis reaches for:
12982///
12983/// - the rendered `Gateway` document's top-level [`KUBE_KEY_KIND`]
12984/// axis (caixa-mesh/src/lib.rs:578 — the `gateway_routes` per-
12985/// Aplicacao `Gateway` [`kube_resource_skeleton`] kind argument).
12986///
12987/// The kind axis names the same Gateway-API-conformant CRD discriminator
12988/// as the sibling [`GATEWAY_API_API_VERSION`] apiVersion axis and must
12989/// move together on any future Gateway-API rebrand. Until this lift
12990/// landed the axis carried an inline `Gateway` literal at the one
12991/// production-code occurrence in caixa-mesh/src/lib.rs:578 (the
12992/// `gateway_routes` `Gateway` [`kube_resource_skeleton`] kind argument)
12993/// plus a matching set inside the in-file
12994/// `gateway_carries_canonical_kube_skeleton_without_labels` /
12995/// `render_all_includes_every_artifact_kind` test fixtures plus the
12996/// `find()` predicate of every per-Gateway-kind test that picks the
12997/// `Gateway` document out of the rendered Aplicacao mesh bundle — five
12998/// occurrences of the same load-bearing Gateway-API-CRD-`kind`-
12999/// discriminator convention, drift-prone by construction. A drift on
13000/// the top-level `Gateway` `kind` axis would have surfaced as a
13001/// non-self-locating "no kind 'Gatway' is registered for version
13002/// 'gateway.networking.k8s.io/v1'" error far from the source caixa.lisp
13003/// at apply parse time, with the rendered per-Aplicacao Gateway never
13004/// landing in the apiserver-side CRD registration and every external
13005/// `:entrada` flow dropping at the gateway-class-controller's reconcile
13006/// loop with no field naming the kind-discriminator-drift root cause.
13007///
13008/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
13009/// "every recurring shape becomes a generator before it becomes a
13010/// pattern; every pattern becomes a library before it becomes
13011/// duplicated code. The duplication budget is zero.") promotes the
13012/// constant to a typed substrate-side `&'static str` on the same
13013/// trajectory the [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
13014/// [`FLUX_KIND_KUSTOMIZATION`] (4114773) /
13015/// [`FLUX_KIND_HELM_RELEASE`] (e24ea3c) /
13016/// [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
13017/// [`GATEWAY_API_API_VERSION`] (3c6cfc3) lifts established on the
13018/// sibling cluster-side-CRD-`kind`-discriminator + canonical-CRD-
13019/// group/version axes — extends the discipline from the apiVersion
13020/// half of the `(apiVersion, kind)` CRD-lookup tuple onto the kind
13021/// half on the same Gateway-API-CRD-axis, beginning the per-Gateway-
13022/// API-CRD kind+apiVersion lift pair the M3 Aplicacao mesh renderer's
13023/// external `:entrada` ingress contract rests on. The render-side
13024/// consumer now threads the same `&'static str` through its
13025/// [`kube_resource_skeleton`] call so a future Gateway-API rebrand
13026/// lands in one place; every future renderer that reaches for the
13027/// canonical Gateway-API `Gateway` kind (the future M4
13028/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
13029/// Gateway fan-out, a future per-cluster `GatewayClass` renderer the
13030/// operator emits for per-cluster gateway-class scoping, a future
13031/// per-edge `TCPRoute` / `TLSRoute` / `GRPCRoute` renderer for non-HTTP
13032/// `:entrada` edges that pair against this same `Gateway` parent)
13033/// inherits the same value by construction with no opportunity for
13034/// per-renderer drift.
13035///
13036/// Same "the typed constant lives in one place" discipline the
13037/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
13038/// [`FLUX_KIND_KUSTOMIZATION`] (4114773) /
13039/// [`FLUX_KIND_HELM_RELEASE`] (e24ea3c) /
13040/// [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
13041/// [`GATEWAY_API_API_VERSION`] (3c6cfc3) /
13042/// [`CILIUM_API_VERSION`] (279d611) lifts apply on the peer
13043/// canonical-cluster-side-CRD-discriminator surface.
13044///
13045/// [cm]: ../../caixa_mesh/index.html
13046pub const GATEWAY_API_KIND_GATEWAY: &str = "Gateway";
13047
13048/// Canonical K8s Gateway API CRD `kind` discriminator the rendered
13049/// `HTTPRoute` document declares at its top-level [`KUBE_KEY_KIND`] axis.
13050/// Pairs with the sibling [`GATEWAY_API_API_VERSION`] (3c6cfc3) and the
13051/// peer [`GATEWAY_API_KIND_GATEWAY`] (fb4639c) — the K8s apiserver-side
13052/// CRD resolution contract is the `(apiVersion, kind)` tuple keyed
13053/// against the registered `CustomResourceDefinition`, so drift on the
13054/// kind axis is exactly as load-bearing as drift on the apiVersion axis
13055/// it accompanies (the apiserver's `RESTMapper` consults both together;
13056/// a `("gateway.networking.k8s.io/v1", "HTTPRout")` typo at the
13057/// production-code call site lands outside the registered Gateway-API-
13058/// conformant `HTTPRoute` CRD's `RESTKind` lookup, surfacing apply-side
13059/// as a non-self-locating "no kind 'HTTPRout' is registered for version
13060/// 'gateway.networking.k8s.io/v1'" error far from the source caixa.lisp /
13061/// the renderer's [`kube_resource_skeleton`] call site).
13062///
13063/// The single source of truth the rendered Aplicacao Gateway-API-side
13064/// ingress bundle's `HTTPRoute`-naming axis reaches for:
13065///
13066/// - the rendered `HTTPRoute` document's top-level [`KUBE_KEY_KIND`]
13067/// axis (caixa-mesh/src/lib.rs:663 — the `gateway_routes` per-
13068/// Aplicacao `HTTPRoute` [`kube_resource_skeleton`] kind argument).
13069///
13070/// The kind axis names the same Gateway-API-conformant CRD discriminator
13071/// as the sibling [`GATEWAY_API_API_VERSION`] apiVersion axis and the
13072/// peer [`GATEWAY_API_KIND_GATEWAY`] parent-Gateway axis, and must move
13073/// together with both on any future Gateway-API rebrand. Until this lift
13074/// landed the axis carried an inline `HTTPRoute` literal at the one
13075/// production-code occurrence in caixa-mesh/src/lib.rs:663 (the
13076/// `gateway_routes` `HTTPRoute` [`kube_resource_skeleton`] kind argument)
13077/// plus a matching set inside the in-file
13078/// `httproute_carries_canonical_kube_skeleton_without_labels` /
13079/// `render_all_includes_every_artifact_kind` test fixtures plus the
13080/// `find()` predicate of every per-HTTPRoute-kind test that picks the
13081/// `HTTPRoute` document out of the rendered Aplicacao mesh bundle —
13082/// multiple occurrences of the same load-bearing Gateway-API-CRD-`kind`-
13083/// discriminator convention, drift-prone by construction.
13084///
13085/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
13086/// "every recurring shape becomes a generator before it becomes a
13087/// pattern; every pattern becomes a library before it becomes
13088/// duplicated code. The duplication budget is zero.") promotes the
13089/// constant to a typed substrate-side `&'static str` on the same
13090/// trajectory the [`GATEWAY_API_KIND_GATEWAY`] (fb4639c) /
13091/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
13092/// [`FLUX_KIND_KUSTOMIZATION`] (4114773) /
13093/// [`FLUX_KIND_HELM_RELEASE`] (e24ea3c) /
13094/// [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
13095/// [`GATEWAY_API_API_VERSION`] (3c6cfc3) lifts established on the
13096/// sibling cluster-side-CRD-`kind`-discriminator + canonical-CRD-
13097/// group/version axes — completes the per-Gateway-API-CRD `kind`-axis
13098/// lift trajectory across the `(Gateway, HTTPRoute)` pair that the
13099/// renderer's `gateway_routes` external `:entrada` ingress contract
13100/// emits together. Every guarantee in [MESH-COMPOSITION.md §V][mc] —
13101/// "every Aplicacao with `:entrada` emits one `Gateway` + one
13102/// `HTTPRoute` per `:paths` entry pointing at the same
13103/// `gateway.networking.k8s.io/v1` group/version — now threads through
13104/// one lifted `&'static str` apiece for both halves of the pair, so a
13105/// future Gateway-API rebrand lands at one substrate-side edit-point
13106/// per axis and no per-renderer drift surface remains across the pair.
13107///
13108/// A future Gateway-API-side renderer the M3.x absorption roadmap
13109/// names — `TCPRoute`, `TLSRoute`, `GRPCRoute` for non-HTTP `:entrada`
13110/// edges, the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
13111/// materializer's per-Aplicacao `HTTPRoute` fan-out, a future per-edge
13112/// route-attached-policy renderer (`BackendTLSPolicy`,
13113/// `BackendLBPolicy`) — inherits the canonical `HTTPRoute` kind
13114/// discriminator by construction with no opportunity for per-renderer
13115/// drift.
13116///
13117/// [mc]: https://github.com/pleme-io/theory/blob/main/MESH-COMPOSITION.md
13118/// [cm]: ../../caixa_mesh/index.html
13119pub const GATEWAY_API_KIND_HTTP_ROUTE: &str = "HTTPRoute";
13120
13121/// Canonical K8s Gateway API `Gateway.spec.listeners[].protocol` HTTP
13122/// listener-protocol scalar value the rendered `Gateway` document's
13123/// first (and V0-only) listener declares under its
13124/// [`KUBE_KEY_PROTOCOL`] axis. Pairs with the sibling
13125/// [`GATEWAY_API_KIND_GATEWAY`] (fb4639c) +
13126/// [`GATEWAY_API_KIND_HTTP_ROUTE`] (1adccc0) — the K8s Gateway API v1
13127/// CRD schema pins the per-listener L7 parser + TLS-termination
13128/// strategy through the `spec.listeners[].protocol` scalar value (the
13129/// gateway-class-controller's per-listener bind loop selects the L7
13130/// parser + TLS termination strategy from this exact byte-sequence;
13131/// the Gateway API v1 `ProtocolType` OpenAPI schema enum admits the
13132/// closed set `{"HTTP", "HTTPS", "TCP", "TLS", "UDP"}` verbatim), so
13133/// drift on the listener-protocol value is exactly as load-bearing as
13134/// drift on the sibling [`GATEWAY_API_KIND_GATEWAY`] +
13135/// [`GATEWAY_API_KIND_HTTP_ROUTE`] CRD `kind` discriminators the pair
13136/// declares together (a `("Gateway", "http")` /
13137/// `("Gateway", "Http")` / `("Gateway", "http/1.1")` typo at the
13138/// production-code call site lands outside the Gateway API v1
13139/// `ProtocolType` OpenAPI schema enum, surfacing apply-side as a
13140/// non-self-locating "spec.listeners[0].protocol: Unsupported value:
13141/// \"http\": supported values: \"HTTP\", \"HTTPS\", \"TCP\", \"TLS\",
13142/// \"UDP\"" apiserver admission-rejection far from the source
13143/// `caixa.lisp` / the renderer's `listener.insert(…)` call site — the
13144/// rendered per-Aplicacao `Gateway` object never reconciles at the
13145/// gateway-class-controller's per-listener bind loop and every
13146/// external `:entrada` HTTP flow drops at the gateway-class-
13147/// controller's admission gate with no field naming the
13148/// listener-protocol-drift root cause).
13149///
13150/// The single source of truth the rendered Aplicacao Gateway-API-side
13151/// ingress bundle's per-listener L7-parser-selection axis reaches for:
13152///
13153/// - the rendered `Gateway` document's `spec.listeners[0].protocol`
13154/// axis (the `gateway_routes` per-`:entrada` `Gateway` emitter's
13155/// `listener.insert(KUBE_KEY_PROTOCOL, "HTTP")` call — the sole
13156/// production-code call site the prior inline `"HTTP".into()`
13157/// literal sat at, caixa-mesh/src/lib.rs:2123).
13158///
13159/// The listener-protocol value names the same Gateway-API-
13160/// implementation-side per-listener L7-parser-selection scalar as the
13161/// sibling [`KUBE_KEY_PROTOCOL`] key-axis discriminator carries the
13162/// value under, and must move together with the sibling K8s Gateway
13163/// API `ProtocolType` OpenAPI schema enum on any future Gateway API
13164/// rebrand (an upstream Gateway API v2 rename of the HTTP listener
13165/// protocol from `HTTP` to `HTTP/1.1` / `HTTP/2` / `http`, coordinated
13166/// with the upstream SIG-Network Gateway API `ProtocolType` enum
13167/// deprecation cycle, would land at this one const rather than
13168/// scattered across every per-emitter listener-block-insertion site).
13169///
13170/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
13171/// "every recurring shape becomes a generator before it becomes a
13172/// pattern; every pattern becomes a library before it becomes
13173/// duplicated code. The duplication budget is zero.") promotes the
13174/// constant to a typed substrate-side `&'static str` on the same
13175/// trajectory the [`GATEWAY_API_KIND_GATEWAY`] (fb4639c) /
13176/// [`GATEWAY_API_KIND_HTTP_ROUTE`] (1adccc0) /
13177/// [`DEFAULT_GATEWAY_CLASS_NAME`] (d9b0743) lifts established on the
13178/// sibling Gateway-API-CRD-`kind`-discriminator + Gateway-controller-
13179/// binding-scalar-value axes — extends the per-Gateway-API-CRD-`kind`-
13180/// discriminator lift pair across the `(Gateway, HTTPRoute)` pair
13181/// onto the sibling per-Gateway `spec.listeners[].protocol`
13182/// listener-protocol-scalar-value axis the same `gateway_routes`
13183/// external `:entrada` ingress emitter carries.
13184///
13185/// A future Gateway-API-side renderer the M3.x absorption roadmap
13186/// names — an HTTPS listener with TLS termination (a sibling
13187/// `GATEWAY_API_PROTOCOL_HTTPS` const value the same enum admits),
13188/// the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` materializer's
13189/// per-Aplicacao multi-listener fan-out over `{HTTP, HTTPS, TLS}`,
13190/// a future per-listener route-attached-policy renderer that binds
13191/// distinct policy chains per listener-protocol — inherits the
13192/// canonical `HTTP` listener-protocol value by construction with no
13193/// opportunity for per-renderer drift.
13194///
13195/// [cm]: ../../caixa_mesh/index.html
13196pub const GATEWAY_API_PROTOCOL_HTTP: &str = "HTTP";
13197
13198/// Canonical K8s Gateway API v1 `PathMatchType` OpenAPI schema enum's
13199/// `PathPrefix` per-`HTTPRouteMatch` path-selection-predicate discriminator
13200/// value every `gateway_routes`-emitted `HTTPRoute` per-rule `matches[]`
13201/// entry declares under its per-match `spec.rules[].matches[].path.type`
13202/// scalar axis. Pairs with the sibling [`GATEWAY_API_KEY_PATH`] (9f45aa4)
13203/// per-`HTTPRouteMatch` path-matcher container-axis key it nests one level
13204/// beneath — the Gateway API v1 CRD schema pins per-`HTTPRouteMatch`
13205/// request-path selection through the `spec.rules[].matches[].path`
13206/// container axis (each match entry names one path-selection predicate the
13207/// request line's `:path` pseudo-header must satisfy under a `type`
13208/// discriminator scalar value; the Gateway API v1 `PathMatchType` OpenAPI
13209/// schema enum admits the closed set `{"Exact", "PathPrefix",
13210/// "RegularExpression"}` verbatim), so drift on the path-match-type value
13211/// is exactly as load-bearing as drift on the sibling
13212/// [`GATEWAY_API_PROTOCOL_HTTP`] (1b57473) per-listener L7-parser-selection
13213/// scalar value the peer `spec.listeners[].protocol` axis carries (a
13214/// `"pathPrefix"` / `"path_prefix"` / `"Prefix"` / `"path-prefix"` typo at
13215/// the production-code call site lands outside the Gateway API v1
13216/// `PathMatchType` OpenAPI schema enum's admitted set, surfacing apply-side
13217/// as a non-self-locating "spec.rules[0].matches[0].path.type: Unsupported
13218/// value: \"pathPrefix\": supported values: \"Exact\", \"PathPrefix\",
13219/// \"RegularExpression\"" apiserver admission-rejection far from the
13220/// source `caixa.lisp` / the renderer's `path_match.insert(…)` call site —
13221/// the rendered per-Aplicacao `HTTPRoute` object never reconciles at the
13222/// gateway-class-controller's per-rule L7 dispatch loop and every external
13223/// `:entrada` path-filtered flow drops at the gateway-class-controller's
13224/// admission gate with no field naming the path-match-type-drift root
13225/// cause).
13226///
13227/// The single source of truth the rendered Aplicacao Gateway-API-side
13228/// ingress bundle's per-`HTTPRouteMatch` path-selection-predicate-
13229/// discriminator-value-naming reaches for:
13230///
13231/// - the rendered `HTTPRoute` document's per-match
13232/// `spec.rules[].matches[].path.type` axis (caixa-mesh/src/lib.rs —
13233/// the `gateway_routes` per-match `path_match.insert("type",
13234/// "PathPrefix")` call the prior inline `"PathPrefix".into()` literal
13235/// sat at).
13236///
13237/// The path-match-type value names the same Gateway-API-implementation-
13238/// side per-`HTTPRouteMatch` request-path-selection-predicate discriminator
13239/// as the sibling [`GATEWAY_API_KEY_PATH`] path-matcher container-axis key
13240/// carries the value under, and must move together with the sibling K8s
13241/// Gateway API v1 `PathMatchType` OpenAPI schema enum on any future
13242/// Gateway API rebrand (an upstream Gateway API v2 rename of the prefix-
13243/// path-selection discriminator from `PathPrefix` to `Prefix` / `path-
13244/// prefix` / `PathPrefixMatch`, coordinated with the upstream SIG-Network
13245/// Gateway API `PathMatchType` enum deprecation cycle, would land at this
13246/// one const rather than scattered across every per-emitter per-match
13247/// path-block-insertion site).
13248///
13249/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
13250/// "every recurring shape becomes a generator before it becomes a
13251/// pattern; every pattern becomes a library before it becomes
13252/// duplicated code. The duplication budget is zero.") promotes the
13253/// constant to a typed substrate-side `&'static str` on the same
13254/// trajectory the [`GATEWAY_API_PROTOCOL_HTTP`] (1b57473) /
13255/// [`GATEWAY_API_KIND_GATEWAY`] (fb4639c) /
13256/// [`GATEWAY_API_KIND_HTTP_ROUTE`] (1adccc0) /
13257/// [`DEFAULT_GATEWAY_CLASS_NAME`] (d9b0743) lifts established on the
13258/// sibling per-listener L7-parser-selection scalar-value +
13259/// Gateway-API-CRD-`kind`-discriminator + Gateway-controller-binding
13260/// scalar-value axes — extends the canonical-Gateway-API-v1-OpenAPI-
13261/// schema-enum-value single-sourcing discipline the `ProtocolType.HTTP`
13262/// lift established onto the sibling `PathMatchType.PathPrefix`
13263/// per-`HTTPRouteMatch` path-selection-predicate discriminator the same
13264/// `gateway_routes` external `:entrada` ingress emitter carries under
13265/// the shared `HTTPRoute` body.
13266///
13267/// A future Gateway-API-side renderer the M3.x absorption roadmap
13268/// names — a sibling `GATEWAY_API_PATH_MATCH_TYPE_EXACT` /
13269/// `GATEWAY_API_PATH_MATCH_TYPE_REGULAR_EXPRESSION` const value the same
13270/// `PathMatchType` enum admits, the future M4
13271/// `mesh.pleme.io/v1alpha1/Aplicacao` materializer's per-Aplicacao
13272/// multi-predicate fan-out over `{Exact, PathPrefix, RegularExpression}`,
13273/// a future per-match `:entrada :paths` typed slot admitting a per-path
13274/// `(:predicate <Exact|Prefix|Regex>)` axis — inherits the canonical
13275/// `PathPrefix` path-match-type value by construction with no opportunity
13276/// for per-renderer drift.
13277///
13278/// [cm]: ../../caixa_mesh/index.html
13279pub const GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX: &str = "PathPrefix";
13280
13281/// Canonical K8s core `Protocol` OpenAPI schema enum's `TCP` L4-transport-
13282/// protocol scalar value every `cilium_network_policies`-emitted
13283/// `CiliumNetworkPolicy` document's per-`spec.ingress[].toPorts[].ports[]`
13284/// port-tuple declares under its per-tuple [`KUBE_KEY_PROTOCOL`] axis.
13285/// Pairs with the sibling [`KUBE_KEY_PROTOCOL`] (0307950) per-CR L4/L7
13286/// protocol-scalar-discriminator container-axis key the value nests
13287/// directly under — the K8s core `Protocol` schema pins per-`ContainerPort`
13288/// / `ServicePort` / `EndpointPort` / `NetworkPolicyPort` L4-transport
13289/// selection through the `protocol` scalar (each port entry names one
13290/// L4-transport-protocol discriminator the CNI / kube-proxy / eBPF-data-
13291/// plane bpf policy dispatch loop keys off before applying the port match;
13292/// the K8s core `Protocol` OpenAPI schema enum admits the closed set
13293/// `{"TCP", "UDP", "SCTP"}` verbatim — see
13294/// https://kubernetes.io/docs/reference/generated/kubernetes-api/v1/#protocol-v1-core),
13295/// so drift on the L4-transport-protocol value is exactly as load-bearing
13296/// as drift on the sibling [`GATEWAY_API_PROTOCOL_HTTP`] (1b57473) per-
13297/// listener L7-parser-selection scalar value the peer Gateway-API v1
13298/// `ProtocolType` OpenAPI schema enum admits under the same
13299/// [`KUBE_KEY_PROTOCOL`] container-axis key (a `"tcp"` / `"Tcp"` /
13300/// `"TCP/IP"` / `"transport-tcp"` typo at the production-code call site
13301/// lands outside the K8s core `Protocol` OpenAPI schema enum's admitted
13302/// set, surfacing apply-side as a non-self-locating
13303/// "spec.ingress[0].toPorts[0].ports[0].protocol: Unsupported value:
13304/// \"tcp\": supported values: \"SCTP\", \"TCP\", \"UDP\"" apiserver
13305/// admission-rejection far from the source `caixa.lisp` / the renderer's
13306/// `port_entry.insert(…)` call site — the rendered per-`(:de, :para)`
13307/// `CiliumNetworkPolicy` object never reconciles at the Cilium operator's
13308/// per-CNP L4 dispatch pass and every intra-mesh `:contratos` L4-tuple-
13309/// gated flow drops at the Cilium operator's admission gate with no field
13310/// naming the L4-transport-protocol-drift root cause; worse — because the
13311/// `protocol` scalar carries a schema-side default of `TCP` on the K8s
13312/// core `Protocol` enum, a silently-elided drift on the emit lands a
13313/// `CiliumNetworkPolicy` whose ingress rule falls back to the default L4-
13314/// transport-protocol and every port-match on a non-default transport
13315/// silently misses at the eBPF data plane's per-tuple dispatch).
13316///
13317/// The single source of truth the rendered Aplicacao Cilium-CNP-side
13318/// intra-mesh L4-tuple-gating bundle's per-`toPorts[].ports[]` port-tuple
13319/// L4-transport-protocol-discriminator-value-naming reaches for:
13320///
13321/// - the rendered `CiliumNetworkPolicy` document's per-tuple
13322/// `spec.ingress[].toPorts[].ports[].protocol` axis (caixa-mesh/src/lib.rs —
13323/// the `cilium_network_policies` per-`(:de, :para)`
13324/// `port_entry.insert(KUBE_KEY_PROTOCOL, "TCP")` call the prior
13325/// inline `"TCP".into()` literal sat at).
13326///
13327/// The L4-transport-protocol value names the same K8s-core-`Protocol`-
13328/// enum-side per-port-tuple L4-transport-selection discriminator as the
13329/// sibling [`KUBE_KEY_PROTOCOL`] key-axis discriminator carries the value
13330/// under, and must move together with the sibling K8s core `Protocol`
13331/// OpenAPI schema enum on any future K8s core `Protocol` rebrand (an
13332/// upstream K8s core `Protocol` rename or extension — e.g. the
13333/// `KEP-3675 QUIC transport` proposal's `"QUIC"` addition to the enum,
13334/// coordinated with the upstream SIG-Network per-version deprecation
13335/// cycle — would land at this one const rather than scattered across
13336/// every per-emitter L4-port-block-insertion site).
13337///
13338/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
13339/// "every recurring shape becomes a generator before it becomes a
13340/// pattern; every pattern becomes a library before it becomes
13341/// duplicated code. The duplication budget is zero.") promotes the
13342/// constant to a typed substrate-side `&'static str` on the same
13343/// trajectory the [`GATEWAY_API_PROTOCOL_HTTP`] (1b57473) /
13344/// [`GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX`] (530705d) /
13345/// [`GATEWAY_API_KIND_GATEWAY`] (fb4639c) /
13346/// [`GATEWAY_API_KIND_HTTP_ROUTE`] (1adccc0) /
13347/// [`DEFAULT_GATEWAY_CLASS_NAME`] (d9b0743) lifts established on the
13348/// sibling per-listener L7-parser-selection scalar-value + per-match
13349/// path-selection-predicate discriminator-value + Gateway-API-CRD-
13350/// `kind`-discriminator + Gateway-controller-binding scalar-value axes —
13351/// extends the canonical-cluster-side-OpenAPI-schema-enum-value single-
13352/// sourcing discipline the Gateway-API v1 `ProtocolType.HTTP` /
13353/// `PathMatchType.PathPrefix` lifts established onto the sibling
13354/// K8s-core `Protocol.TCP` per-port-tuple L4-transport-protocol-
13355/// discriminator the `cilium_network_policies` intra-mesh L4-tuple-gating
13356/// emitter carries under the shared `CiliumNetworkPolicy` body.
13357///
13358/// A future Cilium-CNP-side / K8s-core-`Protocol`-side renderer the M3.x
13359/// absorption roadmap names — a sibling `KUBE_PROTOCOL_UDP` /
13360/// `KUBE_PROTOCOL_SCTP` const value the same `Protocol` enum admits, the
13361/// future M4 `mesh.pleme.io/v1alpha1/Aplicacao` materializer's per-
13362/// Aplicacao multi-transport fan-out over `{TCP, UDP, SCTP}` for
13363/// `nats:pub-sub` / `wasi:sockets/udp` contratos, a future per-contrato
13364/// `:transport <TCP|UDP|SCTP>` typed slot admitting a per-edge transport-
13365/// protocol axis — inherits the canonical `TCP` L4-transport-protocol
13366/// value by construction with no opportunity for per-renderer drift.
13367///
13368/// [cm]: ../../caixa_mesh/index.html
13369pub const KUBE_PROTOCOL_TCP: &str = "TCP";
13370
13371/// Canonical Cilium `CiliumNetworkPolicy` `MutualAuthenticationMode` OpenAPI
13372/// schema enum's `required` per-`ingress[].authentication.mode` mTLS-mandatory
13373/// scalar-value every `cilium_network_policies`-emitted CNP document declares
13374/// under its per-rule mutual-auth-mode-discriminator leaf axis when the typed
13375/// `:politicas :mtls-required` tristate is `Some(true)`. Pairs with the sibling
13376/// [`CILIUM_KEY_MODE`] (4289dfb) per-authn-block mode-discriminator leaf-axis
13377/// key the value nests directly under, and the sibling
13378/// [`CILIUM_AUTH_MODE_DISABLED`] scalar-value the `Some(false)` opt-out arm of
13379/// the same tristate emits — the Cilium CNP `MutualAuthenticationMode` OpenAPI
13380/// schema enum admits the closed set `{"required", "disabled", "test-always-
13381/// fail"}` verbatim (the `test-always-fail` arm is an infrastructure-side
13382/// debugging surface, not an author-reachable slot), so drift on the mTLS-
13383/// mandatory scalar-value is exactly as load-bearing as drift on the sibling
13384/// per-authn-block mode-discriminator leaf axis it nests under (a `"Required"`
13385/// / `"REQUIRED"` / `"mandatory"` / `"mtls-required"` typo at either the
13386/// production-code call site or a downstream probe lands outside the Cilium
13387/// CNP `MutualAuthenticationMode` OpenAPI schema enum's admitted set,
13388/// surfacing apply-side as a Cilium-agent per-rule mutual-auth-block schema-
13389/// validator drop far from the source `caixa.lisp` / the renderer's
13390/// `single_field_overlay(mtls_required, CILIUM_KEY_MODE, …)` call site — the
13391/// rendered per-`(:de, :para)` `CiliumNetworkPolicy` object never enforces
13392/// per-edge SPIFFE-identity-bound mutual-auth at the Cilium data-plane's per-
13393/// rule handshake gate and every intra-mesh `:contratos` flow the CNP was
13394/// authored to protect with per-edge mTLS silently bypasses the handshake at
13395/// the Cilium data-plane's default-authentication mode with no field naming
13396/// the mTLS-mandatory-scalar-value-drift root cause).
13397///
13398/// The single source of truth the rendered Aplicacao Cilium-CNP-side per-edge
13399/// mutual-auth-mode-discriminator affirmative-value-naming reaches for:
13400///
13401/// - the rendered `CiliumNetworkPolicy` document's per-rule
13402/// `spec.ingress[].authentication.mode` leaf value (caixa-mesh/src/lib.rs
13403/// — the `cilium_network_policies` per-`(:de, :para)`
13404/// `single_field_overlay(spec.politicas.mtls_required, CILIUM_KEY_MODE,
13405/// |required| …)` closure's `if required { … }` arm the prior inline
13406/// `"required".into()` literal sat at, plus every test-fixture navigation
13407/// that pins the emitted value under the `:mtls-required t` presence,
13408/// fan-out, and pubsub-carry-overlay-too shapes).
13409///
13410/// The mTLS-mandatory scalar-value names the same Cilium-agent-side per-rule
13411/// SPIFFE-identity-handshake-mandatory enforcement policy as the sibling
13412/// [`CILIUM_KEY_MODE`] leaf-axis key carries the value under, and must move
13413/// together with the sibling Cilium CNP `MutualAuthenticationMode` OpenAPI
13414/// schema enum on any future Cilium CRD schema rebrand (an upstream
13415/// `cilium.io/v3` rename of the mTLS-mandatory scalar-value from `required`
13416/// to `enforce` / `mandatory` / `strict`, coordinated with the Cilium
13417/// project's periodic CRD schema-migration passes, would land at this one
13418/// const rather than scattered across every per-emitter per-rule authn-block-
13419/// insertion site).
13420///
13421/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5, "every
13422/// recurring shape becomes a generator before it becomes a pattern; every
13423/// pattern becomes a library before it becomes duplicated code. The
13424/// duplication budget is zero.") promotes the constant to a typed substrate-
13425/// side `&'static str` on the same trajectory the
13426/// [`GATEWAY_API_PROTOCOL_HTTP`] (1b57473) /
13427/// [`GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX`] (530705d) /
13428/// [`KUBE_PROTOCOL_TCP`] (2123047) scalar-value lifts established on the
13429/// sibling canonical-cluster-side-OpenAPI-schema-enum-value surfaces —
13430/// extends the canonical-cluster-side-OpenAPI-schema-enum-value single-
13431/// sourcing discipline the Gateway-API v1 `ProtocolType.HTTP` /
13432/// `PathMatchType.PathPrefix` / K8s-core `Protocol.TCP` lifts established
13433/// onto the sibling Cilium-CNP-side `MutualAuthenticationMode.required`
13434/// per-rule mTLS-mandatory scalar-value the `cilium_network_policies` per-
13435/// edge SPIFFE-identity-bound mutual-auth emitter carries under the shared
13436/// `CiliumNetworkPolicy` body.
13437///
13438/// [cm]: ../../caixa_mesh/index.html
13439pub const CILIUM_AUTH_MODE_REQUIRED: &str = "required";
13440
13441/// Canonical Cilium `CiliumNetworkPolicy` `MutualAuthenticationMode` OpenAPI
13442/// schema enum's `disabled` per-`ingress[].authentication.mode` mTLS-skipped
13443/// scalar-value every `cilium_network_policies`-emitted CNP document declares
13444/// under its per-rule mutual-auth-mode-discriminator leaf axis when the typed
13445/// `:politicas :mtls-required` tristate is the explicit `Some(false)` opt-out
13446/// arm (an author who *named* the axis and asked for the mTLS handshake to be
13447/// skipped on this Aplicacao's edges — e.g. a debug or legacy-bridge
13448/// Aplicacao that needs to talk to non-mesh peers, distinct from the `None`
13449/// slot-absent arm the renderer maps to omit-the-block-entirely). Peer to
13450/// the sibling [`CILIUM_AUTH_MODE_REQUIRED`] mTLS-mandatory scalar-value the
13451/// `Some(true)` affirmative arm emits under the same tristate branch — the
13452/// Cilium CNP `MutualAuthenticationMode` OpenAPI schema enum admits the two
13453/// arms as a matched author-reachable pair.
13454///
13455/// The single source of truth the rendered Aplicacao Cilium-CNP-side per-edge
13456/// mutual-auth-mode-discriminator negative-value-naming reaches for:
13457///
13458/// - the rendered `CiliumNetworkPolicy` document's per-rule
13459/// `spec.ingress[].authentication.mode` leaf value (caixa-mesh/src/lib.rs
13460/// — the `cilium_network_policies` per-`(:de, :para)`
13461/// `single_field_overlay(spec.politicas.mtls_required, CILIUM_KEY_MODE,
13462/// |required| …)` closure's `else { … }` arm the prior inline
13463/// `"disabled".into()` literal sat at, plus the
13464/// `cnp_explicit_mtls_required_false_emits_disabled_mode` test-fixture
13465/// probe that pins the explicit-opt-out arm's rendered value).
13466///
13467/// Same drift-mode risk as the sibling [`CILIUM_AUTH_MODE_REQUIRED`] pin: a
13468/// `"Disabled"` / `"DISABLED"` / `"off"` / `"skip"` typo lands outside the
13469/// Cilium CNP `MutualAuthenticationMode` OpenAPI schema enum's admitted set;
13470/// the rendered per-`(:de, :para)` `CiliumNetworkPolicy` object never reaches
13471/// the Cilium agent's per-rule mutual-auth-block schema validator's admitted
13472/// set and the author's explicit-opt-out intent silently collapses onto the
13473/// cluster-default authentication mode (typically also "disabled" today, but
13474/// environment-divergent — take effect) with no field naming the mTLS-
13475/// skipped-scalar-value-drift root cause.
13476///
13477/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5, "every
13478/// recurring shape becomes a generator before it becomes a pattern; every
13479/// pattern becomes a library before it becomes duplicated code. The
13480/// duplication budget is zero.") promotes the constant to a typed substrate-
13481/// side `&'static str` on the same trajectory the sibling
13482/// [`CILIUM_AUTH_MODE_REQUIRED`] mTLS-mandatory scalar-value lift establishes
13483/// on the affirmative arm of the same `MutualAuthenticationMode` enum —
13484/// completes the per-authn-block `(mode → {required, disabled})` leaf-axis /
13485/// author-reachable-scalar-value-pair single-sourcing the M3 Aplicacao mesh
13486/// renderer's SPIFFE-identity-bound per-edge mTLS enforcement + explicit-
13487/// opt-out contract rests on across the two arms of the `:politicas
13488/// :mtls-required` tristate.
13489///
13490/// [cm]: ../../caixa_mesh/index.html
13491pub const CILIUM_AUTH_MODE_DISABLED: &str = "disabled";
13492
13493/// Canonical `bool → &'static str` bijection projection every consumer of the
13494/// Cilium `CiliumNetworkPolicy` `MutualAuthenticationMode` OpenAPI schema
13495/// enum's closed-set author-reachable scalar-value pair
13496/// ([`CILIUM_AUTH_MODE_REQUIRED`] / [`CILIUM_AUTH_MODE_DISABLED`]) consults
13497/// so the per-tristate-arm dispatch — `Some(true)` (mTLS handshake
13498/// mandatory) → [`CILIUM_AUTH_MODE_REQUIRED`], `Some(false)` (mTLS
13499/// handshake skipped, explicit opt-out) → [`CILIUM_AUTH_MODE_DISABLED`] —
13500/// lives in exactly one place. The two arms of the `:politicas
13501/// :mtls-required` tristate's non-`None` value-space each land on a
13502/// distinct `MutualAuthenticationMode` scalar; the `None` slot-absent arm
13503/// is the caller's [`single_field_overlay`] emission-gate concern (the
13504/// helper returns `None` and the outer `authentication:` block is omitted
13505/// entirely), not this projection's — see the per-emit-site
13506/// `if let Some(overlay) = mtls_overlay { rule.insert(CILIUM_KEY_AUTHENTICATION,
13507/// overlay.clone()) }` guard.
13508///
13509/// The single source of truth the rendered Aplicacao Cilium-CNP-side
13510/// per-edge mutual-auth-mode-discriminator scalar-value dispatch reaches
13511/// for:
13512///
13513/// - the rendered `CiliumNetworkPolicy` document's per-rule
13514/// `spec.ingress[].authentication.mode` leaf value (caixa-mesh/src/lib.rs
13515/// — the `cilium_network_policies` per-`(:de, :para)`
13516/// `single_field_overlay(spec.politicas.mtls_required, CILIUM_KEY_MODE,
13517/// |required| serde_yaml::Value::String(cilium_auth_mode(required).into()))`
13518/// closure body).
13519/// - the generic-helper pin in this crate's
13520/// `single_field_overlay_threads_typed_value_through_closure` test
13521/// that mirrors the production overlay's shape letter-for-letter and
13522/// now threads through the same shared projection.
13523///
13524/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5, "every
13525/// recurring shape becomes a generator before it becomes a pattern; every
13526/// pattern becomes a library before it becomes duplicated code. The
13527/// duplication budget is zero.") promotes the per-tristate-arm dispatch
13528/// body onto a shared projection on the same trajectory the sibling
13529/// [`CILIUM_AUTH_MODE_REQUIRED`] / [`CILIUM_AUTH_MODE_DISABLED`]
13530/// closed-set-scalar-value lifts established for the two arms of the
13531/// same `MutualAuthenticationMode` enum — closes the pair of related
13532/// lift trajectories the `(value-space, arm-dispatch)` per-authn-block
13533/// leaf's canonical decomposition rests on. The prior inline `if required
13534/// { CILIUM_AUTH_MODE_REQUIRED } else { CILIUM_AUTH_MODE_DISABLED }` body
13535/// split across the two occurrences — the caixa-mesh production emitter's
13536/// closure and the caixa-core generic-helper pin's closure — would have
13537/// let a per-arm reassignment (e.g. an upstream Cilium v3 schema rename
13538/// swap of the `required` ↔ `disabled` scalars, or the addition of a
13539/// third `MutualAuthenticationMode` variant that reshapes the closed set)
13540/// drift on one closure body but not the peer, silently letting a Cilium
13541/// data-plane pod either enforce mTLS where the author asked for skip or
13542/// skip it where the author asked for enforce.
13543///
13544/// Pairs with the [`CILIUM_KEY_MODE`] per-authentication-block mode-
13545/// discriminator leaf-axis key at the caller's
13546/// `single_field_overlay(spec.politicas.mtls_required, CILIUM_KEY_MODE,
13547/// |required| serde_yaml::Value::String(cilium_auth_mode(required).into()))`
13548/// call: the key is the field name the leaf mounts under, this projection
13549/// is the scalar the leaf carries. Same-shape peer to the K8s core
13550/// `Protocol` closed-set enum's future `bool → {"TCP", "UDP"}` /
13551/// K8s Gateway API v1 `PathMatchType` closed-set enum's future variant-
13552/// pick projections the M3.x absorption roadmap acknowledges — the M3
13553/// mesh renderer's `MutualAuthenticationMode` bijection surface is the
13554/// first landed instance of the canonical `(closed-set-CRD-schema-enum-
13555/// value pair, per-typed-arm dispatch projection)` compound.
13556///
13557/// [cm]: ../../caixa_mesh/index.html
13558#[must_use]
13559pub fn cilium_auth_mode(required: bool) -> &'static str {
13560 if required {
13561 CILIUM_AUTH_MODE_REQUIRED
13562 } else {
13563 CILIUM_AUTH_MODE_DISABLED
13564 }
13565}
13566
13567/// Canonical K8s Gateway API `HTTPRoute` parent-Gateway-binding container-
13568/// axis key every `gateway_routes`-emitted `HTTPRoute` document mounts its
13569/// per-route parent-Gateway `[{name}]` list under (`spec.parentRefs[]`).
13570/// Pairs with the sibling [`GATEWAY_API_KIND_HTTP_ROUTE`] (1adccc0) +
13571/// [`GATEWAY_API_KIND_GATEWAY`] (fb4639c) — the Gateway API v1 CRD schema
13572/// pins the per-HTTPRoute parent-Gateway identity through the
13573/// `spec.parentRefs[]` container axis (each entry names the parent
13574/// Gateway the route attaches to; the sibling `hostnames` + `rules`
13575/// container axes carry the per-route host-match + per-rule L7-dispatch
13576/// halves under the same `spec` block), so drift on the parent-Gateway-
13577/// binding axis is exactly as load-bearing as drift on the per-HTTPRoute
13578/// `kind` discriminator axis it accompanies (the K8s apiserver-side
13579/// Gateway API CRD schema validator drops any `spec` block whose parent-
13580/// binding container axis carries an unrecognized key — a `"parentRef"`
13581/// / `"parents"` / `"parentGateways"` typo silently emits an `HTTPRoute`
13582/// whose parent-Gateway attachment the Gateway API implementation's
13583/// per-HTTPRoute reconcile loop no-ops entirely: the route lands
13584/// unattached to any Gateway, and every external `:entrada` flow the
13585/// `HTTPRoute` was authored to accept drops at the Gateway API
13586/// implementation's per-Gateway HTTP-listener fan-in with no field
13587/// naming the parent-Gateway-binding-axis-drift root cause).
13588///
13589/// The single source of truth the rendered Aplicacao Gateway-API-side
13590/// ingress bundle's per-HTTPRoute parent-Gateway-binding-axis-naming
13591/// reaches for:
13592///
13593/// - the rendered `HTTPRoute` document's `spec.parentRefs[]` axis
13594/// (caixa-mesh/src/lib.rs:1389 — the `gateway_routes` per-Aplicacao
13595/// `HTTPRoute`'s `r_spec.insert("parentRefs", …)` call).
13596///
13597/// The parent-Gateway-binding axis names the same Gateway-API-
13598/// implementation-side per-HTTPRoute route→Gateway attachment container
13599/// as the sibling [`GATEWAY_API_KIND_HTTP_ROUTE`] +
13600/// [`GATEWAY_API_KIND_GATEWAY`] CRD `kind` discriminators the pair
13601/// declares together, and must move together on any future Gateway API
13602/// rebrand (an upstream Gateway API v2 rename of the parent-binding
13603/// axis from `parentRefs` to `parents` / `parentGateways` /
13604/// `attachedTo`, coordinated with the upstream SIG-Network Gateway API
13605/// deprecation cycle). Until this lift landed the axis carried an
13606/// inline `parentRefs` literal at the one production-code occurrence in
13607/// caixa-mesh/src/lib.rs:1389 (the `gateway_routes`
13608/// `r_spec.insert("parentRefs", …)` call) — the single load-bearing
13609/// Gateway-API-CRD-`parentRefs`-axis-key occurrence, drift-prone by
13610/// construction. A drift on the production site to `"parentRef"` /
13611/// `"parents"` / `"parentGateways"` would have surfaced as a Gateway-
13612/// API-implementation-side schema validator drop at apply time (the
13613/// affected `HTTPRoute`'s parent-Gateway-binding axis the CRD schema
13614/// validator recognizes as unknown), with every external `:entrada`
13615/// flow the `HTTPRoute` was authored to accept dropping at the Gateway
13616/// API implementation's per-Gateway HTTP-listener fan-in with no field
13617/// naming the parent-Gateway-binding-drift root cause.
13618///
13619/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
13620/// "every recurring shape becomes a generator before it becomes a
13621/// pattern; every pattern becomes a library before it becomes
13622/// duplicated code. The duplication budget is zero.") promotes the
13623/// constant to a typed substrate-side `&'static str` on the same
13624/// trajectory the [`CILIUM_KEY_PORTS`] (1087693) /
13625/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
13626/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
13627/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
13628/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
13629/// [`KUBE_KEY_RULES`] (a205eb3) /
13630/// [`GATEWAY_API_KIND_HTTP_ROUTE`] (1adccc0) /
13631/// [`GATEWAY_API_KIND_GATEWAY`] (fb4639c) lifts established on the
13632/// sibling canonical-Cilium-CNP-body-axis /
13633/// canonical-Gateway-API-CRD-`kind`-discriminator surfaces — pivots the
13634/// per-CNP-body-axis lift discipline onto the sibling per-HTTPRoute-
13635/// body-axis surface, beginning the per-Gateway-API-HTTPRoute-body-axis
13636/// canonical-string-pin set (`parentRefs`, `hostnames`) the M3
13637/// Aplicacao mesh renderer's external `:entrada` ingress contract rests
13638/// on across the Gateway API HTTPRoute-side per-route body-shape. The
13639/// render-side consumer now threads the same `&'static str` through
13640/// its `r_spec.insert(…)` call so a future Gateway API rebrand on the
13641/// parent-Gateway-binding axis (or an upstream SIG-Network Gateway API
13642/// v2 rename to a per-CRD sibling name) lands in one place; every
13643/// future renderer that reaches for the canonical per-HTTPRoute parent-
13644/// Gateway-binding axis (the future M4
13645/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
13646/// `HTTPRoute` fan-out, a future per-edge `TCPRoute` / `TLSRoute` /
13647/// `GRPCRoute` renderer for non-HTTP `:entrada` edges whose per-route
13648/// parent-Gateway-binding nests under the same axis convention, a
13649/// future per-Aplicacao `ReferenceGrant` renderer whose cross-namespace
13650/// parent-Gateway attachment binds against this same axis) inherits the
13651/// same value by construction with no opportunity for per-renderer
13652/// drift.
13653///
13654/// Same "the typed constant lives in one place" discipline the
13655/// [`CILIUM_KEY_PORTS`] (1087693) /
13656/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
13657/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
13658/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
13659/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
13660/// [`KUBE_KEY_RULES`] (a205eb3) /
13661/// [`GATEWAY_API_KIND_HTTP_ROUTE`] (1adccc0) /
13662/// [`GATEWAY_API_KIND_GATEWAY`] (fb4639c) lifts apply on the peer
13663/// canonical-Gateway-API-HTTPRoute-body-axis surface.
13664///
13665/// [cm]: ../../caixa_mesh/index.html
13666pub const GATEWAY_API_KEY_PARENT_REFS: &str = "parentRefs";
13667
13668/// Canonical K8s Gateway API `HTTPRoute` per-`spec.parentRefs[]` entry
13669/// listener-selector sub-axis key every `gateway_routes`-emitted
13670/// `HTTPRoute` document mounts under each parent-Gateway attachment to
13671/// pin the route to one specific listener out of the parent Gateway's
13672/// `spec.listeners[]` list (`spec.parentRefs[].sectionName`). Pairs
13673/// with the sibling [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) — the
13674/// Gateway API v1 CRD schema pins per-HTTPRoute route→Gateway
13675/// attachment through the `spec.parentRefs[]` container axis and the
13676/// per-entry listener-selection sub-axis through `sectionName` beneath
13677/// each entry (each `SectionName`-typed scalar binds to a
13678/// `Gateway.spec.listeners[].name` byte-string). Omitting the
13679/// selector attaches the route to *every* listener on the parent
13680/// Gateway — the Gateway API v1 default fan-out that silently doubles
13681/// route emission once the substrate ships a second listener under
13682/// the HTTPS-by-default trajectory the peer
13683/// [`GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME`] (cd60fde) docstring
13684/// forecasts (`"http"` → `"http-v1"` alongside a sibling `"https"`
13685/// listener once cert-manager-issued per-`:entrada :host` certificates
13686/// land). Pinning the selector by construction binds each substrate-
13687/// emitted route to exactly one listener on the parent Gateway, so a
13688/// future multi-listener migration lands as one const-edit on the
13689/// paired [`GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME`] declaration
13690/// instead of a silent per-route dispatch flip.
13691///
13692/// The single source of truth the rendered Aplicacao Gateway-API-side
13693/// ingress bundle's per-HTTPRoute per-parentRef listener-selector-axis-
13694/// naming reaches for:
13695///
13696/// - the rendered `HTTPRoute` document's per-parentRef
13697/// `spec.parentRefs[].sectionName` axis (the `gateway_routes` per-
13698/// Aplicacao HTTPRoute's `parent_ref.insert(<KEY>, …)` call the
13699/// paired [`GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME`] `&'static str`
13700/// — the same byte-string the parent Gateway's sole
13701/// `listener.insert(GATEWAY_API_KEY_NAME, …)` call emits at
13702/// `spec.listeners[].name` — flows through, so a substrate-side
13703/// rebrand of the canonical listener-name identifier reaches both
13704/// the listener-name emitter and the sectionName selector by
13705/// construction).
13706///
13707/// The per-parentRef listener-selector sub-axis names the same
13708/// Gateway-API-implementation-side per-HTTPRoute route→listener
13709/// attachment sub-container as the sibling
13710/// [`GATEWAY_API_KEY_PARENT_REFS`] per-HTTPRoute parent-Gateway-binding
13711/// container axis it accompanies, and must move together on any future
13712/// Gateway API rebrand (an upstream SIG-Network Gateway API v2 rename
13713/// of the per-entry listener-selection sub-axis from `sectionName` to
13714/// `listenerName` / `listener` / `attachTo`, coordinated with the
13715/// Gateway API deprecation cycle). Until this lift landed the axis had
13716/// zero production-code call sites — the substrate emitted an
13717/// `HTTPRoute` whose `spec.parentRefs[]` entries omitted the selector
13718/// entirely, silently accepting the Gateway API v1 attach-to-every-
13719/// listener default fan-out. A future substrate-side second listener
13720/// under the same parent Gateway (the HTTPS-by-default trajectory the
13721/// [`GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME`] docstring forecasts)
13722/// would have silently doubled every route's emitted per-request
13723/// dispatch surface — every external `:entrada` request the route was
13724/// authored to accept on `:80` would have accepted a matching request
13725/// on `:443` too, with the second-listener leak surfacing only in per-
13726/// request access logs (never in `kubectl describe httproute` — the
13727/// implicit fan-out reads as intended per the Gateway API v1 spec).
13728///
13729/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
13730/// "every recurring shape becomes a generator before it becomes a
13731/// pattern; every pattern becomes a library before it becomes
13732/// duplicated code. The duplication budget is zero.") promotes the
13733/// constant to a typed substrate-side `&'static str` on the same
13734/// trajectory the [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
13735/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
13736/// [`GATEWAY_API_KEY_MATCHES`] (8f9ed08) /
13737/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
13738/// [`GATEWAY_API_KEY_HOSTNAMES`] (bd7ea31) lifts established on the
13739/// sibling canonical-Gateway-API-HTTPRoute-body-axis surface — extends
13740/// the per-Gateway-API-HTTPRoute-body-axis canonical-string-pin set
13741/// onto the per-parentRef listener-selector sub-axis the M3 Aplicacao
13742/// mesh renderer's external `:entrada` ingress contract now rests on.
13743/// The render-side consumer threads the same `&'static str` through
13744/// its `parent_ref.insert(…)` call so a future Gateway API rebrand on
13745/// the per-parentRef listener-selector sub-axis (or an upstream SIG-
13746/// Network Gateway API v2 rename to a per-CRD sibling name) lands in
13747/// one place; every future renderer that reaches for the canonical
13748/// per-HTTPRoute per-parentRef listener-selector sub-axis (the future
13749/// M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-
13750/// Aplicacao `HTTPRoute` fan-out, a future per-edge `TCPRoute` /
13751/// `TLSRoute` / `GRPCRoute` renderer whose per-parentRef listener-
13752/// selection nests under the same axis convention) inherits the same
13753/// value by construction with no opportunity for per-renderer drift.
13754///
13755/// Same "the typed constant lives in one place" discipline the
13756/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
13757/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
13758/// [`GATEWAY_API_KEY_MATCHES`] (8f9ed08) lifts apply on the peer
13759/// canonical-Gateway-API-HTTPRoute-body-axis surface.
13760///
13761/// [cm]: ../../caixa_mesh/index.html
13762pub const GATEWAY_API_KEY_SECTION_NAME: &str = "sectionName";
13763
13764/// Canonical K8s Gateway API `HTTPRoute` per-rule backend-destination
13765/// container-axis key every `gateway_routes`-emitted `HTTPRoute`
13766/// document mounts its per-rule `[{name, port}]` backend list under
13767/// (`spec.rules[].backendRefs[]`). Pairs with the sibling
13768/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) — the Gateway API v1 CRD
13769/// schema pins the per-HTTPRoute route→Gateway attachment through the
13770/// `spec.parentRefs[]` container axis and the per-rule route→Servico
13771/// backend fan-out through the `spec.rules[].backendRefs[]` axis
13772/// beneath each rule entry, so drift on the per-rule backend-destination
13773/// axis is exactly as load-bearing as drift on the per-HTTPRoute
13774/// parent-Gateway-binding axis it accompanies (the K8s apiserver-side
13775/// Gateway API CRD schema validator drops any per-rule block whose
13776/// backend-destination container axis carries an unrecognized key — a
13777/// `"backendRef"` / `"backends"` / `"forwardTo"` typo silently emits an
13778/// `HTTPRoute` whose per-rule backend fan-out the Gateway API
13779/// implementation's per-rule L7 dispatch loop no-ops entirely: no
13780/// backend is picked, and every external `:entrada` request the rule
13781/// was authored to route drops at the gateway-class-controller's
13782/// per-rule reconcile with no field naming the backend-destination-
13783/// axis-drift root cause).
13784///
13785/// The single source of truth the rendered Aplicacao Gateway-API-side
13786/// ingress bundle's per-HTTPRoute per-rule backend-destination-axis-
13787/// naming reaches for:
13788///
13789/// - the rendered `HTTPRoute` document's per-rule
13790/// `spec.rules[].backendRefs[]` axis (caixa-mesh/src/lib.rs:1414 —
13791/// the `gateway_routes` per-Aplicacao HTTPRoute's per-rule
13792/// `rule.insert("backendRefs", …)` call).
13793///
13794/// The per-rule backend-destination container axis names the same
13795/// Gateway-API-implementation-side per-rule route→Servico backend fan-
13796/// out container as the sibling [`GATEWAY_API_KEY_PARENT_REFS`] per-
13797/// HTTPRoute parent-Gateway-binding container axis it accompanies, and
13798/// must move together on any future Gateway API rebrand (an upstream
13799/// SIG-Network Gateway API v2 rename of the backend-destination axis
13800/// from `backendRefs` to `backends` / `forwardTo` / `to`, coordinated
13801/// with the Gateway API deprecation cycle). Until this lift landed the
13802/// axis carried an inline `backendRefs` literal at the one production-
13803/// code occurrence in caixa-mesh/src/lib.rs:1414 (the `gateway_routes`
13804/// per-rule `rule.insert("backendRefs", …)` call) plus a matching set
13805/// inside the in-file `httproute_routes_to_entrada_para` /
13806/// `httproute_rule_keys_pin_overlay_position` test-fixture navigations —
13807/// three occurrences of the same load-bearing Gateway-API-CRD-
13808/// `backendRefs`-axis-key convention, drift-prone by construction. A
13809/// drift on any one production or test-fixture site to `"backendRef"` /
13810/// `"backends"` / `"forwardTo"` would have surfaced as a Gateway API
13811/// implementation-side schema validator drop at apply time (the
13812/// affected per-rule backend-destination axis the CRD schema validator
13813/// recognizes as unknown), with every external `:entrada` request the
13814/// rule was authored to route dropping at the gateway-class-
13815/// controller's per-rule reconcile with no field naming the backend-
13816/// destination-drift root cause. A drift on the test-fixture side
13817/// silently masks the emission-side pin (`.get("backendRefs")` returns
13818/// `None` under both the drifted-key emitter and the drifted-key probe
13819/// — the downstream `.and_then(|b| b.as_sequence())` /
13820/// `.and_then(|s| s.first())` chain short-circuits vacuously because
13821/// the outer per-rule backend-destination lookup is itself `None`).
13822///
13823/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
13824/// "every recurring shape becomes a generator before it becomes a
13825/// pattern; every pattern becomes a library before it becomes
13826/// duplicated code. The duplication budget is zero.") promotes the
13827/// constant to a typed substrate-side `&'static str` on the same
13828/// trajectory the [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
13829/// [`CILIUM_KEY_PORTS`] (1087693) /
13830/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
13831/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
13832/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
13833/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
13834/// [`KUBE_KEY_RULES`] (a205eb3) lifts established on the sibling
13835/// canonical-Gateway-API-HTTPRoute-body-axis /
13836/// canonical-Cilium-CNP-body-axis surfaces — extends the per-Gateway-
13837/// API-HTTPRoute-body-axis canonical-string-pin set the sibling
13838/// `parentRefs` lift began (`parentRefs`, `backendRefs`, future
13839/// `hostnames`) the M3 Aplicacao mesh renderer's external `:entrada`
13840/// ingress contract rests on across the Gateway API HTTPRoute-side per-
13841/// route body-shape. The render-side consumer now threads the same
13842/// `&'static str` through its `rule.insert(…)` call so a future Gateway
13843/// API rebrand on the per-rule backend-destination axis (or an upstream
13844/// SIG-Network Gateway API v2 rename to a per-CRD sibling name) lands
13845/// in one place; every future renderer that reaches for the canonical
13846/// per-HTTPRoute per-rule backend-destination axis (the future M4
13847/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
13848/// `HTTPRoute` fan-out, a future per-edge `TCPRoute` / `TLSRoute` /
13849/// `GRPCRoute` renderer for non-HTTP `:entrada` edges whose per-rule
13850/// backend-destination nests under the same axis convention, a future
13851/// per-route mirroring / traffic-split renderer whose per-weight
13852/// backend list binds against this same axis) inherits the same value
13853/// by construction with no opportunity for per-renderer drift.
13854///
13855/// Same "the typed constant lives in one place" discipline the
13856/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
13857/// [`CILIUM_KEY_PORTS`] (1087693) /
13858/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
13859/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
13860/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
13861/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
13862/// [`KUBE_KEY_RULES`] (a205eb3) lifts apply on the peer canonical-
13863/// Gateway-API-HTTPRoute-body-axis surface.
13864///
13865/// [cm]: ../../caixa_mesh/index.html
13866pub const GATEWAY_API_KEY_BACKEND_REFS: &str = "backendRefs";
13867
13868/// Canonical K8s Gateway API `HTTPRoute` per-rule route-match
13869/// container-axis key every `gateway_routes`-emitted `HTTPRoute`
13870/// per-rule block mounts its per-rule `[{path: {type, value}}]`
13871/// route-match fan-out list under (`spec.rules[].matches[]`). Pairs
13872/// with the sibling [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) — the
13873/// Gateway API v1 CRD schema pins per-rule request-selection through
13874/// the `spec.rules[].matches[]` container axis (each entry names one
13875/// `HTTPRouteMatch` predicate the request line + headers + query must
13876/// satisfy for the rule's backend fan-out to apply) alongside the
13877/// per-rule route→Servico backend fan-out under
13878/// `spec.rules[].backendRefs[]`, so drift on the per-rule route-match
13879/// axis is exactly as load-bearing as drift on the sibling per-rule
13880/// backend-destination axis it accompanies (the K8s apiserver-side
13881/// Gateway API CRD schema validator drops any per-rule block whose
13882/// route-match container axis carries an unrecognized key — a
13883/// `"match"` / `"routeMatches"` / `"predicates"` typo silently emits
13884/// an `HTTPRoute` whose per-rule request-selection axis the Gateway
13885/// API implementation's per-rule L7 dispatch loop no-ops entirely: no
13886/// request predicate is evaluated, the rule matches every request
13887/// unconditionally at the wildcard predicate, and every external
13888/// `:entrada` path filter the rule was authored to enforce drops at
13889/// the gateway-class-controller's per-rule reconcile with no field
13890/// naming the route-match-axis-drift root cause).
13891///
13892/// The single source of truth the rendered Aplicacao Gateway-API-side
13893/// ingress bundle's per-HTTPRoute per-rule route-match-axis-naming
13894/// reaches for:
13895///
13896/// - the rendered `HTTPRoute` document's per-rule
13897/// `spec.rules[].matches[]` axis (caixa-mesh/src/lib.rs — the
13898/// `gateway_routes` per-Aplicacao HTTPRoute's per-rule
13899/// `rule.insert("matches", …)` call seeded from the Aplicacao's
13900/// `:entrada :paths` slot).
13901///
13902/// The per-rule route-match container axis names the same Gateway-
13903/// API-implementation-side per-rule request-selection predicate fan-
13904/// out container as the sibling [`GATEWAY_API_KEY_BACKEND_REFS`]
13905/// per-rule backend-destination container axis it accompanies, and
13906/// must move together on any future Gateway API rebrand (an upstream
13907/// SIG-Network Gateway API v2 rename of the route-match axis from
13908/// `matches` to `match` / `routeMatches` / `predicates`, coordinated
13909/// with the Gateway API deprecation cycle). Until this lift landed
13910/// the axis carried an inline `matches` literal at the one
13911/// production-code occurrence in caixa-mesh/src/lib.rs (the
13912/// `gateway_routes` per-rule `rule.insert("matches", …)` call) plus
13913/// a matching test-fixture navigation inside the in-file
13914/// `httproute_rule_keys_pin_overlay_position` pin's
13915/// `contains_key("matches")` presence assertion — two occurrences of
13916/// the same load-bearing Gateway-API-CRD-`matches`-axis-key
13917/// convention, drift-prone by construction. A drift on the
13918/// production site to `"match"` / `"routeMatches"` / `"predicates"`
13919/// would have surfaced as a Gateway API implementation-side schema
13920/// validator drop at apply time (the affected per-rule route-match
13921/// axis the CRD schema validator recognizes as unknown), with the
13922/// per-rule request predicate degrading to the wildcard match at the
13923/// gateway-class-controller's per-rule reconcile with no field
13924/// naming the route-match-drift root cause. A drift on the test-
13925/// fixture side silently masks the emission-side pin
13926/// (`contains_key("matches")` returns `false` under both the
13927/// drifted-key emitter and the drifted-key probe).
13928///
13929/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
13930/// "every recurring shape becomes a generator before it becomes a
13931/// pattern; every pattern becomes a library before it becomes
13932/// duplicated code. The duplication budget is zero.") promotes the
13933/// constant to a typed substrate-side `&'static str` on the same
13934/// trajectory the [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
13935/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
13936/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
13937/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
13938/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
13939/// [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) /
13940/// [`GATEWAY_API_KEY_RETRY`] (231bbf5) lifts established on the
13941/// sibling canonical-Gateway-API-HTTPRoute-body-axis surface —
13942/// completes the per-rule top-level-axis lifted-string set
13943/// (`matches`, `backendRefs`, `timeouts`, `retry`) the
13944/// `httproute_rule_keys_pin_overlay_position` pin binds against, so
13945/// every one of the four per-rule top-level axes now threads a
13946/// lifted `&'static str` apiece. The render-side consumer now
13947/// threads the same `&'static str` through its `rule.insert(…)`
13948/// call so a future Gateway API rebrand on the per-rule route-match
13949/// axis (or an upstream SIG-Network Gateway API v2 rename to a
13950/// per-CRD sibling name) lands in one place; every future renderer
13951/// that reaches for the canonical per-HTTPRoute per-rule route-match
13952/// axis (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
13953/// materializer's per-Aplicacao `HTTPRoute` fan-out, a future
13954/// per-edge `GRPCRoute` renderer whose per-rule request-match
13955/// predicate nests under the same axis convention, a future
13956/// per-route header-match / query-match renderer whose per-predicate
13957/// list binds against this same axis) inherits the same value by
13958/// construction with no opportunity for per-renderer drift.
13959///
13960/// Same "the typed constant lives in one place" discipline the
13961/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
13962/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
13963/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
13964/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
13965/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
13966/// [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) /
13967/// [`GATEWAY_API_KEY_RETRY`] (231bbf5) lifts apply on the peer
13968/// canonical-Gateway-API-HTTPRoute-per-rule-body-axis surface.
13969///
13970/// [cm]: ../../caixa_mesh/index.html
13971pub const GATEWAY_API_KEY_MATCHES: &str = "matches";
13972
13973/// Canonical K8s Gateway API `Gateway` per-listener-set container-axis
13974/// key every `gateway_routes`-emitted `Gateway` document mounts its
13975/// per-Gateway `[{name, port, protocol, hostname}]` L7-listener fan-out
13976/// list under (`spec.listeners[]`). Pairs with the sibling
13977/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) +
13978/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) — the Gateway API v1 CRD
13979/// schema pins the per-Gateway L7-listener fan-out through the
13980/// `spec.listeners[]` container axis (each entry names one listener the
13981/// Gateway accepts external traffic on; the sibling
13982/// `spec.parentRefs[]` + `spec.rules[].backendRefs[]` container axes
13983/// carry the per-HTTPRoute parent-Gateway attachment + per-rule
13984/// backend-destination fan-out halves under the paired `HTTPRoute`
13985/// `spec` block), so drift on the per-Gateway L7-listener-set axis is
13986/// exactly as load-bearing as drift on the per-HTTPRoute parent-Gateway-
13987/// binding + per-rule backend-destination axes it accompanies (the K8s
13988/// apiserver-side Gateway API CRD schema validator drops any `spec`
13989/// block whose L7-listener-set container axis carries an unrecognized
13990/// key — a `"listener"` / `"listen"` / `"servers"` typo silently emits
13991/// a `Gateway` whose L7-listener fan-out the Gateway API
13992/// implementation's per-Gateway reconcile loop no-ops entirely: no
13993/// listener is opened, and every external `:entrada` flow the Gateway
13994/// was authored to accept drops at the gateway-class-controller's per-
13995/// Gateway HTTP-listener fan-in with no field naming the L7-listener-
13996/// set-axis-drift root cause).
13997///
13998/// The single source of truth the rendered Aplicacao Gateway-API-side
13999/// ingress bundle's per-Gateway L7-listener-set-axis-naming reaches
14000/// for:
14001///
14002/// - the rendered `Gateway` document's `spec.listeners[]` axis
14003/// (caixa-mesh/src/lib.rs — the `gateway_routes` per-Aplicacao
14004/// `Gateway`'s `g_spec.insert("listeners", …)` call).
14005///
14006/// The per-Gateway L7-listener-set container axis names the same
14007/// Gateway-API-implementation-side per-Gateway inbound-traffic-
14008/// acceptance-vector fan-out container as the sibling
14009/// [`GATEWAY_API_KEY_PARENT_REFS`] per-HTTPRoute parent-Gateway-binding
14010/// container axis + [`GATEWAY_API_KEY_BACKEND_REFS`] per-rule backend-
14011/// destination container axis it accompanies, and must move together
14012/// on any future Gateway API rebrand (an upstream SIG-Network Gateway
14013/// API v2 rename of the L7-listener-set axis from `listeners` to
14014/// `servers` / `endpoints` / `bindings`, coordinated with the Gateway
14015/// API deprecation cycle). Until this lift landed the axis carried an
14016/// inline `listeners` literal at the one production-code occurrence in
14017/// caixa-mesh/src/lib.rs (the `gateway_routes` per-Aplicacao Gateway's
14018/// `g_spec.insert("listeners", …)` call) plus a matching test-fixture
14019/// navigation inside the in-file `gateway_listener_carries_aplicacao_host`
14020/// pin's `.get("listeners")` traversal — two occurrences of the same
14021/// load-bearing Gateway-API-CRD-`listeners`-axis-key convention, drift-
14022/// prone by construction. A drift on the production site to
14023/// `"listener"` / `"listen"` / `"servers"` would have surfaced as a
14024/// Gateway API implementation-side schema validator drop at apply time
14025/// (the affected `Gateway`'s L7-listener-set axis the CRD schema
14026/// validator recognizes as unknown), with every external `:entrada`
14027/// flow the Gateway was authored to accept dropping at the gateway-
14028/// class-controller's per-Gateway reconcile with no field naming the
14029/// L7-listener-set-drift root cause. A drift on the test-fixture side
14030/// silently masks the emission-side pin (`.get("listeners")` returns
14031/// `None` under both the drifted-key emitter and the drifted-key probe
14032/// — the downstream `.and_then(|l| l.as_sequence())` /
14033/// `.and_then(|s| s.first())` chain short-circuits vacuously because
14034/// the outer per-Gateway L7-listener-set lookup is itself `None`).
14035///
14036/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
14037/// "every recurring shape becomes a generator before it becomes a
14038/// pattern; every pattern becomes a library before it becomes
14039/// duplicated code. The duplication budget is zero.") promotes the
14040/// constant to a typed substrate-side `&'static str` on the same
14041/// trajectory the [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
14042/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
14043/// [`CILIUM_KEY_PORTS`] (1087693) /
14044/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
14045/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
14046/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
14047/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
14048/// [`KUBE_KEY_RULES`] (a205eb3) lifts established on the sibling
14049/// canonical-Gateway-API-HTTPRoute-body-axis /
14050/// canonical-Cilium-CNP-body-axis surfaces — pivots the per-HTTPRoute-
14051/// body-axis lift discipline onto the sibling per-Gateway-body-axis
14052/// surface, extending the per-Gateway-API-CRD-body-axis canonical-
14053/// string-pin set (`parentRefs`, `backendRefs`, `listeners`, future
14054/// `hostnames`) the M3 Aplicacao mesh renderer's external `:entrada`
14055/// ingress contract rests on across the Gateway API CRD-side body-
14056/// shape. The render-side consumer now threads the same `&'static
14057/// str` through its `g_spec.insert(…)` call so a future Gateway API
14058/// rebrand on the L7-listener-set axis (or an upstream SIG-Network
14059/// Gateway API v2 rename to a per-CRD sibling name) lands in one
14060/// place; every future renderer that reaches for the canonical per-
14061/// Gateway L7-listener-set axis (the future M4
14062/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
14063/// `Gateway` fan-out, a future per-cluster `GatewayClass` /
14064/// `ReferenceGrant` renderer whose per-Gateway listener-set enumeration
14065/// binds against this same axis, a future per-listener TLS terminator
14066/// renderer whose per-listener `tls.mode: Terminate` overlay nests
14067/// under the same axis convention) inherits the same value by
14068/// construction with no opportunity for per-renderer drift.
14069///
14070/// Same "the typed constant lives in one place" discipline the
14071/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
14072/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
14073/// [`CILIUM_KEY_PORTS`] (1087693) /
14074/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
14075/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
14076/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
14077/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
14078/// [`KUBE_KEY_RULES`] (a205eb3) lifts apply on the peer canonical-
14079/// Gateway-API-Gateway-body-axis surface.
14080///
14081/// [cm]: ../../caixa_mesh/index.html
14082pub const GATEWAY_API_KEY_LISTENERS: &str = "listeners";
14083
14084/// Canonical K8s Gateway API `Gateway` per-listener DNS-host-discriminator
14085/// axis key every `gateway_routes`-emitted `Gateway` document mounts each
14086/// listener's virtual-host name under
14087/// (`spec.listeners[].hostname`). Pairs with the sibling
14088/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) — the Gateway API v1 CRD schema
14089/// pins the per-Gateway L7-listener-set fan-out through the
14090/// `spec.listeners[]` container axis (each entry names one listener the
14091/// Gateway accepts external traffic on) and pins each entry's per-listener
14092/// DNS-host discriminator under the nested `hostname` axis (Gateway API v1
14093/// `Listener.hostname` — `PreciseHostname` string, optional per-listener
14094/// virtual-host filter the Gateway-API-implementation-side per-Gateway
14095/// reconcile loop honors when routing external inbound traffic against
14096/// SNI at the TLS handshake / `Host:` header at the HTTP request line), so
14097/// drift on the per-listener DNS-host discriminator axis is exactly as
14098/// load-bearing as drift on the per-Gateway L7-listener-set container
14099/// axis it nests under (the K8s apiserver-side Gateway API CRD schema
14100/// validator drops any per-listener entry whose DNS-host discriminator
14101/// axis carries an unrecognized key — a `"host"` / `"vhost"` /
14102/// `"serverName"` typo silently emits a `Gateway` whose per-listener
14103/// virtual-host filter the Gateway API implementation's per-listener SNI /
14104/// `Host:` header dispatch loop no-ops entirely: the listener accepts
14105/// traffic on the wildcard host rather than the typed `:entrada :host`
14106/// the Aplicacao author declared, and every external `:entrada` flow the
14107/// listener was authored to accept lands on the wrong virtual-host filter
14108/// with no field naming the DNS-host-discriminator-axis-drift root
14109/// cause).
14110///
14111/// The single source of truth the rendered Aplicacao Gateway-API-side
14112/// ingress bundle's per-Gateway per-listener DNS-host-discriminator-axis-
14113/// naming reaches for:
14114///
14115/// - the rendered `Gateway` document's `spec.listeners[].hostname` axis
14116/// (caixa-mesh/src/lib.rs — the `gateway_routes` per-Aplicacao
14117/// `Gateway`'s per-listener `listener.insert("hostname", …)` call
14118/// seeded from the Aplicacao's `:entrada :host` slot).
14119///
14120/// The per-listener DNS-host discriminator axis names the same Gateway-
14121/// API-implementation-side per-listener virtual-host filter container as
14122/// the sibling [`GATEWAY_API_KEY_LISTENERS`] per-Gateway L7-listener-set
14123/// container axis it nests under, and must move together on any future
14124/// Gateway API rebrand (an upstream SIG-Network Gateway API v2 rename of
14125/// the per-listener DNS-host discriminator axis from `hostname` to `host`
14126/// / `vhost` / `serverName`, coordinated with the Gateway API deprecation
14127/// cycle). Until this lift landed the axis carried an inline `hostname`
14128/// literal at the one production-code occurrence in caixa-mesh/src/lib.rs
14129/// (the `gateway_routes` per-Aplicacao Gateway's per-listener
14130/// `listener.insert("hostname", …)` call) plus a matching test-fixture
14131/// navigation inside the in-file `gateway_listener_carries_aplicacao_host`
14132/// pin's `.get("hostname")` traversal — two occurrences of the same load-
14133/// bearing Gateway-API-CRD-`hostname`-axis-key convention, drift-prone by
14134/// construction. A drift on the production site to `"host"` / `"vhost"` /
14135/// `"serverName"` would have surfaced as a Gateway API implementation-
14136/// side schema validator drop at apply time (the affected listener's per-
14137/// listener DNS-host discriminator axis the CRD schema validator
14138/// recognizes as unknown), with every external `:entrada` flow landing on
14139/// the wildcard virtual-host filter rather than the typed `:entrada
14140/// :host` at the gateway-class-controller's per-listener dispatch with no
14141/// field naming the DNS-host-discriminator-drift root cause. A drift on
14142/// the test-fixture side silently masks the emission-side pin
14143/// (`.get("hostname")` returns `None` under both the drifted-key emitter
14144/// and the drifted-key probe — the downstream `.and_then(|h| h.as_str())`
14145/// chain short-circuits vacuously because the outer per-listener DNS-
14146/// host discriminator lookup is itself `None`).
14147///
14148/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
14149/// "every recurring shape becomes a generator before it becomes a
14150/// pattern; every pattern becomes a library before it becomes
14151/// duplicated code. The duplication budget is zero.") promotes the
14152/// constant to a typed substrate-side `&'static str` on the same
14153/// trajectory the [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
14154/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
14155/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
14156/// [`CILIUM_KEY_PORTS`] (1087693) /
14157/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
14158/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
14159/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
14160/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
14161/// [`KUBE_KEY_RULES`] (a205eb3) lifts established on the sibling
14162/// canonical-Gateway-API-CRD-body-axis /
14163/// canonical-Cilium-CNP-body-axis surfaces — nests the per-Gateway-API-
14164/// CRD-body-axis lift discipline one level deeper onto the sibling per-
14165/// listener body-axis surface, extending the per-Gateway-API-CRD-body-
14166/// axis canonical-string-pin set (`parentRefs`, `backendRefs`,
14167/// `listeners`, `hostname`, future `hostnames`) the M3 Aplicacao mesh
14168/// renderer's external `:entrada` ingress contract rests on across the
14169/// Gateway API CRD-side body-shape. The render-side consumer now threads
14170/// the same `&'static str` through its per-listener `listener.insert(…)`
14171/// call so a future Gateway API rebrand on the per-listener DNS-host
14172/// discriminator axis (or an upstream SIG-Network Gateway API v2 rename
14173/// to a per-CRD sibling name) lands in one place; every future renderer
14174/// that reaches for the canonical per-listener DNS-host discriminator
14175/// axis (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
14176/// materializer's per-Aplicacao `Gateway` fan-out, a future per-listener
14177/// TLS terminator renderer whose per-listener `tls.certificateRefs[]`
14178/// resolution keys off the same per-listener virtual-host filter, a
14179/// future per-cluster wildcard-host `Gateway` renderer whose per-listener
14180/// SNI wildcard `*.example.com` matcher binds against this same axis)
14181/// inherits the same value by construction with no opportunity for per-
14182/// renderer drift.
14183///
14184/// Same "the typed constant lives in one place" discipline the
14185/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
14186/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
14187/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
14188/// [`CILIUM_KEY_PORTS`] (1087693) /
14189/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
14190/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
14191/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
14192/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
14193/// [`KUBE_KEY_RULES`] (a205eb3) lifts apply on the peer canonical-
14194/// Gateway-API-Gateway-per-listener-body-axis surface.
14195///
14196/// [cm]: ../../caixa_mesh/index.html
14197pub const GATEWAY_API_KEY_HOSTNAME: &str = "hostname";
14198
14199/// Canonical K8s Gateway API `HTTPRoute` spec-level DNS-host-filter axis key
14200/// every `gateway_routes`-emitted `HTTPRoute` document mounts the route's
14201/// per-route virtual-host filter list under (`spec.hostnames[]`). The
14202/// plural sibling of [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) — same
14203/// Gateway-API-CRD DNS-host-discriminator convention nested one level up on
14204/// the sibling `HTTPRoute` per-route body-axis surface, distinct spelling
14205/// (`hostnames` — plural — is the `HTTPRoute` spec-level filter list; the
14206/// singular `hostname` axis it pairs against is the per-`Gateway`-listener
14207/// virtual-host discriminator).
14208///
14209/// The Gateway API v1 CRD schema pins the per-`HTTPRoute` DNS-host filter
14210/// through the spec-level `hostnames[]` container axis (a list of DNS
14211/// `PreciseHostname` strings, each one an additional virtual-host filter
14212/// the Gateway-API-implementation-side per-route reconcile loop honors
14213/// when routing external inbound traffic against SNI at the TLS handshake
14214/// / `Host:` header at the HTTP request line and against the sibling
14215/// [`GATEWAY_API_KEY_PARENT_REFS`]-declared parent Gateway's per-listener
14216/// [`GATEWAY_API_KEY_HOSTNAME`] filter set). Drift on the per-route DNS-
14217/// host filter axis is exactly as load-bearing as drift on the sibling
14218/// per-listener DNS-host discriminator axis (`hostname`): the K8s
14219/// apiserver-side Gateway API CRD schema validator drops any per-route
14220/// entry whose DNS-host-filter axis carries an unrecognized key — a
14221/// `"hosts"` / `"vhosts"` / `"serverNames"` typo silently emits an
14222/// `HTTPRoute` whose per-route virtual-host filter list the Gateway API
14223/// implementation's per-route SNI / `Host:` header dispatch loop no-ops
14224/// entirely: the route accepts traffic on every host the parent Gateway's
14225/// listener accepts rather than the typed `:entrada :host` the Aplicacao
14226/// author declared, and every external `:entrada` flow the route was
14227/// authored to accept lands on the wildcard virtual-host filter with no
14228/// field naming the DNS-host-filter-axis-drift root cause.
14229///
14230/// The single source of truth the rendered Aplicacao Gateway-API-side
14231/// ingress bundle's per-`HTTPRoute` spec-level DNS-host-filter-axis-naming
14232/// reaches for:
14233///
14234/// - the rendered `HTTPRoute` document's `spec.hostnames[]` axis
14235/// (caixa-mesh/src/lib.rs — the `gateway_routes` per-Aplicacao
14236/// `HTTPRoute`'s spec-level `r_spec.insert("hostnames", …)` call
14237/// seeded from the Aplicacao's `:entrada :host` slot as a
14238/// single-element sequence).
14239///
14240/// The per-route DNS-host filter axis names the same Gateway-API-
14241/// implementation-side per-route virtual-host filter list container as the
14242/// sibling [`GATEWAY_API_KEY_PARENT_REFS`] per-route parent-Gateway-
14243/// binding container axis it sits beside under `spec.*`, and must move
14244/// together on any future Gateway API rebrand (an upstream SIG-Network
14245/// Gateway API v2 rename of the per-route DNS-host filter axis from
14246/// `hostnames` to `hosts` / `vhosts` / `serverNames`, coordinated with
14247/// the Gateway API deprecation cycle). Until this lift landed the axis
14248/// carried an inline `hostnames` literal at the one production-code
14249/// occurrence in caixa-mesh/src/lib.rs (the `gateway_routes` per-
14250/// Aplicacao `HTTPRoute`'s spec-level `r_spec.insert("hostnames", …)`
14251/// call) — one occurrence today, but the sibling per-Gateway-API-CRD-
14252/// body-axis lifts ([`GATEWAY_API_KEY_LISTENERS`] / [`GATEWAY_API_KEY_HOSTNAME`]
14253/// / [`GATEWAY_API_KEY_PARENT_REFS`] / [`GATEWAY_API_KEY_BACKEND_REFS`])
14254/// each closed on the same one-production-emitter-plus-future-test-
14255/// fixture shape before a future per-route DNS-host-filter navigator
14256/// picked up the second occurrence, and the same lift-before-the-second-
14257/// site discipline applies here.
14258///
14259/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
14260/// "every recurring shape becomes a generator before it becomes a
14261/// pattern; every pattern becomes a library before it becomes
14262/// duplicated code. The duplication budget is zero.") promotes the
14263/// constant to a typed substrate-side `&'static str` on the same
14264/// trajectory the [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
14265/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
14266/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
14267/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
14268/// [`CILIUM_KEY_PORTS`] (1087693) /
14269/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
14270/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
14271/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
14272/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
14273/// [`KUBE_KEY_RULES`] (a205eb3) lifts established on the sibling
14274/// canonical-Gateway-API-CRD-body-axis /
14275/// canonical-Cilium-CNP-body-axis surfaces — closes the per-Gateway-API-
14276/// CRD `HTTPRoute` per-route body-axis lift pair across the singular /
14277/// plural DNS-host discriminator surface (`hostname` at the parent-
14278/// Gateway per-listener discriminator + `hostnames` at the child
14279/// HTTPRoute per-route filter list), so both halves of the DNS-host
14280/// discriminator convention across the `(Gateway, HTTPRoute)` pair the
14281/// M3 Aplicacao mesh renderer's external `:entrada` ingress contract
14282/// emits together now live as one lifted `&'static str` apiece. The
14283/// render-side consumer now threads the same `&'static str` through its
14284/// spec-level `r_spec.insert(…)` call so a future Gateway API rebrand on
14285/// the per-route DNS-host filter axis (or an upstream SIG-Network
14286/// Gateway API v2 rename to a per-CRD sibling name) lands in one place;
14287/// every future renderer that reaches for the canonical per-route DNS-
14288/// host filter axis (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
14289/// materializer's per-Aplicacao `HTTPRoute` fan-out, a future per-route
14290/// wildcard-host `*.example.com` filter emitter, a future per-Aplicacao
14291/// multi-`:entrada` `HTTPRoute` fan-out whose per-route DNS-host filter
14292/// lists partition inbound traffic across the same parent Gateway's
14293/// per-listener discriminator) inherits the same value by construction
14294/// with no opportunity for per-renderer drift.
14295///
14296/// Same "the typed constant lives in one place" discipline the
14297/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
14298/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
14299/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
14300/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
14301/// [`CILIUM_KEY_PORTS`] (1087693) /
14302/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
14303/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
14304/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
14305/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
14306/// [`KUBE_KEY_RULES`] (a205eb3) lifts apply on the peer canonical-
14307/// Gateway-API-HTTPRoute-per-route-body-axis surface.
14308///
14309/// [cm]: ../../caixa_mesh/index.html
14310pub const GATEWAY_API_KEY_HOSTNAMES: &str = "hostnames";
14311
14312/// Canonical K8s Gateway API `HTTPRoute` per-rule request-timeout-policy
14313/// body-axis key every `gateway_routes`-emitted `HTTPRoute` document mounts
14314/// its per-rule `:politicas :timeout` overlay under
14315/// (`spec.rules[].timeouts`). Sibling per-rule-body-axis peer to
14316/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) and
14317/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) — same Gateway-API-CRD-body-axis
14318/// discipline nested one level deeper onto the per-rule request-deadline
14319/// slot the Gateway API v1 CRD schema pins under `HTTPRoute.spec.rules[]`.
14320///
14321/// The Gateway API v1 CRD schema pins the per-rule request-timeout policy
14322/// through the `HTTPRouteTimeouts` sub-shape mounted at
14323/// `spec.rules[].timeouts`, whose `request` / `backendRequest` scalars
14324/// carry the per-rule deadline the Gateway-API-implementation-side per-
14325/// rule request-dispatch loop compares each accepted request's
14326/// wall-clock elapsed time against before cancelling the in-flight
14327/// backend call. Drift on the per-rule timeout-policy body-axis is
14328/// exactly as load-bearing as drift on the sibling per-rule backend-
14329/// destination axis (`backendRefs`): the K8s apiserver-side Gateway API
14330/// CRD schema validator drops any per-rule entry whose per-rule
14331/// timeout-policy axis carries an unrecognized key — a
14332/// `"timeout"` (singular) / `"timeoutPolicy"` / `"deadlines"` typo
14333/// silently emits an `HTTPRoute` whose per-rule timeout-policy the
14334/// Gateway API implementation's per-rule request-dispatch loop no-ops
14335/// entirely: the route accepts every inbound request with no per-rule
14336/// wall-clock deadline (the "no infinite blocking" guarantee
14337/// MESH-COMPOSITION.md §V mandates for every rendered per-`:politicas`
14338/// mesh-composition edge silently regresses to the pre-overlay
14339/// unbounded-request semantic, and every external `:entrada` flow the
14340/// route was authored to bound by the typed `:politicas :timeout` slot
14341/// runs to whatever backend deadline the resolved `ComputeUnit` /
14342/// `Service` / `ExternalName` backend's downstream infrastructure
14343/// (Envoy default listener idle timeout, node-local conntrack window,
14344/// TCP keepalive) picks — with no field naming the per-rule-timeout-
14345/// policy-axis-drift root cause).
14346///
14347/// The single source of truth the rendered Aplicacao Gateway-API-side
14348/// ingress bundle's per-`HTTPRoute` per-rule request-timeout-policy-
14349/// axis-naming reaches for:
14350///
14351/// - the rendered `HTTPRoute` document's per-rule `timeouts:` axis
14352/// (caixa-mesh/src/lib.rs — the `gateway_routes` per-Aplicacao
14353/// `HTTPRoute`'s per-rule `rule.insert("timeouts", …)` call
14354/// seeded from the Aplicacao's `:politicas :timeout` overlay
14355/// when the slot is set, elided from the emit sequence when the
14356/// slot is unset).
14357///
14358/// The per-rule request-timeout-policy axis names the same Gateway-
14359/// API-implementation-side per-rule request-dispatch deadline
14360/// container as the sibling [`GATEWAY_API_KEY_BACKEND_REFS`] per-rule
14361/// backend-destination container axis it sits beside under
14362/// `spec.rules[].*`, and must move together on any future Gateway API
14363/// rebrand (an upstream SIG-Network Gateway API v2 rename of the per-
14364/// rule timeout-policy axis from `timeouts` to `timeout` /
14365/// `timeoutPolicy` / `deadlines`, coordinated with the Gateway API
14366/// deprecation cycle). Until this lift landed the axis carried an
14367/// inline `timeouts` literal at nine physical sites in
14368/// caixa-mesh/src/lib.rs (one production emitter at the
14369/// `gateway_routes` per-rule `rule.insert(…)` call plus eight test-
14370/// side navigators pinning the overlay's presence, absence,
14371/// canonical-duration-format contract, per-rule fan-out under
14372/// multi-`:entrada :paths`, and independent-axis coexistence with the
14373/// sibling `retry` per-rule retry-policy axis), the highest per-axis
14374/// occurrence count of any un-lifted Gateway-API-CRD-body-axis in the
14375/// crate.
14376///
14377/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
14378/// "every recurring shape becomes a generator before it becomes a
14379/// pattern; every pattern becomes a library before it becomes
14380/// duplicated code. The duplication budget is zero.") promotes the
14381/// constant to a typed substrate-side `&'static str` on the same
14382/// trajectory the [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
14383/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
14384/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
14385/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
14386/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
14387/// [`CILIUM_KEY_PORTS`] (1087693) /
14388/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
14389/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
14390/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
14391/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
14392/// [`KUBE_KEY_RULES`] (a205eb3) lifts established on the sibling
14393/// canonical-Gateway-API-CRD-body-axis /
14394/// canonical-Cilium-CNP-body-axis surfaces — extends the per-Gateway-
14395/// API-`HTTPRoute` per-rule body-axis lift set onto the load-bearing
14396/// per-rule request-timeout-policy axis every downstream Gateway-API-
14397/// implementation-side per-rule request-dispatch loop keys off before
14398/// it can commit to a per-request wall-clock deadline. The render-
14399/// side consumer now threads the same `&'static str` through its
14400/// per-rule `rule.insert(…)` call and every test-side navigator's
14401/// `.get(…)` retrieval so a future Gateway API rebrand on the per-
14402/// rule timeout-policy axis (or an upstream SIG-Network Gateway API
14403/// v2 rename to a per-CRD sibling name) lands in one place; every
14404/// future renderer that reaches for the canonical per-rule timeout-
14405/// policy axis (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
14406/// materializer's per-Aplicacao per-rule timeout-policy fan-out, a
14407/// future per-edge `backendRequest` sub-timeout emitter honoring the
14408/// downstream `:politicas :backend-timeout` slot the M4 roadmap
14409/// acknowledges, a future per-cluster per-rule idle-timeout emitter
14410/// binding against this same axis) inherits the same value by
14411/// construction with no opportunity for per-renderer drift.
14412///
14413/// Same "the typed constant lives in one place" discipline the
14414/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
14415/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
14416/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
14417/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
14418/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
14419/// [`CILIUM_KEY_PORTS`] (1087693) /
14420/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
14421/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
14422/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
14423/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
14424/// [`KUBE_KEY_RULES`] (a205eb3) lifts apply on the peer canonical-
14425/// Gateway-API-HTTPRoute-per-rule-body-axis surface.
14426///
14427/// [cm]: ../../caixa_mesh/index.html
14428pub const GATEWAY_API_KEY_TIMEOUTS: &str = "timeouts";
14429
14430/// Canonical K8s Gateway API `HTTPRoute` per-rule retry-policy body-axis
14431/// key every `gateway_routes`-emitted `HTTPRoute` document mounts its
14432/// per-rule `:politicas :retries` overlay under (`spec.rules[].retry`).
14433/// Sibling per-rule-body-axis peer to [`GATEWAY_API_KEY_TIMEOUTS`]
14434/// (db31108) — same Gateway-API-CRD-body-axis discipline nested onto the
14435/// per-rule retry-budget slot the Gateway API v1 CRD schema pins under
14436/// `HTTPRoute.spec.rules[]` beside the sibling per-rule request-timeout-
14437/// policy container.
14438///
14439/// The Gateway API v1 CRD schema pins the per-rule retry policy through
14440/// the `HTTPRouteRetry` sub-shape mounted at `spec.rules[].retry`, whose
14441/// `attempts` scalar (peer to future `codes` retryable-status-code list
14442/// and `backoff` inter-attempt backoff-window scalars) carries the per-
14443/// rule retry-budget the Gateway-API-implementation-side per-rule
14444/// request-dispatch loop compares each failed attempt count against
14445/// before giving up on the in-flight backend call. Drift on the per-rule
14446/// retry-policy body-axis is exactly as load-bearing as drift on the
14447/// sibling per-rule request-timeout-policy axis (`timeouts`): the K8s
14448/// apiserver-side Gateway API CRD schema validator drops any per-rule
14449/// entry whose per-rule retry-policy axis carries an unrecognized key —
14450/// a `"retries"` (plural) / `"retryPolicy"` / `"budget"` typo silently
14451/// emits an `HTTPRoute` whose per-rule retry-budget the Gateway API
14452/// implementation's per-rule request-dispatch loop no-ops entirely: the
14453/// route accepts every inbound request with no per-rule retry budget
14454/// (the "no infinite retrying without bound" guarantee
14455/// MESH-COMPOSITION.md §V mandates for every rendered per-`:politicas`
14456/// mesh-composition edge silently regresses to the pre-overlay
14457/// unbounded-retry semantic, and every external `:entrada` flow the
14458/// route was authored to cap by the typed `:politicas :retries` slot
14459/// runs to whatever retry policy the resolved `ComputeUnit` /
14460/// `Service` / `ExternalName` backend's downstream infrastructure —
14461/// Envoy default retry policy, client SDK autoretry, node-local
14462/// conntrack retries — with no field naming the per-rule-retry-policy-
14463/// axis-drift root cause).
14464///
14465/// The single source of truth the rendered Aplicacao Gateway-API-side
14466/// ingress bundle's per-`HTTPRoute` per-rule retry-policy-axis-naming
14467/// reaches for:
14468///
14469/// - the rendered `HTTPRoute` document's per-rule `retry:` axis
14470/// (caixa-mesh/src/lib.rs — the `gateway_routes` per-Aplicacao
14471/// `HTTPRoute`'s per-rule `rule.insert("retry", …)` call seeded
14472/// from the Aplicacao's `:politicas :retries` overlay when the
14473/// slot is set, elided from the emit sequence when the slot is
14474/// unset).
14475///
14476/// The per-rule retry-policy axis names the same Gateway-API-
14477/// implementation-side per-rule request-dispatch retry-budget container
14478/// as the sibling [`GATEWAY_API_KEY_TIMEOUTS`] per-rule request-timeout-
14479/// policy container axis it sits beside under `spec.rules[].*`, and must
14480/// move together on any future Gateway API rebrand (an upstream
14481/// SIG-Network Gateway API v2 rename of the per-rule retry-policy axis
14482/// from `retry` to `retries` / `retryPolicy` / `budget`, coordinated
14483/// with the Gateway API deprecation cycle). Until this lift landed the
14484/// axis carried an inline `retry` literal at nine physical sites in
14485/// caixa-mesh/src/lib.rs (one production emitter at the `gateway_routes`
14486/// per-rule `rule.insert(…)` call plus eight test-side navigators
14487/// pinning the overlay's rule-level top-key-set, presence, absence,
14488/// per-rule fan-out under multi-`:entrada :paths`, round-trip of the
14489/// typed `u32` attempt count, YAML integer scalar-kind, and independent-
14490/// axis coexistence with the sibling `timeouts` per-rule request-
14491/// timeout-policy axis in both directions), the highest per-axis
14492/// occurrence count of any un-lifted Gateway-API-CRD-body-axis in the
14493/// crate — same nine-site count the peer sibling
14494/// [`GATEWAY_API_KEY_TIMEOUTS`] lift closed on the coexisting per-rule
14495/// request-timeout-policy axis.
14496///
14497/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
14498/// "every recurring shape becomes a generator before it becomes a
14499/// pattern; every pattern becomes a library before it becomes
14500/// duplicated code. The duplication budget is zero.") promotes the
14501/// constant to a typed substrate-side `&'static str` on the same
14502/// trajectory the [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) /
14503/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
14504/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
14505/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
14506/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
14507/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
14508/// [`CILIUM_KEY_PORTS`] (1087693) /
14509/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
14510/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
14511/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
14512/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
14513/// [`KUBE_KEY_RULES`] (a205eb3) lifts established on the sibling
14514/// canonical-Gateway-API-CRD-body-axis /
14515/// canonical-Cilium-CNP-body-axis surfaces — closes the pair of per-
14516/// Gateway-API-`HTTPRoute`-per-rule `:politicas` overlay axes
14517/// (`timeouts` for `:politicas :timeout`, `retry` for `:politicas
14518/// :retries`) both MESH-COMPOSITION.md §V "no infinite blocking / no
14519/// infinite retrying" guarantees rest on. The render-side consumer now
14520/// threads the same `&'static str` through its per-rule
14521/// `rule.insert(…)` call and every test-side navigator's `.get(…)`
14522/// retrieval so a future Gateway API rebrand on the per-rule retry-
14523/// policy axis lands in one place; every future renderer that reaches
14524/// for the canonical per-rule retry-policy axis (the future M4
14525/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
14526/// per-rule retry-policy fan-out, a future per-edge `codes`
14527/// retryable-status-code emitter honoring an M4-roadmap `:politicas
14528/// :retry-codes` slot, a future per-edge `backoff` inter-attempt
14529/// backoff-window emitter honoring an M4-roadmap `:politicas
14530/// :retry-backoff` slot) inherits the same value by construction with
14531/// no opportunity for per-renderer drift.
14532///
14533/// Same "the typed constant lives in one place" discipline the
14534/// [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) /
14535/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
14536/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
14537/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
14538/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
14539/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
14540/// [`CILIUM_KEY_PORTS`] (1087693) /
14541/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
14542/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
14543/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
14544/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
14545/// [`KUBE_KEY_RULES`] (a205eb3) lifts apply on the peer canonical-
14546/// Gateway-API-HTTPRoute-per-rule-body-axis surface.
14547///
14548/// [cm]: ../../caixa_mesh/index.html
14549pub const GATEWAY_API_KEY_RETRY: &str = "retry";
14550
14551/// Canonical K8s Gateway API `HTTPRoute` per-rule retry-policy `attempts`
14552/// leaf scalar-key every `gateway_routes`-emitted `HTTPRoute` document
14553/// mounts its per-rule `:politicas :retries` typed `u32` attempt count
14554/// under (`spec.rules[].retry.attempts`). Leaf peer to the container-axis
14555/// parent [`GATEWAY_API_KEY_RETRY`] (231bbf5) — the sibling per-rule
14556/// retry-policy body-axis lifted in the immediately-preceding commit;
14557/// this closes the parent-leaf axis pair (`retry` container +
14558/// `attempts` leaf) the Gateway API v1 `HTTPRouteRetry` sub-shape pins
14559/// under `HTTPRoute.spec.rules[].retry.attempts`.
14560///
14561/// The Gateway API v1 CRD schema pins the per-rule retry attempt budget
14562/// through the `HTTPRouteRetry.attempts` scalar (peer to future
14563/// `HTTPRouteRetry.codes` retryable-status-code list and
14564/// `HTTPRouteRetry.backoff` inter-attempt backoff-window scalars) the
14565/// Gateway-API-implementation-side per-rule request-dispatch loop
14566/// compares each failed backend attempt count against before giving up
14567/// on the in-flight backend call. Drift on this leaf key is exactly as
14568/// load-bearing as drift on the parent per-rule retry-policy container
14569/// axis (`retry`): the K8s apiserver-side Gateway API CRD schema
14570/// validator drops any per-rule `retry:` entry whose leaf attempt-count
14571/// key carries an unrecognized name — a `"attempt"` (singular) /
14572/// `"count"` / `"tries"` / `"maxAttempts"` typo silently emits an
14573/// `HTTPRoute` whose per-rule retry-budget the Gateway-API-
14574/// implementation-side per-rule request-dispatch loop no-ops entirely
14575/// (the sub-shape is parsed as an empty `HTTPRouteRetry` with the
14576/// typed `u32` attempt count silently discarded, the route accepts
14577/// every inbound request with no per-rule retry budget — the "no
14578/// infinite retrying without bound" guarantee MESH-COMPOSITION.md §V
14579/// mandates for every rendered per-`:politicas` mesh-composition edge
14580/// silently regresses to the pre-overlay unbounded-retry semantic,
14581/// and every external `:entrada` flow the route was authored to cap
14582/// by the typed `:politicas :retries` slot runs to whatever retry
14583/// policy the resolved backend's downstream infrastructure — Envoy
14584/// default retry policy, client SDK autoretry, node-local conntrack
14585/// retries — picks with no field naming the per-rule-retry-attempts-
14586/// leaf-key drift root cause).
14587///
14588/// The single source of truth the rendered Aplicacao Gateway-API-side
14589/// ingress bundle's per-`HTTPRoute` per-rule retry-attempts-leaf-key-
14590/// naming reaches for:
14591///
14592/// - the rendered `HTTPRoute` document's per-rule
14593/// `retry.attempts:` leaf (caixa-mesh/src/lib.rs — the
14594/// `gateway_routes` per-Aplicacao `HTTPRoute`'s per-rule
14595/// `single_field_overlay(spec.politicas.retries, …)` call seeded
14596/// from the Aplicacao's `:politicas :retries` overlay when the
14597/// slot is set, emitting the typed `u32` attempt count under this
14598/// leaf key inside the sibling [`GATEWAY_API_KEY_RETRY`] container
14599/// axis).
14600///
14601/// The per-rule retry-attempts leaf key names the same Gateway-API-
14602/// implementation-side per-rule request-dispatch retry-budget scalar
14603/// as the sibling parent [`GATEWAY_API_KEY_RETRY`] container axis it
14604/// sits nested inside under `spec.rules[].retry.attempts`, and must
14605/// move together with the parent on any future Gateway API rebrand
14606/// (an upstream SIG-Network Gateway API v2 rename of the per-rule
14607/// retry-attempts leaf key from `attempts` to `attempt` / `count` /
14608/// `tries` / `maxAttempts`, coordinated with the Gateway API
14609/// deprecation cycle). Until this lift landed the leaf key carried
14610/// an inline `attempts` literal at six physical code sites in
14611/// caixa-mesh/src/lib.rs (one production emitter at the `gateway_routes`
14612/// per-rule `single_field_overlay(spec.politicas.retries, "attempts", …)`
14613/// call plus five test-side navigators pinning the overlay's leaf-
14614/// count value, round-trip of the typed `u32` attempt count, YAML
14615/// integer scalar-kind, per-rule fan-out under multi-`:entrada
14616/// :paths`, and independent-axis coexistence with the sibling
14617/// `timeouts` per-rule request-timeout-policy axis).
14618///
14619/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
14620/// "every recurring shape becomes a generator before it becomes a
14621/// pattern; every pattern becomes a library before it becomes
14622/// duplicated code. The duplication budget is zero.") promotes the
14623/// constant to a typed substrate-side `&'static str` on the same
14624/// trajectory the [`GATEWAY_API_KEY_RETRY`] (231bbf5) /
14625/// [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) /
14626/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
14627/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
14628/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
14629/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
14630/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) lifts established on
14631/// the sibling canonical-Gateway-API-CRD-body-axis surface — closes
14632/// the parent-leaf axis pair (`retry` container +
14633/// `attempts` leaf) the K8s Gateway API v1 `HTTPRouteRetry` sub-shape
14634/// pins under `HTTPRoute.spec.rules[].retry.attempts`, both
14635/// MESH-COMPOSITION.md §V "no infinite retrying" guarantees rest on.
14636/// The render-side consumer now threads the same `&'static str`
14637/// through its `single_field_overlay` call and every test-side
14638/// navigator's `.get(…)` retrieval so a future Gateway API rebrand
14639/// on the per-rule retry-attempts leaf lands in one place; every
14640/// future renderer that reaches for the canonical per-rule retry-
14641/// attempts leaf (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao`
14642/// CR materializer's per-Aplicacao per-rule retry-attempts fan-out)
14643/// inherits the same value by construction with no opportunity for
14644/// per-renderer drift.
14645///
14646/// Same "the typed constant lives in one place" discipline the
14647/// [`GATEWAY_API_KEY_RETRY`] (231bbf5) /
14648/// [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) lifts apply on the peer
14649/// canonical-Gateway-API-HTTPRoute-per-rule-body-axis surface, now
14650/// extended one nesting level deeper onto the retry-container-leaf
14651/// scalar.
14652///
14653/// [cm]: ../../caixa_mesh/index.html
14654pub const GATEWAY_API_KEY_ATTEMPTS: &str = "attempts";
14655
14656/// Canonical K8s Gateway API `HTTPRoute` per-rule request-timeout-policy
14657/// `request` leaf scalar-key every `gateway_routes`-emitted `HTTPRoute`
14658/// document mounts its per-rule `:politicas :timeout` typed K8s-duration
14659/// string under (`spec.rules[].timeouts.request`). Leaf peer to the
14660/// container-axis parent [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) — the
14661/// sibling per-rule request-timeout-policy body-axis — and to the peer
14662/// retry-container leaf [`GATEWAY_API_KEY_ATTEMPTS`] (e2e136b) landed
14663/// on the parallel `retry.attempts` nesting; this closes the parent-leaf
14664/// axis pair (`timeouts` container + `request` leaf) the K8s Gateway API
14665/// v1 `HTTPRouteTimeouts` sub-shape pins under
14666/// `HTTPRoute.spec.rules[].timeouts.request`.
14667///
14668/// The Gateway API v1 CRD schema pins the per-rule request-deadline
14669/// through the `HTTPRouteTimeouts.request` scalar (peer to
14670/// `HTTPRouteTimeouts.backendRequest` per-attempt backend-call deadline
14671/// scalar) the Gateway-API-implementation-side per-rule request-dispatch
14672/// loop keys off before it commits to a per-request wall-clock deadline.
14673/// Drift on this leaf key is exactly as load-bearing as drift on the
14674/// parent per-rule request-timeout-policy container axis (`timeouts`):
14675/// the K8s apiserver-side Gateway API CRD schema validator drops any
14676/// per-rule `timeouts:` entry whose leaf request-deadline key carries an
14677/// unrecognized name — a `"deadline"` / `"requestTimeout"` /
14678/// `"timeout"` / `"upstreamRequest"` typo silently emits an `HTTPRoute`
14679/// whose per-rule request-deadline the Gateway-API-implementation-side
14680/// per-rule request-dispatch loop no-ops entirely (the sub-shape is
14681/// parsed as an empty `HTTPRouteTimeouts` with the typed duration
14682/// silently discarded, the route accepts every inbound request with no
14683/// per-rule request wall-clock deadline — the "no infinite blocking"
14684/// guarantee MESH-COMPOSITION.md §V mandates for every rendered
14685/// per-`:politicas` mesh-composition edge silently regresses to the
14686/// pre-overlay unbounded-blocking semantic, and every external
14687/// `:entrada` flow the route was authored to cap by the typed
14688/// `:politicas :timeout` slot runs to whatever request-deadline the
14689/// resolved backend's downstream infrastructure — Envoy default
14690/// route-timeout, client SDK deadline, node-local conntrack idle-close
14691/// — picks with no field naming the per-rule-request-timeout-leaf-key
14692/// drift root cause).
14693///
14694/// The single source of truth the rendered Aplicacao Gateway-API-side
14695/// ingress bundle's per-`HTTPRoute` per-rule request-deadline-leaf-key-
14696/// naming reaches for:
14697///
14698/// - the rendered `HTTPRoute` document's per-rule
14699/// `timeouts.request:` leaf (caixa-mesh/src/lib.rs — the
14700/// `gateway_routes` per-Aplicacao `HTTPRoute`'s per-rule
14701/// `single_field_overlay(spec.politicas.timeout, …)` call seeded
14702/// from the Aplicacao's `:politicas :timeout` overlay when the slot
14703/// is set, emitting the typed K8s-duration string under this leaf
14704/// key inside the sibling [`GATEWAY_API_KEY_TIMEOUTS`] container
14705/// axis).
14706///
14707/// The per-rule request-deadline leaf key names the same
14708/// Gateway-API-implementation-side per-rule request-dispatch wall-clock
14709/// deadline scalar as the sibling parent [`GATEWAY_API_KEY_TIMEOUTS`]
14710/// container axis it sits nested inside under
14711/// `spec.rules[].timeouts.request`, and must move together with the
14712/// parent on any future Gateway API rebrand (an upstream SIG-Network
14713/// Gateway API v2 rename of the per-rule request-deadline leaf key from
14714/// `request` to `deadline` / `requestTimeout` / `timeout` /
14715/// `upstreamRequest`, coordinated with the Gateway API deprecation
14716/// cycle). Until this lift landed the leaf key carried an inline
14717/// `request` literal at six physical code sites in
14718/// caixa-mesh/src/lib.rs (one production emitter at the
14719/// `gateway_routes` per-rule
14720/// `single_field_overlay(spec.politicas.timeout, "request", …)` call
14721/// plus five test-side navigators pinning the overlay's leaf-value
14722/// presence, the canonical `duration_codec::render` round-trip of a
14723/// 30s / 90s / 1m typed duration, per-rule fan-out under
14724/// multi-`:entrada :paths`, and independent-axis coexistence with the
14725/// sibling `retry` per-rule retry-policy axis).
14726///
14727/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
14728/// "every recurring shape becomes a generator before it becomes a
14729/// pattern; every pattern becomes a library before it becomes
14730/// duplicated code. The duplication budget is zero.") promotes the
14731/// constant to a typed substrate-side `&'static str` on the same
14732/// trajectory the [`GATEWAY_API_KEY_ATTEMPTS`] (e2e136b) /
14733/// [`GATEWAY_API_KEY_RETRY`] (231bbf5) /
14734/// [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) /
14735/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
14736/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
14737/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
14738/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
14739/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) lifts established on the
14740/// sibling canonical-Gateway-API-CRD-body-axis surface — closes the
14741/// second parent-leaf axis pair (`timeouts` container + `request` leaf)
14742/// the K8s Gateway API v1 `HTTPRouteTimeouts` sub-shape pins under
14743/// `HTTPRoute.spec.rules[].timeouts.request`, sibling to the parent-
14744/// leaf pair (`retry` container + `attempts` leaf) closed in the
14745/// immediately-preceding [`GATEWAY_API_KEY_ATTEMPTS`] lift. Both
14746/// MESH-COMPOSITION.md §V "no infinite blocking / no infinite retrying"
14747/// guarantees now rest on typed lifts at both container-axis and leaf-
14748/// scalar-axis nesting levels of the two per-`:politicas` overlays.
14749/// The render-side consumer now threads the same `&'static str`
14750/// through its `single_field_overlay` call and every test-side
14751/// navigator's `.get(…)` retrieval so a future Gateway API rebrand on
14752/// the per-rule request-deadline leaf lands in one place; every future
14753/// renderer that reaches for the canonical per-rule request-deadline
14754/// leaf (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
14755/// materializer's per-Aplicacao per-rule request-deadline fan-out, a
14756/// future per-edge `backendRequest` per-attempt backend-call deadline
14757/// emitter) inherits the same value by construction with no opportunity
14758/// for per-renderer drift.
14759///
14760/// Same "the typed constant lives in one place" discipline the
14761/// [`GATEWAY_API_KEY_ATTEMPTS`] (e2e136b) /
14762/// [`GATEWAY_API_KEY_RETRY`] (231bbf5) /
14763/// [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) lifts apply on the peer
14764/// canonical-Gateway-API-HTTPRoute-per-rule-body-axis surface, now
14765/// extended to the second per-rule container-leaf scalar (parallel to
14766/// the sibling `retry.attempts` container-leaf pair).
14767///
14768/// [cm]: ../../caixa_mesh/index.html
14769pub const GATEWAY_API_KEY_REQUEST: &str = "request";
14770
14771/// Canonical Helm library-chart name every `lareira-<nome>` chart depends
14772/// on — the `pleme-computeunit` library chart in
14773/// `pleme-io/helmworks/charts/pleme-computeunit` that owns the K8s
14774/// resource templates (ComputeUnit + Service + ScaledObject + ConfigMap)
14775/// every per-Servico chart consumes via Helm's per-dep alias convention
14776/// (when no `alias:` is set on a dependency, values are scoped under the
14777/// dependency's `name:`).
14778///
14779/// The single source of truth all three downstream library-name consumers
14780/// reach for:
14781///
14782/// - [`caixa-helm`][ch]'s `DEFAULT_LIBRARY_NAME` re-export — the
14783/// default value of `RenderOpts::library_name`, which drives both
14784/// the Chart.yaml `dependencies[0].name` axis
14785/// (`build_chart_yaml`) and the values.yaml wrap key
14786/// (`build_values_yaml`) so the rendered `lareira-<nome>` chart's
14787/// dep declaration and its values block agree by construction
14788/// (the 17ebd1a `opts.library_name` lift).
14789/// - [`caixa-flux`][cf]'s `DEFAULT_LIBRARY_NAME` re-export — the
14790/// wrap key the `cluster_bundle` `helmrelease.yaml` template uses
14791/// under `spec.values.<library>:` to thread the per-cluster
14792/// overrides (`enabled: true`) through to the rendered chart's
14793/// dep block. Helm's per-dep alias convention scopes those values
14794/// under the dependency's `name:`, so this wrap key must match the
14795/// chart's `dependencies[0].name` exactly — drift here silently
14796/// routes the values block nowhere at `helm template` /
14797/// `helm install` time, and the cluster comes up with the library
14798/// chart's defaults rather than the typed per-cluster overrides.
14799/// - Every future per-Servico renderer the absorption-roadmap
14800/// acknowledges (the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
14801/// materializer's per-edge library-chart resolver, the future
14802/// per-cluster image-registry mirror's `<registry>-computeunit`
14803/// fork, the future per-edition library-chart variant the
14804/// substrate forks once `pleme-computeunit` outlives its scoping
14805/// intent).
14806///
14807/// Until this lift landed the canonical library-chart name lived as
14808/// two production-code call sites: a `pub const DEFAULT_LIBRARY_NAME:
14809/// &str = "pleme-computeunit"` in `caixa-helm` (the
14810/// `RenderOpts::library_name` default, consumed by both the chart's dep
14811/// name axis and the values.yaml wrap key axis) and an inline literal
14812/// `pleme-computeunit:` in `caixa-flux`'s `cluster_bundle`
14813/// `helmrelease.yaml` format-string template (the wrap key the per-
14814/// cluster `enabled: true` override is scoped under). Both consumers
14815/// reach for the same load-bearing Helm library-chart name, but no
14816/// shared constant linked them — the canonical
14817/// "duplicated `pub const` / inline literal across two renderers"
14818/// drift footgun the [`DEFAULT_NAMESPACE`] (a085b26) and
14819/// [`DEFAULT_SERVICO_PORT`] (1e22add) lifts close on the peer
14820/// canonical-K8s-axis-constant surface.
14821///
14822/// A future library-chart rebrand — the substrate forking
14823/// `pleme-computeunit` to `<registry>-computeunit` for a per-cluster
14824/// image-registry mirror, or to `aplicacao-computeunit` for the M4
14825/// typed-Aplicacao renderer's sibling library chart, or to any
14826/// per-edition variant the absorption-roadmap names — without a
14827/// coordinated edit on both consumers would have silently emitted a
14828/// per-Servico chart whose dep declared the new library name (because
14829/// the chart-side override flowed through `opts.library_name`) but
14830/// whose flux-side `HelmRelease.values.pleme-computeunit:` wrap key
14831/// still scoped under the old literal. Helm's per-dep values router
14832/// would route the per-cluster `enabled: true` override to *nowhere*
14833/// at `helm template` / `helm install` time, and the cluster's apply
14834/// would come up with the library chart's defaults — `enabled: false`,
14835/// the typed values block from the chart's own `values.yaml` rather
14836/// than the flux-side override — silently no-op'ing every per-cluster
14837/// override the operator set, far from the rebrand commit's source.
14838/// The apply-time symptom (the workload comes up with the library
14839/// chart's defaults instead of the per-cluster overrides) is invisible
14840/// at admission and surfaces only as "the service is up but not doing
14841/// what we configured it to do", typically far from the rebrand commit.
14842///
14843/// Lifting it to caixa-core's render-constants block alongside the
14844/// peer [`DEFAULT_NAMESPACE`] / [`DEFAULT_SERVICO_PORT`] makes the
14845/// library-name axis discipline structural: every renderer that
14846/// reaches for the canonical library-chart name consults the same
14847/// `&'static str`, and every future renderer inherits the same value
14848/// by construction with no opportunity for per-renderer drift. Same
14849/// "the typed constant lives in one place" discipline the
14850/// [`PLEME_LABEL_PREFIX`] (a8d4d57) / [`KUBE_KEY_API_VERSION`] /
14851/// [`LAREIRA_CHART_NAME_PREFIX`] lifts apply on the peer
14852/// shared-string axes.
14853///
14854/// [ch]: ../../caixa_helm/index.html
14855/// [cf]: ../../caixa_flux/index.html
14856pub const DEFAULT_LIBRARY_NAME: &str = "pleme-computeunit";
14857
14858/// Canonical Flux v2 `spec.interval` reconcile-poll cadence duration
14859/// scalar every [`caixa-flux`][cf]-emitted Flux v2 CR (the per-caixa
14860/// `cluster_bundle` triplet's `GitRepository` + `HelmRelease` +
14861/// `Kustomization`) declares as its default reconcile-schedule when the
14862/// per-caixa [`ClusterBundleOpts::for_caixa`][fc] seed doesn't carry an
14863/// operator-pinned override. Every rendered per-caixa Flux v2 CR consults
14864/// the same `&'static str` at seed time so a future substrate-side
14865/// reconcile-cadence migration (`"10m"` → `"5m"` once the Flux v2 source-
14866/// controller / helm-controller / kustomize-controller trio ships lower-
14867/// latency-poll optimizations that make per-CR cluster load safe at a
14868/// faster cadence, `"10m"` → `"15m"` on cost-optimized clusters where the
14869/// per-CR source-controller poll cost outweighs the reconcile-freshness
14870/// gain) is a one-line edit on this canonical declaration, not a
14871/// coordinated rewrite across the [`ClusterBundleOpts`] default seed and
14872/// every future per-target renderer the substrate adds.
14873///
14874/// The single source of truth the rendered per-caixa Flux v2 cluster
14875/// bundle's per-CR reconcile-poll cadence default seed reaches for:
14876///
14877/// - [`ClusterBundleOpts::for_caixa`][fc]'s per-caixa default seed
14878/// (caixa-flux/src/lib.rs — the `interval: <DEFAULT>.into()` field of
14879/// the [`ClusterBundleOpts`] struct default the substrate's per-caixa
14880/// `cluster_bundle` renderer threads through every emitted Flux v2 CR's
14881/// [`FLUX_KEY_INTERVAL`] axis verbatim).
14882///
14883/// The value is a valid Flux v2 reconcile-poll cadence duration scalar (per
14884/// the upstream Flux v2 `metav1.Duration` OpenAPI schema on each of the
14885/// three Flux v2 CRDs — `source.toolkit.fluxcd.io/v1/GitRepository.spec.
14886/// interval`, `helm.toolkit.fluxcd.io/v2/HelmRelease.spec.interval`,
14887/// `kustomize.toolkit.fluxcd.io/v1/Kustomization.spec.interval`): a
14888/// non-empty Go-duration-format string (e.g. `"10m"`, `"5m"`, `"1h30m"`),
14889/// which the Flux v2 controller-side per-CR admission gate parses via
14890/// `metav1.ParseDuration` before installing the per-CR watch. A future
14891/// rebrand on this lift cannot silently land a value the Flux v2
14892/// controller-side admission gate rejects at the *first* per-caixa
14893/// `HelmRelease` apply against a cluster, far from the rebrand commit's
14894/// source — the pin at the canonical lift documents the Go-duration-format
14895/// grammar contract with the Flux v2 admission gate every downstream
14896/// consumer of the rendered per-CR reconcile-cadence axis rests on.
14897///
14898/// Pairs with the sibling [`FLUX_KEY_INTERVAL`] (48db6e2) per-Flux-v2-CR
14899/// reconcile-poll cadence scalar-axis key the value the substrate seeds
14900/// here nests directly under across every rendered per-caixa Flux v2 CR
14901/// — the key half of the per-CR `spec.interval` scalar-key/scalar-value
14902/// pair lives at [`FLUX_KEY_INTERVAL`], the value half's substrate-side
14903/// default seed lives here. Same "the typed constant lives in one place"
14904/// discipline the [`DEFAULT_NAMESPACE`] (a085b26) /
14905/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) / [`DEFAULT_LIBRARY_NAME`]
14906/// (41438dc) / [`DEFAULT_SERVICO_PORT`] (1e22add) /
14907/// [`DEFAULT_GATEWAY_CLASS_NAME`] (d9b0743) /
14908/// [`DEFAULT_PUBLISH_TAG_PREFIX`] (0a6a602) lifts apply on the peer
14909/// canonical-substrate-default-load-bearing-scalar surface — extends the
14910/// canonical-substrate-default single-sourcing discipline from the peer
14911/// substrate-side default-namespace / default-library-chart-name /
14912/// default-Servico-listen-port / default-Gateway-API-controller-name /
14913/// default-git-publish-tag-prefix surfaces onto the sibling default-Flux-
14914/// v2-per-CR-reconcile-poll-cadence surface every rendered per-caixa
14915/// Flux v2 cluster bundle CR carries.
14916///
14917/// [cf]: ../../caixa_flux/index.html
14918/// [fc]: ../../caixa_flux/struct.ClusterBundleOpts.html#method.for_caixa
14919pub const DEFAULT_FLUX_RECONCILE_INTERVAL: &str = "10m";
14920
14921/// Canonical Flux v2 `HelmRelease.spec.chart.spec.chart` per-CR chart-
14922/// directory-in-GitRepository-source sub-path scalar every
14923/// [`caixa-flux`][cf]-emitted `helmrelease.yaml` document declares as the
14924/// default chart-directory-in-git-source pointer when the per-caixa
14925/// [`ClusterBundleOpts::for_caixa`][fc] seed doesn't carry an operator-
14926/// pinned override. The Flux v2 source-controller resolves the pointer
14927/// relative to the paired [`FLUX_KIND_GIT_REPOSITORY`] the sibling
14928/// [`FLUX_KEY_SOURCE_REF`]-keyed `sourceRef:` block names — the substrate's
14929/// canonical contract with every caixa Servico's git repository is that
14930/// the per-caixa `lareira-<nome>` chart the peer `caixa-helm` renderer
14931/// emits lives at the `./chart/` sub-tree of the repository root, so the
14932/// helm-controller's per-CR chart-open loop keys off this exact scalar to
14933/// locate the [`HELM_CHART_YAML_FILENAME`] + [`HELM_VALUES_YAML_FILENAME`]
14934/// pair the per-caixa rendered chart declares. Every rendered per-caixa
14935/// `HelmRelease` CR consults the same `&'static str` at seed time so a
14936/// future substrate-side chart-directory-in-git-source rebrand
14937/// (`"chart"` → `"charts"` once a per-caixa multi-chart layout lands and
14938/// the substrate publishes N sibling `lareira-<nome>/` charts under one
14939/// git repository, `"chart"` → `"helm"` on a cross-language convention
14940/// alignment with sibling wasm-runtime substrates, `"chart"` → `"deploy"`
14941/// on a per-caixa-deploy-directory naming migration) is a one-line edit
14942/// on this canonical declaration, not a coordinated rewrite across the
14943/// [`ClusterBundleOpts`] default seed and every future per-target
14944/// renderer the substrate adds.
14945///
14946/// The single source of truth the rendered per-caixa Flux v2 cluster
14947/// bundle's per-CR chart-directory-in-git-source default seed reaches for:
14948///
14949/// - [`ClusterBundleOpts::for_caixa`][fc]'s per-caixa default seed
14950/// (caixa-flux/src/lib.rs — the `chart_path: <DEFAULT>.into()` field
14951/// of the [`ClusterBundleOpts`] struct default the substrate's per-
14952/// caixa `cluster_bundle` renderer threads through every emitted per-
14953/// caixa `helmrelease.yaml` document's [`FLUX_HELMCHART_TEMPLATE_KEY_CHART`]
14954/// -keyed `spec.chart.spec.chart` axis verbatim).
14955///
14956/// The value is a valid Flux v2 `HelmRelease.spec.chart.spec.chart` scalar
14957/// (per the upstream Flux v2 `helm.toolkit.fluxcd.io/v2/HelmRelease` `OpenAPI`
14958/// schema — a non-empty string interpreted by the source-controller as a
14959/// relative directory-tree path from the paired `GitRepository` clone
14960/// root): a non-empty ASCII scalar with no leading path separator (which
14961/// would break the source-controller's relative-path composition against
14962/// the per-clone-root anchor). A future rebrand on this lift cannot
14963/// silently land an empty scalar or a leading-separator scalar the source-
14964/// controller-side per-CR chart-open loop would then reject at the *first*
14965/// per-caixa `HelmRelease` apply against a cluster, far from the rebrand
14966/// commit's source — the [`default_flux_chart_source_subpath_is_a_valid_relative_directory_scalar`]
14967/// pin trips at caixa-core build time on any drift past the typed floor.
14968///
14969/// Pairs with the sibling [`FLUX_HELMCHART_TEMPLATE_KEY_CHART`] (0fef82e)
14970/// per-Flux-v2-`HelmRelease.spec.chart.spec.chart` leaf-scalar-key the
14971/// value the substrate seeds here nests directly under across every
14972/// rendered per-caixa `HelmRelease` CR — the key half of the per-CR
14973/// `spec.chart.spec.chart` scalar-key/scalar-value pair lives at
14974/// [`FLUX_HELMCHART_TEMPLATE_KEY_CHART`], the value half's substrate-side
14975/// default seed lives here. Peer with [`flux_kustomization_source_subtree`]
14976/// on the sibling `Kustomization.spec.path` per-cluster / per-caixa `GitOps`-
14977/// repository-relative directory-tree seed composer — both name a load-
14978/// bearing directory-tree relative path the Flux v2 controller family's
14979/// per-CR reconcile loop navigates into, at the two paired axes of the
14980/// per-caixa `cluster_bundle` triplet (the `HelmRelease` chart-directory
14981/// axis names *where in the caixa's own git repo the chart lives*, the
14982/// `Kustomization` sub-tree axis names *where in the k8s-GitOps repo the
14983/// per-cluster manifest sub-tree lives*, and the two together close the
14984/// Flux v2 kustomize-controller → helm-controller reconcile-chain axis
14985/// the substrate's per-caixa cluster-bundle-triplet reconcile-topology
14986/// rests on).
14987///
14988/// Same "the typed constant lives in one place" discipline the
14989/// [`DEFAULT_NAMESPACE`] (a085b26) / [`DEFAULT_FLUX_SYSTEM_NAMESPACE`]
14990/// (7197d38) / [`DEFAULT_LIBRARY_NAME`] (41438dc) /
14991/// [`DEFAULT_SERVICO_PORT`] (1e22add) / [`DEFAULT_GATEWAY_CLASS_NAME`]
14992/// (d9b0743) / [`DEFAULT_PUBLISH_TAG_PREFIX`] (0a6a602) /
14993/// [`DEFAULT_FLUX_RECONCILE_INTERVAL`] (908180f) /
14994/// [`DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT`] (64bdb2b) /
14995/// [`DEFAULT_PLEME_GIT_ORG`] (9952bd9) lifts apply on the peer
14996/// canonical-substrate-default-load-bearing-scalar surface — extends the
14997/// canonical-substrate-default single-sourcing discipline from the peer
14998/// substrate-side default-namespace / default-library-chart-name /
14999/// default-Servico-listen-port / default-Gateway-API-controller-name /
15000/// default-git-publish-tag-prefix / default-Flux-v2-per-CR-reconcile-poll-
15001/// cadence / default-Flux-v2-per-CR-kustomization-reconcile-wall-clock-cap
15002/// / default-pleme-io-git-org surfaces onto the sibling default-Flux-v2-
15003/// per-CR-HelmRelease-chart-directory-in-git-source surface every rendered
15004/// per-caixa Flux v2 cluster bundle `HelmRelease` CR carries.
15005///
15006/// [cf]: ../../caixa_flux/index.html
15007/// [fc]: ../../caixa_flux/struct.ClusterBundleOpts.html#method.for_caixa
15008pub const DEFAULT_FLUX_CHART_SOURCE_SUBPATH: &str = "chart";
15009
15010/// Canonical Flux v2 `HelmRelease.spec.{install,upgrade}.remediation.retries`
15011/// bounded retry-count scalar every [`caixa-flux`][cf]-emitted `helmrelease.yaml`
15012/// document declares under both the install-path and the upgrade-path
15013/// `remediation` blocks. The Flux v2 `helm-controller` per-CR `Install` /
15014/// `Upgrade` action reconciler consumes this scalar as the ceiling on the
15015/// number of times it will re-attempt a failed Helm install or Helm upgrade
15016/// before it marks the `HelmRelease` `Ready: False` and stops retrying — the
15017/// substrate's canonical "how many times we let Flux re-try a chart apply
15018/// before it stops" contract with the helm-controller-side per-CR
15019/// remediation loop.
15020///
15021/// The single source of truth all two duplicated inline `retries: 3`
15022/// scalar-value literal sites the substrate's [`cluster_bundle`][cb]
15023/// `helmrelease.yaml` format-string template reaches for:
15024///
15025/// - `helmrelease.yaml` `spec.install.remediation.retries` — the install-
15026/// path retry cap the helm-controller consumes for the first-time chart
15027/// apply the `HelmRelease` CR gates. Before this lift landed the value
15028/// sat as an inline `retries: 3\n` literal inside
15029/// [`cluster_bundle`][cb]'s `helmrelease.yaml` format-string template's
15030/// `install:` sub-block (caixa-flux/src/lib.rs — the `install.remediation`
15031/// sub-block).
15032/// - `helmrelease.yaml` `spec.upgrade.remediation.retries` — the upgrade-
15033/// path retry cap the helm-controller consumes for every subsequent
15034/// chart re-apply the same `HelmRelease` CR gates on a caixa version
15035/// bump. Before this lift landed the value sat as a second inline
15036/// `retries: 3\n` literal inside the same
15037/// [`cluster_bundle`][cb] `helmrelease.yaml` format-string template's
15038/// `upgrade:` sub-block (caixa-flux/src/lib.rs — the `upgrade.remediation`
15039/// sub-block).
15040/// - Every future per-caixa `HelmRelease` renderer the M3.x + M4
15041/// absorption roadmap acknowledges (the future
15042/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
15043/// `HelmRelease` synthesis, a future per-cluster override `HelmRelease`
15044/// the operator emits for the observability-collector pipeline).
15045///
15046/// Both existing production-code sites carry the *same* substrate-chosen
15047/// retry ceiling — the value is one canonical policy choice, not two
15048/// independent axes: the operator's "how many chart-apply failures we
15049/// tolerate before Flux stops retrying and surfaces the failure at the
15050/// per-caixa `HelmRelease.status.conditions[]` axis the substrate's
15051/// downstream reconciliation-topology consumer watches". A future
15052/// substrate-side retry-ceiling migration (`3` → `5` once per-caixa
15053/// idempotency invariants tighten and higher-retry recovery from
15054/// transient apiserver / registry / oci-source flakes becomes safe, `3`
15055/// → `1` on hardened per-caixa pipelines where a failed apply should
15056/// escalate to operator-attention rather than mask under further retries,
15057/// `3` → `10` on high-churn dev clusters where transient failures
15058/// dominate) without a coordinated edit on *both* sites would have
15059/// silently split the substrate's canonical retry-ceiling between the
15060/// install-path and the upgrade-path — first-time applies would tolerate
15061/// one ceiling while every subsequent per-version re-apply would tolerate
15062/// another, with no field naming the ceiling-drift root cause far from
15063/// the rebrand commit's source. Lifting the value to caixa-core's render-
15064/// constants block alongside the peer [`DEFAULT_FLUX_RECONCILE_INTERVAL`]
15065/// makes the retry-ceiling axis discipline structural: both sites consult
15066/// the same `u32`, and every future per-CR remediation-retries emitter
15067/// inherits the same value by construction with no opportunity for per-
15068/// path drift.
15069///
15070/// The value is a valid Flux v2 `HelmRelease`-remediation-retries scalar
15071/// (per the upstream Flux v2 `HelmRelease.spec.{install,upgrade}.remediation.retries`
15072/// `OpenAPI` schema — a non-negative integer, `-1` reserved as the sentinel
15073/// for "retry indefinitely" which the substrate opts out of by declaring
15074/// a bounded ceiling): a positive `u32` bounded above by the substrate's
15075/// tolerance for silently-masked chart-apply failures. A future rebrand
15076/// on this lift cannot silently land a negative sentinel by construction:
15077/// the [`flux_helmrelease_remediation_retries_default_is_a_bounded_positive_scalar`]
15078/// pin trips at caixa-core build time on any drift past the typed floor.
15079///
15080/// Pairs with the sibling [`DEFAULT_FLUX_RECONCILE_INTERVAL`] (908180f)
15081/// on the peer canonical-Flux-v2-per-CR-substrate-default surface — the
15082/// reconcile-poll cadence default names how often the helm-controller
15083/// re-evaluates the per-CR desired state, and this remediation-retries
15084/// ceiling names how many times a per-evaluation Helm action is allowed
15085/// to fail-and-retry before the controller stops. Both are substrate-side
15086/// policy choices the operator inherits when the per-caixa
15087/// [`ClusterBundleOpts`][co] doesn't pin an override, and both must move
15088/// together on any coordinated substrate-side Flux v2 per-CR-remediation
15089/// tuning-cycle promotion.
15090///
15091/// Same "the typed constant lives in one place" discipline the
15092/// [`DEFAULT_NAMESPACE`] (a085b26) / [`DEFAULT_FLUX_SYSTEM_NAMESPACE`]
15093/// (7197d38) / [`DEFAULT_LIBRARY_NAME`] (41438dc) /
15094/// [`crate::DEFAULT_SERVICO_PORT`] (1e22add) /
15095/// [`DEFAULT_GATEWAY_CLASS_NAME`] (d9b0743) /
15096/// [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] (0a6a602) /
15097/// [`DEFAULT_FLUX_RECONCILE_INTERVAL`] (908180f) lifts apply on the peer
15098/// canonical-substrate-default-load-bearing-scalar surface.
15099///
15100/// [cb]: ../../caixa_flux/fn.cluster_bundle.html
15101/// [cf]: ../../caixa_flux/index.html
15102/// [co]: ../../caixa_flux/struct.ClusterBundleOpts.html
15103pub const FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT: u32 = 3;
15104
15105/// Canonical Flux v2 `HelmRelease.spec.{install,upgrade}.remediation.retries`
15106/// leaf scalar-key every `caixa-flux`-emitted `helmrelease.yaml` document
15107/// carries at both its install-path + upgrade-path per-CR remediation
15108/// blocks. Peer to the sibling
15109/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) scalar-value
15110/// half of the same `(leaf-key, scalar-value)` per-path retry-cap
15111/// declaration pair — the Flux v2 helm-controller's per-CR remediation
15112/// loop reads the scalar under this exact leaf key, so drift on either
15113/// axis is equally load-bearing (a typo on the leaf-key silently strips
15114/// the retry-cap declaration from the emitted `remediation:` sub-block —
15115/// the helm-controller then falls back to the Flux v2 upstream default
15116/// rather than the substrate's chosen ceiling — with no diagnostic
15117/// naming the leaf-key-drift root cause far from the source
15118/// caixa.lisp / the renderer's format-string template).
15119///
15120/// The single source of truth every rendered Flux bundle axis that
15121/// names the per-path per-CR retry-cap leaf reaches for:
15122///
15123/// - the rendered `helmrelease.yaml` document's
15124/// `spec.install.remediation.retries` scalar-key axis
15125/// (caixa-flux/src/lib.rs — the `cluster_bundle` `helmrelease.yaml`
15126/// format-string template's install-path retry-cap leaf under the
15127/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`]-valued sub-block);
15128/// - the rendered `helmrelease.yaml` document's
15129/// `spec.upgrade.remediation.retries` scalar-key axis (caixa-flux/src/
15130/// lib.rs — the sibling `cluster_bundle` `helmrelease.yaml` format-
15131/// string template's upgrade-path retry-cap leaf under the same
15132/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`]-valued sub-block);
15133/// - the two test-fixture navigation sites in caixa-flux's `mod tests`
15134/// that probe the rendered document's `.get("retries")` container
15135/// axis to pin the emitted scalar-value against the sibling
15136/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] canonical-scalar
15137/// lift (the install-path + upgrade-path production-emit pins
15138/// [`cluster_bundle_helmrelease_install_remediation_retries_pins_lifted_default`]
15139/// / [`cluster_bundle_helmrelease_upgrade_remediation_retries_pins_lifted_default`]).
15140///
15141/// Both production emit sites + the two test-fixture navigation sites
15142/// name the same Flux v2 per-path per-CR retry-cap leaf-scalar-key and
15143/// must move together on any hypothetical Flux v3 rename (upstream Flux
15144/// v3 roadmap floats candidates like `attempts` / `maxRetries` /
15145/// `retryCount` in the migration prose — the peer Gateway-API-side
15146/// `spec.rules[].retry.attempts` leaf already uses `attempts` on the
15147/// sibling `GATEWAY_API_KEY_ATTEMPTS` axis, an independent CRD group's
15148/// evolution the two `pub const` declarations stay sibling constants
15149/// against). Until this lift landed the axis carried inline `retries`
15150/// literals across the two production emit sites (caixa-flux/src/lib.rs
15151/// — the two `retries: {retries_default}` sub-block leaf-headers inside
15152/// the `cluster_bundle` `helmrelease.yaml` format-string template) plus
15153/// the two test-fixture navigation sites — four occurrences of the same
15154/// load-bearing Flux-v2-per-CR-retry-cap-leaf-scalar-key convention,
15155/// drift-prone by construction.
15156///
15157/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
15158/// "every recurring shape becomes a generator before it becomes a
15159/// pattern; every pattern becomes a library before it becomes
15160/// duplicated code. The duplication budget is zero.") promotes the
15161/// constant to a typed substrate-side `&'static str` on the same
15162/// trajectory the sibling
15163/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) scalar-
15164/// value half established — extends the discipline from the scalar
15165/// value the leaf holds onto the leaf-key itself, closing the
15166/// `(leaf-key, scalar-value)` pair on both halves. The two render-side
15167/// consumers now thread the same `&'static str` through their format-
15168/// string template via a `{retries_key}` named-arg interpolation so a
15169/// future Flux v3 rebrand lands in one place; every future renderer
15170/// that reaches for the canonical Flux v2 per-CR per-path retry-cap
15171/// leaf-key (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
15172/// materializer's per-Aplicacao `HelmRelease`, a future per-edge
15173/// `HelmRelease` the operator emits for the
15174/// `CiliumClusterwideEnvoyConfig` pipeline, a future `caixa-otel`
15175/// collector-pipeline `HelmRelease`) inherits the same value by
15176/// construction with no opportunity for per-renderer drift.
15177///
15178/// Same "the typed constant lives in one place" discipline the
15179/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) sibling
15180/// scalar-value lift plus the peer [`FLUX_KEY_SOURCE_REF`] (236ef01) /
15181/// [`FLUX_KEY_CHART`] / [`FLUX_KEY_VALUES`] / [`FLUX_KEY_INTERVAL`] /
15182/// [`FLUX_KEY_HEALTH_CHECKS`] container-axis-key lifts apply on the
15183/// peer canonical-Flux-v2-load-bearing-string surface.
15184///
15185/// [cf]: ../../caixa_flux/index.html
15186pub const FLUX_HELMRELEASE_KEY_RETRIES: &str = "retries";
15187
15188/// Canonical Flux v2 `HelmRelease.spec.{install,upgrade}.remediation`
15189/// sub-container-axis-key every `caixa-flux`-emitted `helmrelease.yaml`
15190/// document nests the sibling
15191/// [`FLUX_HELMRELEASE_KEY_RETRIES`] retry-cap leaf-scalar-key under, at
15192/// both the install-path + upgrade-path per-CR remediation blocks. The
15193/// parent-container-axis-key half of the same
15194/// `(container-axis-key, leaf-scalar-key, scalar-value)` per-path
15195/// retry-cap declaration triple the sibling
15196/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) scalar-value
15197/// + [`FLUX_HELMRELEASE_KEY_RETRIES`] (a12f9fc) leaf-scalar-key halves
15198/// closed on the value the leaf holds + the leaf-key itself — this lift
15199/// closes the third and final axis on the same per-path retry-cap
15200/// declaration by extending the discipline from the leaf up to the sub-
15201/// container-axis-key the leaf sits under. The Flux v2 helm-controller
15202/// per-CR remediation loop navigates through this exact sub-container
15203/// axis to reach the retry-cap scalar-key, so drift on this axis is
15204/// equally load-bearing (a typo on the sub-container-axis-key silently
15205/// strips the entire per-path remediation block from the emitted per-CR
15206/// document — the helm-controller then falls back to the Flux v2
15207/// upstream defaults for the whole remediation surface rather than the
15208/// substrate's chosen ceiling, with no diagnostic naming the container-
15209/// axis-key-drift root cause far from the source caixa.lisp / the
15210/// renderer's format-string template).
15211///
15212/// The single source of truth every rendered Flux bundle axis that
15213/// names the per-path per-CR remediation sub-container reaches for:
15214///
15215/// - the rendered `helmrelease.yaml` document's `spec.install.remediation`
15216/// sub-block-header axis (caixa-flux/src/lib.rs — the `cluster_bundle`
15217/// `helmrelease.yaml` format-string template's install-path
15218/// remediation sub-block-header nesting the retry-cap leaf under the
15219/// sibling
15220/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`]-valued scalar);
15221/// - the rendered `helmrelease.yaml` document's `spec.upgrade.remediation`
15222/// sub-block-header axis (caixa-flux/src/lib.rs — the sibling
15223/// `cluster_bundle` `helmrelease.yaml` format-string template's
15224/// upgrade-path remediation sub-block-header, additionally nesting
15225/// the `remediateLastFailure: true` toggle on the upgrade-path
15226/// sibling axis);
15227/// - the two test-fixture navigation sites in caixa-flux's `mod tests`
15228/// that probe the rendered document's `.get("remediation")` container
15229/// axis to reach the sibling [`FLUX_HELMRELEASE_KEY_RETRIES`] leaf-
15230/// scalar-key pin (the install-path + upgrade-path production-emit
15231/// pins
15232/// [`cluster_bundle_helmrelease_install_remediation_retries_pins_lifted_default`]
15233/// / [`cluster_bundle_helmrelease_upgrade_remediation_retries_pins_lifted_default`]).
15234///
15235/// Both production emit sites + the two test-fixture navigation sites
15236/// name the same Flux v2 per-path per-CR remediation sub-container-axis
15237/// key and must move together on any hypothetical Flux v3 rename
15238/// (upstream Flux v3 roadmap floats candidates like `recovery` /
15239/// `retryPolicy` / `errorHandling` in the migration prose). Until this
15240/// lift landed the axis carried inline `remediation` literals across the
15241/// two production emit sites (caixa-flux/src/lib.rs — the two
15242/// `remediation:` sub-block-header lines inside the `cluster_bundle`
15243/// `helmrelease.yaml` format-string template's install-path + upgrade-
15244/// path per-CR blocks) plus the two test-fixture navigation sites —
15245/// four occurrences of the same load-bearing Flux-v2-per-CR-remediation-
15246/// sub-container-axis-key convention, drift-prone by construction.
15247///
15248/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
15249/// "every recurring shape becomes a generator before it becomes a
15250/// pattern; every pattern becomes a library before it becomes
15251/// duplicated code. The duplication budget is zero.") promotes the
15252/// constant to a typed substrate-side `&'static str` on the same
15253/// trajectory the sibling [`FLUX_HELMRELEASE_KEY_RETRIES`] (a12f9fc)
15254/// leaf-scalar-key half + the sibling
15255/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) scalar-
15256/// value half established — closes the parent-container-axis-key axis
15257/// on the same per-path retry-cap declaration triple, so all three
15258/// halves now live in one place. The two render-side consumers now
15259/// thread the same `&'static str` through their format-string template
15260/// via a `{remediation_key}` named-arg interpolation so a future Flux v3
15261/// rebrand lands in one place; every future renderer that reaches for
15262/// the canonical Flux v2 per-CR per-path remediation sub-container-axis
15263/// key inherits the same value by construction with no opportunity for
15264/// per-renderer drift.
15265///
15266/// Same "the typed constant lives in one place" discipline the sibling
15267/// [`FLUX_HELMRELEASE_KEY_RETRIES`] (a12f9fc) leaf-scalar-key half plus
15268/// the peer [`FLUX_KEY_SOURCE_REF`] (236ef01) / [`FLUX_KEY_CHART`] /
15269/// [`FLUX_KEY_VALUES`] / [`FLUX_KEY_INTERVAL`] /
15270/// [`FLUX_KEY_HEALTH_CHECKS`] container-axis-key lifts apply on the
15271/// peer canonical-Flux-v2-load-bearing-string surface.
15272///
15273/// [cf]: ../../caixa_flux/index.html
15274pub const FLUX_HELMRELEASE_KEY_REMEDIATION: &str = "remediation";
15275
15276/// Canonical Flux v2 `HelmRelease.spec.install` per-CR helm-action-phase
15277/// discriminator parent-container-axis-key every `caixa-flux`-emitted
15278/// `helmrelease.yaml` document nests the sibling
15279/// [`FLUX_HELMRELEASE_KEY_REMEDIATION`] (6fe4e7e) sub-container-axis-key
15280/// under, at the first-time chart apply per-CR phase the Flux v2 helm-
15281/// controller reconciles when the emitted `HelmRelease` CR first lands in
15282/// the cluster. Pairs with the sibling [`FLUX_HELMRELEASE_KEY_UPGRADE`]
15283/// per-CR helm-action-phase discriminator parent-container-axis-key on
15284/// the peer per-CR upgrade-path phase the helm-controller reconciles on
15285/// every subsequent per-version chart re-apply the same CR gates. The
15286/// Flux v2 helm-controller-side per-CR phase-dispatch loop keys off this
15287/// exact parent-container-axis-key to select the install-path per-CR
15288/// action pipeline (`createNamespace` seeder, first-time chart values
15289/// merge, `spec.install.remediation.retries` retry-cap ceiling under the
15290/// nested [`FLUX_HELMRELEASE_KEY_REMEDIATION`] sub-container), so drift
15291/// on this axis is exactly as load-bearing as drift on the nested
15292/// [`FLUX_HELMRELEASE_KEY_REMEDIATION`] sub-container-axis-key it hosts
15293/// (a `"initialize"` / `"apply"` / `"create"` / `"first-run"` typo at
15294/// the production-code call site silently strips the entire install-path
15295/// per-CR phase block from the emitted per-CR document — the helm-
15296/// controller then falls back to the Flux v2 upstream defaults for the
15297/// whole install-path phase surface rather than the substrate's chosen
15298/// per-CR install-path knob-set — `createNamespace` never fires, the
15299/// per-CR retry-cap ceiling silently drops off the emitted document,
15300/// with no diagnostic naming the phase-discriminator-drift root cause
15301/// far from the source `caixa.lisp` / the renderer's format-string
15302/// template).
15303///
15304/// The single source of truth every rendered Flux bundle axis that names
15305/// the per-CR install-path phase parent-container reaches for:
15306///
15307/// - the rendered `helmrelease.yaml` document's `spec.install` sub-
15308/// block-header axis (caixa-flux/src/lib.rs — the `cluster_bundle`
15309/// `helmrelease.yaml` format-string template's install-path sub-
15310/// block-header nesting the `createNamespace: true` seeder + the
15311/// sibling [`FLUX_HELMRELEASE_KEY_REMEDIATION`]-container-keyed
15312/// retry-cap sub-block);
15313/// - the test-fixture navigation site in caixa-flux's `mod tests` that
15314/// probes the rendered document's `.get("install")` container axis
15315/// to reach the sibling [`FLUX_HELMRELEASE_KEY_REMEDIATION`] sub-
15316/// container (the install-path production-emit pin
15317/// [`cluster_bundle_helmrelease_install_remediation_retries_pins_lifted_default`]).
15318///
15319/// Both the production emit site + the test-fixture navigation site name
15320/// the same Flux v2 per-CR install-path helm-action-phase discriminator
15321/// parent-container-axis-key and must move together on any hypothetical
15322/// Flux v3 rename (upstream Flux v3 roadmap floats candidates like
15323/// `initialize` / `apply` / `create` / `first-run` in the migration
15324/// prose). Until this lift landed the axis carried inline `install`
15325/// literals across the one production emit site (caixa-flux/src/lib.rs —
15326/// the `install:` sub-block-header line inside the `cluster_bundle`
15327/// `helmrelease.yaml` format-string template's per-CR install-path block)
15328/// plus the one test-fixture navigation site — two occurrences of the
15329/// same load-bearing Flux-v2-per-CR-install-path-phase-discriminator-
15330/// parent-container-axis-key convention, drift-prone by construction.
15331///
15332/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5, "every
15333/// recurring shape becomes a generator before it becomes a pattern; every
15334/// pattern becomes a library before it becomes duplicated code. The
15335/// duplication budget is zero.") promotes the constant to a typed
15336/// substrate-side `&'static str` on the same trajectory the sibling
15337/// [`FLUX_HELMRELEASE_KEY_REMEDIATION`] (6fe4e7e) sub-container-axis-key +
15338/// [`FLUX_HELMRELEASE_KEY_RETRIES`] (a12f9fc) leaf-scalar-key +
15339/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) scalar-
15340/// value halves of the same `(parent-container-key, sub-container-key,
15341/// leaf-key, scalar-value)` per-path retry-cap declaration quartet
15342/// established — extends the discipline from the sub-container-axis-key
15343/// one level up to the parent-container-axis-key hosting it, so the
15344/// four-level nested `spec.install.remediation.retries` declaration now
15345/// resolves through four lifted `&'static str` / `u32` values. Companion
15346/// to the sibling [`FLUX_HELMRELEASE_KEY_UPGRADE`] per-CR upgrade-path
15347/// phase-discriminator parent-container-axis-key on the peer per-CR
15348/// helm-action-phase surface — completes the per-CR helm-action-phase
15349/// discriminator parent-container-axis-key pair the Flux v2 helm-
15350/// controller reconciles between at first-time chart apply time
15351/// (install-path phase) vs. every subsequent per-version chart re-apply
15352/// (upgrade-path phase).
15353///
15354/// Same "the typed constant lives in one place" discipline the sibling
15355/// [`FLUX_HELMRELEASE_KEY_REMEDIATION`] (6fe4e7e) sub-container-axis-key +
15356/// the peer [`FLUX_KEY_SOURCE_REF`] (236ef01) / [`FLUX_KEY_CHART`] /
15357/// [`FLUX_KEY_VALUES`] / [`FLUX_KEY_INTERVAL`] /
15358/// [`FLUX_KEY_HEALTH_CHECKS`] per-CR container-axis-key lifts apply on
15359/// the peer canonical-Flux-v2-load-bearing-string surface.
15360///
15361/// [cf]: ../../caixa_flux/index.html
15362pub const FLUX_HELMRELEASE_KEY_INSTALL: &str = "install";
15363
15364/// Canonical Flux v2 `HelmRelease.spec.upgrade` per-CR helm-action-phase
15365/// discriminator parent-container-axis-key every `caixa-flux`-emitted
15366/// `helmrelease.yaml` document nests the sibling
15367/// [`FLUX_HELMRELEASE_KEY_REMEDIATION`] (6fe4e7e) sub-container-axis-key
15368/// under, at every subsequent per-version chart re-apply per-CR phase the
15369/// Flux v2 helm-controller reconciles after the initial install-path
15370/// phase completes. Pairs with the sibling
15371/// [`FLUX_HELMRELEASE_KEY_INSTALL`] per-CR helm-action-phase discriminator
15372/// parent-container-axis-key on the peer per-CR install-path phase the
15373/// helm-controller reconciles at first-time chart apply. The Flux v2
15374/// helm-controller-side per-CR phase-dispatch loop keys off this exact
15375/// parent-container-axis-key to select the upgrade-path per-CR action
15376/// pipeline (`remediateLastFailure` toggle the substrate pins to `true`
15377/// on the upgrade-path per-CR sibling axis, the per-CR retry-cap ceiling
15378/// under the nested [`FLUX_HELMRELEASE_KEY_REMEDIATION`] sub-container),
15379/// so drift on this axis is exactly as load-bearing as drift on the
15380/// nested [`FLUX_HELMRELEASE_KEY_REMEDIATION`] sub-container-axis-key it
15381/// hosts (a `"reapply"` / `"reconcile"` / `"update"` / `"promote"` typo
15382/// at the production-code call site silently strips the entire upgrade-
15383/// path per-CR phase block from the emitted per-CR document — the helm-
15384/// controller then falls back to the Flux v2 upstream defaults for the
15385/// whole upgrade-path phase surface rather than the substrate's chosen
15386/// per-CR upgrade-path knob-set — `remediateLastFailure` never fires, the
15387/// per-CR retry-cap ceiling silently drops off the emitted document, with
15388/// no diagnostic naming the phase-discriminator-drift root cause far
15389/// from the source `caixa.lisp` / the renderer's format-string template).
15390///
15391/// The single source of truth every rendered Flux bundle axis that names
15392/// the per-CR upgrade-path phase parent-container reaches for:
15393///
15394/// - the rendered `helmrelease.yaml` document's `spec.upgrade` sub-
15395/// block-header axis (caixa-flux/src/lib.rs — the `cluster_bundle`
15396/// `helmrelease.yaml` format-string template's upgrade-path sub-
15397/// block-header nesting the substrate's `remediateLastFailure: true`
15398/// toggle + the sibling [`FLUX_HELMRELEASE_KEY_REMEDIATION`]-
15399/// container-keyed retry-cap sub-block);
15400/// - the test-fixture navigation site in caixa-flux's `mod tests` that
15401/// probes the rendered document's `.get("upgrade")` container axis to
15402/// reach the sibling [`FLUX_HELMRELEASE_KEY_REMEDIATION`] sub-
15403/// container (the upgrade-path production-emit pin
15404/// [`cluster_bundle_helmrelease_upgrade_remediation_retries_pins_lifted_default`]).
15405///
15406/// Both the production emit site + the test-fixture navigation site name
15407/// the same Flux v2 per-CR upgrade-path helm-action-phase discriminator
15408/// parent-container-axis-key and must move together on any hypothetical
15409/// Flux v3 rename (upstream Flux v3 roadmap floats candidates like
15410/// `reapply` / `reconcile` / `update` / `promote` in the migration
15411/// prose). Until this lift landed the axis carried inline `upgrade`
15412/// literals across the one production emit site plus the one test-
15413/// fixture navigation site — two occurrences of the same load-bearing
15414/// Flux-v2-per-CR-upgrade-path-phase-discriminator-parent-container-
15415/// axis-key convention, drift-prone by construction.
15416///
15417/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5, "every
15418/// recurring shape becomes a generator before it becomes a pattern; every
15419/// pattern becomes a library before it becomes duplicated code. The
15420/// duplication budget is zero.") promotes the constant to a typed
15421/// substrate-side `&'static str` on the same trajectory the sibling
15422/// [`FLUX_HELMRELEASE_KEY_INSTALL`] per-CR install-path phase-
15423/// discriminator parent-container-axis-key +
15424/// [`FLUX_HELMRELEASE_KEY_REMEDIATION`] (6fe4e7e) sub-container-axis-key +
15425/// [`FLUX_HELMRELEASE_KEY_RETRIES`] (a12f9fc) leaf-scalar-key +
15426/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) scalar-
15427/// value halves of the same `(parent-container-key, sub-container-key,
15428/// leaf-key, scalar-value)` per-path retry-cap declaration quartet
15429/// established — pairs with the [`FLUX_HELMRELEASE_KEY_INSTALL`]
15430/// mandatory-arm parent-container-axis-key to close the per-CR helm-
15431/// action-phase discriminator parent-container-axis-key pair across
15432/// both per-CR phases the helm-controller reconciles between (install-
15433/// path at first-time chart apply, upgrade-path at every subsequent
15434/// per-version chart re-apply).
15435///
15436/// Same "the typed constant lives in one place" discipline the sibling
15437/// [`FLUX_HELMRELEASE_KEY_INSTALL`] per-CR install-path-phase-
15438/// discriminator + [`FLUX_HELMRELEASE_KEY_REMEDIATION`] (6fe4e7e) sub-
15439/// container-axis-key + the peer [`FLUX_KEY_SOURCE_REF`] (236ef01) /
15440/// [`FLUX_KEY_CHART`] / [`FLUX_KEY_VALUES`] / [`FLUX_KEY_INTERVAL`] /
15441/// [`FLUX_KEY_HEALTH_CHECKS`] per-CR container-axis-key lifts apply on
15442/// the peer canonical-Flux-v2-load-bearing-string surface.
15443///
15444/// [cf]: ../../caixa_flux/index.html
15445pub const FLUX_HELMRELEASE_KEY_UPGRADE: &str = "upgrade";
15446
15447/// Canonical Flux v2 `HelmRelease.spec.upgrade.remediation.remediateLastFailure`
15448/// upgrade-path-only per-CR remediation-toggle leaf-scalar-key every
15449/// `caixa-flux`-emitted `helmrelease.yaml` document seeds to `true` under
15450/// the sibling [`FLUX_HELMRELEASE_KEY_UPGRADE`] per-CR upgrade-path phase-
15451/// discriminator parent-container-axis-key's nested
15452/// [`FLUX_HELMRELEASE_KEY_REMEDIATION`] sub-container-axis-key. Sibling to
15453/// the peer [`FLUX_HELMRELEASE_KEY_RETRIES`] retry-cap leaf-scalar-key at
15454/// the same per-CR upgrade-path per-CR remediation sub-container position —
15455/// closes the `spec.upgrade.remediation.{retries, remediateLastFailure}`
15456/// per-path remediation-block leaf-scalar-key pair the substrate seeds into
15457/// every emitted per-caixa `HelmRelease` CR on the upgrade-path per-CR
15458/// remediation block, with retries capping the per-version chart re-apply
15459/// retry-count and remediateLastFailure gating the "the Flux v2 helm-
15460/// controller must actively remediate — roll back to the prior success —
15461/// when the final per-version chart re-apply attempt still fails" post-
15462/// retry-exhaustion behavior. The Flux v2 helm-controller-side per-CR
15463/// upgrade-path remediation loop keys off this exact leaf to decide
15464/// whether to leave a failed upgrade in place (`false`) or trigger the
15465/// prior-release rollback pipeline (`true`); drift on this axis silently
15466/// drops the substrate's chosen post-retry-exhaustion rollback semantic
15467/// from every emitted per-caixa `HelmRelease` document (the helm-
15468/// controller then leaves every terminally-failed upgrade in the failed
15469/// state without rolling back to the prior last-known-good release the
15470/// substrate's "no chart apply leaves a per-caixa CR in a stalled,
15471/// unremediated state" MESH-COMPOSITION.md §V guarantee mandates — with
15472/// no diagnostic naming the remediation-toggle-drift root cause far from
15473/// the source `caixa.lisp` / the renderer's format-string template).
15474///
15475/// Note the axis is asymmetric across the peer install-path per-CR
15476/// remediation block: the substrate emits the toggle only under
15477/// `spec.upgrade.remediation` and not under `spec.install.remediation`
15478/// because the Flux v2 helm-controller's install-path per-CR remediation
15479/// loop treats a failed first-time chart apply as an uninstall-and-retry
15480/// pipeline whose "prior success" state is the empty pre-install cluster
15481/// state — the "roll back to the prior success" post-retry-exhaustion
15482/// behavior the toggle gates is well-defined only on the upgrade-path
15483/// where the prior success is a previous chart-version release, which is
15484/// why the [`FLUX_HELMRELEASE_KEY_RETRIES`] retry-cap leaf-scalar-key
15485/// sits under both per-CR remediation sub-containers (retry-cap applies
15486/// on both paths) but this per-CR remediation-toggle leaf-scalar-key
15487/// sits under the upgrade-path per-CR remediation sub-container only.
15488///
15489/// The single source of truth every rendered Flux bundle axis that names
15490/// the upgrade-path per-CR remediation-toggle leaf reaches for:
15491///
15492/// - the rendered `helmrelease.yaml` document's
15493/// `spec.upgrade.remediation.remediateLastFailure` leaf-scalar-key
15494/// axis (caixa-flux/src/lib.rs — the `cluster_bundle` `helmrelease
15495/// .yaml` format-string template's upgrade-path remediation-toggle
15496/// leaf under the [`FLUX_HELMRELEASE_KEY_REMEDIATION`]-container-keyed
15497/// sub-block, threading the same `&'static str` through a new
15498/// `{remediate_last_failure_key}` named-arg interpolation);
15499/// - the one test-fixture navigation site in caixa-flux's `mod tests`
15500/// that probes the rendered document's `.get("remediateLastFailure")`
15501/// leaf axis to pin the substrate's canonical `true` seed
15502/// (the [`cluster_bundle_helmrelease_upgrade_remediation_remediate_last_failure_pins_lifted_true`]
15503/// upgrade-path production-emit pin).
15504///
15505/// Both the production emit site + the one test-fixture navigation site
15506/// name the same Flux v2 per-CR upgrade-path remediation-toggle leaf-
15507/// scalar-key and must move together on any hypothetical Flux v3 rename
15508/// (upstream Flux v3 roadmap floats candidates like
15509/// `rollbackOnFailure` / `remediateOnFailure` / `recoverLastFailure` in
15510/// the migration prose). Until this lift landed the axis carried inline
15511/// `remediateLastFailure` literals across the one production emit site
15512/// (caixa-flux/src/lib.rs — the `remediateLastFailure: true` leaf inside
15513/// the `cluster_bundle` `helmrelease.yaml` format-string template's per-
15514/// CR upgrade-path remediation sub-block) — the sole occurrence of the
15515/// same load-bearing Flux-v2-per-CR-upgrade-path-remediation-toggle-
15516/// leaf-scalar-key convention, drift-prone by construction ahead of the
15517/// second occurrence the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
15518/// materializer's per-Aplicacao `HelmRelease` synthesis will surface,
15519/// where a per-renderer local `pub const FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE:
15520/// &str = "…"` (the canonical drift footgun where a sibling local
15521/// `pub const` could happen to carry the same string at the source while
15522/// pointing at a different `&'static` allocation) would let the two
15523/// renderers silently disagree on the post-retry-exhaustion remediation
15524/// semantic.
15525///
15526/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5, "every
15527/// recurring shape becomes a generator before it becomes a pattern; every
15528/// pattern becomes a library before it becomes duplicated code. The
15529/// duplication budget is zero.") promotes the constant to a typed
15530/// substrate-side `&'static str` in advance of the second occurrence the
15531/// M4 materializer will surface — so the second consumer inherits the
15532/// canonical upgrade-path per-CR remediation-toggle leaf-scalar-key by
15533/// construction without opportunity for per-renderer drift.
15534///
15535/// Same "the typed constant lives in one place" discipline the sibling
15536/// [`FLUX_HELMRELEASE_KEY_RETRIES`] (a12f9fc) leaf-scalar-key +
15537/// [`FLUX_HELMRELEASE_KEY_REMEDIATION`] (6fe4e7e) sub-container-axis-key +
15538/// [`FLUX_HELMRELEASE_KEY_INSTALL`] / [`FLUX_HELMRELEASE_KEY_UPGRADE`]
15539/// (7767c26) parent-container-axis-key pair +
15540/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) scalar-
15541/// value halves of the per-path per-CR remediation surface established —
15542/// closes the sibling upgrade-path-only per-CR remediation-toggle leaf-
15543/// scalar-key half at the same `spec.upgrade.remediation.*` position the
15544/// retries leaf sits at.
15545///
15546/// [cf]: ../../caixa_flux/index.html
15547pub const FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE: &str = "remediateLastFailure";
15548
15549/// Canonical Flux v2 `HelmRelease.spec.upgrade.remediation.remediateLastFailure`
15550/// upgrade-path-only per-CR remediation-toggle scalar-value default the
15551/// substrate seeds into every per-caixa `helmrelease.yaml` document at the
15552/// paired [`FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE`] leaf-scalar-key
15553/// axis. Pairs with the sibling [`FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE`]
15554/// (96581b7) leaf-scalar-key half of the same `(leaf-key, scalar-value)`
15555/// per-CR upgrade-path per-CR post-retry-exhaustion-rollback-toggle
15556/// declaration pair — the Flux v2 helm-controller's per-CR upgrade-path
15557/// remediation loop reads the scalar under that exact leaf key to decide
15558/// whether to trigger the prior-release rollback pipeline once the paired
15559/// [`FLUX_HELMRELEASE_KEY_RETRIES`] retry-cap ceiling has been exhausted,
15560/// so drift on either axis is equally load-bearing (a rebrand on this
15561/// canonical scalar-value default that failed to reach every renderer's
15562/// emit site would silently split the substrate's chosen post-retry-
15563/// exhaustion rollback semantic between the operator-facing canonical
15564/// default and every per-caixa `HelmRelease` document's per-CR upgrade-
15565/// path remediation-toggle, with no field naming the semantic-drift root
15566/// cause far from the source `caixa.lisp` / the renderer's format-string
15567/// template).
15568///
15569/// The `true` seed opts every emitted per-caixa `HelmRelease` into the
15570/// substrate's canonical "no chart apply leaves a per-caixa CR in a
15571/// stalled, unremediated state" semantic (MESH-COMPOSITION.md §V): once
15572/// the paired [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] retry-cap
15573/// ceiling is exhausted on the upgrade-path per-CR reconcile loop the
15574/// helm-controller rolls the per-caixa release back to the prior last-
15575/// known-good `HelmRelease.status.lastAppliedRevision` snapshot rather
15576/// than leaving the per-caixa `HelmRelease` parked at `Ready: False`
15577/// with no forward-progress on the substrate's per-caixa reconciliation
15578/// topology. A future substrate-side rebrand to `false` (or a per-caixa
15579/// opt-out slot the ABSORPTION-ROADMAP.md M4 typed-slot trajectory adds
15580/// once the substrate grows a `:upgrade :remediate-last-failure` author-
15581/// side toggle) is a one-line edit on this canonical declaration, not a
15582/// coordinated rewrite across every future per-target renderer the
15583/// substrate adds. Peer with the sibling
15584/// [`FLUX_KUSTOMIZATION_PRUNE_DEFAULT`] (ea857d8) scalar-value default on
15585/// the peer canonical-Flux-v2-per-CR-substrate-default surface — the
15586/// garbage-collection-toggle default names whether the per-CR
15587/// `Kustomization` reconcile loop sweeps orphaned resources at all, and
15588/// this remediation-toggle default names whether the per-CR `HelmRelease`
15589/// upgrade-path remediation loop rolls back to the prior last-known-good
15590/// release once the retry-cap ceiling is exhausted. Both are substrate-
15591/// side policy choices the operator inherits when the per-caixa
15592/// [`ClusterBundleOpts`][co] doesn't pin an override, and both must move
15593/// together on any coordinated substrate-side Flux v2 per-CR
15594/// tuning-cycle promotion.
15595///
15596/// The single source of truth every rendered Flux bundle axis that
15597/// names the per-CR upgrade-path remediation-toggle scalar reaches for:
15598///
15599/// - the rendered `helmrelease.yaml` document's
15600/// `spec.upgrade.remediation.remediateLastFailure` scalar-value axis
15601/// (caixa-flux/src/lib.rs — the [`cluster_bundle`][cb]
15602/// `helmrelease.yaml` format-string template's per-CR upgrade-path
15603/// remediation-toggle scalar under the
15604/// [`FLUX_HELMRELEASE_KEY_UPGRADE`]-keyed sub-block, threading the
15605/// same `bool` through a `{remediate_last_failure_default}` named-arg
15606/// interpolation);
15607/// - the one test-fixture navigation site in caixa-flux's `mod tests`
15608/// that probes the rendered document's
15609/// `.get("remediateLastFailure")` scalar axis to pin the substrate's
15610/// canonical `true` seed against the lifted default (the
15611/// [`cluster_bundle_helmrelease_upgrade_remediation_remediate_last_failure_pins_lifted_true`]
15612/// per-CR production-emit pin).
15613///
15614/// Both the production emit site + the one test-fixture navigation site
15615/// now consume the same `bool` at emit time through the sibling
15616/// re-export [`caixa_flux::FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT`][cf],
15617/// so a future substrate-side toggle migration on the canonical scalar-
15618/// value axis reaches every consumer through one `bool` by construction —
15619/// with no opportunity for per-renderer drift where a rebrand on one
15620/// axis without a coordinated edit on the other would silently disagree
15621/// on the post-retry-exhaustion rollback semantic. Until this lift
15622/// landed the axis carried an inline `true` scalar-value literal at the
15623/// sole production-code call site (the `remediateLastFailure: true` leaf
15624/// inside the [`cluster_bundle`][cb] `helmrelease.yaml` format-string
15625/// template's per-CR `spec.upgrade.remediation` sub-block) plus the
15626/// sibling test-fixture navigation site — two occurrences of the same
15627/// load-bearing Flux-v2-per-CR-upgrade-path-remediation-toggle-scalar-
15628/// value convention, drift-prone by construction ahead of the third
15629/// occurrence the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
15630/// per-Aplicacao `HelmRelease` synthesis will surface, where a per-
15631/// renderer local `pub const FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT: bool = …`
15632/// at any downstream renderer would let the two consumers silently
15633/// disagree on the substrate's canonical seed.
15634///
15635/// Same "the typed constant lives in one place" discipline the
15636/// [`DEFAULT_NAMESPACE`] (a085b26) / [`DEFAULT_FLUX_SYSTEM_NAMESPACE`]
15637/// (7197d38) / [`DEFAULT_LIBRARY_NAME`] (41438dc) /
15638/// [`crate::DEFAULT_SERVICO_PORT`] (1e22add) /
15639/// [`DEFAULT_GATEWAY_CLASS_NAME`] (d9b0743) /
15640/// [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] (0a6a602) /
15641/// [`DEFAULT_FLUX_RECONCILE_INTERVAL`] (908180f) /
15642/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) /
15643/// [`FLUX_KUSTOMIZATION_PRUNE_DEFAULT`] (ea857d8) lifts apply on the peer
15644/// canonical-substrate-default-load-bearing-scalar surface.
15645///
15646/// [cb]: ../../caixa_flux/fn.cluster_bundle.html
15647/// [cf]: ../../caixa_flux/index.html
15648/// [co]: ../../caixa_flux/struct.ClusterBundleOpts.html
15649pub const FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT: bool = true;
15650
15651/// Canonical Flux v2 `HelmRelease.spec.install.createNamespace` install-path-
15652/// only per-CR namespace-seeder-toggle leaf-scalar-key every `caixa-flux`-
15653/// emitted `helmrelease.yaml` document seeds to `true` under the sibling
15654/// [`FLUX_HELMRELEASE_KEY_INSTALL`] per-CR install-path phase-discriminator
15655/// parent-container-axis-key. Peer to the sibling
15656/// [`FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE`] upgrade-path-only per-CR
15657/// remediation-toggle leaf-scalar-key at the co-resident per-CR install/
15658/// upgrade phase-discriminator parent-container position — closes the
15659/// `spec.{install.createNamespace, upgrade.remediation.remediateLastFailure}`
15660/// per-path per-CR phase-specific toggle leaf-scalar-key pair the substrate
15661/// seeds into every emitted per-caixa `HelmRelease` CR: `createNamespace`
15662/// gates the "the Flux v2 helm-controller creates the target namespace
15663/// itself if the emitted `HelmRelease.metadata.namespace` (or its
15664/// `spec.targetNamespace` override) does not already exist" install-path
15665/// pre-apply seeder pipeline, while `remediateLastFailure` gates the
15666/// upgrade-path post-retry-exhaustion rollback pipeline. The Flux v2 helm-
15667/// controller-side per-CR install-path pre-apply loop keys off this exact
15668/// leaf to decide whether to first materialize the target namespace or
15669/// refuse the first-time chart apply when the target namespace does not
15670/// yet exist (`false`); drift on this axis silently drops the substrate's
15671/// chosen first-apply namespace-seeder semantic from every emitted per-
15672/// caixa `HelmRelease` document (the helm-controller then refuses every
15673/// first-time per-caixa chart apply against a fresh cluster whose target
15674/// namespace has not been pre-provisioned by an out-of-band pipeline —
15675/// the substrate's "no per-caixa Servico apply is blocked on manual
15676/// namespace preprovisioning" MESH-COMPOSITION.md §V install-path-fluency
15677/// guarantee silently regresses, with no diagnostic naming the seeder-
15678/// toggle-drift root cause far from the source `caixa.lisp` / the
15679/// renderer's format-string template).
15680///
15681/// Note the axis is asymmetric across the peer upgrade-path per-CR phase
15682/// block: the substrate emits the toggle only under `spec.install` and not
15683/// under `spec.upgrade` because the Flux v2 helm-controller's upgrade-path
15684/// per-CR reconcile loop presupposes the target namespace already carries
15685/// the prior release's resources (the upgrade-path is by definition a
15686/// re-apply against an already-materialized namespace whose pre-apply
15687/// seeding was resolved at the sibling install-path phase's first-time
15688/// apply), so the "seed the target namespace if it does not already exist"
15689/// pre-apply behavior the toggle gates is well-defined only on the
15690/// install-path where the target namespace's existence is not yet
15691/// established. This is the mirror of the peer sibling
15692/// [`FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE`] axis, which is
15693/// upgrade-path-only for the mirror reason (the "roll back to the prior
15694/// success" post-retry-exhaustion behavior is well-defined only on the
15695/// upgrade-path where a prior success exists) — the two per-CR phase-
15696/// specific toggle leaf-scalar-keys sit under mirror-symmetric
15697/// parent-container-axis-keys and together close the install/upgrade
15698/// phase-block per-CR-phase-specific toggle leaf-scalar-key pair.
15699///
15700/// The single source of truth every rendered Flux bundle axis that names
15701/// the install-path per-CR namespace-seeder-toggle leaf reaches for:
15702///
15703/// - the rendered `helmrelease.yaml` document's
15704/// `spec.install.createNamespace` leaf-scalar-key axis
15705/// (caixa-flux/src/lib.rs — the `cluster_bundle` `helmrelease.yaml`
15706/// format-string template's install-path namespace-seeder-toggle leaf
15707/// under the [`FLUX_HELMRELEASE_KEY_INSTALL`]-container-keyed sub-block,
15708/// threading the same `&'static str` through a new
15709/// `{create_namespace_key}` named-arg interpolation);
15710/// - the one test-fixture navigation site in caixa-flux's `mod tests`
15711/// that probes the rendered document's `.get("createNamespace")` leaf
15712/// axis to pin the substrate's canonical `true` seed
15713/// (the [`cluster_bundle_helmrelease_install_create_namespace_pins_lifted_true`]
15714/// install-path production-emit pin).
15715///
15716/// Both the production emit site + the one test-fixture navigation site
15717/// name the same Flux v2 per-CR install-path namespace-seeder-toggle leaf-
15718/// scalar-key and must move together on any hypothetical Flux v3 rename
15719/// (upstream Flux v3 roadmap floats candidates like `createTargetNamespace`
15720/// / `seedNamespace` / `provisionNamespace` in the migration prose). Until
15721/// this lift landed the axis carried inline `createNamespace` literals
15722/// across the one production emit site (caixa-flux/src/lib.rs — the
15723/// `createNamespace: true` leaf inside the `cluster_bundle` `helmrelease
15724/// .yaml` format-string template's per-CR install-path sub-block) — the
15725/// sole occurrence of the same load-bearing Flux-v2-per-CR-install-path-
15726/// namespace-seeder-toggle-leaf-scalar-key convention, drift-prone by
15727/// construction ahead of the second occurrence the M4
15728/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
15729/// `HelmRelease` synthesis will surface, where a per-renderer local
15730/// `pub const FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE: &str = "…"` (the
15731/// canonical drift footgun where a sibling local `pub const` could happen
15732/// to carry the same string at the source while pointing at a different
15733/// `&'static` allocation) would let the two renderers silently disagree on
15734/// the install-path namespace-seeder semantic.
15735///
15736/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5, "every
15737/// recurring shape becomes a generator before it becomes a pattern; every
15738/// pattern becomes a library before it becomes duplicated code. The
15739/// duplication budget is zero.") promotes the constant to a typed
15740/// substrate-side `&'static str` in advance of the second occurrence the
15741/// M4 materializer will surface — so the second consumer inherits the
15742/// canonical install-path per-CR namespace-seeder-toggle leaf-scalar-key
15743/// by construction without opportunity for per-renderer drift.
15744///
15745/// Same "the typed constant lives in one place" discipline the sibling
15746/// [`FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE`] (96581b7) upgrade-path-
15747/// only per-CR remediation-toggle leaf-scalar-key +
15748/// [`FLUX_HELMRELEASE_KEY_RETRIES`] (a12f9fc) leaf-scalar-key +
15749/// [`FLUX_HELMRELEASE_KEY_REMEDIATION`] (6fe4e7e) sub-container-axis-key +
15750/// [`FLUX_HELMRELEASE_KEY_INSTALL`] / [`FLUX_HELMRELEASE_KEY_UPGRADE`]
15751/// (7767c26) parent-container-axis-key pair +
15752/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) scalar-value
15753/// halves of the per-path per-CR HelmRelease spec surface established —
15754/// closes the mirror install-path-only per-CR namespace-seeder-toggle
15755/// leaf-scalar-key half at the `spec.install.createNamespace` position the
15756/// peer `spec.upgrade.remediation.remediateLastFailure` upgrade-path-only
15757/// per-CR remediation-toggle leaf mirrors.
15758///
15759/// [cf]: ../../caixa_flux/index.html
15760pub const FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE: &str = "createNamespace";
15761
15762/// Canonical Flux v2 `HelmRelease.spec.install.createNamespace` install-path-
15763/// only per-CR namespace-seeder-toggle scalar-value default the substrate
15764/// seeds into every per-caixa `helmrelease.yaml` document at the paired
15765/// [`FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE`] leaf-scalar-key axis. Pairs
15766/// with the sibling [`FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE`] (ba9ab8b)
15767/// leaf-scalar-key half of the same `(leaf-key, scalar-value)` per-CR
15768/// install-path per-CR namespace-seeder-toggle declaration pair — the
15769/// Flux v2 helm-controller's per-CR install-path pre-apply loop reads
15770/// the scalar under that exact leaf key to decide whether to first
15771/// materialize the target namespace before the first-time chart apply,
15772/// so drift on either axis is equally load-bearing (a rebrand on this
15773/// canonical scalar-value default that failed to reach every renderer's
15774/// emit site would silently split the substrate's chosen first-apply
15775/// namespace-seeder semantic between the operator-facing canonical
15776/// default and every per-caixa `HelmRelease` document's per-CR install-
15777/// path namespace-seeder-toggle, with no field naming the semantic-drift
15778/// root cause far from the source `caixa.lisp` / the renderer's format-
15779/// string template).
15780///
15781/// The `true` seed opts every emitted per-caixa `HelmRelease` into the
15782/// substrate's canonical "no per-caixa Servico apply is blocked on
15783/// manual namespace preprovisioning" semantic (MESH-COMPOSITION.md §V
15784/// install-path-fluency guarantee): on every first-time per-caixa chart
15785/// apply the helm-controller first materializes the target namespace
15786/// itself if the emitted `HelmRelease.metadata.namespace` (or its
15787/// `spec.targetNamespace` override) does not already exist, rather than
15788/// refusing the apply and requiring an out-of-band pipeline to have
15789/// pre-provisioned the namespace. A future substrate-side rebrand to
15790/// `false` (or a per-caixa opt-out slot the ABSORPTION-ROADMAP.md M4
15791/// typed-slot trajectory adds once the substrate grows a `:install
15792/// :create-namespace` author-side toggle) is a one-line edit on this
15793/// canonical declaration, not a coordinated rewrite across every future
15794/// per-target renderer the substrate adds. Peer with the sibling
15795/// [`FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT`] mirror-symmetric
15796/// upgrade-path-only scalar-value default + the peer
15797/// [`FLUX_KUSTOMIZATION_PRUNE_DEFAULT`] (ea857d8) scalar-value default on
15798/// the peer canonical-Flux-v2-per-CR-substrate-default surface — the
15799/// three defaults name the substrate's canonical (install-path
15800/// namespace-seeder) / (upgrade-path post-retry-exhaustion rollback) /
15801/// (garbage-collection-toggle) toggle triple across the per-caixa
15802/// `HelmRelease` and `Kustomization` co-resident CRs. All three are
15803/// substrate-side policy choices the operator inherits when the per-
15804/// caixa [`ClusterBundleOpts`][co] doesn't pin an override, and all
15805/// three must move together on any coordinated substrate-side Flux v2
15806/// per-CR tuning-cycle promotion.
15807///
15808/// The single source of truth every rendered Flux bundle axis that
15809/// names the per-CR install-path namespace-seeder-toggle scalar reaches
15810/// for:
15811///
15812/// - the rendered `helmrelease.yaml` document's
15813/// `spec.install.createNamespace` scalar-value axis
15814/// (caixa-flux/src/lib.rs — the [`cluster_bundle`][cb]
15815/// `helmrelease.yaml` format-string template's per-CR install-path
15816/// namespace-seeder-toggle scalar under the
15817/// [`FLUX_HELMRELEASE_KEY_INSTALL`]-keyed sub-block, threading the
15818/// same `bool` through a `{create_namespace_default}` named-arg
15819/// interpolation);
15820/// - the one test-fixture navigation site in caixa-flux's `mod tests`
15821/// that probes the rendered document's `.get("createNamespace")`
15822/// scalar axis to pin the substrate's canonical `true` seed against
15823/// the lifted default (the
15824/// [`cluster_bundle_helmrelease_install_create_namespace_pins_lifted_true`]
15825/// per-CR production-emit pin).
15826///
15827/// Both the production emit site + the one test-fixture navigation site
15828/// now consume the same `bool` at emit time through the sibling
15829/// re-export [`caixa_flux::FLUX_HELMRELEASE_CREATE_NAMESPACE_DEFAULT`][cf],
15830/// so a future substrate-side toggle migration on the canonical scalar-
15831/// value axis reaches every consumer through one `bool` by construction —
15832/// with no opportunity for per-renderer drift where a rebrand on one
15833/// axis without a coordinated edit on the other would silently disagree
15834/// on the first-apply namespace-seeder semantic. Until this lift landed
15835/// the axis carried an inline `true` scalar-value literal at the sole
15836/// production-code call site (the `createNamespace: true` leaf inside
15837/// the [`cluster_bundle`][cb] `helmrelease.yaml` format-string
15838/// template's per-CR `spec.install` sub-block) plus the sibling test-
15839/// fixture navigation site — two occurrences of the same load-bearing
15840/// Flux-v2-per-CR-install-path-namespace-seeder-toggle-scalar-value
15841/// convention, drift-prone by construction ahead of the third occurrence
15842/// the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-
15843/// Aplicacao `HelmRelease` synthesis will surface, where a per-renderer
15844/// local `pub const FLUX_HELMRELEASE_CREATE_NAMESPACE_DEFAULT: bool = …`
15845/// at any downstream renderer would let the two consumers silently
15846/// disagree on the substrate's canonical seed.
15847///
15848/// Same "the typed constant lives in one place" discipline the
15849/// [`DEFAULT_NAMESPACE`] (a085b26) / [`DEFAULT_FLUX_SYSTEM_NAMESPACE`]
15850/// (7197d38) / [`DEFAULT_LIBRARY_NAME`] (41438dc) /
15851/// [`crate::DEFAULT_SERVICO_PORT`] (1e22add) /
15852/// [`DEFAULT_GATEWAY_CLASS_NAME`] (d9b0743) /
15853/// [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] (0a6a602) /
15854/// [`DEFAULT_FLUX_RECONCILE_INTERVAL`] (908180f) /
15855/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) /
15856/// [`FLUX_KUSTOMIZATION_PRUNE_DEFAULT`] (ea857d8) /
15857/// [`FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT`] lifts apply on
15858/// the peer canonical-substrate-default-load-bearing-scalar surface.
15859///
15860/// [cb]: ../../caixa_flux/fn.cluster_bundle.html
15861/// [cf]: ../../caixa_flux/index.html
15862/// [co]: ../../caixa_flux/struct.ClusterBundleOpts.html
15863pub const FLUX_HELMRELEASE_CREATE_NAMESPACE_DEFAULT: bool = true;
15864
15865/// Canonical Flux v2 `Kustomization.spec.prune` per-CR garbage-collection-
15866/// toggle leaf-scalar-key every `caixa-flux`-emitted `kustomization.yaml`
15867/// document seeds to `true` at the top-level `spec` position of the
15868/// emitted [`Kustomization`][kust] CR. The Flux v2 kustomize-controller-
15869/// side per-CR reconcile loop keys off this exact leaf to decide whether
15870/// to garbage-collect resources that were previously reconciled by the
15871/// CR but no longer appear in the CR's current desired-state manifest set
15872/// (`spec.prune: true` opts every emitted per-caixa `Kustomization` into
15873/// the substrate's canonical GitOps-side sweep-what-you-removed semantic;
15874/// `spec.prune: false` (or absent — Flux v2 defaults the axis to `false`
15875/// on any CR that omits the leaf) leaves orphaned resources dangling in
15876/// the cluster after the source manifest set removes them, silently
15877/// splitting per-caixa live cluster state from the caixa's tatara-lisp
15878/// source-of-truth and every downstream `feira app deploy` / `feira
15879/// deploy` reconcile the substrate's per-caixa GitOps pipeline emits).
15880///
15881/// Drift on this axis silently drops the substrate's chosen sweep-what-
15882/// you-removed semantic from every emitted per-caixa `Kustomization`
15883/// document — the kustomize-controller then leaves every per-caixa
15884/// resource the source manifest set previously reconciled but no longer
15885/// carries dangling in the cluster with no diagnostic naming the toggle-
15886/// drift root cause far from the source `caixa.lisp` / the renderer's
15887/// format-string template, and the substrate's "the cluster's per-caixa
15888/// live state converges to the caixa's tatara-lisp source-of-truth on
15889/// every reconcile — resources the source no longer carries are swept
15890/// by the kustomize-controller, not left dangling" CAIXA-SDLC.md §V
15891/// author-to-live-convergence guarantee silently regresses.
15892///
15893/// Note the axis is asymmetric across the co-resident `HelmRelease` CR:
15894/// the peer `HelmRelease` document seeds no `spec.prune` leaf because
15895/// the Flux v2 helm-controller-side per-CR reconcile loop keys off Helm
15896/// 3's own release-scoped resource-tracking manifest (the per-release
15897/// `helm.sh/release-name` label + `secrets/sh.helm.release.v1.*` release
15898/// snapshots) to garbage-collect resources removed between chart
15899/// versions rather than a CR-level toggle, so the `spec.prune` leaf is
15900/// well-defined only on the `Kustomization` CR whose kustomize-controller
15901/// reconcile loop tracks resources by the CR's manifest set rather than
15902/// Helm's per-release snapshots. This is the mirror of the peer sibling
15903/// [`FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE`] axis, which is `HelmRelease`-
15904/// CR-only for the mirror reason (Helm 3's chart-side `Chart.yaml`
15905/// declares no target-namespace-creation semantic of its own, so the
15906/// helm-controller carries a per-CR toggle at `spec.install.createNamespace`
15907/// that the peer kustomize-controller has no need to mirror since the
15908/// upstream Kustomize project's per-CR spec block establishes the
15909/// target-namespace independently at each `kustomization.yaml` document's
15910/// own `metadata.namespace` axis).
15911///
15912/// The single source of truth every rendered Flux bundle axis that names
15913/// the per-CR garbage-collection-toggle leaf reaches for:
15914///
15915/// - the rendered `kustomization.yaml` document's `spec.prune` leaf-
15916/// scalar-key axis (caixa-flux/src/lib.rs — the `cluster_bundle`
15917/// `kustomization.yaml` format-string template's per-CR garbage-
15918/// collection-toggle leaf under the top-level `spec` position,
15919/// threading the same `&'static str` through a new `{prune_key}`
15920/// named-arg interpolation);
15921/// - the one test-fixture navigation site in caixa-flux's `mod tests`
15922/// that probes the rendered document's `.get("prune")` leaf axis to
15923/// pin the substrate's canonical `true` seed (the
15924/// [`cluster_bundle_kustomization_prune_pins_lifted_true`] per-CR
15925/// production-emit pin).
15926///
15927/// Both the production emit site + the one test-fixture navigation site
15928/// name the same Flux v2 per-CR garbage-collection-toggle leaf-scalar-
15929/// key and must move together on any hypothetical Flux v3 rename
15930/// (upstream Flux v3 roadmap floats candidates like `garbageCollect` /
15931/// `sweep` / `pruneOrphaned` / `deleteOrphans` in the migration prose).
15932/// Until this lift landed the axis carried an inline `prune` literal at
15933/// the one production emit site (caixa-flux/src/lib.rs — the
15934/// `prune: true` leaf inside the `cluster_bundle` `kustomization.yaml`
15935/// format-string template's top-level `spec` position) — the sole
15936/// occurrence of the same load-bearing Flux-v2-per-CR-garbage-
15937/// collection-toggle-leaf-scalar-key convention, drift-prone by
15938/// construction ahead of the second occurrence the M4
15939/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
15940/// `Kustomization` synthesis will surface, where a per-renderer local
15941/// `pub const FLUX_KUSTOMIZATION_KEY_PRUNE: &str = "…"` (the canonical
15942/// drift footgun where a sibling local `pub const` could happen to
15943/// carry the same string at the source while pointing at a different
15944/// `&'static` allocation) would let the two renderers silently disagree
15945/// on the substrate's canonical sweep-what-you-removed semantic.
15946///
15947/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
15948/// "every recurring shape becomes a generator before it becomes a
15949/// pattern; every pattern becomes a library before it becomes
15950/// duplicated code. The duplication budget is zero.") promotes the
15951/// constant to a typed substrate-side `&'static str` in advance of the
15952/// second occurrence the M4 materializer will surface — so the second
15953/// consumer inherits the canonical per-CR garbage-collection-toggle
15954/// leaf-scalar-key by construction without opportunity for per-renderer
15955/// drift.
15956///
15957/// Same "the typed constant lives in one place" discipline the sibling
15958/// [`FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE`] (ba9ab8b) install-path-only
15959/// per-CR namespace-seeder-toggle leaf-scalar-key +
15960/// [`FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE`] (96581b7) upgrade-
15961/// path-only per-CR remediation-toggle leaf-scalar-key +
15962/// [`FLUX_HELMRELEASE_KEY_RETRIES`] (a12f9fc) leaf-scalar-key +
15963/// [`FLUX_HELMRELEASE_KEY_REMEDIATION`] (6fe4e7e) sub-container-axis-key
15964/// + [`FLUX_HELMRELEASE_KEY_INSTALL`] / [`FLUX_HELMRELEASE_KEY_UPGRADE`]
15965/// (7767c26) parent-container-axis-key pair +
15966/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) scalar-
15967/// value halves of the per-path per-CR HelmRelease spec surface
15968/// established — extends the discipline from the co-resident per-caixa
15969/// `HelmRelease` CR spec surface onto the co-resident per-caixa
15970/// `Kustomization` CR spec surface at the mirror-symmetric top-level
15971/// `spec.prune` position.
15972///
15973/// [cf]: ../../caixa_flux/index.html
15974/// [kust]: https://fluxcd.io/flux/components/kustomize/kustomizations/
15975pub const FLUX_KUSTOMIZATION_KEY_PRUNE: &str = "prune";
15976
15977/// Canonical Flux v2 `Kustomization.spec.prune` per-CR garbage-collection-
15978/// toggle scalar-value default the substrate seeds into every per-caixa
15979/// `kustomization.yaml` document at the paired
15980/// [`FLUX_KUSTOMIZATION_KEY_PRUNE`] leaf-scalar-key axis. Pairs with the
15981/// sibling [`FLUX_KUSTOMIZATION_KEY_PRUNE`] (8ec7917) leaf-scalar-key
15982/// half of the same `(leaf-key, scalar-value)` per-CR garbage-collection-
15983/// toggle declaration pair — the Flux v2 kustomize-controller's per-CR
15984/// reconcile loop reads the scalar under that exact leaf key, so drift
15985/// on either axis is equally load-bearing (a rebrand on this canonical
15986/// scalar-value default that failed to reach every renderer's emit site
15987/// would silently split the substrate's chosen sweep-what-you-removed
15988/// semantic between the operator-facing canonical default and every
15989/// per-caixa `Kustomization` document's per-CR garbage-collection-toggle,
15990/// with no field naming the semantic-drift root cause far from the
15991/// source `caixa.lisp` / the renderer's format-string template).
15992///
15993/// The `true` seed opts every emitted per-caixa `Kustomization` into
15994/// the substrate's canonical GitOps-side sweep-what-you-removed
15995/// semantic: on every reconcile the kustomize-controller garbage-
15996/// collects any per-caixa resource the source manifest set previously
15997/// reconciled but no longer carries, converging the cluster's per-
15998/// caixa live state to the caixa's tatara-lisp source-of-truth
15999/// verbatim. A future substrate-side rebrand to `false` (or a per-
16000/// cluster override the operator pins for a class of clusters where a
16001/// human is expected to prune orphaned resources by hand, or a
16002/// per-caixa opt-out slot the ABSORPTION-ROADMAP.md M4 typed-slot
16003/// trajectory adds once the substrate grows a `:kustomization :prune`
16004/// author-side toggle) is a one-line edit on this canonical declaration,
16005/// not a coordinated rewrite across every future per-target renderer
16006/// the substrate adds. Peer with the sibling
16007/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) scalar-
16008/// value default on the peer canonical-Flux-v2-per-CR-substrate-
16009/// default surface — the retry-cap default names the per-path per-CR
16010/// remediation retry ceiling, and this garbage-collection-toggle
16011/// default names whether the per-CR reconcile loop sweeps orphaned
16012/// resources at all. Both are substrate-side policy choices the
16013/// operator inherits when the per-caixa [`ClusterBundleOpts`][co]
16014/// doesn't pin an override.
16015///
16016/// The single source of truth every rendered Flux bundle axis that
16017/// names the per-CR garbage-collection-toggle scalar reaches for:
16018///
16019/// - the rendered `kustomization.yaml` document's `spec.prune`
16020/// scalar-value axis (caixa-flux/src/lib.rs — the [`cluster_bundle`][cb]
16021/// `kustomization.yaml` format-string template's per-CR garbage-
16022/// collection-toggle scalar under the top-level `spec` position,
16023/// threading the same `bool` through a `{prune_default}` named-arg
16024/// interpolation);
16025/// - the one test-fixture navigation site in caixa-flux's `mod tests`
16026/// that probes the rendered document's `.get("prune")` scalar axis
16027/// to pin the substrate's canonical `true` seed against the lifted
16028/// default (the
16029/// [`cluster_bundle_kustomization_prune_pins_lifted_true`] per-CR
16030/// production-emit pin).
16031///
16032/// Both the production emit site + the one test-fixture navigation site
16033/// now consume the same `bool` at emit time through the sibling
16034/// re-export [`caixa_flux::FLUX_KUSTOMIZATION_PRUNE_DEFAULT`][cf], so a
16035/// future substrate-side toggle migration on the canonical scalar-value
16036/// axis reaches every consumer through one `bool` by construction —
16037/// with no opportunity for per-renderer drift where a rebrand on one
16038/// axis without a coordinated edit on the other would silently disagree
16039/// on the sweep-what-you-removed semantic. Until this lift landed the
16040/// axis carried an inline `true` scalar-value literal at the sole
16041/// production-code call site (the `prune: true` leaf inside the
16042/// [`cluster_bundle`][cb] `kustomization.yaml` format-string template's
16043/// top-level `spec` position) plus the sibling test-fixture navigation
16044/// site — two occurrences of the same load-bearing Flux-v2-per-CR-
16045/// garbage-collection-toggle-scalar-value convention, drift-prone by
16046/// construction ahead of the third occurrence the M4
16047/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
16048/// `Kustomization` synthesis will surface, where a per-renderer local
16049/// `pub const FLUX_KUSTOMIZATION_PRUNE_DEFAULT: bool = …` at any
16050/// downstream renderer would let the two consumers silently disagree
16051/// on the substrate's canonical seed.
16052///
16053/// Same "the typed constant lives in one place" discipline the
16054/// [`DEFAULT_NAMESPACE`] (a085b26) / [`DEFAULT_FLUX_SYSTEM_NAMESPACE`]
16055/// (7197d38) / [`DEFAULT_LIBRARY_NAME`] (41438dc) /
16056/// [`crate::DEFAULT_SERVICO_PORT`] (1e22add) /
16057/// [`DEFAULT_GATEWAY_CLASS_NAME`] (d9b0743) /
16058/// [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] (0a6a602) /
16059/// [`DEFAULT_FLUX_RECONCILE_INTERVAL`] (908180f) /
16060/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) lifts apply
16061/// on the peer canonical-substrate-default-load-bearing-scalar surface.
16062///
16063/// [cb]: ../../caixa_flux/fn.cluster_bundle.html
16064/// [cf]: ../../caixa_flux/index.html
16065/// [co]: ../../caixa_flux/struct.ClusterBundleOpts.html
16066pub const FLUX_KUSTOMIZATION_PRUNE_DEFAULT: bool = true;
16067
16068/// Canonical substrate-side default for the
16069/// `HelmRelease.spec.values.<library>.enabled` scalar-value toggle every
16070/// [`caixa_flux::cluster_bundle`][cb]-emitted `helmrelease.yaml` document
16071/// seeds inside its per-caixa values overlay to force-on the paired
16072/// [`DEFAULT_LIBRARY_NAME`] child chart at the per-cluster
16073/// `HelmRelease`-side apply step. Pairs with the sibling
16074/// [`HELM_VALUES_KEY_ENABLED`] leaf-scalar-key half of the
16075/// `(leaf-key, scalar-value)` per-values-overlay child-chart
16076/// enablement-toggle declaration pair — the key half names the
16077/// canonical `values.<library>.enabled` leaf-scalar-key axis every
16078/// consumer (`caixa-helm`'s `values.yaml` per-chart default, this
16079/// crate's `cluster_bundle` overlay) probes on, and this scalar-value
16080/// half names the substrate-side default the `cluster_bundle` overlay
16081/// path seeds under it. Semantically distinct from — and inverse of —
16082/// the `RenderOpts::enabled_default = false` default that
16083/// [`caixa_helm::RenderOpts::default`] seeds for the standalone
16084/// `lareira-<nome>` chart's own `values.yaml` (that path renders
16085/// `enabled: false` so cluster operators must opt each caixa in
16086/// per-cluster); the `cluster_bundle` composition path is the
16087/// substrate-side opt-in path where the operator has already asserted
16088/// per-caixa cluster-scoped ownership by materializing a per-caixa
16089/// `GitRepository` + `HelmRelease` + `Kustomization` trio, so the overlay
16090/// forces the child chart on by seeding `enabled: true` under the
16091/// `values.<library>` wrap.
16092///
16093/// Rendered to canonical YAML `true` verbatim. A future substrate-side
16094/// rebrand to `false` (or the M4 typed-slot trajectory adding a per-caixa
16095/// `:cluster-bundle :enabled` author-side toggle the operator flips per
16096/// caixa) is a one-line edit on this canonical declaration, not a
16097/// coordinated rewrite across the sole production emit site + its
16098/// paired test-fixture navigation site. Peer with the sibling
16099/// [`FLUX_HELMRELEASE_CREATE_NAMESPACE_DEFAULT`] (be1904b),
16100/// [`FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT`] (be1904b),
16101/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae), and
16102/// [`FLUX_KUSTOMIZATION_PRUNE_DEFAULT`] (ea857d8) scalar-value defaults
16103/// on the peer canonical-Flux-v2-per-CR-substrate-default surface — the
16104/// four sibling scalar-value defaults name per-CR toggle-shape axes at
16105/// the `HelmRelease.spec.install.*` / `HelmRelease.spec.upgrade.*` /
16106/// `HelmRelease.spec.upgrade.remediation.retries` /
16107/// `Kustomization.spec.prune` sub-block positions, and this
16108/// scalar-value default names the child-chart-enablement toggle at the
16109/// deeper `HelmRelease.spec.values.<library>.enabled` values-overlay
16110/// position — all five are substrate-side policy choices the operator
16111/// inherits when the per-caixa [`ClusterBundleOpts`][co] doesn't pin an
16112/// override.
16113///
16114/// The single source of truth every rendered Flux bundle axis that
16115/// names the per-CR values-overlay child-chart-enablement-toggle
16116/// scalar reaches for:
16117///
16118/// - the rendered `helmrelease.yaml` document's
16119/// `spec.values.<library>.enabled` scalar-value axis
16120/// (caixa-flux/src/lib.rs — the [`cluster_bundle`][cb]
16121/// `helmrelease.yaml` format-string template's per-CR values-overlay
16122/// child-chart-enablement-toggle scalar under the per-`{library_name}`
16123/// wrap position, threading the same `bool` through a
16124/// `{lareira_enabled_default}` named-arg interpolation);
16125/// - the one test-fixture navigation site in caixa-flux's `mod tests`
16126/// that probes the rendered document's
16127/// `values.<library>.enabled` scalar axis to pin the substrate's
16128/// canonical `true` seed against the lifted default (the
16129/// `cluster_bundle_helmrelease_wrap_key_pins_canonical_pleme_computeunit_string`
16130/// per-CR production-emit pin's `Some(true)` assertion).
16131///
16132/// Both the production emit site + the test-fixture navigation site now
16133/// consume the same `bool` at emit time through the sibling re-export
16134/// [`caixa_flux::CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT`][cf], so a
16135/// future substrate-side toggle migration reaches every consumer through
16136/// one `bool` by construction — with no opportunity for per-renderer
16137/// drift where a rebrand on one axis without a coordinated edit on the
16138/// other would silently disagree on the substrate's chosen child-chart
16139/// force-on-under-composition semantic. Until this lift landed the
16140/// axis carried an inline `true` scalar-value literal at the sole
16141/// production-code call site (the `{enabled_key}: true` leaf inside the
16142/// [`cluster_bundle`][cb] `helmrelease.yaml` format-string template's
16143/// per-`{library_name}` wrap position) plus the test-fixture
16144/// navigation-site `Some(true)` assertion — two occurrences of the same
16145/// load-bearing values-overlay child-chart-enablement-toggle-scalar-value
16146/// convention, drift-prone by construction ahead of the third occurrence
16147/// the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
16148/// per-Aplicacao `HelmRelease` synthesis will surface.
16149///
16150/// [cb]: ../../caixa_flux/fn.cluster_bundle.html
16151/// [cf]: ../../caixa_flux/index.html
16152/// [co]: ../../caixa_flux/struct.ClusterBundleOpts.html
16153pub const CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT: bool = true;
16154
16155/// Canonical substrate-side default for the
16156/// `values.<library>.enabled` scalar-value toggle every
16157/// [`caixa_helm::render_chart_for_servico`][cs]-emitted standalone
16158/// `lareira-<nome>` chart's `values.yaml` document seeds inside its per-caixa
16159/// [`DEFAULT_LIBRARY_NAME`] wrap block to leave the paired
16160/// [`DEFAULT_LIBRARY_NAME`] child chart opted-out at the per-cluster
16161/// `helm template` / `helm install` apply step. Pairs with the sibling
16162/// [`HELM_VALUES_KEY_ENABLED`] leaf-scalar-key half of the
16163/// `(leaf-key, scalar-value)` per-values-block child-chart-enablement-toggle
16164/// declaration pair — the key half names the canonical
16165/// `values.<library>.enabled` leaf-scalar-key axis every consumer (this
16166/// standalone-path default, [`caixa_flux::cluster_bundle`][cb]'s per-CR
16167/// values-overlay) probes on, and this scalar-value half names the
16168/// substrate-side default the standalone per-chart path seeds under it.
16169/// Semantically distinct from — and inverse of — the peer
16170/// [`CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT`] default that
16171/// [`caixa_flux::cluster_bundle`][cb]'s `helmrelease.yaml` values overlay
16172/// seeds for the substrate-side composition-path force-on (that path
16173/// renders `enabled: true` in the per-cluster `HelmRelease.spec.values.<library>`
16174/// overlay so the operator's per-caixa cluster-scoped ownership at bundle
16175/// materialization time carries a force-on for the child chart); the
16176/// standalone per-chart path is the substrate-side opt-out path where the
16177/// operator has not yet asserted per-caixa cluster-scoped ownership by
16178/// materializing a per-caixa `GitRepository` + `HelmRelease` +
16179/// `Kustomization` trio, so the per-chart `values.yaml` seeds
16180/// `enabled: false` under the `values.<library>` wrap and cluster operators
16181/// must opt each caixa in per-cluster.
16182///
16183/// Rendered to canonical YAML `false` verbatim. A future substrate-side
16184/// rebrand to `true` (or the M4 typed-slot trajectory adding a per-caixa
16185/// `:standalone :enabled` author-side toggle the author flips per caixa) is
16186/// a one-line edit on this canonical declaration, not a coordinated rewrite
16187/// across the sole production emit site + its paired test-fixture
16188/// navigation sites. Peer with the sibling
16189/// [`CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT`] scalar-value default on the
16190/// peer canonical-Helm-per-values-block-substrate-default surface — the two
16191/// sibling scalar-value defaults name mirror-symmetric per-path
16192/// child-chart-enablement-toggle-scalar-value defaults at the exact same
16193/// `values.<library>.enabled` sub-block position on the standalone
16194/// per-chart-`values.yaml` path (this const) and the composition
16195/// per-cluster-`HelmRelease` values-overlay path
16196/// ([`CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT`]) — both are substrate-side
16197/// policy choices the operator inherits when the per-caixa
16198/// [`caixa_helm::RenderOpts`][cro] / [`caixa_flux::ClusterBundleOpts`][co]
16199/// doesn't pin an override.
16200///
16201/// The single source of truth every rendered `values.yaml` axis that
16202/// names the per-values-block child-chart-enablement-toggle scalar on the
16203/// standalone per-chart path reaches for:
16204///
16205/// - the rendered `values.yaml` document's
16206/// `<library>.enabled` scalar-value axis
16207/// (caixa-helm/src/lib.rs — the [`caixa_helm::build_values_yaml`][cbv]
16208/// `serde_yaml::Value::Bool(opts.enabled_default)` block-insertion
16209/// under the per-`{library_name}` wrap position, threading the same
16210/// `bool` through the [`caixa_helm::RenderOpts::enabled_default`][cro]
16211/// default-knob);
16212/// - the [`caixa_helm::RenderOpts::default()`][cro] impl-body
16213/// `enabled_default: STANDALONE_LAREIRA_ENABLED_DEFAULT` field seed
16214/// the standalone per-chart path threads into every per-caixa
16215/// `render_chart_for_servico` call site.
16216///
16217/// Both the production emit site + the default-knob seed now consume the
16218/// same `bool` at emit time through the sibling re-export
16219/// [`caixa_helm::STANDALONE_LAREIRA_ENABLED_DEFAULT`][ch], so a future
16220/// substrate-side toggle migration reaches every consumer through one
16221/// `bool` by construction — with no opportunity for per-renderer drift
16222/// where a rebrand on one axis without a coordinated edit on the other
16223/// would silently disagree on the substrate's chosen
16224/// standalone-per-chart-path opt-out semantic. Until this lift landed
16225/// the axis carried an inline `enabled_default: false` scalar-value
16226/// literal at the sole production-code call site (the
16227/// [`caixa_helm::RenderOpts::default()`][cro] impl-body field seed at
16228/// `caixa-helm/src/lib.rs:700`) — one occurrence of the same
16229/// load-bearing per-values-block child-chart-enablement-toggle-scalar-value
16230/// convention as the peer [`CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT`] on the
16231/// composition path, drift-prone by construction ahead of the M4
16232/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
16233/// per-Servico standalone-chart synthesis surfacing the third occurrence.
16234///
16235/// [cb]: ../../caixa_flux/fn.cluster_bundle.html
16236/// [cs]: ../../caixa_helm/fn.render_chart_for_servico.html
16237/// [cbv]: ../../caixa_helm/fn.build_values_yaml.html
16238/// [ch]: ../../caixa_helm/index.html
16239/// [cro]: ../../caixa_helm/struct.RenderOpts.html
16240/// [co]: ../../caixa_flux/struct.ClusterBundleOpts.html
16241pub const STANDALONE_LAREIRA_ENABLED_DEFAULT: bool = false;
16242
16243/// Canonical Flux v2 `Kustomization.spec.path` per-CR source-sub-tree
16244/// leaf-scalar-key every `caixa-flux`-emitted `kustomization.yaml`
16245/// document seeds under its top-level `spec` position to name the sub-
16246/// tree of the paired [`FLUX_GITREPOSITORY_YAML_FILENAME`] GitRepository
16247/// the Flux v2 kustomize-controller-side per-CR reconcile loop pulls
16248/// the desired-state manifest set from at reconcile time. Drift on this
16249/// leaf silently unbinds every per-caixa `Kustomization` from its
16250/// paired per-caixa sub-tree of the pleme-io k8s repository — the
16251/// kustomize-controller then either reconciles the whole GitRepository
16252/// root (when the CR omits the leaf, the controller defaults to `./`,
16253/// pulling every unrelated cluster's manifests through the wrong
16254/// per-caixa `Kustomization`) or refuses to reconcile at all (when the
16255/// leaf points at a path the GitRepository doesn't carry, the CR sits
16256/// perpetually at `BuildFailed` naming the missing sub-tree far from
16257/// the source `caixa.lisp` / the renderer's format-string template).
16258///
16259/// Distinct from the sibling K8s-Gateway-API-side [`GATEWAY_API_KEY_PATH`]
16260/// (9f45aa4) per-`HTTPRouteMatch` path-matcher container-axis key and
16261/// the sibling Cilium-CNP-side [`CILIUM_KEY_PATH`] (bec2ce9) per-
16262/// `toPorts[].rules.http[]` URL-path predicate leaf-scalar-key: all
16263/// three constants spell the same underlying `"path"` string but name
16264/// distinct schema axes on distinct CRD groups — the Flux-side axis is a
16265/// per-`Kustomization`-CR source-sub-tree leaf scalar on the Flux v2
16266/// `kustomize.toolkit.fluxcd.io/v1` `Kustomization` CRD's `spec.path`
16267/// entry, the Gateway-API-side axis is a per-`HTTPRouteMatch` path-
16268/// matcher two-leaf container (`{type, value}`) on the K8s Gateway API
16269/// v1 `HTTPRoute` CRD's `spec.rules[].matches[]` entry, the Cilium-side
16270/// axis is a per-HTTP-rule URL-path predicate leaf scalar on the Cilium
16271/// `cilium.io/v2` `CiliumNetworkPolicy` CRD's per-`toPorts[].rules.http[]`
16272/// entry. Keeping them as sibling `pub const` declarations (rather than
16273/// coalescing onto a single shared constant that happens to carry the
16274/// same string) mirrors the deliberate axis-independence discipline the
16275/// sibling [`CILIUM_KEY_PATH`] / [`GATEWAY_API_KEY_PATH`] pair already
16276/// codifies on the sibling per-CRD-group axes, so a future Flux v3 per-
16277/// `Kustomization`-CR source-sub-tree leaf-key rebrand (candidates like
16278/// `sourcePath` / `manifestsPath` / `sourceRoot` upstream Flux v3
16279/// roadmap floats in the migration prose) can land independently of
16280/// any Cilium-side or Gateway-API-side per-CRD-schema rebrand without
16281/// any cross-CRD coordination footgun where a shared constant would
16282/// force a coupled edit against schema evolutions the three CRD
16283/// projects run on independent cadences. Note: Rust's `&'static str`
16284/// interner coalesces identical byte-sequences onto one storage
16285/// allocation at codegen time, so at runtime a `.as_ptr()` comparison
16286/// across the trio can't distinguish "sibling `pub const` declarations
16287/// carrying identical bytes" from "coalesced canonical declaration" —
16288/// the axis-independence discipline lives at the rustc symbol-name
16289/// axis (the three `pub const CILIUM_KEY_PATH` / `pub const
16290/// GATEWAY_API_KEY_PATH` / `pub const FLUX_KUSTOMIZATION_KEY_PATH`
16291/// symbols a future rebrand of one leaves the other two structurally
16292/// untouched under) rather than the runtime-address axis, and the
16293/// per-axis re-export identity pins in the consuming renderer crates
16294/// (each pinning the local re-export against its own canonical
16295/// declaration on its own axis) remain the load-bearing "no sibling
16296/// local `pub const` drift" gate for the trio.
16297///
16298/// The single source of truth every rendered Flux bundle axis that
16299/// names the per-`Kustomization`-CR source-sub-tree leaf reaches for:
16300///
16301/// - the rendered `kustomization.yaml` document's `spec.path` leaf-
16302/// scalar-key axis (caixa-flux/src/lib.rs — the [`cluster_bundle`]
16303/// `kustomization.yaml` format-string template's per-CR source-sub-
16304/// tree leaf under the top-level `spec` position, threading the
16305/// same `&'static str` through a new `{path_key}` named-arg
16306/// interpolation);
16307/// - the one test-fixture navigation site in caixa-flux's `mod tests`
16308/// that probes the rendered document's `.get("path")` leaf axis to
16309/// pin the substrate's canonical per-cluster / per-caixa sub-tree
16310/// path seed (the [`cluster_bundle_kustomization_path_pins_lifted_sub_tree`]
16311/// per-CR production-emit pin).
16312///
16313/// Both the production emit site + the one test-fixture navigation
16314/// site name the same Flux v2 per-`Kustomization`-CR source-sub-tree
16315/// leaf-scalar-key and must move together on any hypothetical Flux v3
16316/// rename. Until this lift landed the axis carried an inline `path`
16317/// literal at the one production emit site (caixa-flux/src/lib.rs —
16318/// the `path: ./clusters/{cluster}/services/{name}` leaf inside the
16319/// `cluster_bundle` `kustomization.yaml` format-string template's top-
16320/// level `spec` position) — the sole occurrence of the same load-
16321/// bearing Flux-v2-per-`Kustomization`-CR-source-sub-tree-leaf-scalar-
16322/// key convention, drift-prone by construction ahead of the second
16323/// occurrence the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
16324/// materializer's per-Aplicacao `Kustomization` synthesis will
16325/// surface, where a per-renderer local
16326/// `pub const FLUX_KUSTOMIZATION_KEY_PATH: &str = "…"` (the canonical
16327/// drift footgun where a sibling local `pub const` could happen to
16328/// carry the same string at the source while pointing at a different
16329/// `&'static` allocation) would let the two renderers silently
16330/// disagree on the substrate's canonical per-`Kustomization`-CR
16331/// source-sub-tree leaf-scalar-key convention.
16332///
16333/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5) lifts
16334/// the constant in advance of the second occurrence the M4 materializer
16335/// will surface — so the second consumer inherits the canonical per-CR
16336/// source-sub-tree leaf-scalar-key by construction without opportunity
16337/// for per-renderer drift. Same "the typed constant lives in one place"
16338/// discipline the sibling [`FLUX_KUSTOMIZATION_KEY_PRUNE`] (8ec7917)
16339/// per-CR garbage-collection-toggle leaf-scalar-key lift on the same
16340/// per-`Kustomization`-CR spec surface established — extends the
16341/// discipline from the co-resident per-`Kustomization`-CR `spec.prune`
16342/// top-level per-CR-toggle leaf-scalar-key onto the co-resident per-
16343/// `Kustomization`-CR `spec.path` top-level per-CR-source-sub-tree
16344/// leaf-scalar-key at the mirror-symmetric top-level `spec` position.
16345///
16346/// [cf]: ../../caixa_flux/index.html
16347/// [kust]: https://fluxcd.io/flux/components/kustomize/kustomizations/
16348pub const FLUX_KUSTOMIZATION_KEY_PATH: &str = "path";
16349
16350/// Canonical substrate-side per-cluster / per-caixa `Kustomization.spec.path`
16351/// source-sub-tree scalar composer — the `./clusters/<cluster>/services/<nome>`
16352/// GitRepository-relative directory-tree seed every `caixa-flux`-emitted
16353/// `kustomization.yaml` document mounts under its lifted
16354/// [`FLUX_KUSTOMIZATION_KEY_PATH`] leaf-scalar-key at the top-level `spec`
16355/// position so the Flux v2 kustomize-controller's per-CR reconcile loop
16356/// walks into the paired per-cluster / per-caixa sub-tree of the pleme-io
16357/// k8s repository (rather than the `GitRepository` root, which would pull
16358/// every unrelated cluster's manifests through the wrong per-caixa
16359/// `Kustomization`).
16360///
16361/// The rendered string is the substrate's contract with the pleme-io k8s
16362/// repository's canonical directory-tree layout: every per-caixa Servico's
16363/// rendered manifests live at `pleme-io/k8s/clusters/<cluster>/services/<nome>/`,
16364/// so the Flux v2 kustomize-controller-side per-CR reconcile loop keys off
16365/// the same GitRepository-relative sub-tree seed by construction — the
16366/// composer output is the exact `spec.path` scalar the substrate seeds into
16367/// every emitted per-caixa `kustomization.yaml` document under its top-
16368/// level `spec` position.
16369///
16370/// Composes two axes:
16371///
16372/// - the per-cluster prefix — the `./clusters/<cluster>/` half of the
16373/// sub-tree seed that scopes the emit to the paired cluster's
16374/// manifest set (so two clusters hosting the same per-caixa Servico —
16375/// `rio` vs `paris` — land at distinct `spec.path` scalars with no
16376/// cross-cluster reconcile drift at the kustomize-controller's per-CR
16377/// apply loop);
16378/// - the per-caixa suffix — the `/services/<nome>` half of the sub-tree
16379/// seed that scopes the emit to the paired per-caixa Servico's
16380/// manifest sub-directory under the cluster's `services/` directory
16381/// (so two per-caixa Servicos co-resident under the same cluster —
16382/// `hello-rio` vs `cart` — land at distinct `spec.path` scalars with
16383/// no per-caixa reconcile drift at the same kustomize-controller
16384/// apply loop).
16385///
16386/// Peer to [`cilium_network_policy_name`] / [`gateway_api_http_route_name`]
16387/// / [`oci_chart_ref`] / [`lareira_chart_name`] on the sibling substrate-
16388/// side canonical-composer-of-a-canonical-scalar-that-consumers-key-off
16389/// axis: every writer-side helper composes a canonical load-bearing
16390/// scalar the substrate contracts with a downstream consumer's index
16391/// (Cilium's per-CNP `metadata.name`, Gateway API's per-HTTPRoute
16392/// `metadata.name`, Helm's OCI-artifact ref, Helm's Chart.yaml `name:`
16393/// axis). This composer's `Kustomization.spec.path` peer names the Flux
16394/// v2 kustomize-controller-side per-CR reconcile-target sub-tree index —
16395/// same "the load-bearing multi-axis composition lives in one place"
16396/// discipline extended from the mesh renderer's per-CR-identity-scalar
16397/// axes onto the flux renderer's per-CR-source-sub-tree axis.
16398///
16399/// Until this lift landed the two-axis composition sat as a verbatim
16400/// inline `format!("./clusters/{cluster}/services/{name}")` template at
16401/// the sole `cluster_bundle` `kustomization.yaml` format-string
16402/// production emit site plus a mirror-symmetric verbatim inline
16403/// `format!("./clusters/{cluster}/services/{name}", …)` at the paired
16404/// `cluster_bundle_kustomization_path_pins_lifted_sub_tree` test-fixture
16405/// navigation site — the substrate's canonical per-cluster / per-caixa
16406/// sub-tree seed had no compile-time link between the two sites. A
16407/// future substrate-side directory-tree axis rebrand (`clusters/` →
16408/// `environments/` for a multi-env-per-cluster axis extension, `services/`
16409/// → `servicos/` for a portuguese-canonical directory-name migration
16410/// matching the sibling `:servicos` slot spelling, a per-tenant scoping
16411/// prefix for multi-tenant Aplicacao hosting) would have had to be
16412/// threaded through both sites in lockstep or the two would silently
16413/// split: the production emit would key off the drifted encoding while
16414/// the test pin still asserts the original. Lifting closes the drift
16415/// footgun ahead of the second production-emit occurrence the M4
16416/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
16417/// `Kustomization` synthesis will surface — the second consumer inherits
16418/// the canonical per-cluster / per-caixa sub-tree composition by
16419/// construction without opportunity for per-renderer drift.
16420///
16421/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5) lifts
16422/// the composition in advance of the second occurrence the M4 materializer
16423/// will surface, so the second consumer inherits the canonical sub-tree
16424/// seed by construction.
16425#[must_use]
16426pub fn flux_kustomization_source_subtree(cluster: &str, nome: &str) -> String {
16427 format!("./clusters/{cluster}/services/{nome}")
16428}
16429
16430/// Canonical Flux v2 `Kustomization.spec.timeout` per-CR reconcile wall-
16431/// clock cap leaf-scalar-key every `caixa-flux`-emitted
16432/// `kustomization.yaml` document seeds under its top-level `spec`
16433/// position to name the ceiling on how long the Flux v2 kustomize-
16434/// controller-side per-CR reconcile loop is allowed to spend applying
16435/// the paired [`FLUX_KUSTOMIZATION_KEY_PATH`]-scoped sub-tree of the
16436/// paired [`FLUX_GITREPOSITORY_YAML_FILENAME`] GitRepository before it
16437/// marks the `Kustomization` `Ready: False` and stops retrying — the
16438/// substrate's canonical "how long we let a per-caixa manifest-set
16439/// reconcile run before Flux gives up" contract with the kustomize-
16440/// controller's per-CR reconcile loop. Drift on this leaf silently
16441/// strips the substrate's chosen reconcile-ceiling from every emitted
16442/// per-caixa `Kustomization` document — the kustomize-controller then
16443/// falls back to the upstream Flux v2 controller-side default cap
16444/// (which the upstream project ships at a value tuned for the average
16445/// upstream Flux-managed manifest set, not the substrate's per-caixa
16446/// idempotency-checkpoint cadence the sibling
16447/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] retry-ceiling and
16448/// [`DEFAULT_FLUX_RECONCILE_INTERVAL`] reconcile-poll cadence are
16449/// jointly tuned against), letting a persistently-failing per-caixa
16450/// manifest apply consume kustomize-controller reconcile-loop cycles
16451/// past the substrate's chosen ceiling with no field naming the
16452/// timeout-drift root cause.
16453///
16454/// The single source of truth every rendered Flux bundle axis that
16455/// names the per-`Kustomization`-CR reconcile wall-clock cap leaf
16456/// reaches for:
16457///
16458/// - the rendered `kustomization.yaml` document's `spec.timeout`
16459/// leaf-scalar-key axis (caixa-flux/src/lib.rs — the
16460/// [`cluster_bundle`] `kustomization.yaml` format-string template's
16461/// per-CR reconcile wall-clock cap leaf under the top-level `spec`
16462/// position, threading the same `&'static str` through a new
16463/// `{timeout_key}` named-arg interpolation);
16464/// - the one test-fixture navigation site in caixa-flux's `mod tests`
16465/// that probes the rendered document's `.get("timeout")` leaf axis
16466/// to pin the substrate's canonical wall-clock cap seed.
16467///
16468/// Both the production emit site + the one test-fixture navigation
16469/// site name the same Flux v2 per-`Kustomization`-CR reconcile wall-
16470/// clock cap leaf-scalar-key and must move together on any
16471/// hypothetical Flux v3 rename. Until this lift landed the axis
16472/// carried an inline `timeout` literal at the one production emit site
16473/// (caixa-flux/src/lib.rs — the `timeout: 5m` leaf inside the
16474/// `cluster_bundle` `kustomization.yaml` format-string template's top-
16475/// level `spec` position) — the sole occurrence of the same load-
16476/// bearing Flux-v2-per-`Kustomization`-CR-reconcile-wall-clock-cap-
16477/// leaf-scalar-key convention, drift-prone by construction ahead of
16478/// the second occurrence the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
16479/// materializer's per-Aplicacao `Kustomization` synthesis will
16480/// surface, where a per-renderer local
16481/// `pub const FLUX_KUSTOMIZATION_KEY_TIMEOUT: &str = "…"` (the
16482/// canonical drift footgun where a sibling local `pub const` could
16483/// happen to carry the same string at the source while pointing at a
16484/// different `&'static` allocation) would let the two renderers
16485/// silently disagree on the substrate's canonical reconcile-ceiling-
16486/// declaration leaf-scalar-key convention.
16487///
16488/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5)
16489/// lifts the constant in advance of the second occurrence the M4
16490/// materializer will surface — so the second consumer inherits the
16491/// canonical per-CR reconcile wall-clock cap leaf-scalar-key by
16492/// construction without opportunity for per-renderer drift. Pairs
16493/// with the sibling [`DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT`] scalar-
16494/// value half of the same `(leaf-key, scalar-value)` per-path
16495/// reconcile-ceiling-declaration pair — extends the drift-closing
16496/// discipline the scalar-value lift established from the value the
16497/// leaf holds onto the leaf-key itself. Same shape as the sibling
16498/// [`FLUX_KUSTOMIZATION_KEY_PATH`] (613d7ed) per-CR source-sub-tree
16499/// leaf-scalar-key + [`FLUX_KUSTOMIZATION_KEY_PRUNE`] (8ec7917) per-CR
16500/// garbage-collection-toggle leaf-scalar-key lifts on the co-resident
16501/// per-`Kustomization`-CR spec surface — extends the discipline from
16502/// the co-resident per-`Kustomization`-CR `spec.path` source-sub-tree
16503/// leaf-scalar-key and per-`Kustomization`-CR `spec.prune` garbage-
16504/// collection-toggle leaf-scalar-key onto the co-resident per-
16505/// `Kustomization`-CR `spec.timeout` reconcile wall-clock cap leaf-
16506/// scalar-key at the mirror-symmetric top-level `spec` position.
16507///
16508/// [cf]: ../../caixa_flux/index.html
16509/// [kust]: https://fluxcd.io/flux/components/kustomize/kustomizations/
16510pub const FLUX_KUSTOMIZATION_KEY_TIMEOUT: &str = "timeout";
16511
16512/// Canonical Flux v2 `Kustomization.spec.timeout` per-CR reconcile
16513/// wall-clock cap default the substrate seeds into every per-caixa
16514/// `kustomization.yaml` document. Every rendered per-caixa Flux v2
16515/// `Kustomization` CR consults the same `&'static str` at emit time so
16516/// a future substrate-side reconcile-ceiling migration (`"5m"` → `"3m"`
16517/// on faster per-caixa idempotency-checkpoint cadence once the sibling
16518/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] retry-ceiling
16519/// tightens, `"5m"` → `"10m"` on larger per-caixa manifest sets where
16520/// the upstream Flux v2 kustomize-controller-side per-CR reconcile
16521/// duration outgrows the substrate's default ceiling — coordinated
16522/// with the sibling [`DEFAULT_FLUX_RECONCILE_INTERVAL`] reconcile-poll
16523/// cadence tuning cycle) is a one-line edit on this canonical
16524/// declaration, not a coordinated rewrite across the
16525/// [`cluster_bundle`] `kustomization.yaml` template + every future
16526/// per-target renderer the substrate adds.
16527///
16528/// The single source of truth the rendered per-caixa Flux v2 cluster
16529/// bundle's per-`Kustomization`-CR reconcile wall-clock cap default
16530/// seed reaches for:
16531///
16532/// - the rendered `kustomization.yaml` document's `spec.timeout`
16533/// scalar-value axis (caixa-flux/src/lib.rs — the
16534/// [`cluster_bundle`] `kustomization.yaml` format-string template's
16535/// per-CR reconcile wall-clock cap leaf under the top-level `spec`
16536/// position, threading the same `&'static str` through a new
16537/// `{timeout_default}` named-arg interpolation on the leaf keyed
16538/// by the sibling [`FLUX_KUSTOMIZATION_KEY_TIMEOUT`]).
16539///
16540/// The value is a valid Flux v2 reconcile wall-clock cap duration
16541/// scalar (per the upstream Flux v2
16542/// `kustomize.toolkit.fluxcd.io/v1/Kustomization.spec.timeout`
16543/// `metav1.Duration` OpenAPI schema): a non-empty Go-duration-format
16544/// string (e.g. `"5m"`, `"3m"`, `"1h30m"`), which the Flux v2
16545/// controller-side per-CR admission gate parses via
16546/// `metav1.ParseDuration` before installing the per-CR watch. A future
16547/// rebrand on this lift cannot silently land a value the Flux v2
16548/// controller-side admission gate rejects at the *first* per-caixa
16549/// `Kustomization` apply against a cluster, far from the rebrand
16550/// commit's source — the pin at the canonical lift documents the Go-
16551/// duration-format grammar contract with the Flux v2 admission gate
16552/// every downstream consumer of the rendered per-CR reconcile-cap
16553/// axis rests on.
16554///
16555/// Pairs with the sibling [`FLUX_KUSTOMIZATION_KEY_TIMEOUT`] per-Flux-
16556/// v2-`Kustomization`-CR reconcile wall-clock cap scalar-axis key the
16557/// value the substrate seeds here nests directly under across every
16558/// rendered per-caixa Flux v2 `Kustomization` CR — the key half of
16559/// the per-CR `spec.timeout` scalar-key/scalar-value pair lives at
16560/// [`FLUX_KUSTOMIZATION_KEY_TIMEOUT`], the value half's substrate-side
16561/// default seed lives here. Same "the typed constant lives in one
16562/// place" discipline the [`DEFAULT_FLUX_RECONCILE_INTERVAL`] (908180f)
16563/// / [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) /
16564/// [`DEFAULT_APLICACAO_INSTALL_TIMEOUT`](caixa_tatara::DEFAULT_APLICACAO_INSTALL_TIMEOUT)
16565/// (813343f) lifts apply on the peer canonical-substrate-default-
16566/// load-bearing-scalar surface — extends the canonical-substrate-
16567/// default single-sourcing discipline from the peer per-Flux-v2-CR-
16568/// reconcile-poll-cadence / per-HelmRelease-CR-remediation-retry-
16569/// ceiling / per-tatara-Process-install-wall-clock-cap surfaces onto
16570/// the sibling per-Kustomization-CR-reconcile-wall-clock-cap surface
16571/// every rendered per-caixa Flux v2 cluster bundle CR carries.
16572///
16573/// [cf]: ../../caixa_flux/index.html
16574pub const DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT: &str = "5m";
16575
16576/// Canonical K8s Gateway API `GatewayClass` name every `caixa-mesh`-emitted
16577/// [`Gateway`][gw] document declares at its `spec.gatewayClassName` axis —
16578/// the controller-discriminator that binds the emitted `Gateway` to a
16579/// specific `GatewayClass` resource, which in turn names the controller
16580/// (`spec.controllerName`) that reconciles every `HTTPRoute` /
16581/// `GRPCRoute` / `TLSRoute` / `TCPRoute` attached to `Gateway`s bound to
16582/// that class.
16583///
16584/// The single source of truth [`caixa-mesh`][cm]'s `gateway_routes`
16585/// per-`:entrada` `Gateway` emitter (the sole production-code site the
16586/// prior inline `"cilium".into()` literal sat at — the `spec.gatewayClassName`
16587/// field of the emitted `Gateway`'s `spec` block) and every future
16588/// per-target renderer the M3.x + M4 absorption roadmap acknowledges
16589/// (the future `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
16590/// `Gateway` synthesis, a future per-cluster / per-edge `Gateway`
16591/// renderer for non-HTTP `:entrada` shapes) consult for the substrate's
16592/// chosen Gateway API controller.
16593///
16594/// The value pins the substrate on the Cilium Gateway API implementation
16595/// — the same eBPF-identity data plane that reconciles every
16596/// [`CILIUM_KIND_NETWORK_POLICY`] the mesh renderer emits alongside the
16597/// `Gateway`. Same-controller Gateway ingress + intra-mesh identity
16598/// policy is the load-bearing "one identity layer, one data plane"
16599/// mesh-composition invariant (MESH-COMPOSITION.md §V — "the `:entrada`
16600/// external ingress and the intra-mesh `:contratos` identity checks
16601/// share an eBPF data plane; a per-caixa split between the ingress
16602/// controller and the identity controller reintroduces the
16603/// two-data-planes drift the mesh composition invariant closes"), so
16604/// splitting the controller across renderers would silently reintroduce
16605/// the exact drift the substrate's mesh composition invariant closes.
16606///
16607/// Until this lift landed the substrate's Gateway API controller choice
16608/// carried an inline `"cilium".into()` literal at the one production-code
16609/// occurrence in caixa-mesh (the `gateway_routes` `Gateway`
16610/// `spec.gatewayClassName` field). The PRIME DIRECTIVE duplication-budget
16611/// rule (THEORY.md §I.3.5, "every recurring shape becomes a generator
16612/// before it becomes a pattern; every pattern becomes a library before it
16613/// becomes duplicated code. The duplication budget is zero.") promotes
16614/// the constant to a typed substrate-side `&'static str` in advance of the
16615/// second occurrence — the M4 `mesh.pleme.io/v1alpha1/Aplicacao`
16616/// materializer's per-Aplicacao `Gateway` synthesis, a future per-cluster
16617/// per-edge `Gateway` renderer, or any per-edition variant the substrate
16618/// forks — so the second consumer inherits the canonical controller
16619/// choice by construction without opportunity for per-renderer drift.
16620///
16621/// A future substrate-side controller migration (the substrate forking
16622/// from Cilium Gateway to Envoy Gateway, Istio Gateway, or any
16623/// per-edition Gateway API v1.x GA controller variant the SIG-Network
16624/// roadmap names) without a coordinated edit on every renderer's inline
16625/// literal would have silently emitted a `Gateway` whose
16626/// `spec.gatewayClassName` referenced a class no controller reconciles —
16627/// apply-side: the `Gateway` sits at `Programmed: False` with no route
16628/// reconciled, every external `:entrada` flow drops at the ingress with
16629/// no field naming the controller-drift root cause. Lifting the value
16630/// here makes the controller-choice axis discipline structural: the
16631/// per-`:entrada` `Gateway` and every future per-Aplicacao materializer
16632/// consult the same `&'static str`, and a future controller migration
16633/// is a one-line edit on the canonical declaration.
16634///
16635/// The value is a valid DNS-1123 label (the K8s apiserver-side floor
16636/// every cluster-scoped `GatewayClass.metadata.name` axis enforces):
16637/// lowercase ASCII alphanumeric with `-` separators, no leading /
16638/// trailing hyphen, length within the [`DNS_1123_LABEL_MAX_LEN`] (63-byte)
16639/// cap. A future rebrand on this lift cannot silently land a value the
16640/// apiserver refuses at the *first* `Gateway` apply against a cluster,
16641/// far from the rebrand commit's source — the typed [`is_dns_1123_label`]
16642/// floor rejects it at caixa-core build time on the canonical lift,
16643/// before any renderer consumes the value. Same "the typed constant
16644/// lives in one place" discipline the [`DEFAULT_NAMESPACE`] (a085b26) /
16645/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) / [`DEFAULT_LIBRARY_NAME`]
16646/// (41438dc) / [`DEFAULT_SERVICO_PORT`] (1e22add) lifts apply on the
16647/// peer canonical-substrate-default-resource-name surface.
16648///
16649/// [gw]: https://gateway-api.sigs.k8s.io/api-types/gateway/
16650/// [cm]: ../../caixa_mesh/index.html
16651pub const DEFAULT_GATEWAY_CLASS_NAME: &str = "cilium";
16652
16653/// Canonical K8s Gateway API `Gateway` per-Gateway controller-binding
16654/// scalar-axis key every `gateway_routes`-emitted `Gateway` document
16655/// mounts its per-Gateway `GatewayClass.metadata.name` reference under
16656/// (`spec.gatewayClassName`). Pairs with the sibling
16657/// [`DEFAULT_GATEWAY_CLASS_NAME`] (d9b0743) — the K8s Gateway API v1 CRD
16658/// schema pins the per-Gateway controller-binding through the scalar
16659/// `spec.gatewayClassName` axis (each `Gateway` names exactly one
16660/// `GatewayClass.metadata.name`; the sibling `spec.listeners[]` +
16661/// `spec.addresses[]` container axes carry the L7-listener fan-out +
16662/// per-Gateway address hint under the same `spec` block), so drift on
16663/// the per-Gateway controller-binding scalar-axis KEY is exactly as
16664/// load-bearing as drift on the sibling `DEFAULT_GATEWAY_CLASS_NAME`
16665/// VALUE the axis wraps (the K8s apiserver-side Gateway API CRD schema
16666/// validator drops any `spec` block whose controller-binding scalar-
16667/// axis carries an unrecognized key — a `"gatewayClass"` /
16668/// `"className"` / `"gatewayClassRef"` typo silently emits a `Gateway`
16669/// whose controller-binding the Gateway API implementation's per-
16670/// Gateway reconcile loop no-ops entirely: no `GatewayClass` is
16671/// resolved, no `controllerName` is looked up, and every external
16672/// `:entrada` flow the Gateway was authored to accept drops at the
16673/// gateway-class-controller's per-Gateway reconcile with no field
16674/// naming the controller-binding-axis-drift root cause).
16675///
16676/// The single source of truth the rendered Aplicacao Gateway-API-side
16677/// ingress bundle's per-Gateway controller-binding-axis-naming reaches
16678/// for:
16679///
16680/// - the rendered `Gateway` document's `spec.gatewayClassName` axis
16681/// (caixa-mesh/src/lib.rs:2016 — the `gateway_routes` per-Aplicacao
16682/// `Gateway`'s `g_spec.insert("gatewayClassName", …)` call).
16683///
16684/// The per-Gateway controller-binding scalar axis names the same
16685/// Gateway-API-implementation-side per-Gateway `GatewayClass`
16686/// resolution axis as the sibling [`DEFAULT_GATEWAY_CLASS_NAME`] VALUE
16687/// it wraps, and must move together on any future Gateway API rebrand
16688/// (an upstream SIG-Network Gateway API v2 rename of the controller-
16689/// binding scalar-axis from `gatewayClassName` to `className` /
16690/// `gatewayClassRef` / `class`, coordinated with the Gateway API
16691/// deprecation cycle). Until this lift landed the KEY axis carried an
16692/// inline `gatewayClassName` literal at the one production-code
16693/// occurrence in caixa-mesh/src/lib.rs:2016 (the `gateway_routes` per-
16694/// Aplicacao Gateway's `g_spec.insert("gatewayClassName", …)` call)
16695/// plus a matching test-fixture navigation inside the in-file
16696/// `gateway_gateway_class_name_uses_lifted_default_gateway_class_name`
16697/// pin's `.get("gatewayClassName")` traversal (caixa-mesh/src/lib.rs:5315)
16698/// — two occurrences of the same load-bearing Gateway-API-CRD-
16699/// `gatewayClassName`-axis-KEY convention, drift-prone by
16700/// construction. A drift on the production site to `"gatewayClass"` /
16701/// `"className"` / `"gatewayClassRef"` would have surfaced as a
16702/// Gateway API implementation-side schema validator drop at apply
16703/// time (the affected `Gateway`'s controller-binding scalar-axis the
16704/// CRD schema validator recognizes as unknown), with every external
16705/// `:entrada` flow the Gateway was authored to accept dropping at the
16706/// gateway-class-controller's per-Gateway reconcile with no field
16707/// naming the controller-binding-drift root cause. A drift on the
16708/// test-fixture side silently masks the emission-side pin
16709/// (`.get("gatewayClassName")` returns `None` under both the drifted-
16710/// key emitter and the drifted-key probe — the downstream
16711/// `.and_then(|c| c.as_str())` chain short-circuits vacuously because
16712/// the outer per-Gateway controller-binding lookup is itself `None`).
16713///
16714/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
16715/// "every recurring shape becomes a generator before it becomes a
16716/// pattern; every pattern becomes a library before it becomes
16717/// duplicated code. The duplication budget is zero.") promotes the
16718/// constant to a typed substrate-side `&'static str` on the same
16719/// trajectory the [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
16720/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
16721/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
16722/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) lifts established on the
16723/// sibling canonical-Gateway-API-body-axis surfaces — completes the
16724/// per-Gateway-body-axis canonical-string-pin set the sibling
16725/// `spec.listeners[]` lift began, closing the (`gatewayClassName`,
16726/// `listeners`) per-`Gateway`-spec-body-axis pair the M3 Aplicacao
16727/// mesh renderer's external `:entrada` ingress contract rests on.
16728/// Together with the peer [`DEFAULT_GATEWAY_CLASS_NAME`] VALUE lift
16729/// (d9b0743) — the `(key, value)` pair-lift discipline the sibling
16730/// `(KUBE_KEY_METADATA, {"name","namespace","labels"})` axis
16731/// established — the per-Gateway controller-binding scalar axis now
16732/// threads both halves of its `(key, value)` typed contract through
16733/// one lifted `&'static str` apiece at the substrate boundary. The
16734/// render-side consumer now threads the same `&'static str` through
16735/// its `g_spec.insert(…)` call so a future Gateway API rebrand on
16736/// the controller-binding scalar axis (or an upstream SIG-Network
16737/// Gateway API v2 rename to a per-CRD sibling name) lands in one
16738/// place; every future renderer that reaches for the canonical
16739/// per-Gateway controller-binding scalar axis (the future M4
16740/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
16741/// `Gateway` fan-out, a future per-cluster `GatewayClass` /
16742/// `ReferenceGrant` renderer whose per-Gateway class-name enumeration
16743/// binds against this same axis, a future per-`Gateway` typed-listener
16744/// TLS terminator renderer whose per-Gateway `spec` block nests
16745/// alongside this same axis) inherits the same value by construction
16746/// with no opportunity for per-renderer drift.
16747///
16748/// [cm]: ../../caixa_mesh/index.html
16749pub const GATEWAY_API_KEY_GATEWAY_CLASS_NAME: &str = "gatewayClassName";
16750
16751/// Canonical K8s Gateway API `HTTPRoute` per-`HTTPRouteMatch` path-matcher
16752/// container-axis key every `gateway_routes`-emitted `HTTPRoute` per-rule
16753/// `matches[]` entry mounts its per-match `{type, value}` path-selection
16754/// predicate under (`spec.rules[].matches[].path`). Nests one level
16755/// beneath the sibling [`GATEWAY_API_KEY_MATCHES`] (b9ede1a) per-rule
16756/// route-match container-axis it hangs off of — the Gateway API v1 CRD
16757/// schema pins per-`HTTPRouteMatch` request-path selection through the
16758/// `spec.rules[].matches[].path` container axis (each match entry names
16759/// one path-selection predicate the request line's `:path` pseudo-header
16760/// must satisfy under a `type` discriminator of
16761/// `Exact | PathPrefix | RegularExpression`) alongside the sibling per-
16762/// `HTTPRouteMatch` `headers[]` / `queryParams[]` / `method` axes it
16763/// nests under, so drift on the per-match path-matcher container axis
16764/// is exactly as load-bearing as drift on the per-rule route-match
16765/// axis it nests inside of (the K8s apiserver-side Gateway API CRD
16766/// schema validator drops any per-match block whose path-matcher
16767/// container axis carries an unrecognized key — a `"pathMatch"` /
16768/// `"prefix"` / `"url"` typo silently emits an `HTTPRoute` whose per-
16769/// match path-selection axis the Gateway API implementation's per-rule
16770/// L7 dispatch loop no-ops entirely: no path predicate is evaluated,
16771/// the match degrades to the wildcard predicate at the gateway-class-
16772/// controller's per-rule reconcile, the rule matches every request
16773/// path unconditionally, and every external `:entrada` path filter the
16774/// rule was authored to enforce drops with no field naming the path-
16775/// matcher-axis-drift root cause).
16776///
16777/// The single source of truth the rendered Aplicacao Gateway-API-side
16778/// ingress bundle's per-`HTTPRouteMatch` path-matcher-container-axis-
16779/// naming reaches for:
16780///
16781/// - the rendered `HTTPRoute` document's per-match
16782/// `spec.rules[].matches[].path` axis (caixa-mesh/src/lib.rs — the
16783/// `gateway_routes` per-Aplicacao `HTTPRoute`'s per-match
16784/// `match_entry.insert("path", …)` call seeded from the Aplicacao's
16785/// `:entrada :paths` slot).
16786///
16787/// The per-`HTTPRouteMatch` path-matcher container axis names the same
16788/// Gateway-API-implementation-side per-match request-path-selection
16789/// predicate container as the sibling
16790/// [`GATEWAY_API_KEY_MATCHES`] per-rule route-match container axis it
16791/// nests inside of, and must move together on any future Gateway API
16792/// rebrand (an upstream SIG-Network Gateway API v2 rename of the path-
16793/// matcher axis from `path` to `pathMatch` / `prefix` / `url`,
16794/// coordinated with the Gateway API deprecation cycle). Until this lift
16795/// landed the axis carried an inline `path` literal at the one
16796/// production-code occurrence in caixa-mesh/src/lib.rs (the
16797/// `gateway_routes` per-match `match_entry.insert("path", …)` call) —
16798/// one occurrence of the same load-bearing Gateway-API-CRD-
16799/// `path`-axis-key convention, drift-prone by construction. A drift on
16800/// the production site to `"pathMatch"` / `"prefix"` / `"url"` would
16801/// have surfaced as a Gateway API implementation-side schema validator
16802/// drop at apply time (the affected per-match path-matcher axis the
16803/// CRD schema validator recognizes as unknown), with the per-match
16804/// path predicate degrading to the wildcard match at the gateway-
16805/// class-controller's per-rule reconcile with no field naming the
16806/// path-matcher-drift root cause.
16807///
16808/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
16809/// "every recurring shape becomes a generator before it becomes a
16810/// pattern; every pattern becomes a library before it becomes
16811/// duplicated code. The duplication budget is zero.") promotes the
16812/// constant to a typed substrate-side `&'static str` on the same
16813/// trajectory the [`GATEWAY_API_KEY_MATCHES`] (b9ede1a) /
16814/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
16815/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
16816/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
16817/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
16818/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
16819/// [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) /
16820/// [`GATEWAY_API_KEY_RETRY`] (231bbf5) /
16821/// [`GATEWAY_API_KEY_GATEWAY_CLASS_NAME`] (1bc727d) lifts established on
16822/// the sibling canonical-Gateway-API-HTTPRoute-body-axis / per-Gateway-
16823/// body-axis surfaces — nests the per-Gateway-API-HTTPRoute-per-rule-
16824/// body-axis canonical-string-pin set (`matches`, `backendRefs`,
16825/// `timeouts`, `retry`) one level deeper onto the per-`HTTPRouteMatch`
16826/// body-axis surface, so the container-axis key beneath the sibling
16827/// `matches[]` axis now threads a lifted `&'static str` alongside its
16828/// parent-container-axis key. The render-side consumer now threads the
16829/// same `&'static str` through its `match_entry.insert(…)` call so a
16830/// future Gateway API rebrand on the per-`HTTPRouteMatch` path-matcher
16831/// axis (or an upstream SIG-Network Gateway API v2 rename to a per-
16832/// `HTTPRouteMatch` sibling name) lands in one place; every future
16833/// renderer that reaches for the canonical per-`HTTPRouteMatch` path-
16834/// matcher axis (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
16835/// materializer's per-Aplicacao `HTTPRoute` fan-out, a future per-edge
16836/// `GRPCRoute` renderer whose per-match request-method / service /
16837/// method predicate nests alongside the path predicate, a future
16838/// per-match header-match / query-match renderer whose per-predicate
16839/// list binds against sibling axes of this one under the same match
16840/// entry) inherits the same value by construction with no opportunity
16841/// for per-renderer drift.
16842///
16843/// Same "the typed constant lives in one place" discipline the
16844/// [`GATEWAY_API_KEY_MATCHES`] (b9ede1a) /
16845/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
16846/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
16847/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
16848/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
16849/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
16850/// [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) /
16851/// [`GATEWAY_API_KEY_RETRY`] (231bbf5) /
16852/// [`GATEWAY_API_KEY_GATEWAY_CLASS_NAME`] (1bc727d) lifts apply on the
16853/// peer canonical-Gateway-API-HTTPRoute-per-`HTTPRouteMatch`-body-axis
16854/// surface.
16855///
16856/// [cm]: ../../caixa_mesh/index.html
16857pub const GATEWAY_API_KEY_PATH: &str = "path";
16858
16859/// Canonical K8s Gateway API v1 `HTTPPathMatch` `value` scalar-axis key
16860/// every `gateway_routes`-emitted `HTTPRoute` per-match `path` block
16861/// mounts its request-path-selection scalar payload under
16862/// (`spec.rules[].matches[].path.value`). Nests one level beneath the
16863/// sibling [`GATEWAY_API_KEY_PATH`] per-`HTTPRouteMatch` path-matcher
16864/// container-axis it hangs off of — the Gateway API v1 CRD schema
16865/// pins per-`HTTPPathMatch` request-path selection through the
16866/// `{type, value}` two-axis pair (a
16867/// [`GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX`]-typed `type`
16868/// discriminator picks `Exact | PathPrefix | RegularExpression`; the
16869/// `value` scalar carries the per-match request-path string the
16870/// discriminator is applied against), so drift on the `value` scalar
16871/// axis is exactly as load-bearing as drift on the peer `type`
16872/// discriminator axis it nests alongside (the K8s apiserver-side
16873/// Gateway API CRD schema validator drops any per-match block whose
16874/// `HTTPPathMatch` scalar-payload axis carries an unrecognized key —
16875/// a `"path"` / `"prefix"` / `"pattern"` typo silently emits an
16876/// `HTTPRoute` whose per-match request-path predicate the Gateway API
16877/// implementation's per-rule L7 dispatch loop treats as bare (no
16878/// value evaluated against the `type` discriminator), the match
16879/// degrades to the wildcard predicate at the gateway-class-
16880/// controller's per-rule reconcile, the rule matches every request
16881/// path unconditionally, and every external `:entrada` path filter the
16882/// rule was authored to enforce drops with no field naming the
16883/// `HTTPPathMatch`-scalar-payload-drift root cause).
16884///
16885/// The single source of truth the rendered Aplicacao Gateway-API-side
16886/// ingress bundle's per-`HTTPPathMatch` scalar-payload-axis-naming
16887/// reaches for:
16888///
16889/// - the rendered `HTTPRoute` document's per-match
16890/// `spec.rules[].matches[].path.value` axis (caixa-mesh/src/lib.rs
16891/// — the `gateway_routes` per-Aplicacao `HTTPRoute`'s per-match
16892/// `path_match.insert("value", …)` call seeded from the
16893/// Aplicacao's `:entrada :paths` slot).
16894///
16895/// The per-`HTTPPathMatch` scalar-payload axis names the same
16896/// Gateway-API-implementation-side per-match request-path-selection
16897/// scalar as the sibling [`GATEWAY_API_KEY_PATH`] per-`HTTPRouteMatch`
16898/// path-matcher container-axis it nests inside of, and must move
16899/// together on any future Gateway API rebrand (an upstream
16900/// SIG-Network Gateway API v2 rename of the `HTTPPathMatch` scalar-
16901/// payload axis from `value` to `path` / `pattern` / `expression`,
16902/// coordinated with the Gateway API deprecation cycle). Until this
16903/// lift landed the axis carried an inline `"value"` literal at the
16904/// one production-code occurrence in caixa-mesh/src/lib.rs (the
16905/// `gateway_routes` per-match `path_match.insert("value", …)` call) —
16906/// one occurrence of the same load-bearing Gateway-API-CRD-
16907/// `HTTPPathMatch`-`value`-axis-key convention, drift-prone by
16908/// construction. A drift on the production site to `"path"` /
16909/// `"prefix"` / `"pattern"` would have surfaced as a Gateway API
16910/// implementation-side schema validator drop at apply time (the
16911/// affected per-match `HTTPPathMatch` scalar-payload axis the CRD
16912/// schema validator recognizes as unknown), with the per-match path
16913/// predicate degrading to the wildcard match at the gateway-class-
16914/// controller's per-rule reconcile with no field naming the
16915/// `HTTPPathMatch`-scalar-payload-drift root cause.
16916///
16917/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
16918/// "every recurring shape becomes a generator before it becomes a
16919/// pattern; every pattern becomes a library before it becomes
16920/// duplicated code. The duplication budget is zero.") promotes the
16921/// constant to a typed substrate-side `&'static str` on the same
16922/// trajectory the [`GATEWAY_API_KEY_PATH`] (9f45aa4) /
16923/// [`GATEWAY_API_KEY_MATCHES`] (b9ede1a) /
16924/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
16925/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
16926/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
16927/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
16928/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
16929/// [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) /
16930/// [`GATEWAY_API_KEY_RETRY`] (231bbf5) /
16931/// [`GATEWAY_API_KEY_GATEWAY_CLASS_NAME`] (1bc727d) lifts established
16932/// on the sibling canonical-Gateway-API-body-axis surfaces — nests
16933/// the per-Gateway-API-HTTPRoute-per-match-body-axis canonical-
16934/// string-pin set (`path` container-axis, `type` discriminator
16935/// scalar-key, `value` scalar-payload key) two levels deeper onto the
16936/// per-`HTTPPathMatch` body-axis surface, so both halves of the
16937/// `HTTPPathMatch.{type, value}` typed contract now thread one lifted
16938/// `&'static str` apiece at the substrate boundary alongside the
16939/// parent-container-axis key. The render-side consumer now threads
16940/// the same `&'static str` through its `path_match.insert(…)` call
16941/// so a future Gateway API rebrand on the `HTTPPathMatch` scalar-
16942/// payload axis (or an upstream SIG-Network Gateway API v2 rename to
16943/// a per-`HTTPPathMatch` sibling name) lands in one place; every
16944/// future renderer that reaches for the canonical per-`HTTPPathMatch`
16945/// scalar-payload axis (the future M4
16946/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
16947/// `HTTPRoute` fan-out, a future per-edge `GRPCRoute` renderer whose
16948/// per-match `GRPCMethodMatch.method` scalar-payload nests alongside
16949/// this same axis, a future per-match header-match / query-match
16950/// renderer whose per-predicate `HTTPHeaderMatch.value` /
16951/// `HTTPQueryParamMatch.value` scalar-payload binds against sibling
16952/// axes on the same `value` axis-key) inherits the same value by
16953/// construction with no opportunity for per-renderer drift.
16954///
16955/// [cm]: ../../caixa_mesh/index.html
16956pub const GATEWAY_API_KEY_VALUE: &str = "value";
16957
16958/// Canonical K8s Gateway API v1 per-child-object name-reference
16959/// discriminator axis key every `gateway_routes`-emitted `Gateway`
16960/// listener + `HTTPRoute` `parentRefs[]` / `backendRefs[]` entry
16961/// mounts its named-object binding under. Three peer sub-schemas on
16962/// the shared `spec.…[].name` axis:
16963///
16964/// - `Gateway.spec.listeners[].name` — Gateway API v1 `SectionName`,
16965/// the listener's per-section identifier the sibling
16966/// `HTTPRoute.spec.parentRefs[].sectionName` binds against;
16967/// - `HTTPRoute.spec.parentRefs[].name` — Gateway API v1
16968/// `ObjectName`, the per-`HTTPRoute` parent-Gateway reference the
16969/// Gateway API implementation's per-HTTPRoute attach reconciler
16970/// resolves against a `Gateway` object in the same namespace;
16971/// - `HTTPRoute.spec.rules[].backendRefs[].name` — Gateway API v1
16972/// `ObjectName`, the per-rule backend-Service reference the
16973/// Gateway API implementation's per-rule L7 dispatch loop
16974/// resolves against a `Service` object in the same namespace.
16975///
16976/// All three sub-schemas key their named-reference discriminator on
16977/// the identical three-byte `"name"` axis at every level of the
16978/// Gateway API v1 CRD schema (`Gateway.spec.listeners[].name`,
16979/// `HTTPRoute.spec.parentRefs[].name`,
16980/// `HTTPRoute.spec.rules[].backendRefs[].name`), so drift on any one
16981/// of them silently splits the substrate's Aplicacao gateway bundle
16982/// at whichever schema the drift hits (the K8s apiserver-side Gateway
16983/// API CRD schema validator drops a per-listener / per-parentRef /
16984/// per-backendRef block whose name-reference axis carries an
16985/// unrecognized key — a `"Name"` / `"target"` / `"ref"` typo silently
16986/// emits a `Gateway` whose listener carries no section identity, or
16987/// an `HTTPRoute` whose parent-Gateway attachment reconciles as
16988/// unbound, or an `HTTPRoute` whose per-rule backend fan-out resolves
16989/// no Service, and every external `:entrada` flow the bundle was
16990/// authored to accept drops at the gateway-class-controller's per-
16991/// rule/per-listener/per-parentRef reconcile with no field naming the
16992/// name-reference-axis-drift root cause).
16993///
16994/// The single source of truth the rendered Aplicacao Gateway-API-side
16995/// ingress bundle's per-child-object name-reference-axis-naming
16996/// reaches for:
16997///
16998/// - the rendered `Gateway` document's `spec.listeners[].name` axis
16999/// (caixa-mesh/src/lib.rs — the `gateway_routes` per-Aplicacao
17000/// `Gateway`'s per-listener `listener.insert("name", …)` call);
17001/// - the rendered `HTTPRoute` document's `spec.parentRefs[].name`
17002/// axis (caixa-mesh/src/lib.rs — the `gateway_routes` per-
17003/// Aplicacao `HTTPRoute`'s per-parentRef
17004/// `parent_ref.insert("name", …)` call);
17005/// - the rendered `HTTPRoute` document's
17006/// `spec.rules[].backendRefs[].name` axis (caixa-mesh/src/lib.rs
17007/// — the `gateway_routes` per-rule per-backendRef
17008/// `backend_ref.insert("name", …)` call).
17009///
17010/// The per-child-object name-reference discriminator axis names the
17011/// same Gateway-API-implementation-side named-object binding container
17012/// as the sibling [`GATEWAY_API_KEY_LISTENERS`] +
17013/// [`GATEWAY_API_KEY_PARENT_REFS`] + [`GATEWAY_API_KEY_BACKEND_REFS`]
17014/// per-container list axes it nests directly beneath, and must move
17015/// together on any future Gateway API rebrand (an upstream SIG-Network
17016/// Gateway API v2 rename of the name-reference axis from `name` to
17017/// `target` / `ref` / `objectName`, coordinated with the Gateway API
17018/// deprecation cycle). Until this lift landed the axis carried inline
17019/// `"name"` literals at four occurrences across caixa-mesh — three
17020/// production emitter sites (the per-listener `listener.insert("name",
17021/// …)`, the per-parentRef `parent_ref.insert("name", …)`, and the per-
17022/// backendRef `backend_ref.insert("name", …)` calls in
17023/// `gateway_routes`) plus one in-file test-fixture navigation (the
17024/// `httproute_routes_to_entrada_para` fixture's per-backendRef
17025/// `.get("name")` retrieval) — four occurrences of the same load-
17026/// bearing Gateway-API-CRD-`name`-axis-key convention, drift-prone by
17027/// construction. A drift on any one production site to `"Name"` /
17028/// `"target"` / `"ref"` would have surfaced as a Gateway API
17029/// implementation-side schema validator drop at apply time (the
17030/// affected per-listener / per-parentRef / per-backendRef name-
17031/// reference axis the CRD schema validator recognizes as unknown),
17032/// with the listener carrying no section identity or the `HTTPRoute`
17033/// carrying an unbound parent-Gateway attachment or the per-rule
17034/// backend fan-out resolving no Service at the gateway-class-
17035/// controller's reconcile with no field naming the name-reference-
17036/// drift root cause. A drift on the test-fixture side silently masks
17037/// the emission-side pin (`.get("name")` returns `None` under both
17038/// the drifted-key emitter and the drifted-key probe — the downstream
17039/// `.and_then(|n| n.as_str())` chain short-circuits vacuously because
17040/// the outer per-backendRef name-reference lookup is itself `None`).
17041///
17042/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
17043/// "every recurring shape becomes a generator before it becomes a
17044/// pattern; every pattern becomes a library before it becomes
17045/// duplicated code. The duplication budget is zero.") promotes the
17046/// constant to a typed substrate-side `&'static str` on the same
17047/// trajectory the [`GATEWAY_API_KEY_PATH`] (9f45aa4) /
17048/// [`GATEWAY_API_KEY_MATCHES`] (b9ede1a) /
17049/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
17050/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
17051/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
17052/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
17053/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
17054/// [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) /
17055/// [`GATEWAY_API_KEY_RETRY`] (231bbf5) /
17056/// [`GATEWAY_API_KEY_GATEWAY_CLASS_NAME`] (1bc727d) lifts established
17057/// on the sibling canonical-Gateway-API-CRD-body-axis surface —
17058/// completes the four-way per-child-object axis-key set (`name` on
17059/// listeners + parentRefs + backendRefs, alongside sibling
17060/// `hostname`/`port`/`protocol` per-listener and `port` per-
17061/// backendRef) the M3 Aplicacao mesh renderer's external `:entrada`
17062/// ingress contract rests on. The render-side consumer now threads
17063/// the same `&'static str` through every one of its `.insert(…)`
17064/// calls so a future Gateway API rebrand on the name-reference axis
17065/// (or an upstream SIG-Network Gateway API v2 rename to a per-CRD
17066/// sibling name) lands in one place; every future renderer that
17067/// reaches for the canonical per-child-object name-reference axis
17068/// (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
17069/// materializer's per-Aplicacao `Gateway` + `HTTPRoute` fan-out, a
17070/// future per-edge `GRPCRoute` / `TCPRoute` / `TLSRoute` renderer
17071/// whose per-rule backend-Service reference binds against this same
17072/// axis, a future per-Aplicacao `ReferenceGrant` renderer whose per-
17073/// cross-namespace parent-Gateway attachment resolves against this
17074/// same axis) inherits the same value by construction with no
17075/// opportunity for per-renderer drift.
17076///
17077/// Byte-identical to [`KUBE_KEY_NAME`] today — both resolve to the
17078/// same three-byte `"name"` literal — but semantically distinct:
17079/// [`KUBE_KEY_NAME`] names the K8s CR canonical `metadata.name` axis
17080/// (every rendered CR's outer-level identity discriminator, spelled
17081/// per the K8s apiserver's per-object `OpenAPI` v3 schema), while this
17082/// constant names the Gateway API v1 CRD schema's per-child-object
17083/// name-reference discriminator axis on `Listener` / `ParentReference`
17084/// / `BackendObjectReference` sub-schemas (spelled per the Gateway API
17085/// v1 CRD schema — a separate schema contract). Splitting the two
17086/// lets each schema's future rebrand land independently at its
17087/// canonical const definition without coupling the K8s CR canonical-
17088/// key axis to the Gateway API v1 per-child-object name-reference
17089/// axis (or vice versa) — the same discipline
17090/// [`FLEET_PROGRAMS_KEY_NAME`] establishes vs. [`KUBE_KEY_NAME`] on
17091/// the `lareira-fleet-programs` values-schema per-entry name-axis.
17092///
17093/// [cm]: ../../caixa_mesh/index.html
17094pub const GATEWAY_API_KEY_NAME: &str = "name";
17095
17096/// Canonical Helm 3 `Chart.yaml` `apiVersion` every `caixa-helm`-rendered
17097/// `lareira-<nome>` chart declares at its top-level `apiVersion` axis. The
17098/// Helm 3 chart-schema resolution contract keys off this exact `"v2"` value:
17099/// `helm dependency build`, `helm lint`, and `helm template` all parse the
17100/// chart under the Helm 3 v2 schema (which requires
17101/// [`ChartYaml::description`][chart-yaml-desc] and permits
17102/// `dependencies:` at the top level); drift to the legacy Helm 2 `"v1"`
17103/// (the pre-Helm-3 chart schema every upstream Helm-3-migration doc names)
17104/// silently reroutes the rendered `Chart.yaml` through the Helm 2 parser,
17105/// where the top-level `dependencies:` block is unknown and the chart's
17106/// dep on the `pleme-computeunit` library chart never resolves —
17107/// `helm dependency build` reports "no requirements found" and every
17108/// downstream `helm template` / `helm install` on the rendered chart
17109/// emits an empty release (no ComputeUnit / Service / ScaledObject
17110/// resources land) far from the source caixa.lisp / the renderer's
17111/// `build_chart_yaml` call site.
17112///
17113/// The single source of truth the [`caixa-helm`][ch]'s `build_chart_yaml`
17114/// `Chart.yaml` `apiVersion` axis reaches for (caixa-helm/src/lib.rs:298).
17115/// Peer with the [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
17116/// [`FLUX_GITREPOSITORY_API_VERSION`] (dbbcf29) /
17117/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) /
17118/// [`GATEWAY_API_API_VERSION`] (3c6cfc3) / [`CILIUM_API_VERSION`] (279d611)
17119/// lifts on the sibling cluster-side-CRD-apiVersion surface — those pin
17120/// the K8s apiserver-side `(apiVersion, kind)` `RESTMapper` contract,
17121/// this one pins the Helm-side chart-schema-parser contract that gates
17122/// every rendered `lareira-<nome>` chart's dependency resolution before
17123/// any K8s resource lands. Both axes are load-bearing schema-version
17124/// discriminators drift-prone by construction across renderer forks.
17125///
17126/// A future Helm 4 chart-schema promotion (the upstream Helm roadmap
17127/// names a `"v3"` apiVersion once the Helm 3 LTS branch closes) is a
17128/// coordinated migration alongside the upstream Helm chart-schema
17129/// deprecation cycle, not an incidental edit — pinning it here means
17130/// the migration lands as one edit at the const + a re-run of the
17131/// pin tests rather than a per-renderer sweep with no single source
17132/// of truth to consult. Same "the typed constant lives in one place"
17133/// discipline the [`DEFAULT_LIBRARY_NAME`] (41438dc) /
17134/// [`LAREIRA_CHART_NAME_PREFIX`] / [`FLUX_HELMRELEASE_API_VERSION`]
17135/// (55f0fd9) lifts apply on the peer canonical-Helm-load-bearing-string
17136/// and cluster-side-CRD-apiVersion axes.
17137///
17138/// [chart-yaml-desc]: https://helm.sh/docs/topics/charts/#the-chartyaml-file
17139/// [ch]: ../../caixa_helm/index.html
17140pub const HELM_CHART_API_VERSION: &str = "v2";
17141
17142/// Canonical Helm 3 `Chart.yaml` `type` field per-chart-kind discriminator
17143/// scalar-value every rendered `lareira-<nome>` chart declares. The Helm
17144/// chart-schema pins the per-chart-kind axis to the closed set
17145/// `{"application", "library"}` (see [chart-type-doc]) — the
17146/// `application` chart-kind is Helm's default install-shape (an
17147/// application chart that installs into a namespace as a workload +
17148/// rendered manifests), while the `library` chart-kind is Helm's
17149/// dependency-only shape (a chart authored as a shared-template
17150/// substrate that can only be consumed as a dependency, never installed
17151/// directly). Each `lareira-<nome>` chart the caixa-helm renderer emits
17152/// declares itself as an `application` chart because it is the per-
17153/// Servico install shape a cluster operator's `helm install` /
17154/// `helm upgrade` per-Servico release cycle materializes — the sibling
17155/// [`DEFAULT_LIBRARY_NAME`] `pleme-computeunit` chart (the substrate-
17156/// side library-chart the `lareira-<nome>` chart depends on for
17157/// template-shape) carries the sibling `library` value verbatim in its
17158/// authored Chart.yaml (out-of-tree at the `pleme-io/helmworks` repo,
17159/// so not this crate's authority).
17160///
17161/// The single source of truth the rendered `lareira-<nome>` chart's
17162/// Chart.yaml per-chart-kind discriminator axis naming reaches for:
17163///
17164/// - [`caixa-helm`][ch]'s `build_chart_yaml` `chart_type` field
17165/// assignment (caixa-helm/src/lib.rs — the sole production emitter
17166/// site the prior inline `"application".into()` literal sat at,
17167/// writing the per-chart-kind discriminator scalar-value the
17168/// `helm install` / `helm upgrade` per-release install-shape dispatch
17169/// loop keys off to select the per-chart-kind install pathway).
17170///
17171/// Until this lift landed the axis carried an inline `"application"`
17172/// literal at the one production-code site (`build_chart_yaml`'s
17173/// `chart_type` field assignment). A drift on the value at the emitter
17174/// (a `"Application"` / `"APPLICATION"` / `"app"` / `"workload"` typo,
17175/// or an accidental collapse onto the sibling `"library"` shape) would
17176/// have surfaced as one of two silent failure modes at `helm install`
17177/// time:
17178///
17179/// - a value outside the schema's admitted set (`{"application",
17180/// "library"}`) — Helm's chart-schema parser silently treats an
17181/// unrecognized `type:` scalar as the default `application` shape,
17182/// so a typo like `"Application"` still installs but with no
17183/// drift-signal in the process log, silently masking the schema
17184/// violation;
17185/// - a schema-admitted-but-wrong-shape drift onto `"library"` —
17186/// `helm install lareira-<nome>` refuses the release with an
17187/// "Error: library charts cannot be installed" error, and the
17188/// per-Servico release cycle drops with no field naming the
17189/// chart-kind-drift root cause (the operator sees "the chart won't
17190/// install" far from the drift site, and troubleshooting has no
17191/// canonical anchor to compare the rendered value against).
17192///
17193/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
17194/// "every recurring shape becomes a generator before it becomes a
17195/// pattern; every pattern becomes a library before it becomes
17196/// duplicated code. The duplication budget is zero.") promotes the
17197/// constant to a typed substrate-side `&'static str` on the same
17198/// trajectory the peer [`HELM_CHART_API_VERSION`] /
17199/// [`DEFAULT_LIBRARY_NAME`] / [`LAREIRA_CHART_NAME_PREFIX`] lifts
17200/// established on the sibling canonical-Helm-load-bearing-string axes —
17201/// extends the canonical-Helm-chart-schema-axis single-sourcing
17202/// discipline the `apiVersion` lift established onto the sibling
17203/// per-chart-kind discriminator scalar-value axis every rendered
17204/// `lareira-<nome>` chart declares in its Chart.yaml. Peer to the
17205/// canonical-cluster-side-OpenAPI-schema-enum-value lifts
17206/// ([`KUBE_PROTOCOL_TCP`] / [`GATEWAY_API_PROTOCOL_HTTP`] /
17207/// [`GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX`] /
17208/// [`CILIUM_AUTH_MODE_REQUIRED`] / [`CILIUM_AUTH_MODE_DISABLED`]) on
17209/// the sibling K8s-CR-side enum-value surfaces — pivots the discipline
17210/// from the K8s-CR-side OpenAPI-schema-enum-value axis onto the
17211/// Helm-chart-schema-enum-value axis every rendered Chart.yaml carries
17212/// at its per-chart-kind discriminator field.
17213///
17214/// [chart-type-doc]: https://helm.sh/docs/topics/charts/#chart-types
17215/// [ch]: ../../caixa_helm/index.html
17216pub const HELM_CHART_TYPE_APPLICATION: &str = "application";
17217
17218/// Canonical Helm 3 `Chart.yaml` `type` field per-chart-kind discriminator
17219/// scalar-value the sibling library-chart shape lands on — the second and
17220/// only other arm of the closed set `{"application", "library"}` the Helm
17221/// chart-schema pins the per-chart-kind axis to (see [chart-type-doc]).
17222/// The `library` chart-kind is Helm's dependency-only install-shape: a
17223/// chart authored as a shared-template substrate the per-Aplicacao
17224/// `lareira-<nome>` application charts depend on for their emitted-
17225/// object templates (the [`DEFAULT_LIBRARY_NAME`] `pleme-computeunit`
17226/// chart out-of-tree at `pleme-io/helmworks` is the substrate's
17227/// canonical instance today), and Helm refuses to install it directly
17228/// (`helm install <library-chart>` fails with "Error: library charts
17229/// cannot be installed") — a chart declaring itself under this
17230/// scalar-value is only ever consumed as a dependency by a sibling
17231/// `application`-typed chart.
17232///
17233/// Peer of [`HELM_CHART_TYPE_APPLICATION`] on the same closed
17234/// canonical-Helm-chart-schema-per-chart-kind-discriminator axis: the
17235/// two consts together name the two-arm schema-admitted set as a pair
17236/// of `&'static str`s at the substrate-side canonical surface, so any
17237/// consumer that reaches for either shape (the caixa-helm renderer at
17238/// [`HELM_CHART_TYPE_APPLICATION`]'s single emitter site today; the
17239/// future per-Aplicacao library chart the [`HELM_CHART_TYPE_APPLICATION`]
17240/// docstring names as a trajectory item, whose emit site would land at
17241/// this const; the future `mesh.pleme.io/v1alpha1/Aplicacao` CR
17242/// materializer's per-chart-kind admission gate that needs to accept
17243/// exactly the two-arm closed set) reads from one canonical declaration
17244/// per arm, not a scattered mix of substrate-side const + prose-only
17245/// sibling. Same "one canonical declaration per arm, next to the
17246/// closed set's peer" discipline the peer
17247/// [`CILIUM_AUTH_MODE_REQUIRED`] / [`CILIUM_AUTH_MODE_DISABLED`]
17248/// (2c3f11b — the two-arm Cilium `MutualAuthenticationMode` `OpenAPI`
17249/// enum's closed set) established for the sibling Cilium-CR-side
17250/// per-enum-value axis, and the peer
17251/// [`M3_PLACEMENT_ESTRATEGIA_SINGLE_NODE`] /
17252/// [`M3_PLACEMENT_ESTRATEGIA_REPLICATED`] /
17253/// [`M3_PLACEMENT_ESTRATEGIA_SHARDED`] (b0ce0a5 — the three-arm typed
17254/// [`crate::PlacementStrategy`] variant discriminator-value set) applies
17255/// on the sibling M3 typed-enum discriminator-scalar axis — extends the
17256/// discipline onto the Helm-chart-schema-enum-value closed set every
17257/// rendered Chart.yaml declares its per-chart-kind axis over.
17258///
17259/// Until this lift landed the sibling `"library"` value lived only in
17260/// prose across the [`HELM_CHART_TYPE_APPLICATION`] docstring's
17261/// closed-set enumeration (3+ mentions naming the sibling `library`
17262/// shape as the schema-admitted second arm, including the accidental-
17263/// collapse-onto-sibling failure-mode arm the pin test
17264/// [`tests::helm_chart_type_application_and_library_are_distinct`]
17265/// closes), with no compile-time link between the substrate-side
17266/// canonical const and the sibling closed-set arm the docstring
17267/// referenced — a hypothetical future consumer reaching for the
17268/// sibling shape (an operator-side per-chart-kind classifier, a
17269/// helmworks-side value-drift detector, the future per-Aplicacao
17270/// library chart's emit site) had to re-derive the value from the
17271/// prose enumeration rather than reading the same `&'static str` the
17272/// substrate declares. This lift closes that gap by pairing the
17273/// canonical-Helm-chart-schema-per-chart-kind axis at both closed-set
17274/// arms, so drift-detection between the two shapes is a build-time
17275/// constant-value comparison at
17276/// [`tests::helm_chart_type_application_and_library_are_distinct`]
17277/// rather than a runtime silent-collapse-onto-sibling far from the
17278/// drift's source.
17279///
17280/// [chart-type-doc]: https://helm.sh/docs/topics/charts/#chart-types
17281pub const HELM_CHART_TYPE_LIBRARY: &str = "library";
17282
17283/// Canonical Helm 3 `Chart.yaml` top-level YAML axis-key naming the
17284/// per-chart chart-schema-apiVersion field whose scalar-value
17285/// [`HELM_CHART_API_VERSION`] already owns as the peer axis-value
17286/// lift. Where the peer axis-value lift pins the byte-shape of the
17287/// `apiVersion:` field's admitted scalar (Helm 3's `"v2"`), this
17288/// axis-key lift pins the byte-shape of the `apiVersion:` field's
17289/// YAML-key name itself: the load-bearing serde-rename literal at
17290/// [`caixa-helm`][ch]'s `ChartYaml` struct
17291/// (`caixa-helm/src/lib.rs:145`, `#[serde(rename = "apiVersion")]`)
17292/// that selects how the Rust field `api_version` serializes into
17293/// the rendered `Chart.yaml` YAML mapping.
17294///
17295/// The byte-shape (`"apiVersion"`) is byte-identical to the K8s-CR
17296/// top-level per-CR schema-apiVersion axis key ([`KUBE_KEY_API_VERSION`])
17297/// by Helm's design decision to inherit the K8s CR top-level shape
17298/// verbatim (see [chart-yaml-desc]) — the paired
17299/// `helm_chart_key_api_version_matches_kube_key_api_version` pin
17300/// asserts the two byte-shapes coincide, so a future K8s-side
17301/// rebrand at [`KUBE_KEY_API_VERSION`] that dropped the byte-
17302/// identity would fail the pin, surfacing the axis divergence at
17303/// substrate-build time rather than as a silent Helm-chart-schema-
17304/// parser rejection at `helm lint` / `helm template` time. The two
17305/// axes are structurally-independent schema surfaces (the Helm 3
17306/// chart-schema top-level shape vs. the K8s apiserver-side CR
17307/// top-level shape) whose byte-shapes happen to coincide today; the
17308/// paired pin makes the coincidence load-bearing rather than
17309/// accidental.
17310///
17311/// The single source of truth every consumer that names the per-
17312/// Chart.yaml top-level chart-schema-apiVersion YAML key reaches for:
17313///
17314/// - [`caixa-helm`][ch]'s `ChartYaml` struct's `api_version` field
17315/// `#[serde(rename = "apiVersion")]` attribute (the sole
17316/// production serialize-side site the literal appears at as a
17317/// syntactic serde-rename argument; the attribute itself cannot
17318/// consume a `const` because Rust's attribute grammar admits
17319/// only string literals, so the discipline here is: the const's
17320/// byte-shape must remain byte-identical to the literal the
17321/// attribute pins, and the paired drift-detection pin at
17322/// [`caixa-helm`]'s
17323/// `chart_yaml_serializes_api_version_axis_under_lifted_helm_chart_key_api_version`
17324/// round-trips a rendered [`caixa-helm`]-emitted `Chart.yaml`
17325/// through `serde_yaml::from_str::<serde_yaml::Value>` and
17326/// asserts the top-level `Mapping::get(HELM_CHART_KEY_API_VERSION)`
17327/// resolves — closing the drift the syntactic-literal-only
17328/// attribute would otherwise leave silent);
17329/// - every test-side navigator that inspects the serialized
17330/// [`caixa-helm`]-emitted `Chart.yaml` YAML mapping by the top-
17331/// level chart-schema-apiVersion key.
17332///
17333/// A drift on the emitter's serde-rename literal (a future refactor
17334/// that dropped the `#[serde(rename = "apiVersion")]` attribute or
17335/// changed the target key to `"ApiVersion"` / `"apiversion"` /
17336/// `"schemaVersion"`) would silently serialize the field under
17337/// Rust's default snake_case `api_version:` key, which Helm's
17338/// chart-schema parser rejects at `helm lint` / `helm dependency
17339/// build` / `helm template` time with an "apiVersion is required"
17340/// error — the failure surfaces far from the drift site, and every
17341/// downstream `lareira-<nome>` chart consumer drops with no field
17342/// naming the serde-rename-drift root cause. Same drift-detection-
17343/// pin discipline the peer [`HELM_CHART_KEY_TYPE`] /
17344/// [`HELM_CHART_KEY_APP_VERSION`] lifts (d29bc23) established on the
17345/// sibling per-Chart.yaml serde-rename-literal-only axis pair —
17346/// extends the discipline from the two axes those lifts closed onto
17347/// the third and last serde-rename-literal-only axis at
17348/// [`caixa-helm`]'s `ChartYaml` struct, so every `#[serde(rename =
17349/// "...")]` literal on the struct threads through a canonical
17350/// substrate-side `&'static str` with a paired drift-detection pin.
17351///
17352/// [chart-yaml-desc]: https://helm.sh/docs/topics/charts/#the-chartyaml-file
17353/// [ch]: ../../caixa_helm/index.html
17354pub const HELM_CHART_KEY_API_VERSION: &str = "apiVersion";
17355
17356/// Canonical Helm 3 `Chart.yaml` top-level YAML axis-key naming the
17357/// per-chart-kind discriminator field whose closed-set scalar-value
17358/// pair [`HELM_CHART_TYPE_APPLICATION`] / [`HELM_CHART_TYPE_LIBRARY`]
17359/// already owns as the peer axis-value lift. Where the peer
17360/// axis-value lifts pin the byte-shape of the `type:` field's
17361/// admitted-value set, this axis-key lift pins the byte-shape of the
17362/// `type:` field's YAML-key name itself: the load-bearing serde-
17363/// rename literal at [`caixa-helm`][ch]'s `ChartYaml` struct
17364/// (`caixa-helm/src/lib.rs:149`, `#[serde(rename = "type")]`) that
17365/// selects how the Rust field `chart_type` serializes into the
17366/// rendered `Chart.yaml` YAML mapping.
17367///
17368/// The single source of truth every consumer that names the per-
17369/// Chart.yaml top-level per-chart-kind discriminator key reaches for:
17370///
17371/// - [`caixa-helm`][ch]'s `ChartYaml` struct's `chart_type` field
17372/// `#[serde(rename = "type")]` attribute (the sole production
17373/// serialize-side site the literal appears at as a syntactic
17374/// serde-rename argument; the attribute itself cannot consume a
17375/// `const` because Rust's attribute grammar admits only string
17376/// literals, so the discipline here is: the const's byte-shape
17377/// must remain byte-identical to the literal the attribute pins,
17378/// and the drift-detection pin at
17379/// [`caixa-helm`]'s
17380/// `chart_yaml_serializes_type_axis_under_lifted_helm_chart_key_type`
17381/// round-trips a rendered [`caixa-helm`]-emitted `Chart.yaml`
17382/// through `serde_yaml::from_str::<serde_yaml::Value>` and
17383/// asserts the top-level `Mapping::get(HELM_CHART_KEY_TYPE)`
17384/// resolves — closing the drift the syntactic-literal-only
17385/// attribute would otherwise leave silent);
17386/// - every test-side navigator that inspects the serialized
17387/// [`caixa-helm`]-emitted `Chart.yaml` YAML mapping by the top-
17388/// level per-chart-kind discriminator key.
17389///
17390/// A drift on the emitter's serde-rename literal (a future refactor
17391/// that dropped the `#[serde(rename = "type")]` attribute or
17392/// changed the target key to `"Type"` / `"kind"` / `"chartType"`)
17393/// would surface as one of two silent failure modes at
17394/// `helm dependency build` / `helm lint` / `helm template` time
17395/// far from the drift site: the rendered `Chart.yaml`'s top-level
17396/// mapping carries an unrecognized key (`chart_type:` from Rust's
17397/// default snake_case serialization) that Helm's chart-schema
17398/// parser silently ignores, defaulting the per-chart-kind axis to
17399/// `application` with no process-log drift-signal (masking the
17400/// schema-shape violation); or the drift accidentally collapses
17401/// the key onto the sibling `kind` / K8s-CR `KUBE_KEY_KIND`
17402/// axis (byte-distinct today at the substrate — see the paired
17403/// `helm_chart_key_type_is_byte_distinct_from_kube_key_kind` pin)
17404/// that Helm's chart-schema parser silently treats as an unknown
17405/// field, again defaulting the per-chart-kind axis.
17406///
17407/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5)
17408/// promotes the axis-key to a typed substrate-side `&'static str`
17409/// on the same trajectory the peer axis-value lifts
17410/// ([`HELM_CHART_TYPE_APPLICATION`] / [`HELM_CHART_TYPE_LIBRARY`])
17411/// established — completes the per-Chart.yaml per-chart-kind
17412/// discriminator axis single-sourcing at both the key and value
17413/// halves (`{HELM_CHART_KEY_TYPE, HELM_CHART_TYPE_APPLICATION,
17414/// HELM_CHART_TYPE_LIBRARY}`), so the full
17415/// `(key, admitted-value-set)` per-axis lift lives at one canonical
17416/// declaration site. Same "(key, value) axis-pair lift completes at
17417/// one canonical source per half" discipline the peer
17418/// [`KUBE_KEY_API_VERSION`] (7994) + [`HELM_CHART_API_VERSION`]
17419/// (14580) pair carries on the sibling apiVersion axis, and the
17420/// [`FLEET_PROGRAMS_KEY_NAME`] (7651) + `Servico :nome` value pair
17421/// carries on the sibling per-fleet-programs-entry axis.
17422///
17423/// [chart-type-doc]: https://helm.sh/docs/topics/charts/#chart-types
17424/// [ch]: ../../caixa_helm/index.html
17425pub const HELM_CHART_KEY_TYPE: &str = "type";
17426
17427/// Canonical Helm 3 `Chart.yaml` top-level YAML axis-key naming the
17428/// per-chart underlying-application-version field — the load-bearing
17429/// serde-rename literal at [`caixa-helm`][ch]'s `ChartYaml` struct
17430/// (`caixa-helm/src/lib.rs:152`, `#[serde(rename = "appVersion")]`)
17431/// that selects how the Rust field `app_version` serializes into the
17432/// rendered `Chart.yaml` YAML mapping. Distinct from the sibling
17433/// [`Chart.yaml` `version:` field][chart-yaml-desc] (the chart's own
17434/// SemVer, incremented per release of the chart itself); the
17435/// `appVersion:` field the Helm 3 chart-schema pins carries the
17436/// underlying application's version (see [app-version-doc]) — the
17437/// version the containerized workload the chart installs advertises
17438/// (an OCI image tag, a wasm-component `:versao`, a package release
17439/// tag). At the caixa-helm renderer today the two axes both draw
17440/// from the caixa's `:versao` at [`build_chart_yaml`] because a
17441/// [`caixa-core::Caixa`]'s `:versao` names both the chart's own
17442/// release cadence and the underlying wasm-component release
17443/// cadence in one axis (`caixa`'s per-caixa BLAKE3-closure identity
17444/// binds a caixa's chart + wasm-binary + declared source at exactly
17445/// one release axis), but the Chart.yaml schema pins the two YAML
17446/// keys distinctly regardless — every downstream Helm-consumer
17447/// (Artifact Hub's per-chart-search index, `helm search` /
17448/// `helm show chart` operator surfaces) routes the two axes onto
17449/// distinct display fields at chart-inspection time.
17450///
17451/// The single source of truth every consumer that names the per-
17452/// Chart.yaml top-level app-version YAML key reaches for:
17453///
17454/// - [`caixa-helm`][ch]'s `ChartYaml` struct's `app_version` field
17455/// `#[serde(rename = "appVersion")]` attribute (the sole
17456/// production serialize-side site the literal appears at as a
17457/// syntactic serde-rename argument; the same
17458/// attribute-literal-only-grammar constraint the peer
17459/// [`HELM_CHART_KEY_TYPE`] docstring enumerates applies, and
17460/// the paired drift-detection pin at [`caixa-helm`]'s
17461/// `chart_yaml_serializes_app_version_axis_under_lifted_helm_chart_key_app_version`
17462/// round-trips a rendered `Chart.yaml` and asserts the top-level
17463/// `Mapping::get(HELM_CHART_KEY_APP_VERSION)` resolves);
17464/// - every test-side navigator that inspects the serialized
17465/// [`caixa-helm`]-emitted `Chart.yaml` YAML mapping by the top-
17466/// level per-chart-app-version key.
17467///
17468/// A drift on the emitter's serde-rename literal (a future refactor
17469/// that dropped the `#[serde(rename = "appVersion")]` attribute or
17470/// changed the target key to `"AppVersion"` / `"applicationVersion"`
17471/// / `"version"`) would surface as one of two silent failure modes
17472/// at Helm-chart-consumption time far from the drift site: the
17473/// rendered `Chart.yaml`'s top-level mapping carries an unrecognized
17474/// key (`app_version:` from Rust's default snake_case serialization)
17475/// that Helm's chart-schema parser silently drops from the parsed
17476/// chart-metadata shape (masking the schema-shape violation with no
17477/// process-log drift-signal, and every downstream Artifact Hub /
17478/// `helm search` per-chart index falls back to "no application
17479/// version" for the rendered chart); or the drift accidentally
17480/// collapses the app-version key onto the sibling chart-own-version
17481/// `version:` axis (byte-distinct today at the substrate — see the
17482/// paired
17483/// `helm_chart_key_app_version_is_byte_distinct_from_helm_chart_key_version`
17484/// pin) that Helm's chart-schema parser then silently reads under
17485/// the wrong axis, and the chart's own SemVer collides with the
17486/// underlying-application version at every downstream Helm-consumer.
17487///
17488/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5)
17489/// promotes the axis-key to a typed substrate-side `&'static str`
17490/// on the same trajectory the peer [`HELM_CHART_KEY_TYPE`] lift
17491/// established — extends the per-Chart.yaml top-level YAML axis-key
17492/// single-sourcing discipline from the per-chart-kind discriminator
17493/// key onto the sibling per-chart-app-version key, so every
17494/// substrate-side renderer that emits or navigates a `Chart.yaml`
17495/// top-level mapping consults one canonical `&'static str` per axis.
17496///
17497/// [chart-yaml-desc]: https://helm.sh/docs/topics/charts/#the-chartyaml-file
17498/// [app-version-doc]: https://helm.sh/docs/topics/charts/#the-appversion-field
17499/// [ch]: ../../caixa_helm/index.html
17500pub const HELM_CHART_KEY_APP_VERSION: &str = "appVersion";
17501
17502/// Canonical Helm 3 `Chart.yaml` top-level YAML axis-key naming the
17503/// per-chart dependency-list field — the load-bearing serde
17504/// field-name at [`caixa-helm`][ch]'s `ChartYaml` struct's
17505/// `dependencies` field, the parent list-container the already-lifted
17506/// [`HELM_CHART_DEPENDENCY_KEY_NAME`] / [`HELM_CHART_DEPENDENCY_KEY_VERSION`]
17507/// / [`HELM_CHART_DEPENDENCY_KEY_REPOSITORY`] /
17508/// [`HELM_CHART_DEPENDENCY_KEY_ALIAS`] per-entry sub-mapping tetrad
17509/// (69f62db) mounts under. The chart-schema top-level `dependencies:`
17510/// field pins the list of chart-registry references Helm's per-dep
17511/// resolver consults at `helm dependency build` /
17512/// `helm dependency update` time to vendor each dependency chart
17513/// under the substrate's canonical [`DEFAULT_LIBRARY_NAME`] wrap-key
17514/// convention. Every rendered `lareira-<nome>` chart declares exactly
17515/// one entry today (the [`DEFAULT_LIBRARY_NAME`] `pleme-computeunit`
17516/// library-chart dep the sibling [`caixa-helm`][ch]'s `build_chart_yaml`
17517/// mounts) — see [chart-dependencies-doc] for the Helm 3 upstream axis
17518/// documentation.
17519///
17520/// The single source of truth every consumer that names the per-
17521/// Chart.yaml top-level dependency-list key reaches for:
17522///
17523/// - [`caixa-helm`][ch]'s `ChartYaml` struct's `dependencies` field
17524/// (the sole production serialize-side site the wire-key appears
17525/// at — Rust's default field-name-verbatim serde emission means
17526/// no `#[serde(rename = "…")]` attribute pins the key today; the
17527/// paired drift-detection pin at [`caixa-helm`]'s
17528/// `chart_yaml_serializes_dependencies_axis_under_lifted_helm_chart_key_dependencies`
17529/// round-trips a rendered `Chart.yaml` through
17530/// `serde_yaml::from_str::<serde_yaml::Value>` and asserts the
17531/// top-level `Mapping::get(HELM_CHART_KEY_DEPENDENCIES)` resolves —
17532/// closing the drift a future hostile refactor could otherwise
17533/// leave silent: a rename of the Rust field to `Vec<ChartDependency>
17534/// under a `deps:` / `chartDependencies:` name, or an accidental
17535/// `#[serde(rename_all = "camelCase")]` attribute on `ChartYaml`
17536/// that stays a no-op on the four identity-mapped top-level keys
17537/// today but silently activates on a future multi-word field
17538/// addition);
17539/// - every test-side navigator that inspects the serialized
17540/// [`caixa-helm`]-emitted `Chart.yaml` YAML mapping by the top-
17541/// level per-chart-dependency-list key.
17542///
17543/// A drift on this per-Chart.yaml top-level list-container axis-key
17544/// would silently rebrand the wire key — Helm's chart-schema parser
17545/// silently drops the dep list from the parsed chart-metadata shape,
17546/// `helm dependency build` finds no chart to vendor, and every
17547/// rendered `lareira-<nome>` chart's install fails with
17548/// `template: no template ... associated with template ...` far from
17549/// the drift site with no field naming the top-level-list-key-drift
17550/// root cause. The failure mode is byte-shape-symmetric with the peer
17551/// [`HELM_CHART_DEPENDENCY_KEY_NAME`] drift narrative (which closes on
17552/// the per-entry name axis one level down) — both close on the
17553/// `helm dependency build` / apply-time path.
17554///
17555/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5)
17556/// promotes the top-level list-container axis-key to a typed
17557/// substrate-side `&'static str` on the same trajectory the peer
17558/// [`HELM_CHART_KEY_TYPE`] / [`HELM_CHART_KEY_APP_VERSION`] /
17559/// [`HELM_CHART_KEY_API_VERSION`] top-level axis-key lifts (d29bc23,
17560/// cc44e4b) established — completes the parent+children canonical-pin
17561/// pair with the already-lifted per-`dependencies[]`-entry
17562/// sub-mapping tetrad. Where the child tetrad pins the byte-shape of
17563/// each per-dep entry's four sub-mapping keys (`name`, `version`,
17564/// `repository`, `alias`), this parent-axis lift pins the byte-shape
17565/// of the top-level list-container the tetrad mounts under, so the
17566/// full `(dependencies: → [name/version/repository/alias])`
17567/// per-Chart.yaml dependency-list schema surface lives at one
17568/// canonical `&'static str` per YAML axis-key. Same
17569/// "parent list-container + child sub-mapping tetrad" canonical-pin
17570/// discipline the peer [`SUPERVISOR_KEY_CHILDREN`] (parent) +
17571/// [`SUPERVISOR_CHILD_KEY_CAIXA`] / [`SUPERVISOR_CHILD_KEY_VERSAO`] /
17572/// [`SUPERVISOR_CHILD_KEY_RESTART`] (children) pair (40cc4e5, ef912df)
17573/// established on the sibling per-`:supervisor :children` axis, and the
17574/// peer [`M2_KEY_UPGRADE_FROM`] (parent) +
17575/// [`M2_UPGRADE_FROM_KEY_FROM`] / [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`]
17576/// (children) pair established on the sibling per-`:upgrade-from` axis.
17577///
17578/// [chart-yaml-desc]: https://helm.sh/docs/topics/charts/#the-chartyaml-file
17579/// [chart-dependencies-doc]: https://helm.sh/docs/topics/charts/#chart-dependencies
17580/// [ch]: ../../caixa_helm/index.html
17581pub const HELM_CHART_KEY_DEPENDENCIES: &str = "dependencies";
17582
17583/// Canonical Helm 3 `Chart.yaml` per-`dependencies[]`-entry sub-mapping
17584/// YAML axis-key naming the per-dep chart-name field — the load-bearing
17585/// serde field-name at [`caixa-helm`][ch]'s `ChartDependency` struct's
17586/// `name` field. Byte-identical to the sibling K8s CR
17587/// [`KUBE_KEY_NAME`] axis-key by Helm's design decision to inherit the
17588/// K8s CR body-key vocabulary at every schema surface it consumes
17589/// (chart-metadata, per-CR install-payload, per-dep dependency-list);
17590/// the paired
17591/// [`tests::helm_chart_dependency_key_name_matches_kube_key_name`] pin
17592/// asserts the two byte-shapes coincide, so a future K8s-side rebrand
17593/// at [`KUBE_KEY_NAME`] that dropped the byte-identity would fail the
17594/// pin at substrate-build time rather than silently drop the per-dep
17595/// name lookup at `helm dependency build` time far from the drift site.
17596///
17597/// The chart-schema per-dep entry's `name:` value pins the exact
17598/// Helm-registry chart-name Helm's per-dep alias convention scopes the
17599/// per-dep values sub-block under when no `alias:` is set (see the
17600/// sibling [`HELM_CHART_DEPENDENCY_KEY_ALIAS`] docstring for the alias
17601/// axis) — every rendered `lareira-<nome>` chart's Chart.yaml
17602/// `dependencies[0].name:` binds to the same `&'static str` as its
17603/// values.yaml wrap key (see [`caixa-helm`][ch]'s
17604/// `values_yaml_wrap_key_matches_chart_dependency_name` pin on the
17605/// structural alignment). A drift on this per-dep sub-key (a future
17606/// refactor that renamed the `ChartDependency::name` Rust field to
17607/// `ChartDependency::nome`, or added a
17608/// `#[serde(rename_all = "camelCase")]` attribute that stays a no-op
17609/// on the four identity-mapped keys today but silently activates on a
17610/// future field addition) would rebrand the wire key silently — Helm's
17611/// per-dep dependency-router silently drops the dep from the parsed
17612/// chart-metadata (the substrate ships a Chart.yaml that lists no
17613/// `pleme-computeunit` dep, `helm dependency build` finds no chart to
17614/// vendor, and every rendered lareira-`<nome>` chart's install fails
17615/// with "template: no template ... associated with template ..." far
17616/// from the drift site). Peer to [`HELM_CHART_DEPENDENCY_KEY_VERSION`]
17617/// / [`HELM_CHART_DEPENDENCY_KEY_REPOSITORY`] /
17618/// [`HELM_CHART_DEPENDENCY_KEY_ALIAS`] on the sibling per-dep sub-key
17619/// axes — completes the per-`dependencies[]`-entry YAML axis-key
17620/// canonical-pin tetrad at the substrate. Same per-entry-sub-key
17621/// canonical-lift discipline the peer
17622/// [`SUPERVISOR_CHILD_KEY_CAIXA`] / [`SUPERVISOR_CHILD_KEY_VERSAO`] /
17623/// [`SUPERVISOR_CHILD_KEY_RESTART`] triad (ef912df) established on the
17624/// sibling per-`:children` sub-mapping surface, and the
17625/// [`ENTRADA_KEY_HOST`] / [`ENTRADA_KEY_PARA`] / [`ENTRADA_KEY_PATHS`]
17626/// / [`ENTRADA_KEY_PORT`] tetrad (a3d6162) established on the sibling
17627/// per-`:entrada` sub-mapping surface.
17628///
17629/// [ch]: ../../caixa_helm/index.html
17630pub const HELM_CHART_DEPENDENCY_KEY_NAME: &str = "name";
17631
17632/// Canonical Helm 3 `Chart.yaml` per-`dependencies[]`-entry sub-mapping
17633/// YAML axis-key naming the per-dep chart-version-constraint field —
17634/// the load-bearing serde field-name at [`caixa-helm`][ch]'s
17635/// `ChartDependency` struct's `version` field. Distinct from the
17636/// sibling per-Chart.yaml top-level chart-own-SemVer axis-key
17637/// (`version:` at the top level, whose byte-shape coincides with this
17638/// per-dep sub-key at the wire — a coincidence the substrate-side
17639/// paired [`tests::helm_chart_dependency_key_version_pins_canonical_value`]
17640/// pin holds byte-verbatim). The chart-schema per-dep entry's
17641/// `version:` value pins the SemVer-range constraint Helm's per-dep
17642/// resolver matches against the target dep's Chart.yaml `version:`
17643/// scalar at `helm dependency build` / `helm dependency update` time.
17644/// A drift on this per-dep sub-key would surface as one of two silent
17645/// failure modes at chart-vendor time far from the drift site: Helm's
17646/// per-dep chart-schema parser silently drops the version-constraint
17647/// scalar from the parsed dep-entry (the per-dep resolver falls back
17648/// to the wildcard `*` shape and vendors whatever chart-version the
17649/// upstream registry currently advertises, silently promoting a chart
17650/// upgrade the operator never authored), or a subsequent
17651/// `#[serde(rename_all)]` addition rebrands the key to Helm's
17652/// unrecognized shape and the per-dep entry silently vanishes from the
17653/// parsed dep-list. Peer to [`HELM_CHART_DEPENDENCY_KEY_NAME`] /
17654/// [`HELM_CHART_DEPENDENCY_KEY_REPOSITORY`] /
17655/// [`HELM_CHART_DEPENDENCY_KEY_ALIAS`] on the sibling per-dep sub-key
17656/// axes — extends the per-entry-sub-key canonical-lift tetrad at the
17657/// substrate. See [`HELM_CHART_DEPENDENCY_KEY_NAME`] for the shared
17658/// per-entry-sub-mapping lift rationale.
17659///
17660/// [ch]: ../../caixa_helm/index.html
17661pub const HELM_CHART_DEPENDENCY_KEY_VERSION: &str = "version";
17662
17663/// Canonical Helm 3 `Chart.yaml` per-`dependencies[]`-entry sub-mapping
17664/// YAML axis-key naming the per-dep chart-registry URL field — the
17665/// load-bearing serde field-name at [`caixa-helm`][ch]'s
17666/// `ChartDependency` struct's `repository` field. The chart-schema
17667/// per-dep entry's `repository:` value pins the Helm-registry URL
17668/// (`file://…`, `https://…`, `oci://…`) Helm's per-dep resolver
17669/// consults at `helm dependency build` time to fetch the per-dep
17670/// chart bytes. At the caixa-helm substrate the default value is the
17671/// canonical [`caixa_helm::DEFAULT_LIBRARY_REPO`] pointing at the
17672/// helmworks file:// path; the future per-edition library-chart
17673/// re-emission for the OCI registry (once `pleme-io/helmworks/charts`
17674/// lands as an OCI-registry-backed chart-source) reaches this axis
17675/// through a paired scalar-value lift on the per-dep repo axis. A
17676/// drift on this per-dep sub-key would surface as one of two silent
17677/// failure modes at chart-vendor time far from the drift site: Helm's
17678/// per-dep resolver silently drops the repository scalar from the
17679/// parsed dep-entry (the per-dep resolver falls back to the "no
17680/// repository set" shape and refuses to vendor the dep with
17681/// `no repository defined`), or the per-dep chart-schema parser
17682/// silently absorbs a rename drift via `#[serde(default)]`
17683/// fall-through at the struct-side and the per-dep repo axis lands
17684/// under Rust's `""` default — Helm rejects the empty URL at
17685/// `helm dependency build` time. Peer to
17686/// [`HELM_CHART_DEPENDENCY_KEY_NAME`] /
17687/// [`HELM_CHART_DEPENDENCY_KEY_VERSION`] /
17688/// [`HELM_CHART_DEPENDENCY_KEY_ALIAS`] on the sibling per-dep sub-key
17689/// axes. See [`HELM_CHART_DEPENDENCY_KEY_NAME`] for the shared
17690/// per-entry-sub-mapping lift rationale.
17691///
17692/// [ch]: ../../caixa_helm/index.html
17693pub const HELM_CHART_DEPENDENCY_KEY_REPOSITORY: &str = "repository";
17694
17695/// Canonical Helm 3 `Chart.yaml` per-`dependencies[]`-entry sub-mapping
17696/// YAML axis-key naming the per-dep chart-alias override field — the
17697/// load-bearing serde field-name at [`caixa-helm`][ch]'s
17698/// `ChartDependency` struct's `alias` field. The chart-schema per-dep
17699/// entry's `alias:` value, when set, overrides the per-dep values
17700/// wrap-key (Helm's per-dep alias convention scopes the per-dep values
17701/// sub-block under `alias:` when set, and under the sibling
17702/// [`HELM_CHART_DEPENDENCY_KEY_NAME`] `name:` value otherwise); the
17703/// caixa-helm substrate today emits the axis as `None` at every
17704/// rendered `lareira-<nome>` chart's `dependencies[0].alias:` (the
17705/// `#[serde(default, skip_serializing_if = "Option::is_none")]`
17706/// attribute on the `alias` field elides the axis entirely from the
17707/// emitted YAML when unset), so the values wrap-key defaults to the
17708/// per-dep `name:` value — but the axis-key remains part of the
17709/// substrate-side chart-schema-per-dep-entry contract for the future
17710/// per-Aplicacao library chart's per-Servico per-dep aliasing
17711/// [`HELM_CHART_TYPE_LIBRARY`] docstring names as a trajectory item.
17712/// A drift on this per-dep sub-key (a future refactor that renamed
17713/// the `ChartDependency::alias` Rust field, or added a
17714/// `#[serde(rename_all = "camelCase")]` attribute that silently
17715/// activates on a future field addition) would rebrand the wire key
17716/// silently — Helm's per-dep alias-convention router would silently
17717/// drop the alias from the parsed dep-entry (the per-dep values wrap-
17718/// key falls back to the sibling `name:` value, and every per-cluster
17719/// per-Servico per-dep values override the operator authored under
17720/// the alias-key silently routes nowhere at `helm template` time). Peer
17721/// to [`HELM_CHART_DEPENDENCY_KEY_NAME`] /
17722/// [`HELM_CHART_DEPENDENCY_KEY_VERSION`] /
17723/// [`HELM_CHART_DEPENDENCY_KEY_REPOSITORY`] on the sibling per-dep
17724/// sub-key axes — completes the per-`dependencies[]`-entry YAML
17725/// axis-key canonical-pin tetrad. See [`HELM_CHART_DEPENDENCY_KEY_NAME`]
17726/// for the shared per-entry-sub-mapping lift rationale.
17727///
17728/// [ch]: ../../caixa_helm/index.html
17729pub const HELM_CHART_DEPENDENCY_KEY_ALIAS: &str = "alias";
17730
17731/// Canonical Helm 3 per-chart-directory metadata-file filename every
17732/// rendered `lareira-<nome>` chart carries at its top-level directory —
17733/// the fixed filename Helm's chart-schema parser (`helm dependency
17734/// build`, `helm lint`, `helm template`, `helm install`) looks up by
17735/// name at the chart-directory root to locate the per-chart
17736/// [`HELM_CHART_API_VERSION`] + [`HELM_CHART_TYPE_APPLICATION`] +
17737/// name/version/dependencies scalars each `lareira-<nome>` chart
17738/// declares (see [chart-yaml-desc]). The single source of truth every
17739/// consumer that names the metadata file — the sole caixa-helm
17740/// production emit site the prior inline `"Chart.yaml"` literal sat at
17741/// ([`caixa-helm`][ch]'s [`render_chart_for_servico`][rcs] `ChartDir`
17742/// assembly's per-file `path` axis, one of the three canonical
17743/// `lareira-<nome>` chart-directory files the renderer emits as a
17744/// bundle) plus every test-side round-trip navigator that reaches into
17745/// the rendered `ChartDir` by the metadata filename (six sites across
17746/// [`caixa-helm`][ch]'s per-chart-metadata-field sweep tests +
17747/// [`ChartDir::write_to`] post-write existence pin) — reaches for the
17748/// same `&'static str` by construction.
17749///
17750/// Until this lift landed the filename `"Chart.yaml"` lived as seven
17751/// verbatim inline literals (one production `PathBuf::from("Chart.yaml")`
17752/// at the `ChartDir` files-vec construction site + six test-side
17753/// `PathBuf::from("Chart.yaml")` / `chart_root.join("Chart.yaml")` /
17754/// `names.contains(&"Chart.yaml".to_string())` fixture navigators).
17755/// A drift on the emit side (a `"chart.yaml"` / `"chart.YAML"` /
17756/// `"Chart.yml"` / `"chart.yaml.tmpl"` typo, or an accidental collapse
17757/// onto Helm 2's sibling per-chart-metadata-filename axis, or a
17758/// per-fork `Chartfile.yaml` rebrand any per-edition packaging
17759/// substrate might introduce) at any one site would surface as one of
17760/// two silent failure modes at chart-consumption time:
17761///
17762/// - Helm's chart-schema parser refuses to open the rendered chart-
17763/// directory as a chart at all — `helm lint` / `helm dependency
17764/// build` fails with "Error: Chart.yaml file is missing" far from
17765/// the emit-drift commit's source, and the per-Servico release
17766/// cycle drops with no field naming the metadata-filename-drift
17767/// root cause (the operator sees "the chart isn't being recognized"
17768/// with no canonical anchor to compare the rendered filename
17769/// against);
17770/// - the rendered chart's `ChartFile` collection lists a file at the
17771/// emit-side drifted name (e.g. `"chart.yaml"`) while the sibling
17772/// [`caixa-flux`][cf] `Kustomization` bundle-path emitter's per-
17773/// chart reference (a future per-cluster snapshot bundle that
17774/// re-lists the chart-dir contents by filename) continues to look
17775/// under the canonical `"Chart.yaml"` — the two-crate pair silently
17776/// goes out of sync, with the flux bundle's chart-directory
17777/// resolver returning `None` for the metadata file at cluster-side
17778/// `feira app deploy` time.
17779///
17780/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
17781/// "every recurring shape becomes a generator before it becomes a
17782/// pattern; every pattern becomes a library before it becomes
17783/// duplicated code. The duplication budget is zero.") promotes the
17784/// filename to a typed substrate-side `&'static str` on the same
17785/// trajectory the peer [`HELM_CHART_API_VERSION`] /
17786/// [`HELM_CHART_TYPE_APPLICATION`] / [`DEFAULT_LIBRARY_NAME`] /
17787/// [`LAREIRA_CHART_NAME_PREFIX`] lifts established on the sibling
17788/// canonical-Helm-load-bearing-string axes — pivots the discipline
17789/// from the per-Chart.yaml top-level *body* axes (`apiVersion`,
17790/// `type`) onto the sibling per-chart-directory *filename* axis every
17791/// rendered chart directory carries as the fixed lookup name Helm's
17792/// chart-schema parser consults at chart-open time. Peer to the
17793/// canonical-Helm-chart-schema-axis lifts on the sibling per-Chart.yaml
17794/// body surfaces — completes the per-`lareira-<nome>`-chart-directory
17795/// `(filename, apiVersion, type)` canonical-scalar-axis re-export triple
17796/// every rendered chart declares at its top-level metadata file.
17797///
17798/// [chart-yaml-desc]: https://helm.sh/docs/topics/charts/#the-chartyaml-file
17799/// [ch]: ../../caixa_helm/index.html
17800/// [cf]: ../../caixa_flux/index.html
17801/// [rcs]: ../../caixa_helm/fn.render_chart_for_servico.html
17802pub const HELM_CHART_YAML_FILENAME: &str = "Chart.yaml";
17803
17804/// Canonical Helm 3 per-chart-directory values-file filename every
17805/// rendered `lareira-<nome>` chart carries at its top-level directory —
17806/// the fixed filename Helm's chart-schema parser (`helm dependency
17807/// build`, `helm lint`, `helm template`, `helm install`) looks up by
17808/// name at the chart-directory root to locate the per-chart
17809/// [`DEFAULT_LIBRARY_NAME`]-wrapped values block that
17810/// [`HELM_VALUES_KEY_ENABLED`] toggles (see [values-yaml-desc]). The
17811/// single source of truth every consumer that names the values file —
17812/// the sole caixa-helm production emit site the prior inline
17813/// `"values.yaml"` literal sat at ([`caixa-helm`][ch]'s
17814/// [`render_chart_for_servico`][rcs] `ChartDir` assembly's per-file
17815/// `path` axis, the second of the three canonical `lareira-<nome>`
17816/// chart-directory files the renderer emits as a bundle, sibling to
17817/// the metadata-file [`HELM_CHART_YAML_FILENAME`] axis) plus every
17818/// test-side round-trip navigator that reaches into the rendered
17819/// `ChartDir` by the values filename (eleven sites across
17820/// [`caixa-helm`][ch]'s per-chart-values-field sweep tests +
17821/// [`ChartDir::write_to`] post-write existence pin) — reaches for the
17822/// same `&'static str` by construction.
17823///
17824/// Until this lift landed the filename `"values.yaml"` lived as twelve
17825/// verbatim inline literals (one production `PathBuf::from("values.yaml")`
17826/// at the `ChartDir` files-vec construction site + eleven test-side
17827/// `PathBuf::from("values.yaml")` / `chart_root.join("values.yaml")` /
17828/// `names.contains(&"values.yaml".to_string())` fixture navigators).
17829/// A drift on the emit side (a `"Values.yaml"` / `"values.YAML"` /
17830/// `"values.yml"` / `"values.yaml.tmpl"` typo, or an accidental collapse
17831/// onto Helm 2's sibling per-chart-values-filename axis, or a per-fork
17832/// `defaults.yaml` rebrand any per-edition packaging substrate might
17833/// introduce) at any one site would surface as one of two silent
17834/// failure modes at chart-consumption time:
17835///
17836/// - Helm's per-chart values-loader silently falls back to the empty
17837/// values block — `helm template` / `helm install` emits the
17838/// `pleme-computeunit` library chart under its admission-time
17839/// defaults (`enabled: false`, no per-`:limits` / `:behavior` /
17840/// `:upgrade-from` M2 overlay), the workload silently comes up
17841/// disabled or without any per-Servico M2 overlay applied, and
17842/// the per-Servico release cycle drops with no field naming the
17843/// values-filename-drift root cause (the operator sees "the
17844/// Servico isn't doing what we configured it to do" with no
17845/// canonical anchor to compare the rendered filename against);
17846/// - the rendered chart's `ChartFile` collection lists a file at the
17847/// emit-side drifted name (e.g. `"Values.yaml"`) while the sibling
17848/// [`caixa-flux`][cf] `Kustomization` bundle-path emitter's per-
17849/// chart reference (a future per-cluster snapshot bundle that
17850/// re-lists the chart-dir contents by filename to route per-cluster
17851/// values overlays through the canonical values file) continues to
17852/// look under the canonical `"values.yaml"` — the two-crate pair
17853/// silently goes out of sync, with the flux bundle's chart-directory
17854/// resolver returning `None` for the values file at cluster-side
17855/// `feira app deploy` time, and every per-cluster overlay the
17856/// bundle path threads through silently drops.
17857///
17858/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
17859/// "every recurring shape becomes a generator before it becomes a
17860/// pattern; every pattern becomes a library before it becomes
17861/// duplicated code. The duplication budget is zero.") promotes the
17862/// filename to a typed substrate-side `&'static str` on the same
17863/// trajectory the peer [`HELM_CHART_YAML_FILENAME`] (c2c99b0) /
17864/// [`HELM_CHART_API_VERSION`] / [`HELM_CHART_TYPE_APPLICATION`] /
17865/// [`HELM_VALUES_KEY_ENABLED`] / [`DEFAULT_LIBRARY_NAME`] /
17866/// [`LAREIRA_CHART_NAME_PREFIX`] lifts established on the sibling
17867/// canonical-Helm-load-bearing-string axes — pivots the discipline
17868/// from the metadata-file half of the `(Chart.yaml, values.yaml)`
17869/// canonical per-chart-directory filename pair onto the values-file
17870/// half, completing the per-`lareira-<nome>`-chart-directory
17871/// canonical-scalar-axis re-export triple every rendered chart declares
17872/// as its `ChartDir::files` entries (`{Chart.yaml, values.yaml,
17873/// README.md}` — the two schema-load-bearing filenames now share the
17874/// same substrate-side single-source discipline).
17875///
17876/// [values-yaml-desc]: https://helm.sh/docs/chart_template_guide/values_files/
17877/// [ch]: ../../caixa_helm/index.html
17878/// [cf]: ../../caixa_flux/index.html
17879/// [rcs]: ../../caixa_helm/fn.render_chart_for_servico.html
17880pub const HELM_VALUES_YAML_FILENAME: &str = "values.yaml";
17881
17882/// Canonical `lareira-<nome>` chart-directory human-facing readme filename
17883/// every rendered chart carries at its top-level directory — the fixed
17884/// filename the `caixa-helm` renderer emits alongside the two schema-load-
17885/// bearing [`HELM_CHART_YAML_FILENAME`] + [`HELM_VALUES_YAML_FILENAME`]
17886/// files as the third leg of the canonical `{Chart.yaml, values.yaml,
17887/// README.md}` per-`lareira-<nome>` chart-directory `ChartFile` triple the
17888/// peer [`HELM_CHART_YAML_FILENAME`] docstring explicitly acknowledges is
17889/// the one axis where the substrate-side single-source discipline had not
17890/// yet landed at the third file. The single source of truth every
17891/// consumer that names the readme file — the sole caixa-helm production
17892/// emit site the prior inline `"README.md"` literal sat at
17893/// ([`caixa-helm`][ch]'s [`render_chart_for_servico`][rcs] `ChartDir`
17894/// assembly's per-file `path` axis, the third of the three canonical
17895/// `lareira-<nome>` chart-directory files the renderer emits as a bundle,
17896/// sibling to the metadata-file [`HELM_CHART_YAML_FILENAME`] +
17897/// values-file [`HELM_VALUES_YAML_FILENAME`] axes) plus every test-side
17898/// round-trip navigator that reaches into the rendered `ChartDir` by the
17899/// readme filename (two sites: the `renders_three_files` files-vec-
17900/// membership pin + the `ChartDir::write_to` post-write existence pin) —
17901/// reaches for the same `&'static str` by construction.
17902///
17903/// Until this lift landed the filename `"README.md"` lived as three
17904/// verbatim inline literals (one production `ChartFile::new("README.md",
17905/// …)` at the `ChartDir` files-vec construction site + two test-side
17906/// `names.contains(&"README.md".to_string())` / `chart_root.join("README.md")`
17907/// fixture navigators). A drift on the emit side (a `"readme.md"` /
17908/// `"Readme.md"` / `"README"` / `"README.MD"` typo, or an accidental
17909/// collapse onto the sibling per-workspace `readme.txt` axis any
17910/// per-edition packaging substrate might introduce) at any one site would
17911/// surface as one of two silent failure modes at chart-consumption time:
17912///
17913/// - GitHub / Artifact Hub / any downstream per-chart README-surfacing
17914/// UI silently falls back to "no README available" — the chart lists
17915/// with no per-chart elevator pitch or install instructions far from
17916/// the drift commit's source, and the operator sees a chart in the
17917/// hub without the canonical `## Install` block the emitter wrote,
17918/// with no field naming the readme-filename-drift root cause;
17919/// - the rendered chart's `ChartFile` collection lists a file at the
17920/// emit-side drifted name (e.g. `"readme.md"`) while the sibling
17921/// [`caixa-flux`][cf] `Kustomization` bundle-path emitter's future
17922/// per-chart-directory resolver — a per-cluster snapshot bundle that
17923/// re-lists the chart-dir contents by filename to surface the
17924/// canonical README to per-cluster tooling — continues to look under
17925/// the canonical `"README.md"` — the two-crate pair silently goes out
17926/// of sync, with the flux bundle's chart-directory resolver returning
17927/// `None` for the readme file at cluster-side `feira app deploy`
17928/// time, and every downstream README-consuming path silently drops.
17929///
17930/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
17931/// "every recurring shape becomes a generator before it becomes a
17932/// pattern; every pattern becomes a library before it becomes
17933/// duplicated code. The duplication budget is zero.") promotes the
17934/// filename to a typed substrate-side `&'static str` on the same
17935/// trajectory the peer [`HELM_CHART_YAML_FILENAME`] (c2c99b0) /
17936/// [`HELM_VALUES_YAML_FILENAME`] (9a980ba) lifts established on the
17937/// sibling canonical-Helm-per-chart-directory-filename axes — pivots the
17938/// discipline from the two schema-load-bearing filename halves onto the
17939/// human-facing readme-file half, completing the per-`lareira-<nome>`-
17940/// chart-directory `(Chart.yaml, values.yaml, README.md)` canonical-per-
17941/// chart-directory-filename-axis re-export triple every rendered chart
17942/// declares as its three `ChartDir::files` entries — the third file the
17943/// peer [`HELM_VALUES_YAML_FILENAME`] docstring explicitly names as the
17944/// missing leg of the triple at its "completing the per-`lareira-<nome>`-
17945/// chart-directory canonical-scalar-axis re-export triple every rendered
17946/// chart declares as its `ChartDir::files` entries (`{Chart.yaml,
17947/// values.yaml, README.md}` — the two schema-load-bearing filenames now
17948/// share the same substrate-side single-source discipline)" close.
17949///
17950/// [ch]: ../../caixa_helm/index.html
17951/// [cf]: ../../caixa_flux/index.html
17952/// [rcs]: ../../caixa_helm/fn.render_chart_for_servico.html
17953pub const HELM_CHART_README_FILENAME: &str = "README.md";
17954
17955/// Canonical `pleme-computeunit` library-chart values-block enable-toggle
17956/// key — the `enabled: <bool>` axis every `lareira-<nome>` chart's values
17957/// block carries under its [`DEFAULT_LIBRARY_NAME`] wrap key, and every
17958/// [`caixa-flux`][cf]-rendered `HelmRelease` `spec.values.<library>.enabled`
17959/// per-cluster override targets. The single source of truth all four
17960/// downstream consumers reach for:
17961///
17962/// - [`caixa-helm`][ch]'s [`build_values_yaml`][bvy] inserts
17963/// `enabled: <opts.enabled_default>` under the values wrap key
17964/// (caixa-helm/src/lib.rs:389) — the rendered `values.yaml`'s
17965/// default-off toggle a cluster operator flips on per environment;
17966/// - [`caixa-flux`][cf]'s [`cluster_bundle`][cb] emits
17967/// `<library>: { enabled: true }` under the `HelmRelease`
17968/// `spec.values` block (caixa-flux/src/lib.rs:844) — the per-cluster
17969/// override the bundle path threads through so a Servico deployed via
17970/// the bundle path lands enabled at the target cluster;
17971/// - the peer test-fixture navigators in both crates
17972/// (`caixa-helm/src/lib.rs:566, 616` sweeping the default-off arm +
17973/// `caixa-flux/src/lib.rs:1889` sweeping the bundle-path enabled-true
17974/// override arm) resolve the same `&'static str` when parsing back the
17975/// rendered `values.yaml` / `helmrelease.yaml` to pin the round-trip;
17976/// - every future per-Servico renderer the absorption-roadmap
17977/// acknowledges (the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
17978/// materializer's per-member values fan-out, a future per-cluster
17979/// values overlay emitter, a future per-edition `<lib>-computeunit`
17980/// values-block schema fork) that reads or emits the same values-
17981/// block-toggle key.
17982///
17983/// Until this lift landed the value `"enabled"` lived as two production-
17984/// code call sites (caixa-helm's `build_values_yaml` insert +
17985/// caixa-flux's `cluster_bundle` `helmrelease.yaml` format-string) plus
17986/// three test-fixture-navigation sites (caixa-helm's default-off round-
17987/// trip + caixa-flux's bundle-path round-trip). A future rebrand of the
17988/// library-chart's per-values enable-toggle axis (the `pleme-computeunit`
17989/// library chart moving to a `chart.enabled` / `spec.enabled` scoping to
17990/// leave room for a sibling `component.enabled` sub-chart toggle, the
17991/// substrate forking the library chart to `<edition>-computeunit` with a
17992/// migrated toggle key, or Helm's own per-values-block convention drift)
17993/// without a coordinated edit on both consumers would silently emit a
17994/// chart whose default-off toggle lands in the values block under one key
17995/// while the cluster-side override lands under another — Helm's per-values
17996/// merge treats them as sibling scalars, the enable-toggle the library
17997/// chart's own template consults never sees the flip, and the workload
17998/// silently comes up with the library chart's admission-time defaults
17999/// (disabled, or the sibling schema fork's own default) instead of the
18000/// per-cluster override the operator set. The apply-time symptom (the
18001/// workload is registered but not running, or is running without the
18002/// per-cluster overlay) surfaces only as "the service isn't doing what we
18003/// configured it to do" far from the rebrand commit, with no field
18004/// naming the enable-toggle-drift root cause. Lifting the literal to
18005/// a shared constant closes the drift footgun structurally — both
18006/// production emit sites and every test-side round-trip navigator now
18007/// consult the same `&'static str`, so any rebrand reaches every consumer
18008/// by construction.
18009///
18010/// Same "the typed constant lives in one place" discipline the peer
18011/// [`DEFAULT_LIBRARY_NAME`] (41438dc) / [`HELM_CHART_API_VERSION`]
18012/// (7e4bdb8) / [`KUBE_KEY_SPEC`] lifts apply on the sibling canonical-
18013/// Helm-load-bearing-string / canonical-Helm-chart-schema-axis /
18014/// canonical-K8s-CR-body-axis surfaces — extends the discipline from
18015/// the Chart.yaml schema axes and the K8s CR body axes onto the Helm
18016/// values-block schema axis nested inside every `lareira-<nome>` chart
18017/// under its [`DEFAULT_LIBRARY_NAME`] wrap key.
18018///
18019/// [ch]: ../../caixa_helm/index.html
18020/// [cf]: ../../caixa_flux/index.html
18021/// [bvy]: ../../caixa_helm/fn.build_values_yaml.html
18022/// [cb]: ../../caixa_flux/fn.cluster_bundle.html
18023pub const HELM_VALUES_KEY_ENABLED: &str = "enabled";
18024
18025/// Canonical Helm chart-name prefix for every per-Servico chart the
18026/// substrate emits — the `"lareira-"` segment of the well-known
18027/// `lareira-<nome>` shape every caixa Servico renderer prepends to a
18028/// caixa's `:nome` to derive its [`Chart.yaml` `name:`][chart-yaml] field,
18029/// its OCI artifact reference (`oci://<registry>/lareira-<nome>`), and
18030/// the resulting cluster-side `HelmRelease` `release_name`. The single
18031/// source of truth all three downstream Servico renderers consult —
18032/// [`caixa-helm`][cf]'s `render_chart_for_servico` chart-dir name
18033/// (caixa-helm/src/lib.rs:207), [`caixa-flux`][cm]'s `cluster_bundle`
18034/// `HelmRelease` `chart:` field (caixa-flux/src/lib.rs:329), and
18035/// [`caixa-tatara`][ct]'s `process_for_aplicacao` `release_name` +
18036/// `derive_chart_ref` OCI ref (caixa-tatara/src/lib.rs:124,182) — so a
18037/// future per-chart-name-prefix rebrand (e.g. moving to `forno-` once
18038/// `lareira-` outlives its scoping intent, or any segment-namespace
18039/// migration the chart-publishing pipeline requires) is a one-line edit
18040/// here, not a coordinated rewrite across every renderer crate's chart-
18041/// name-derivation site.
18042///
18043/// Until this lift landed all three renderers carried inline
18044/// `format!("lareira-{}", caixa.nome)` / `format!("lareira-{name}")` /
18045/// `format!("oci://{}/lareira-{}", registry, caixa.nome.as_str())`
18046/// expressions — three verbatim copies of the same substrate-wide
18047/// naming convention. The PRIME DIRECTIVE duplication budget of zero
18048/// (THEORY.md §I.3.5) lands the lift here at the third occurrence: a
18049/// future rebrand on any one site without a coordinated edit on the
18050/// others would have silently published a chart at one name, registered
18051/// its OCI ref at a second, and resolved the `HelmRelease` at a third —
18052/// the cluster's apply would surface as a `chart pull failed: image not
18053/// found` error far from the source rebrand commit, with no field
18054/// naming the prefix-drift root cause.
18055///
18056/// Lifting it to caixa-core's render-constants block alongside the peer
18057/// [`DEFAULT_NAMESPACE`] (a085b26) makes the chart-name-prefix axis
18058/// discipline structural: every renderer that derives a per-Servico
18059/// chart name consults [`lareira_chart_name`], and every future renderer
18060/// (the future per-cluster snapshot bundle emitter, the future M4
18061/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's chart-ref slot,
18062/// the future caixa-otel collector chart name) inherits the same prefix
18063/// by construction, with no opportunity for per-renderer drift. Same
18064/// "the typed constant lives in one place" discipline the
18065/// [`PLEME_LABEL_PREFIX`] / [`DEFAULT_NAMESPACE`] / [`KUBE_KEY_API_VERSION`]
18066/// lifts apply on the peer shared-string axes.
18067///
18068/// [chart-yaml]: https://helm.sh/docs/topics/charts/#the-chartyaml-file
18069/// [cf]: ../../caixa_helm/index.html
18070/// [cm]: ../../caixa_flux/index.html
18071/// [ct]: ../../caixa_tatara/index.html
18072pub const LAREIRA_CHART_NAME_PREFIX: &str = "lareira-";
18073
18074/// Derive the canonical per-Servico Helm chart name from a caixa's
18075/// `:nome` — the substrate-wide `lareira-<nome>` shape every
18076/// per-Servico renderer ([`caixa-helm`][cf]'s `render_chart_for_servico`
18077/// chart-dir name, [`caixa-flux`][cm]'s `cluster_bundle` `HelmRelease`
18078/// `chart:` field, [`caixa-tatara`][ct]'s `process_for_aplicacao`
18079/// `release_name`, and the `oci://<registry>/lareira-<nome>` OCI ref)
18080/// composes by prepending [`LAREIRA_CHART_NAME_PREFIX`].
18081///
18082/// Single source of truth for the prefix-application: every consumer
18083/// reaches for this helper rather than re-deriving the `format!(…)`
18084/// shape inline, so a future change to the prefix axis (the lift's
18085/// raison d'être) is one edit here, not a coordinated sweep across
18086/// every renderer.
18087///
18088/// The input `nome` is the caixa's typed `:nome` field, already
18089/// DNS-1123-label-validated at [`Caixa::validate_nome`] (6c992f8) —
18090/// every value reaching this helper is structurally a valid Helm
18091/// chart-name segment. The prepended prefix is a fixed lowercase ASCII
18092/// alphanumeric + hyphen string, so the concatenation is structurally a
18093/// valid Helm chart name by construction (Helm's chart-name accepted
18094/// set is the DNS-1123 label rule, and DNS-1123 labels concatenate with
18095/// the prefix-and-hyphen separator into valid DNS-1123 labels as long
18096/// as the joint length stays ≤ 63 bytes; the M4 admission webhook will
18097/// pin the joint-length invariant when it lands).
18098///
18099/// [cf]: ../../caixa_helm/index.html
18100/// [cm]: ../../caixa_flux/index.html
18101/// [ct]: ../../caixa_tatara/index.html
18102#[must_use]
18103pub fn lareira_chart_name(nome: &str) -> String {
18104 format!("{LAREIRA_CHART_NAME_PREFIX}{nome}")
18105}
18106
18107/// Canonical substrate-fixed Chart.yaml `keywords:` entries every
18108/// rendered `lareira-<nome>` Helm chart carries — the ordered
18109/// (`BTreeSet`-canonical, ascii-alphabetical) list of registry-search
18110/// tags `caixa-helm`'s `build_chart_yaml` unions in on top of the
18111/// caixa author's own `:etiquetas` before folding the joint set into a
18112/// `BTreeSet<String>` for the emitted `Chart.yaml`. Every entry —
18113/// `"caixa-servico"` (the substrate-wide per-`:kind Servico` marker
18114/// axis), `"lareira"` (the [`LAREIRA_CHART_NAME_PREFIX`] chart-family
18115/// tag), `"tatara-lisp"` (the tatara-lisp source-language marker), and
18116/// `"wasm"` (the runtime execution-format marker) — is a load-bearing
18117/// discovery axis for the Artifact Hub keyword-search index and the
18118/// future caixa-registry keyword axis, so a drift between the
18119/// production emit at `caixa-helm::build_chart_yaml` and the two
18120/// substrate-side positive-set sweep tests
18121/// ([`crate::manifest::tests::validate_etiquetas_accepts_canonical_shaped_forms`]
18122/// and this crate's own `chart_keyword_shape_accepts_canonical_forms`)
18123/// would silently cause every rendered chart to miss the search-index
18124/// axis the substrate-fixed tag encodes — a chart published without
18125/// the `"caixa-servico"` tag would silently drop off the
18126/// `helm search hub caixa-servico` results the substrate's chart
18127/// discovery pipeline promises. Two production-side call sites
18128/// (this crate's `is_chart_keyword_shape` docstring narrates the
18129/// four canonical tags verbatim + [`caixa-helm`][ch]'s `build_chart_yaml`
18130/// unions them into the emitted `keywords:` sequence) and two
18131/// test-side positive-sweep sites this array anchors under one source
18132/// of truth.
18133///
18134/// The array is `BTreeSet`-canonical-ordered (ascii-alphabetical: the
18135/// same order the emitted `Chart.yaml` `keywords:` sequence lists them
18136/// after `build_chart_yaml`'s intermediate `BTreeSet<String>` fold), so
18137/// a future substrate-fixed keyword addition (an `"opentelemetry"`
18138/// entry once the caixa-otel collector-pipeline chart lands, a
18139/// `"lunatic"` entry once the wasm-process-runtime marker lands, a
18140/// `"gen_server"` entry once the OTP-shape callback marker lands per
18141/// the [`crate::behavior`] surface) lands at one edit point rather
18142/// than a coordinated four-file sweep across the production emit
18143/// site, the two test-side sweeps, and this docstring. Same
18144/// "one canonical typed array lives in one place" discipline as
18145/// the peer [`crate::aplicacao::WIT_HTTP_SHAPE_PREFIXES`] /
18146/// [`crate::aplicacao::WIT_PUBSUB_SHAPE_PREFIXES`] /
18147/// [`crate::aplicacao::WIT_STORE_SHAPE_PREFIXES`] arm-shape-prefix
18148/// arrays apply on the sibling `:contratos :wit` dispatch-shape axis.
18149///
18150/// Every entry structurally satisfies [`is_chart_keyword_shape`] (the
18151/// substrate's per-`Chart.yaml` `keywords:` entry validation
18152/// predicate) — the substrate-side pin
18153/// `lareira_chart_keywords_each_entry_passes_is_chart_keyword_shape`
18154/// enforces the invariant so a future addition that happens to break
18155/// the shape rule (a leading digit, an uppercase letter, a byte over
18156/// the [`CHART_KEYWORD_MAX_LEN`] cap) fails at caixa-core build time
18157/// rather than surfacing at chart-lint time downstream.
18158///
18159/// [ch]: ../../caixa_helm/index.html
18160pub const LAREIRA_CHART_KEYWORDS: &[&str] = &["caixa-servico", "lareira", "tatara-lisp", "wasm"];
18161
18162/// Canonical OCI URL scheme prefix — the `"oci://"` byte-string every
18163/// substrate-side renderer that composes an OCI artifact reference for a
18164/// Helm chart prepends. The Helm 3 OCI storage protocol (Helm 3.8+) and
18165/// the `FluxCD` `HelmRepository` `type: oci` source both key off this
18166/// literal — `helm pull` / `helm install` / `helm registry login` /
18167/// `FluxCD`'s source-controller all reject any other scheme on the OCI
18168/// path — so a byte-shape drift on this prefix silently splits the
18169/// substrate's published chart references from the cluster-side
18170/// resolvers that consume them at `helm registry` / `FluxCD` reconcile
18171/// time far from the source renderer.
18172///
18173/// The single source of truth every downstream renderer that composes
18174/// an `oci://<registry>/<chart>` reference reaches for —
18175/// [`caixa-tatara`][ct]'s `derive_chart_ref` OCI ref
18176/// (caixa-tatara/src/lib.rs:202), and every future OCI-ref emitter
18177/// (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
18178/// `chart_ref` slot on the tatara `Process` intent, the future
18179/// per-cluster snapshot bundle's OCI chart references, the future
18180/// caixa-otel collector chart's OCI publish shape) inherits the prefix
18181/// through this const by construction. Same "one canonical scheme /
18182/// prefix / separator lives in one place" discipline the peer
18183/// [`LAREIRA_CHART_NAME_PREFIX`] (f7320d7), [`CONTRATO_EDGE_LABEL_SEPARATOR`]
18184/// (6d9b04e), [`PLEME_LABEL_PREFIX`] (b473c00 / 9d9813f) lifts apply
18185/// on the sibling canonical-load-bearing-substrate-string axes.
18186///
18187/// [ct]: ../../caixa_tatara/index.html
18188pub const OCI_SCHEME_PREFIX: &str = "oci://";
18189
18190/// Compose the canonical OCI artifact reference for a per-Servico Helm
18191/// chart — the `oci://<registry>/lareira-<nome>` shape every renderer
18192/// that materializes a chart-publish target (or a cluster-side chart
18193/// resolver keyed off one) composes by prepending
18194/// [`OCI_SCHEME_PREFIX`], joining the caller-supplied registry, and
18195/// appending the per-Servico chart name derived through the canonical
18196/// [`lareira_chart_name`] helper.
18197///
18198/// Single source of truth for the two-axis composition: every consumer
18199/// reaches for this helper rather than re-deriving the
18200/// `format!("oci://{}/lareira-{}", …)` shape inline, so a future change
18201/// to either input axis (the [`OCI_SCHEME_PREFIX`] rebrand once Helm /
18202/// `FluxCD` introduce a new registry protocol, the
18203/// [`LAREIRA_CHART_NAME_PREFIX`] rebrand once `lareira-` outlives its
18204/// scoping intent) is one edit here, not a coordinated sweep across
18205/// every renderer crate's OCI-ref composition site.
18206///
18207/// The rendered reference is the substrate's contract with the
18208/// chart-publishing pipeline (`helm registry login` +
18209/// `helm push chart.tgz oci://<registry>/lareira-<nome>`), the
18210/// cluster-side `FluxCD` `HelmRelease` `chart:` field (which Flux's
18211/// source-controller resolves through the same OCI ref), and the
18212/// tatara `Process` CR's `intent.aplicacao.chart_ref` slot the
18213/// reconciler feeds into `helm install`. Every consumer keys off the
18214/// same byte-shape by construction.
18215///
18216/// [ct]: ../../caixa_tatara/index.html
18217#[must_use]
18218pub fn oci_chart_ref(registry: &str, nome: &str) -> String {
18219 let chart = lareira_chart_name(nome);
18220 format!("{OCI_SCHEME_PREFIX}{registry}/{chart}")
18221}
18222
18223/// The `:nome`-side budget the [`lareira_chart_name`] composition
18224/// imposes on every caixa `:nome` reaching a renderer that derives a
18225/// `lareira-<nome>` artifact (`caixa-helm`'s `ChartDir.name` +
18226/// `Chart.yaml` `name:`, `caixa-flux`'s `cluster_bundle` `HelmRelease`
18227/// `chart:` slot, `caixa-tatara`'s `process_for_aplicacao`
18228/// `release_name` + `oci://<registry>/lareira-<nome>` chart ref).
18229///
18230/// The joint length of `lareira-` + `<nome>` must satisfy the K8s
18231/// DNS-1123 label cap ([`DNS_1123_LABEL_MAX_LEN`] = 63) every downstream
18232/// consumer enforces — Helm's `Chart.yaml::name` field (`helm lint`
18233/// rejects at chart-package time per the DNS-1123 rule), the
18234/// `HelmRelease`'s `release_name` field (the Helm operator's tracking
18235/// secret name is derived from `release_name` and is itself a DNS-1123
18236/// label), the rendered chart's K8s object `metadata.name` axes that
18237/// embed the chart name as a prefix. The arithmetic is therefore
18238/// `DNS_1123_LABEL_MAX_LEN - LAREIRA_CHART_NAME_PREFIX.len()` = 63 - 8
18239/// = 55 bytes the caixa's `:nome` may itself occupy.
18240///
18241/// Lifted to a `pub const` so a future change to either axis
18242/// ([`LAREIRA_CHART_NAME_PREFIX`] rebrand, [`DNS_1123_LABEL_MAX_LEN`]
18243/// shift if Helm/K8s ever relax the chart-name rule) re-derives the
18244/// budget mechanically — every per-axis call site
18245/// ([`is_lareira_chart_name_shape`] consults it, the
18246/// `Caixa::validate_nome_chart_name_budget` diagnostic names it
18247/// verbatim) inherits the new value with no coordinated edit.
18248pub const LAREIRA_CHART_NAME_NOME_MAX_LEN: usize =
18249 DNS_1123_LABEL_MAX_LEN - LAREIRA_CHART_NAME_PREFIX.len();
18250
18251/// Predicate: assert that `nome` produces a [`lareira_chart_name`]
18252/// output satisfying the K8s DNS-1123 label rule — the joint-length
18253/// invariant the canonical `lareira_chart_name` helper's doc comment
18254/// (f7320d7) defers to "the M4 admission webhook will pin … when it
18255/// lands". This predicate lands it at the manifest-validate layer
18256/// rather than waiting for the apiserver.
18257///
18258/// Returns the parser-shaped reason on rejection (without wrapping in
18259/// any error variant) — same call-site discipline as the peer
18260/// [`is_dns_1123_label`] predicate. Each per-axis caller wraps the
18261/// returned reason in its own typed `*Error::*Exceeded { … }` variant
18262/// (today: `Caixa::validate_nome_chart_name_budget` → the new
18263/// [`crate::ManifestError::NomeChartNameBudgetExceeded`] arm).
18264///
18265/// The predicate composes via [`lareira_chart_name`] + [`is_dns_1123_label`]
18266/// — the same two primitives every renderer consults — so a future
18267/// rebrand of either axis (`LAREIRA_CHART_NAME_PREFIX`,
18268/// `DNS_1123_LABEL_MAX_LEN`) re-derives the budget mechanically. A
18269/// `:nome` that already passes [`is_dns_1123_label`] (≤63 bytes,
18270/// boundary-anchored, `[a-z0-9-]` only) but whose prefixed chart name
18271/// exceeds the joint cap is what this gate catches — every byte the
18272/// inner DNS-1123 check accepts the prefixed form may still reject.
18273///
18274/// # Errors
18275///
18276/// Returns a parser-shaped reason naming the budget
18277/// ([`LAREIRA_CHART_NAME_NOME_MAX_LEN`]), the offending `:nome`
18278/// length, and the rendered chart name's length — so the diagnostic is
18279/// self-locating and the author can shorten in one edit.
18280pub fn is_lareira_chart_name_shape(nome: &str) -> Result<(), String> {
18281 let chart_name = lareira_chart_name(nome);
18282 if chart_name.len() > DNS_1123_LABEL_MAX_LEN {
18283 return Err(format!(
18284 "produces `{chart_name}` ({chart_len} bytes), which exceeds the \
18285 DNS-1123 label max length of {DNS_1123_LABEL_MAX_LEN} bytes that \
18286 Helm's `Chart.yaml::name` field and every downstream K8s artifact \
18287 derived from the chart name enforce; the per-`:nome` budget is \
18288 {budget} bytes (DNS-1123 cap minus the `{prefix}` prefix), shorten \
18289 `:nome` to ≤ {budget} bytes",
18290 chart_name = chart_name,
18291 chart_len = chart_name.len(),
18292 budget = LAREIRA_CHART_NAME_NOME_MAX_LEN,
18293 prefix = LAREIRA_CHART_NAME_PREFIX,
18294 ));
18295 }
18296 Ok(())
18297}
18298
18299/// Build the canonical Cilium `matchLabels` selector for a single
18300/// pleme-io program **scoped to its Aplicacao** — the safe default
18301/// every per-Aplicacao mesh renderer (caixa-mesh's
18302/// `cilium_network_policies` `fromEndpoints`, future per-edge policy
18303/// emission, Gateway API `backendRefs` filters) should use, since
18304/// two different Aplicacaos can carry programs with the same `:nome`
18305/// in the same cluster (e.g. two `cart` Servicos under different
18306/// applications) and a `LABEL_PROGRAM`-only selector would match
18307/// pods belonging to the wrong Aplicacao.
18308///
18309/// Returned as a [`BTreeMap`] keyed by `&'static str` so iteration is
18310/// alphabetical (THEORY.md §V.2.7 render determinism: the rendered
18311/// YAML's `matchLabels:` block appears in a deterministic order
18312/// independent of source-code declaration order). The two keys
18313/// alphabetize as [`LABEL_APLICACAO`] before [`LABEL_PROGRAM`], the
18314/// same order the renderer's `serde_yaml::Mapping` iteration will
18315/// preserve through to the rendered YAML.
18316#[must_use]
18317pub fn pleme_program_in_aplicacao_selector(
18318 program: &str,
18319 aplicacao: &str,
18320) -> BTreeMap<&'static str, String> {
18321 let mut out = BTreeMap::new();
18322 out.insert(LABEL_APLICACAO, aplicacao.to_string());
18323 out.insert(LABEL_PROGRAM, program.to_string());
18324 out
18325}
18326
18327/// Build the canonical Cilium `matchLabels` selector for a single
18328/// pleme-io program **without** the Aplicacao constraint —
18329/// deliberately broader than [`pleme_program_in_aplicacao_selector`]
18330/// for the cases where matching a program across every Aplicacao that
18331/// hosts it is the *intent* (cluster-wide rate limits, breakglass
18332/// observability, the per-cluster operator identity scope).
18333///
18334/// **Prefer [`pleme_program_in_aplicacao_selector`]** for typed
18335/// per-Aplicacao mesh emission — using `pleme_program_selector` there
18336/// would let a policy unintentionally match a same-named program in
18337/// a different Aplicacao. Both helpers exist so the caller's *intent*
18338/// (Aplicacao-scoped vs. cluster-wide) is named at the call site,
18339/// not buried in inline label-key string literals.
18340#[must_use]
18341pub fn pleme_program_selector(program: &str) -> BTreeMap<&'static str, String> {
18342 let mut out = BTreeMap::new();
18343 out.insert(LABEL_PROGRAM, program.to_string());
18344 out
18345}
18346
18347/// Convert a typed string-valued mapping (e.g. one of the canonical
18348/// [`pleme_program_selector`] / [`pleme_program_in_aplicacao_selector`]
18349/// selectors, or any caller-built `BTreeMap<&'static str, String>`)
18350/// into a [`serde_yaml::Value::Mapping`] with `String → String` shape —
18351/// the surface every Cilium / Gateway / HTTPRoute / ComputeUnit
18352/// `matchLabels` / `metadata.labels` / `selector` field expects.
18353///
18354/// Iteration order is whatever the input iterator yields; pass a
18355/// [`BTreeMap`] for alphabetical determinism (THEORY.md §V.2.7 render
18356/// determinism: rendered YAML key order is independent of source-code
18357/// declaration order). The two pleme-io selector helpers above already
18358/// return `BTreeMap`s for exactly this reason.
18359///
18360/// Lifted from `caixa-mesh`'s prior `yaml_string_mapping` private
18361/// helper to make the same primitive available to every other
18362/// `caixa-<target>` renderer that needs to emit a string→string YAML
18363/// mapping (the future per-Aplicacao Gateway-API filter rules, the
18364/// caixa-otel resource-attribute emitter, the `app-operator`'s typed
18365/// CR materializer, the per-cluster CiliumClusterwideEnvoyConfig
18366/// renderer for `:politicas` defaults). Without the lift each new
18367/// renderer would re-inline the same five-line `for (k, v)` body and
18368/// inherit the same drift footguns.
18369#[must_use]
18370pub fn yaml_string_mapping<K, V, M>(m: M) -> serde_yaml::Value
18371where
18372 M: IntoIterator<Item = (K, V)>,
18373 K: Into<String>,
18374 V: Into<String>,
18375{
18376 let mut out = serde_yaml::Mapping::new();
18377 for (k, v) in m {
18378 out.insert_str_key(&k.into(), serde_yaml::Value::String(v.into()));
18379 }
18380 serde_yaml::Value::Mapping(out)
18381}
18382
18383/// Wrap a typed string-valued label mapping in the canonical K8s
18384/// [`LabelSelector`][k8s-ls] shape — `{matchLabels: <string-string-map>}`
18385/// — and return it as a [`serde_yaml::Value::Mapping`] ready to drop
18386/// directly under any K8s field that takes a label selector
18387/// (Cilium `endpointSelector` / `fromEndpoints[].matchLabels`, Gateway
18388/// API `BackendRef` filters, ComputeUnit `selector`, Service
18389/// `spec.selector`, the future `mesh.pleme.io/v1alpha1/Aplicacao` CR
18390/// `spec.selector`).
18391///
18392/// Lifted from two inline `serde_yaml::Mapping::new() +
18393/// insert(Value::String("matchLabels".into()), yaml_string_mapping(_))`
18394/// blocks in `caixa-mesh::cilium_network_policies` (the destination
18395/// `endpointSelector` and the source `fromEndpoints[0]` selector) so
18396/// the next renderer to land — the per-`:politicas`
18397/// `CiliumClusterwideEnvoyConfig` emitter (MESH-COMPOSITION §III.2 #3),
18398/// the `app-operator`'s typed `mesh.pleme.io/v1alpha1/Aplicacao` CR
18399/// materializer (§III.2 #5), the M4 cross-cluster fan-out's per-cluster
18400/// `Service`/`HTTPRoute backendRefs` selectors, the future `caixa-otel`
18401/// OpenTelemetry-Collector resource-selector pipeline — gets the
18402/// canonical K8s label-selector shape for free with one function call,
18403/// instead of re-inlining the same four-line `Mapping::new() +
18404/// insert("matchLabels", yaml_string_mapping(_))` boilerplate.
18405///
18406/// V0 emits the equality-based selector axis only (`matchLabels`); the
18407/// set-based axis ([`matchExpressions`][k8s-ls]) is deliberately out
18408/// of scope. A future `:contratos` axis whose selector needs
18409/// `matchExpressions` (e.g. `In`, `NotIn`, `Exists`, `DoesNotExist`
18410/// operators against a label key) is a future struct-shaped extension
18411/// of this helper —
18412/// e.g. a richer [`LabelSelector`] view type with `match_labels` +
18413/// `match_expressions` fields — not a per-renderer rewrite of
18414/// every selector emission site.
18415///
18416/// Iteration order is whatever the input iterator yields; pass a
18417/// [`BTreeMap`] for alphabetical determinism (THEORY.md §V.2.7 render
18418/// determinism: rendered YAML key order is independent of source-code
18419/// declaration order). The two pleme-io selector helpers
18420/// ([`pleme_program_selector`] / [`pleme_program_in_aplicacao_selector`])
18421/// already return `BTreeMap`s for exactly this reason, so a
18422/// `label_selector(pleme_program_in_aplicacao_selector(_, _))` call
18423/// renders deterministically end-to-end.
18424///
18425/// [k8s-ls]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#labelselector-v1-meta
18426#[must_use]
18427pub fn label_selector<K, V, M>(labels: M) -> serde_yaml::Value
18428where
18429 M: IntoIterator<Item = (K, V)>,
18430 K: Into<String>,
18431 V: Into<String>,
18432{
18433 let mut out = serde_yaml::Mapping::new();
18434 out.insert_str_key(KUBE_KEY_MATCH_LABELS, yaml_string_mapping(labels));
18435 serde_yaml::Value::Mapping(out)
18436}
18437
18438/// Build the canonical K8s-resource skeleton — the
18439/// `apiVersion` + `kind` + `metadata.{name, namespace, labels?}`
18440/// block every cluster artifact emitted by every caixa-side renderer
18441/// carries — and return it as a fresh [`serde_yaml::Mapping`] the
18442/// caller adds its `spec:` (and any other top-level keys) to.
18443///
18444/// `labels` is inserted under `metadata.labels` only when non-empty.
18445/// An empty `labels` map leaves the labels key absent — the K8s API
18446/// server's interpretation of "no labels declared" is "labels key
18447/// missing", not `labels: {}` (which serializes differently in some
18448/// YAML libraries and is a sharp tool for label-based selectors that
18449/// match the empty set silently).
18450///
18451/// Iteration order under `metadata` is alphabetical (the inner
18452/// projection is a [`BTreeMap`] keyed by `&'static str`), so the
18453/// rendered YAML's `metadata:` block appears in
18454/// `labels?, name, namespace` order regardless of source-code
18455/// declaration order. Same render-determinism contract the M2 overlay
18456/// helper and the pleme-io selector helpers enshrine.
18457///
18458/// Lifted from three inline `serde_yaml::Mapping::new()` blocks in
18459/// `caixa-mesh` ([`cilium_network_policies`][cnp] CNP construction,
18460/// [`gateway_routes`][gw] Gateway construction, the same fn's
18461/// HTTPRoute construction) so the next renderer to land — the
18462/// per-`:politicas` `CiliumClusterwideEnvoyConfig` emitter, the
18463/// `app-operator`'s typed `mesh.pleme.io/v1alpha1/Aplicacao` CR
18464/// materializer, the M4 cross-cluster fan-out's per-cluster Kustomization
18465/// and HelmRelease emission, the future `caixa-otel`
18466/// OpenTelemetry-Collector pipeline emitter — gets the canonical
18467/// skeleton for free with one function call, instead of re-inlining
18468/// the same five-key insert() boilerplate.
18469///
18470/// [cnp]: https://docs.cilium.io/en/stable/security/policy/index.html
18471/// [gw]: https://gateway-api.sigs.k8s.io/
18472#[must_use]
18473pub fn kube_resource_skeleton(
18474 api_version: &str,
18475 kind: &str,
18476 name: &str,
18477 namespace: &str,
18478 labels: BTreeMap<&'static str, String>,
18479) -> serde_yaml::Mapping {
18480 let mut metadata: BTreeMap<&'static str, serde_yaml::Value> = BTreeMap::new();
18481 metadata.insert(KUBE_KEY_NAME, serde_yaml::Value::String(name.to_string()));
18482 metadata.insert(
18483 KUBE_KEY_NAMESPACE,
18484 serde_yaml::Value::String(namespace.to_string()),
18485 );
18486 if !labels.is_empty() {
18487 metadata.insert(KUBE_KEY_LABELS, yaml_string_mapping(labels));
18488 }
18489
18490 let mut metadata_map = serde_yaml::Mapping::new();
18491 for (k, v) in metadata {
18492 metadata_map.insert_str_key(k, v);
18493 }
18494
18495 let mut out = serde_yaml::Mapping::new();
18496 out.insert_string(KUBE_KEY_API_VERSION, api_version.to_string());
18497 out.insert_string(KUBE_KEY_KIND, kind.to_string());
18498 out.insert_mapping(KUBE_KEY_METADATA, metadata_map);
18499 out
18500}
18501
18502/// Build a single-field [`serde_yaml::Value::Mapping`] from a typed
18503/// `Option<T>` slot — `None` when the slot is unset, `Some(Mapping {
18504/// inner_key: f(t) })` otherwise.
18505///
18506/// The canonical shape every per-`:politicas` overlay across `caixa-mesh`
18507/// uses to wire a typed `MeshPolicy` axis through to its single-key
18508/// cluster artifact:
18509///
18510/// * `:politicas :timeout` → `timeouts: { request: <duration> }`
18511/// (Gateway API `HTTPRoute.spec.rules[].timeouts`, wired in 5f477a6)
18512/// * `:politicas :retries` → `retry: { attempts: <number> }`
18513/// (Gateway API `HTTPRoute.spec.rules[].retry`, wired in 23b7f00)
18514/// * `:politicas :mtls-required` → `authentication: { mode: <enum> }`
18515/// (Cilium `CiliumNetworkPolicy.spec.ingress[].authentication`,
18516/// wired in 878bf81)
18517///
18518/// Until this lift the three call sites each carried a verbatim copy
18519/// of the same six-line block — `let mut m = serde_yaml::Mapping::new();
18520/// m.insert(Value::String(<key>.into()), <value>); Value::Mapping(m)` —
18521/// wrapped in `spec.politicas.<axis>.map(|v| { … })`. Three-of-the-pattern
18522/// across one emit-site (and now structurally one-of-the-pattern in each
18523/// of the next two emit-sites the M3.x roadmap acknowledges: the
18524/// `:circuit-breaker` and `:rate-limit` axes' `CiliumClusterwideEnvoyConfig`
18525/// emitter, MESH-COMPOSITION §III.2 #3) overflows the duplication
18526/// budget; this helper is the lifted typed primitive.
18527///
18528/// The caller passes:
18529/// * the typed `Option<T>` slot,
18530/// * the inner YAML key the artifact's per-axis schema names
18531/// (`request` / `attempts` / `mode` for the three landed overlays;
18532/// `consecutiveErrors` / `requestsPerUnit` for the two roadmap
18533/// axes), and
18534/// * a closure converting the typed `T` into the inner field's
18535/// [`serde_yaml::Value`] (typically a `String` for canonical
18536/// duration / enum scalars or a `Number` for typed integer
18537/// attempt counts).
18538///
18539/// Returns `Some(Mapping)` when the slot is `Some`, `None` otherwise —
18540/// the caller's `if let Some(overlay) = … { rule.insert(<outer_key>,
18541/// overlay.clone()) }` guard for the *outer* key (`timeouts` / `retry`
18542/// / `authentication` — which the per-rule iteration applies to every
18543/// emitted item) becomes the single emission gate, and the *inner*
18544/// shape is built once by the closure.
18545///
18546/// Pairs with the `MeshPolicy::is_empty` predicate at the typed-axis
18547/// emptiness layer: `is_empty()` short-circuits the whole `:politicas`
18548/// block when every axis is `None`; this helper short-circuits the
18549/// per-axis overlay when its single axis is `None`. Two layers, same
18550/// "named-axis-with-None-means-skip-emit" contract THEORY.md §V.2.7
18551/// render determinism extends to.
18552#[must_use]
18553pub fn single_field_overlay<T, F>(
18554 slot: Option<T>,
18555 inner_key: &'static str,
18556 f: F,
18557) -> Option<serde_yaml::Value>
18558where
18559 F: FnOnce(T) -> serde_yaml::Value,
18560{
18561 slot.map(|v| {
18562 let mut m = serde_yaml::Mapping::new();
18563 m.insert_str_key(inner_key, f(v));
18564 serde_yaml::Value::Mapping(m)
18565 })
18566}
18567
18568/// Wrap a single [`serde_yaml::Mapping`] as the sole element of a
18569/// [`serde_yaml::Value::Sequence`], returning the ready-to-drop
18570/// singleton-mapping-sequence `Value`.
18571///
18572/// The canonical shape every K8s-CRD schema-list-shape-required field
18573/// with exactly one entry to emit lands the same
18574/// `Value::Sequence(vec![Value::Mapping(m)])` three-token block in
18575/// front of. Seven identical-shape call sites across
18576/// [`caixa-mesh`][mesh] collapse onto this helper:
18577///
18578/// * Cilium `CiliumNetworkPolicy.spec.ingress[].toPorts[].ports`
18579/// (one `port_entry` per typed edge, wrapped in the CRD's
18580/// required-list-shape `ports:` axis);
18581/// * Cilium `CiliumNetworkPolicy.spec.ingress[].toPorts[].rules.http`
18582/// (one `http_rule` per L7-introspection-capable
18583/// [`crate::WitTarget::Http`] contract, wrapped in the CRD's
18584/// required-list-shape `http:` axis);
18585/// * Cilium `CiliumNetworkPolicy.spec.ingress` (one `ingress_rule`
18586/// per policy — Cilium's CRD schema lists the per-policy ingress
18587/// ruleset even though V0 emits exactly one entry);
18588/// * Gateway API `Gateway.spec.listeners` (one `listener` per
18589/// Gateway — V0 emits the single HTTP-listener shape the sibling
18590/// [`GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME`] +
18591/// [`GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT`] consts pin);
18592/// * Gateway API `HTTPRoute.spec.rules[].matches` (one `match_entry`
18593/// per rule — V0 emits a single per-path prefix-match);
18594/// * Gateway API `HTTPRoute.spec.rules[].backendRefs` (one
18595/// `backend_ref` per rule — V0 emits a single-backend fan-in on
18596/// the `:entrada :para` destination Servico);
18597/// * Gateway API `HTTPRoute.spec.parentRefs` (one `parent_ref` per
18598/// route — every route attaches to exactly one Gateway).
18599///
18600/// Until this lift landed all seven call sites re-inlined the same
18601/// three-token boilerplate — `serde_yaml::` path re-quote,
18602/// `Value::Sequence(_)` promotion, `vec![serde_yaml::Value::Mapping(_)]`
18603/// singleton-list wrapping — around a one-token semantic payload (the
18604/// per-site `Mapping`). Lifting collapses the boilerplate into one
18605/// function call the caller reads as intent (`singleton_mapping_sequence
18606/// (<mapping>)` — "wrap this single mapping as the CRD-required list-
18607/// shape") rather than three hand-spelled positional artifacts. The
18608/// next renderer to land — the per-`:politicas`
18609/// `CiliumClusterwideEnvoyConfig` emitter (MESH-COMPOSITION §III.2 #3,
18610/// which drops singleton `resources:[]` / `listeners:[]` /
18611/// `virtualHosts:[]` blocks under its per-policy CR spec), the
18612/// `app-operator`'s typed `mesh.pleme.io/v1alpha1/Aplicacao` CR
18613/// materializer (§III.2 #5, whose `spec.selectors:[]` / `spec.gates:[]`
18614/// blocks list-shape a single per-Aplicacao entry), the M4 cross-
18615/// cluster fan-out's per-cluster `Service.spec.ports[]` /
18616/// `HTTPRoute.spec.rules[].backendRefs[]` emission, the future
18617/// `caixa-otel` OpenTelemetry-Collector `pipelines.traces.receivers[]`
18618/// / `pipelines.traces.exporters[]` singleton-list-shape emission —
18619/// gets the canonical CRD-list-shape-wrap for free with one function
18620/// call, instead of re-inlining the same three-token block. Peer with
18621/// the sibling render-side helpers on the [`serde_yaml::Value`]-
18622/// construction surface ([`yaml_string_mapping`], [`label_selector`],
18623/// [`kube_resource_skeleton`], [`single_field_overlay`], the sibling
18624/// [`MappingExt::insert_str_key`] primitive) — each closes a distinct
18625/// axis of the K8s-artifact-emit surface's "same shape, written N
18626/// times" duplication.
18627///
18628/// The helper takes an owned [`serde_yaml::Mapping`] (moving into the
18629/// wrapping `vec!` without a clone) because every call site has just
18630/// finished building the mapping locally and passes it by value to the
18631/// insert-under-outer-key step. A [`Value::Mapping`] wrapping of the
18632/// same mapping is one step further along the emit trajectory — the
18633/// helper closes the gap in one primitive.
18634///
18635/// The seven caixa-mesh call sites all followed the same
18636/// insert-under-outer-key step, so the composition
18637/// `mapping.insert_str_key(K, singleton_mapping_sequence(m))` is
18638/// itself lifted onto the sibling [`MappingExt::insert_singleton_mapping_sequence`]
18639/// method — every caixa-mesh site now reaches for the composed
18640/// method rather than nesting the two calls at the call site. This
18641/// standalone helper remains the semantic primitive for the
18642/// singleton-Mapping-list-shape `Value` (the trait method's impl
18643/// composes it internally), and stays public for future callers that
18644/// want the raw `Value::Sequence(vec![Value::Mapping(m)])` payload
18645/// without inserting it under a schema key.
18646///
18647/// [mesh]: https://docs.rs/caixa-mesh
18648#[must_use]
18649#[inline]
18650pub fn singleton_mapping_sequence(m: serde_yaml::Mapping) -> serde_yaml::Value {
18651 serde_yaml::Value::Sequence(vec![serde_yaml::Value::Mapping(m)])
18652}
18653
18654/// Iterator over the string-keyed entries of a [`serde_yaml::Value`]
18655/// that may or may not be a [`serde_yaml::Mapping`] — the canonical
18656/// shape both per-Servico renderers reach for when splicing the
18657/// upstream `ComputeUnit` YAML's `spec.*` fields into their emitted
18658/// output map.
18659///
18660/// Two identical-shape call sites collapse onto this helper — both
18661/// per-Servico renderers previously carried a five-line
18662/// `if let Value::Mapping(_) = spec { for (k, v) in _ { if let
18663/// Some(s) = k.as_str() { <dst>.insert(s, v.clone()) } } }` block:
18664///
18665/// * [`caixa_flux`][flux-programs]'s `programs_yaml_entry` splices
18666/// `computeunit_yaml.spec.*` into the emitted programs.yaml entry
18667/// ([`serde_yaml::Mapping`] destination, via
18668/// [`MappingExt::insert_str_key`]);
18669/// * [`caixa_helm`][helm-values]'s `build_values_yaml` splices the
18670/// same `computeunit_yaml.spec.*` into the values.yaml wrapped
18671/// block ([`std::collections::BTreeMap`]`<String, Value>`
18672/// destination, via `BTreeMap::insert`).
18673///
18674/// Both sites need the same walk (destructure as [`serde_yaml::Mapping`],
18675/// iterate its entries, keep only string-keyed pairs, hand the caller
18676/// each `(&str, &Value)` pair) but drop the values into different
18677/// destination map types, so the lift is at the iterator layer, not
18678/// the insert layer. The caller keeps its own insert idiom (
18679/// [`MappingExt::insert_str_key`] on a [`serde_yaml::Mapping`],
18680/// `BTreeMap::insert` on the [`BTreeMap`]-shaped values block, a
18681/// future renderer's own destination) but reaches through one lifted
18682/// walk with one contract on how non-string-keyed entries are handled:
18683/// silently dropped, matching the behavior both renderers implemented
18684/// inline via the `if let Some(s) = k.as_str()` filter.
18685///
18686/// Returns an empty iterator when `v` is not a
18687/// [`serde_yaml::Value::Mapping`] — the shape the prior `if let
18688/// Value::Mapping(_) = v` arm silently no-ops on (so a Null / String
18689/// / Sequence / Number / Bool `spec` field, itself schema-invalid
18690/// upstream but tolerated by the renderer, contributes zero entries
18691/// to the destination map instead of raising a per-shape error).
18692/// Non-string-keyed entries within a valid Mapping are silently
18693/// dropped — the same behavior the prior `if let Some(s) = k.as_str()`
18694/// arm carried, since `serde_yaml` permits arbitrary [`Value`] keys
18695/// (numeric, boolean, sub-mapping) that don't round-trip through the
18696/// downstream K8s YAML-key surface (which requires string keys).
18697///
18698/// The next per-Servico renderer to land — the future per-Servico
18699/// OCI packager whose emitted `Dockerfile` LABEL block spliced through
18700/// the same `computeunit_yaml.spec.*` string-key set, the M4
18701/// per-Servico `wasm.pleme.io/v1alpha1/ComputeUnit` CR materializer
18702/// whose emitted `spec.*` block splices the same set through onto the
18703/// typed [`kube::api::CustomResource`] view, the future `caixa-otel`
18704/// renderer's per-Servico OpenTelemetry-Collector resource-attribute
18705/// splice — gets the canonical string-key filter for free with one
18706/// method call, instead of re-inlining the same five-line
18707/// `if let Value::Mapping(_) = _` walk.
18708///
18709/// [flux-programs]: https://docs.rs/caixa-flux
18710/// [helm-values]: https://docs.rs/caixa-helm
18711pub fn string_keyed_entries(
18712 v: &serde_yaml::Value,
18713) -> impl Iterator<Item = (&str, &serde_yaml::Value)> + '_ {
18714 v.as_mapping()
18715 .into_iter()
18716 .flat_map(|m| m.iter())
18717 .filter_map(|(k, v)| k.as_str().map(|s| (s, v)))
18718}
18719
18720/// Read the string-scalar value at `metadata.<field>` on a K8s custom
18721/// resource YAML document, returning `None` when either the top-level
18722/// [`KUBE_KEY_METADATA`] block is absent (a defensively-tolerated
18723/// missing sub-mapping — the caller's own test-side `expect(...)` /
18724/// production-side `unwrap_or(...)` names the axis), the requested
18725/// `<field>` scalar is absent under it, or the scalar is present but
18726/// carries a non-string YAML type (a numeric, boolean, or nested
18727/// mapping — invalid K8s CR shape per the apiserver's OpenAPI schema
18728/// but tolerated here as `None` so the readback stays a total
18729/// function). The returned `&str` borrows into the input `Value` — the
18730/// caller decides whether to compare (`==`), clone (`.to_string()`),
18731/// or unwrap-then-panic. The three-hop navigation happens in one
18732/// method call the caller reads as intent
18733/// (`kube_metadata_str_field(<value>, <FIELD>)` — "read this
18734/// `metadata.<FIELD>` string-scalar off this K8s CR document") rather
18735/// than three hand-spelled positional artifacts (the
18736/// `get(KUBE_KEY_METADATA)` outer hop, the `and_then(|m| m.get(<FIELD>))`
18737/// inner hop, the `and_then(|n| n.as_str())` shape gate).
18738///
18739/// The canonical shape 8 call sites across `caixa-mesh` (six tests) +
18740/// `caixa-flux` (one production, one test) previously carried inline
18741/// as the three-line block
18742///
18743/// ```ignore
18744/// value
18745/// .get(KUBE_KEY_METADATA)
18746/// .and_then(|m| m.get(<FIELD>))
18747/// .and_then(|n| n.as_str())
18748/// ```
18749///
18750/// around a one-token semantic payload (the `<FIELD>` axis-key —
18751/// [`KUBE_KEY_NAME`] on the six `metadata.name` per-CNP filter /
18752/// per-CNP name-collect sites in caixa-mesh, [`KUBE_KEY_NAMESPACE`] on
18753/// the caixa-flux `programs_yaml_entry` production readback with
18754/// [`DEFAULT_NAMESPACE`] fallback + the caixa-flux `cluster_bundle`
18755/// test-side `kustomization.yaml` pin).
18756///
18757/// Sites lifted:
18758///
18759/// * caixa-mesh's `cilium_network_policies_emit_per_de_para_edges` —
18760/// the per-CNP names collect ([`KUBE_KEY_NAME`] readback across
18761/// every emitted policy);
18762/// * caixa-mesh's `cilium_fans_same_de_para_edges_into_one_policy` —
18763/// the per-CNP filter on the merged `cart-to-catalog` name
18764/// ([`KUBE_KEY_NAME`] readback + string equality);
18765/// * caixa-mesh's `cilium_pubsub_contracts_skip_l7_rules` — the
18766/// per-CNP find on the `cart-to-catalog` L7-emission witness
18767/// ([`KUBE_KEY_NAME`] readback + string equality);
18768/// * caixa-mesh's `cnp_l4_fallback_port_routes_through_lifted_
18769/// default_servico_port` — the per-CNP find on the
18770/// `payment-to-cart` L4-fallback witness ([`KUBE_KEY_NAME`]
18771/// readback + string equality);
18772/// * caixa-mesh's `cilium_mtls_required_contract_emits_
18773/// authentication_required` — the per-CNP find on the
18774/// `payment-to-cart` mTLS overlay witness ([`KUBE_KEY_NAME`]
18775/// readback + string equality);
18776/// * caixa-mesh's `cilium_mtls_not_required_omits_authentication` —
18777/// the per-CNP find on the `cart-to-payment` overlay-omit
18778/// witness ([`KUBE_KEY_NAME`] readback + string equality);
18779/// * caixa-flux's `programs_yaml_entry` — the production
18780/// `computeunit_yaml.metadata.namespace` readback with
18781/// [`DEFAULT_NAMESPACE`] fallback ([`KUBE_KEY_NAMESPACE`] readback
18782/// + `unwrap_or(DEFAULT_NAMESPACE)`);
18783/// * caixa-flux's `cluster_bundle_kustomization_metadata_namespace_
18784/// pins_flux_system_default` test-side pin — the emitted
18785/// `kustomization.yaml`'s `metadata.namespace` readback
18786/// ([`KUBE_KEY_NAMESPACE`] readback + string equality).
18787///
18788/// Peer to the sibling emit-side [`kube_resource_skeleton`] on the K8s
18789/// CR-document surface: [`kube_resource_skeleton`] closes the per-CR
18790/// `apiVersion` + `kind` + `metadata.{name,namespace,labels}` build
18791/// primitive on the emit side; this closes the reverse per-CR
18792/// `metadata.<field>` readback primitive on the readback side. The
18793/// two together bracket the K8s-CR-YAML round-trip axis so the same
18794/// [`KUBE_KEY_METADATA`] navigation string sits in exactly one place
18795/// on both the write and the read side, and a future
18796/// [`KUBE_KEY_METADATA`] rebrand — a schema-migration to a versioned
18797/// `metadataV2:` axis in a future K8s API-machinery revision, a
18798/// per-CRD-side rename to a wrapped `spec.metadata:` sub-mapping
18799/// under Server-Side-Apply's per-field ownership annotations —
18800/// reaches both sides through the same lifted constant + the same
18801/// lifted helper, not a coordinated rewrite across the emitter +
18802/// every per-CR readback path across every renderer.
18803///
18804/// The next renderer to land — the per-`:politicas`
18805/// `CiliumClusterwideEnvoyConfig` emitter (whose per-policy test
18806/// harness reaches through `metadata.name` to pin per-`(:de, :para)`
18807/// naming and through `metadata.namespace` to pin the
18808/// [`DEFAULT_NAMESPACE`] contract, MESH-COMPOSITION §III.2 #3), the
18809/// `app-operator`'s typed `mesh.pleme.io/v1alpha1/Aplicacao` CR
18810/// materializer's per-CR readback (per-Aplicacao `metadata.name` /
18811/// `metadata.namespace` pins on the emitted `Aplicacao` CR, §III.2 #5),
18812/// the M4 cross-cluster fan-out's per-cluster `HelmRelease.metadata.
18813/// namespace` readback, the future `caixa-otel` per-Servico
18814/// OpenTelemetry-Collector CR's `metadata.name` pin — gets the
18815/// canonical `metadata.<field>` string readback for free with one
18816/// function call, instead of re-inlining the same three-hop chain.
18817///
18818/// The `field` axis stays parametric (rather than pinned to
18819/// [`KUBE_KEY_NAME`] or [`KUBE_KEY_NAMESPACE`] as two separate
18820/// helpers) so the same lift closes every string-scalar sub-field
18821/// under `metadata.*` a future K8s API-machinery revision surfaces
18822/// (`metadata.generateName` on Server-Side-Apply-authored CRs,
18823/// `metadata.resourceVersion` on optimistic-concurrency-controlled
18824/// updates, `metadata.uid` on cross-CR ownerReference bookkeeping) —
18825/// each new axis reaches for the same helper with a new
18826/// [`KUBE_KEY_<AXIS>`] const, not a fresh per-axis helper.
18827pub fn kube_metadata_str_field<'a>(value: &'a serde_yaml::Value, field: &str) -> Option<&'a str> {
18828 value
18829 .get(KUBE_KEY_METADATA)
18830 .and_then(|m| m.get(field))
18831 .and_then(|n| n.as_str())
18832}
18833
18834/// Read the string-scalar value at a top-level `<field>` axis-key on a
18835/// K8s custom resource YAML document — the root-level readback peer to
18836/// [`kube_metadata_str_field`] on the sub-`metadata:` axis. Returns
18837/// `None` when either the requested `<field>` scalar is absent
18838/// (defensively tolerated — the caller's own `unwrap_or(...)` /
18839/// `expect(...)` names the axis) or the scalar is present but carries a
18840/// non-string YAML type (a numeric, boolean, or nested mapping —
18841/// invalid K8s CR shape per the apiserver's OpenAPI schema but
18842/// tolerated here as `None` so the readback stays a total function).
18843/// The returned `&str` borrows into the input `Value` — the caller
18844/// decides whether to compare (`==`), clone (`.to_string()`), or
18845/// unwrap-then-panic. The two-hop navigation happens in one function
18846/// call the caller reads as intent (`kube_root_str_field(<value>,
18847/// <FIELD>)` — "read this K8s CR's top-level `<FIELD>` string-scalar")
18848/// rather than two hand-spelled positional artifacts (the
18849/// `get(<FIELD>)` outer hop, the `and_then(|n| n.as_str())` shape gate).
18850///
18851/// The canonical shape 32 call sites across `caixa-mesh` (24) +
18852/// `caixa-flux` (8) previously carried inline as the two-line block
18853///
18854/// ```ignore
18855/// value
18856/// .get(<FIELD>)
18857/// .and_then(|n| n.as_str())
18858/// ```
18859///
18860/// around a one-token semantic payload (the `<FIELD>` axis-key —
18861/// [`KUBE_KEY_KIND`] on 22 sites, [`KUBE_KEY_API_VERSION`] on 10
18862/// sites). Every routed caller keeps its downstream idiom
18863/// (`.unwrap()`, `.expect(...)`, `== Some(<KIND>)`, `assert_eq!(...,
18864/// Some(<API_VERSION>))`) unchanged — the lift closes the navigation
18865/// surface, not the per-site error-handling posture.
18866///
18867/// Sites lifted include:
18868///
18869/// * caixa-flux's `cluster_bundle_helmrelease_uses_lifted_flux_api_version`
18870/// + peer test-side pins on the emitted `helmrelease.yaml`,
18871/// `gitrepository.yaml`, `kustomization.yaml` per-document
18872/// top-level [`KUBE_KEY_API_VERSION`] axis;
18873/// * caixa-flux's per-document top-level [`KUBE_KEY_KIND`] axis pins
18874/// across the same `cluster_bundle` multi-file sequence;
18875/// * caixa-mesh's `docs.iter().find(|d| d.get(KUBE_KEY_KIND).
18876/// and_then(|k| k.as_str()) == Some(<KIND>))` per-CR filter over
18877/// the emitted `Gateway` + `HTTPRoute` multi-doc sequence — the 15
18878/// `gateway_routes` test-harness `find` sites plus the sibling
18879/// [`CILIUM_KIND_NETWORK_POLICY`] filter in
18880/// `cilium_authentication_mode_serialized_as_yaml_string`;
18881/// * caixa-mesh's per-CR top-level [`KUBE_KEY_API_VERSION`] +
18882/// [`KUBE_KEY_KIND`] discriminator-pair pins across
18883/// `cilium_network_policies_emit_per_de_para_edges` +
18884/// `gateway_routes_emit_gateway_and_httproute_per_aplicacao` +
18885/// sibling gateway/route pins.
18886///
18887/// Peer to sibling [`kube_metadata_str_field`] (6809867) on the K8s
18888/// CR-document readback surface: [`kube_metadata_str_field`] closes
18889/// the `metadata.<field>` string-scalar readback at the sub-`metadata:`
18890/// axis; this closes the root-level `<field>` string-scalar readback at
18891/// the top-level axis. The two together bracket the K8s-CR YAML
18892/// readback surface so every navigation into a rendered K8s CR
18893/// document — the top-level `(apiVersion, kind)` discriminator pair,
18894/// the sub-`metadata.(name, namespace)` identity pair — reaches
18895/// through one canonical lifted helper. A future K8s API-machinery
18896/// rebrand on either axis (a hypothetical `apiVersionV2:` scalar under
18897/// a wrapper CRD group's schema-migration, a Server-Side-Apply-driven
18898/// `metadata.name` rename under per-field ownership annotations)
18899/// reaches every consumer through one lifted helper, not a coordinated
18900/// rewrite across every renderer + every test-side per-CR readback
18901/// path.
18902///
18903/// The `field` axis stays parametric (rather than pinned to
18904/// [`KUBE_KEY_KIND`] or [`KUBE_KEY_API_VERSION`] as two separate
18905/// helpers) so the same lift closes every top-level string-scalar
18906/// axis a future K8s API-machinery revision surfaces (e.g. the
18907/// `caixa-otel` per-Servico OpenTelemetry-Collector CR's top-level
18908/// scalar pins, the future `mesh.pleme.io/v1alpha1/Aplicacao` CR
18909/// materializer's per-CR discriminator readback in the app-operator,
18910/// MESH-COMPOSITION §III.2 #5) — each new axis reaches for the same
18911/// helper with a new [`KUBE_KEY_<AXIS>`] const, not a fresh per-axis
18912/// helper.
18913pub fn kube_root_str_field<'a>(value: &'a serde_yaml::Value, field: &str) -> Option<&'a str> {
18914 value.get(field).and_then(|n| n.as_str())
18915}
18916
18917/// Predicate: does the K8s custom resource YAML document at `value`
18918/// declare its top-level `kind` discriminator axis as exactly `kind`?
18919///
18920/// Composes on top of [`kube_root_str_field`] (ae83f4e) — same two-hop
18921/// `.get(KUBE_KEY_KIND).and_then(as_str)` navigation — and closes the
18922/// "top-level kind-discriminator equality" predicate axis every
18923/// multi-doc mesh emission traversal reaches for to split the emitted
18924/// sequence by CRD-kind.
18925///
18926/// The canonical shape 15 test-side `.find(|d| kube_root_str_field(d,
18927/// KUBE_KEY_KIND) == Some(<KIND>))` + `.filter(|d| … == Some(<KIND>))`
18928/// call sites in `caixa-mesh` previously carried inline as the
18929/// three-token composition
18930///
18931/// ```ignore
18932/// kube_root_str_field(d, KUBE_KEY_KIND) == Some(<KIND>)
18933/// ```
18934///
18935/// around a one-token semantic payload (the `<KIND>` axis-value —
18936/// [`GATEWAY_API_KIND_GATEWAY`] on the per-Gateway filter sites,
18937/// [`GATEWAY_API_KIND_HTTP_ROUTE`] on the per-HTTPRoute filter sites,
18938/// [`CILIUM_KIND_NETWORK_POLICY`] on the sibling CNP filter site). The
18939/// lift collapses the three-token composition — the readback helper
18940/// call, the `== Some(...)` equality wrap, the discriminator-axis pin
18941/// on [`KUBE_KEY_KIND`] — onto one predicate function the caller
18942/// reads as intent (`kube_kind_is(d, <KIND>)` — "is this K8s CR
18943/// document of kind `<KIND>`") rather than as a three-hop
18944/// `readback → wrap → compare` chain.
18945///
18946/// The [`KUBE_KEY_KIND`] axis is pinned inside the helper (unlike the
18947/// parametric `field` axis of the underlying [`kube_root_str_field`])
18948/// because the "does this CR document match kind X" question is a
18949/// semantically-distinct discriminator predicate, not a generic
18950/// scalar-readback: the K8s CRD schema pins `kind` as the load-bearing
18951/// discriminator on every `CustomResource` across every group/version,
18952/// so this predicate lives one abstraction step above the generic
18953/// readback. Peer predicates for other top-level discriminators
18954/// (e.g. `kube_api_version_is` on a hypothetical multi-version
18955/// migration harness) land as sibling helpers with their own
18956/// pinned axis, not as re-parameterizations of this one.
18957///
18958/// Sites lifted:
18959///
18960/// * caixa-mesh's `gateway_routes` test-harness — 14
18961/// `docs.iter().find(|d| kube_root_str_field(d, KUBE_KEY_KIND) ==
18962/// Some(GATEWAY_API_KIND_{GATEWAY,HTTP_ROUTE}))` sites splitting
18963/// the multi-doc emission by `Gateway` vs `HTTPRoute` for per-CR
18964/// body-axis assertions;
18965/// * caixa-mesh's `cilium_authentication_mode_serialized_as_yaml_string`
18966/// — 1 `docs.iter().filter(|d| kube_root_str_field(d,
18967/// KUBE_KEY_KIND) == Some(CILIUM_KIND_NETWORK_POLICY))` filter
18968/// over the emitted CNP sequence.
18969///
18970/// Every future per-CRD-kind traversal (the per-`:politicas`
18971/// `CiliumClusterwideEnvoyConfig` emitter's per-CR filter,
18972/// MESH-COMPOSITION §III.2 #3; the `app-operator`'s
18973/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-status
18974/// discriminator predicate, §III.2 #5; the M4 cross-cluster fan-out's
18975/// per-cluster `HelmRelease` vs `Kustomization` split by kind) reaches
18976/// the same helper by construction, with no `== Some(...)` inline
18977/// composition and no drift surface on the `kind` scalar-key axis.
18978pub fn kube_kind_is(value: &serde_yaml::Value, kind: &str) -> bool {
18979 kube_root_str_field(value, KUBE_KEY_KIND) == Some(kind)
18980}
18981
18982/// Locate the first K8s CR YAML document in `docs` whose top-level
18983/// `kind` discriminator axis equals `kind`.
18984///
18985/// Composes on top of [`kube_kind_is`] (2902d9d) — same one-hop
18986/// `.get(KUBE_KEY_KIND).and_then(as_str) == Some(kind)` predicate —
18987/// and closes the "find the one document of a given kind inside a
18988/// multi-doc mesh emission" navigator axis every per-Aplicacao
18989/// renderer's post-emit test harness reaches for to split the
18990/// emitted sequence by CRD-kind before probing a per-CR body-axis.
18991///
18992/// The canonical shape 14 test-side
18993///
18994/// ```ignore
18995/// docs.iter().find(|d| kube_kind_is(d, <KIND>))
18996/// ```
18997///
18998/// call sites in [`caixa-mesh`][mesh]'s `gateway_routes` +
18999/// `cilium_network_policies` test harnesses previously threaded the
19000/// three-token `.iter().find(closure)` combinator chain around a
19001/// one-token semantic payload (the `<KIND>` axis-value —
19002/// [`GATEWAY_API_KIND_GATEWAY`] on the per-Gateway navigator sites,
19003/// [`GATEWAY_API_KIND_HTTP_ROUTE`] on the per-HTTPRoute navigator
19004/// sites). The lift collapses the three-token chain — the `.iter()`
19005/// receiver-widen, the `.find(closure)` combinator, the inline
19006/// closure wrap around [`kube_kind_is`] — onto one navigator
19007/// function the caller reads as intent (`find_by_kind(&docs,
19008/// <KIND>)` — "give me the K8s CR document of kind `<KIND>`")
19009/// rather than as a receiver-widen → combinator → predicate chain.
19010///
19011/// Composition-symmetric to [`kube_kind_is`]: the lifted predicate
19012/// answers "does *this* one document match kind `<KIND>`?", the
19013/// lifted navigator answers "find the one document of kind
19014/// `<KIND>` in *this list*?". Same axis, different arity — the two
19015/// call shapes emit-side test harnesses reach for when splitting
19016/// multi-doc CR emissions by top-level kind.
19017///
19018/// Every future per-CRD-kind multi-doc-navigator site (the
19019/// per-`:politicas` `CiliumClusterwideEnvoyConfig` emitter's post-
19020/// emit test harness, MESH-COMPOSITION §III.2 #3; the
19021/// `app-operator`'s `mesh.pleme.io/v1alpha1/Aplicacao` CR
19022/// materializer's per-status doc-navigator, §III.2 #5; the M4
19023/// cross-cluster fan-out's per-cluster multi-doc split by kind)
19024/// reaches the same helper by construction, with no inline
19025/// `.iter().find(closure)` combinator chain and no drift surface
19026/// on the receiver-widen or combinator axes.
19027///
19028/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
19029#[must_use]
19030pub fn find_by_kind<'a>(
19031 docs: &'a [serde_yaml::Value],
19032 kind: &str,
19033) -> Option<&'a serde_yaml::Value> {
19034 docs.iter().find(|d| kube_kind_is(d, kind))
19035}
19036
19037/// Predicate: does the K8s custom resource YAML document at `value`
19038/// declare its `metadata.name` identity axis as exactly `name`?
19039///
19040/// Composes on top of [`kube_metadata_str_field`] (6809867) — same
19041/// two-hop `.get(KUBE_KEY_METADATA).and_then(get(KUBE_KEY_NAME))
19042/// .and_then(as_str)` navigation — and closes the
19043/// "metadata.name identity equality" predicate axis every multi-doc
19044/// mesh emission traversal reaches for to split the emitted sequence
19045/// by per-CR name (the CR identity axis) rather than by CRD-kind
19046/// (the CR shape axis) the sibling [`kube_kind_is`] already closes.
19047///
19048/// The canonical shape 6 test-side
19049///
19050/// ```ignore
19051/// kube_metadata_str_field(p, KUBE_KEY_NAME) == Some(<NAME>)
19052/// ```
19053///
19054/// call sites in [`caixa-mesh`][mesh]'s per-CNP-name /
19055/// per-Aplicacao-edge test harnesses previously carried inline as
19056/// the three-token composition — the readback helper call, the
19057/// `== Some(...)` equality wrap, the identity-axis pin on
19058/// [`KUBE_KEY_NAME`] — around a one-token semantic payload (the
19059/// `<NAME>` axis-value: `"checkout-cart-to-catalog"`,
19060/// `"checkout-payment-to-cart"`, `"checkout-cart-to-payment"`, each
19061/// a [`cilium_network_policy_name`]-composed byte-string). The lift
19062/// collapses the three-token composition onto one predicate the
19063/// caller reads as intent (`kube_name_is(p, <NAME>)` — "is this K8s
19064/// CR document named `<NAME>`") rather than as a
19065/// `readback → wrap → compare` chain.
19066///
19067/// The [`KUBE_KEY_NAME`] axis is pinned inside the helper (unlike
19068/// the parametric `field` axis of the underlying
19069/// [`kube_metadata_str_field`]) because the "is this CR document
19070/// named X" question is a semantically-distinct identity predicate,
19071/// not a generic scalar-readback: the K8s API-machinery pins
19072/// `metadata.name` as the load-bearing per-CR identity axis on every
19073/// `CustomResource` across every group/version (paired with
19074/// `metadata.namespace` for cluster-scoped-vs-namespaced disambiguation),
19075/// so this predicate lives one abstraction step above the generic
19076/// readback. Peer predicates for other `metadata.*` sub-axes (e.g. a
19077/// hypothetical `kube_namespace_is` on a per-namespace router harness,
19078/// a future `kube_uid_is` for ownerReference bookkeeping) land as
19079/// sibling helpers with their own pinned axis, not as
19080/// re-parameterizations of this one.
19081///
19082/// Structural peer to [`kube_kind_is`] (2902d9d) on the sibling
19083/// top-level `kind:` discriminator axis: [`kube_kind_is`] answers
19084/// "does this document match kind X" (the CR shape axis);
19085/// [`kube_name_is`] answers "does this document match name X" (the
19086/// CR identity axis). Same one-hop readback + equality-wrap shape,
19087/// different pinned scalar-key — together they bracket the two
19088/// canonical CR discriminator axes every multi-doc mesh emission
19089/// traversal reaches for.
19090///
19091/// Sites lifted:
19092///
19093/// * caixa-mesh's `cilium_network_policies` test harness — 6
19094/// `.find(|p| kube_metadata_str_field(p, KUBE_KEY_NAME) ==
19095/// Some(<NAME>))` + `.filter(|p| … == Some(<NAME>))` sites
19096/// splitting the emitted CNP multi-doc sequence by the
19097/// [`cilium_network_policy_name`]-composed `<aplicacao>-<de>-to-
19098/// <para>` byte-string for per-CR body-axis assertions.
19099///
19100/// Every future per-CR-name traversal (the M4 cross-cluster fan-out's
19101/// per-cluster `HelmRelease`-name-router; the `app-operator`'s
19102/// per-Aplicacao `mesh.pleme.io/v1alpha1/Aplicacao` CR status-name
19103/// join; the future per-`:contratos`
19104/// `CiliumClusterwideEnvoyConfig`-name filter) reaches the same
19105/// helper by construction, with no `== Some(...)` inline composition
19106/// and no drift surface on the `metadata.name` scalar-key axis.
19107///
19108/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
19109#[must_use]
19110pub fn kube_name_is(value: &serde_yaml::Value, name: &str) -> bool {
19111 kube_name(value) == Some(name)
19112}
19113
19114/// Read the `metadata.name` string-scalar identity axis of a K8s
19115/// custom resource YAML document as `Option<&str>` — the pinned peer
19116/// on the identity axis to the parametric [`kube_metadata_str_field`]
19117/// on the two-hop `metadata.<field>` sub-axis surface. Returns `None`
19118/// when either the enclosing `metadata:` block is absent, the sub-
19119/// `name:` scalar is absent, or the sub-`name:` scalar carries a
19120/// non-string YAML type — the same three-way vacuous-`None` short-
19121/// circuit the parent [`kube_metadata_str_field`] closes on the
19122/// underlying two-hop navigation.
19123///
19124/// The [`KUBE_KEY_NAME`] axis is pinned inside the helper (unlike
19125/// the parametric `field` axis of the underlying
19126/// [`kube_metadata_str_field`]) because the K8s API-machinery pins
19127/// `metadata.name` as the load-bearing per-CR identity axis on every
19128/// `CustomResource` across every group/version. Every readback
19129/// consumer downstream (`.unwrap()`, `.expect(...)`, `== Some(...)`
19130/// equality wraps, `.to_string()` clone, `.strip_prefix(...)` /
19131/// `.split_once(...)` decompose chains) drives off the same pinned
19132/// return; a hypothetical future K8s API-machinery rename on the
19133/// `metadata.name` axis (a Server-Side-Apply-driven identity
19134/// migration under per-field ownership annotations, an alias table
19135/// bridging a new `metadata.identity` sub-axis) reaches every
19136/// caller through one lift, not a coordinated rewrite across every
19137/// per-CR readback site.
19138///
19139/// The canonical shape 12 emit-side test-harness readback sites
19140/// across [`caixa-mesh`][mesh] (9) + [`caixa-flux`][flux] (3)
19141/// previously carried inline as the two-token composition
19142///
19143/// ```ignore
19144/// kube_metadata_str_field(<value>, KUBE_KEY_NAME)
19145/// ```
19146///
19147/// around a one-token semantic payload (the readback intent — "what
19148/// name did the emitter write into this CR?"). The lift collapses
19149/// the two-token composition — the parametric readback helper, the
19150/// pinned identity-axis scalar-key argument — onto one accessor the
19151/// caller reads as intent (`kube_name(<value>)` — "what is this K8s
19152/// CR document's `metadata.name`?") rather than a
19153/// `readback → axis-pin` two-arg call.
19154///
19155/// Structural peer to sibling [`kube_kind_is`] (predicate arity) /
19156/// [`find_by_kind`] (navigator arity) / [`kube_name_is`] (predicate
19157/// arity) / [`find_by_name`] (navigator arity) on the same canonical
19158/// K8s CR discriminator+identity axis pair: this closes the accessor
19159/// arity on the identity axis — the "what is this document's name?"
19160/// question the peer predicate answers as equality and the peer
19161/// navigator answers as filter-then-first-hit. Same axis, three
19162/// arities — the accessor (`kube_name`) reads, the predicate
19163/// (`kube_name_is`) tests, the navigator (`find_by_name`) locates —
19164/// each pinned to [`KUBE_KEY_NAME`] inside the helper so the axis-
19165/// key drift class is closed across every consumer surface.
19166///
19167/// Sites lifted:
19168///
19169/// * caixa-mesh's per-CNP `metadata.name` readback loop in the
19170/// five test bodies `cilium_network_policy_metadata_name_uses_lifted_composer`,
19171/// `cilium_network_policy_metadata_name_derives_from_caixa_nome_accessor`,
19172/// `cilium_emits_one_policy_per_de_para_pair`, and
19173/// `cilium_network_policy_l4_port_matches_dest_servico_port` —
19174/// each `p → kube_metadata_str_field(p, KUBE_KEY_NAME).expect|unwrap`
19175/// readback inside the fan-in `.iter().map(...)` or per-policy
19176/// `for` loop over the multi-doc CNP emission;
19177/// * caixa-mesh's per-Gateway / per-HTTPRoute `metadata.name`
19178/// readback across the four test bodies
19179/// `gateway_routes_httproute_metadata_name_uses_lifted_composer`,
19180/// `gateway_routes_gateway_metadata_name_routes_through_caixa_nome_accessor`,
19181/// `gateway_routes_httproute_metadata_name_routes_through_caixa_nome_accessor`,
19182/// and the per-`:entrada :para` parametric permutation harness —
19183/// each `find_by_kind(&docs, <KIND>) → kube_metadata_str_field(..,
19184/// KUBE_KEY_NAME).expect(...)` chain over the paired-Gateway/HTTPRoute
19185/// emission;
19186/// * caixa-flux's three
19187/// `cluster_bundle_{gitrepository,helmrelease,kustomization}_metadata_name_routes_through_caixa_nome_accessor`
19188/// tests — each per-emitted-file
19189/// `parsed → kube_metadata_str_field(&parsed, KUBE_KEY_NAME).expect(...)`
19190/// site on the per-Flux-CR bundle-path emission.
19191///
19192/// Every future per-CR `metadata.name` readback (the M4 cross-cluster
19193/// fan-out's per-cluster `HelmRelease` name-router, the `app-
19194/// operator`'s per-Aplicacao `mesh.pleme.io/v1alpha1/Aplicacao` CR
19195/// status-name join, MESH-COMPOSITION §III.2 #5; the future per-
19196/// `:contratos` `CiliumClusterwideEnvoyConfig`-name introspection
19197/// filter) reaches the same pinned accessor by construction, with no
19198/// axis-key argument drift and no re-inlined
19199/// `kube_metadata_str_field(_, KUBE_KEY_NAME)` two-token composition.
19200///
19201/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
19202/// [flux]: https://github.com/pleme-io/caixa/tree/main/caixa-flux
19203#[must_use]
19204pub fn kube_name(value: &serde_yaml::Value) -> Option<&str> {
19205 kube_metadata_str_field(value, KUBE_KEY_NAME)
19206}
19207
19208/// Read the `metadata.namespace` string-scalar per-CR-namespace-scoping
19209/// axis of a K8s custom resource YAML document as `Option<&str>` — the
19210/// pinned peer on the namespace-scoping axis to the parametric
19211/// [`kube_metadata_str_field`] on the two-hop `metadata.<field>` sub-axis
19212/// surface, and the sibling on the identity-axis pair to the just-landed
19213/// [`kube_name`] (c9cdecb) accessor on the `metadata.name` per-CR
19214/// identity axis. Returns `None` when either the enclosing `metadata:`
19215/// block is absent, the sub-`namespace:` scalar is absent, or the sub-
19216/// `namespace:` scalar carries a non-string YAML type — the same three-
19217/// way vacuous-`None` short-circuit the parent [`kube_metadata_str_field`]
19218/// closes on the underlying two-hop navigation.
19219///
19220/// The [`KUBE_KEY_NAMESPACE`] axis is pinned inside the helper (unlike
19221/// the parametric `field` axis of the underlying
19222/// [`kube_metadata_str_field`]) because the K8s API-machinery pins
19223/// `metadata.namespace` as the load-bearing per-CR namespace-scoping
19224/// axis on every namespaced `CustomResource` across every group/version
19225/// (paired with `metadata.name` for cluster-scoped-vs-namespaced
19226/// disambiguation — the [`kube_name`] sibling closes the identity half
19227/// of the pair; this one closes the namespace-scoping half). Every
19228/// readback consumer downstream (`.unwrap()`, `.expect(...)`,
19229/// `== Some(...)` equality wraps, `.unwrap_or(DEFAULT_NAMESPACE)` fallback,
19230/// `.to_string()` clone) drives off the same pinned return; a hypothetical
19231/// future K8s API-machinery rename on the `metadata.namespace` axis (a
19232/// tenancy-driven migration to a wrapped `metadata.tenant:` sub-axis
19233/// under a per-tenant namespace-slice model, a Server-Side-Apply-driven
19234/// per-field-ownership migration under a versioned `metadata.namespaceV2:`
19235/// axis) reaches every caller through one lift, not a coordinated
19236/// rewrite across every per-CR namespace-scoping readback site.
19237///
19238/// The canonical shape 4 emit-side sites across [`caixa-flux`][flux] (1
19239/// production + 1 test) + [`caixa-mesh`][mesh] (2 test) previously
19240/// carried inline as either the two-token parametric composition
19241///
19242/// ```ignore
19243/// kube_metadata_str_field(<value>, KUBE_KEY_NAMESPACE)
19244/// ```
19245///
19246/// (the caixa-flux [`programs_yaml_entry`] production readback with
19247/// [`DEFAULT_NAMESPACE`] fallback + the sibling `cluster_bundle`
19248/// kustomization.yaml pin) or the three-token raw two-hop navigation
19249///
19250/// ```ignore
19251/// metadata.get(KUBE_KEY_NAMESPACE).and_then(|v| v.as_str())
19252/// ```
19253///
19254/// on an already-extracted `metadata: &Mapping` sub-view (the two
19255/// caixa-mesh CNP + Gateway skeleton pins on the emitted CR fixture's
19256/// `metadata:` sub-mapping) — a two-shape open-coded readback surface
19257/// where a future rebrand on either shape (a schema-migration on the
19258/// [`KUBE_KEY_NAMESPACE`] const the parametric shape reads through, an
19259/// intermediate `metadata: &Mapping` extraction the raw two-hop shape
19260/// walks) would silently split the two-shape readers into disagreement
19261/// on which per-CR namespace-scoping scalar a given emitted CR resolves
19262/// to. Lifting the resolution to one accessor pinned on the substrate
19263/// primitive means every downstream consumer of the per-CR namespace-
19264/// scoping surface reaches for exactly one typed dispatch — the
19265/// resolver's accept-set migrates as a unit on any future axis addition.
19266///
19267/// Structural peer to sibling [`kube_name`] (c9cdecb) on the identity-
19268/// axis half of the canonical `metadata.{name, namespace}` per-CR
19269/// disambiguation pair the K8s API-machinery pins as the two load-
19270/// bearing per-CR coordinates every `CustomResource` carries: [`kube_name`]
19271/// answers "what is this document's identity coordinate?"; [`kube_namespace`]
19272/// answers "what is this document's namespace-scoping coordinate?". Same
19273/// two-hop `metadata.<field>` readback shape, different pinned scalar-
19274/// key — together they bracket the two canonical per-CR coordinates
19275/// every namespaced-CR readback site reaches for.
19276///
19277/// Sites lifted:
19278///
19279/// * caixa-flux's `programs_yaml_entry` — the production
19280/// `computeunit_yaml.metadata.namespace` readback with
19281/// [`DEFAULT_NAMESPACE`] `.unwrap_or(...)` fallback (the load-
19282/// bearing per-programs-entry namespace-scoping resolver the
19283/// `lareira-fleet-programs` aggregator + wasm-operator per-
19284/// `ComputeUnit` dispatch both key off);
19285/// * caixa-flux's `cluster_bundle_kustomization_metadata_namespace_
19286/// pins_flux_system_default` test-side pin — the emitted
19287/// `kustomization.yaml`'s `metadata.namespace` readback against
19288/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`];
19289/// * caixa-mesh's `cilium_policy_carries_canonical_kube_skeleton`
19290/// test-side pin — the per-CNP `metadata.namespace` readback
19291/// against [`DEFAULT_NAMESPACE`] across every emitted CNP;
19292/// * caixa-mesh's `gateway_carries_canonical_kube_skeleton_without_labels`
19293/// test-side pin — the per-Gateway `metadata.namespace` readback
19294/// against [`DEFAULT_NAMESPACE`] on the single emitted Gateway CR.
19295///
19296/// Every future per-CR `metadata.namespace` readback (the future M4
19297/// per-Aplicacao `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
19298/// per-CR namespace-scoping join, MESH-COMPOSITION §III.2 #5; the future
19299/// per-`:politicas` `CiliumClusterwideEnvoyConfig` emitter's per-CR
19300/// namespace-scoping pin, §III.2 #3; the future `caixa-otel`
19301/// per-Servico OpenTelemetry-Collector CR's namespace-scoping pin; the
19302/// future M4 cross-cluster fan-out's per-cluster `HelmRelease.metadata.
19303/// namespace` readback) reaches the same pinned accessor by
19304/// construction, with no axis-key argument drift and no re-inlined
19305/// `kube_metadata_str_field(_, KUBE_KEY_NAMESPACE)` two-token composition
19306/// or `metadata.get(KUBE_KEY_NAMESPACE).and_then(|v| v.as_str())` three-
19307/// token raw two-hop navigation.
19308///
19309/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
19310/// [flux]: https://github.com/pleme-io/caixa/tree/main/caixa-flux
19311/// [`programs_yaml_entry`]: https://docs.rs/caixa-flux
19312#[must_use]
19313pub fn kube_namespace(value: &serde_yaml::Value) -> Option<&str> {
19314 kube_metadata_str_field(value, KUBE_KEY_NAMESPACE)
19315}
19316
19317/// Test whether a K8s custom resource YAML document's `metadata.namespace`
19318/// per-CR namespace-scoping axis equals `namespace` — the pinned predicate
19319/// peer of the [`kube_namespace`] (e18297b) accessor on the namespace-
19320/// scoping half of the canonical `metadata.{name, namespace}` per-CR
19321/// coordinate pair, and the structural mirror on the namespace-scoping
19322/// axis of the [`kube_name_is`] (092965d) predicate on the identity axis.
19323/// Composes as `kube_namespace(value) == Some(namespace)` — same one-hop
19324/// readback + equality-wrap shape the sibling predicate carries on the
19325/// identity axis, differing only in which of the two canonical per-CR
19326/// coordinates it pins.
19327///
19328/// The [`KUBE_KEY_NAMESPACE`] axis is pinned inside the helper (unlike
19329/// the parametric `field` axis of the underlying
19330/// [`kube_metadata_str_field`]) because the "is this CR document scoped
19331/// to namespace X" question is a semantically-distinct namespace-scoping
19332/// predicate, not a generic scalar-readback: the K8s API-machinery pins
19333/// `metadata.namespace` as the load-bearing per-CR namespace-scoping
19334/// axis on every namespaced `CustomResource` across every group/version
19335/// (paired with `metadata.name` for cluster-scoped-vs-namespaced
19336/// disambiguation), so this predicate lives one abstraction step above
19337/// the generic readback. Peer predicates for other `metadata.*` sub-
19338/// axes (a hypothetical `kube_uid_is` for ownerReference bookkeeping, a
19339/// future `kube_resource_version_is` for optimistic-concurrency
19340/// bookkeeping) land as sibling helpers with their own pinned axis, not
19341/// as re-parameterizations of this one.
19342///
19343/// Structural peer to [`kube_name_is`] (092965d) on the sibling
19344/// `metadata.name` identity axis: [`kube_name_is`] answers "does this
19345/// document match name X" (the identity coordinate); [`kube_namespace_is`]
19346/// answers "does this document match namespace X" (the namespace-
19347/// scoping coordinate). Same one-hop readback + equality-wrap shape,
19348/// different pinned scalar-key — together they bracket the two
19349/// canonical per-CR coordinates every namespaced-CR filter reaches
19350/// for. Same three-arity closure discipline the sibling identity axis
19351/// carries — the accessor ([`kube_namespace`]) reads, the predicate
19352/// ([`kube_namespace_is`]) tests, the navigator ([`find_by_namespace`])
19353/// locates — each pinned to [`KUBE_KEY_NAMESPACE`] inside the helper
19354/// so the axis-key drift class is closed across every consumer surface.
19355///
19356/// Every future per-CR namespace-filter site (the future M4 cross-
19357/// cluster fan-out's per-tenant `HelmRelease` router split by
19358/// `metadata.namespace`, MESH-COMPOSITION §III.2 #3; the future per-
19359/// `:politicas` `CiliumClusterwideEnvoyConfig` per-namespace
19360/// introspection filter; the future `app-operator`'s per-Aplicacao
19361/// `mesh.pleme.io/v1alpha1/Aplicacao` CR namespace-scoping equality
19362/// join, §III.2 #5; the future per-tenant CNP audit surface that
19363/// filters emitted CNPs by their per-tenant namespace-scoping
19364/// coordinate) reaches this same predicate by construction, with no
19365/// inline `kube_namespace(v) == Some(...)` equality wrap and no
19366/// re-parameterization on the pinned [`KUBE_KEY_NAMESPACE`] axis-key.
19367#[must_use]
19368pub fn kube_namespace_is(value: &serde_yaml::Value, namespace: &str) -> bool {
19369 kube_namespace(value) == Some(namespace)
19370}
19371
19372/// Locate the first K8s CR YAML document in `docs` whose
19373/// `metadata.name` identity axis equals `name`.
19374///
19375/// Composes on top of [`kube_name_is`] — same one-hop
19376/// `.get(KUBE_KEY_METADATA).and_then(get(KUBE_KEY_NAME))
19377/// .and_then(as_str) == Some(name)` predicate — and closes the
19378/// "find the one document with a given name inside a multi-doc mesh
19379/// emission" navigator axis every per-Aplicacao renderer's post-emit
19380/// test harness reaches for to split the emitted sequence by per-CR
19381/// identity before probing a per-CR body-axis.
19382///
19383/// The canonical shape 5 test-side
19384///
19385/// ```ignore
19386/// docs.iter().find(|d| kube_name_is(d, <NAME>))
19387/// ```
19388///
19389/// call sites in [`caixa-mesh`][mesh]'s `cilium_network_policies`
19390/// test harness previously threaded the three-token
19391/// `.iter().find(closure)` combinator chain around a one-token
19392/// semantic payload (the [`cilium_network_policy_name`]-composed
19393/// `<aplicacao>-<de>-to-<para>` byte-string). The lift collapses
19394/// the three-token chain — the `.iter()` receiver-widen, the
19395/// `.find(closure)` combinator, the inline closure wrap around
19396/// [`kube_name_is`] — onto one navigator function the caller reads
19397/// as intent (`find_by_name(&docs, <NAME>)` — "give me the K8s CR
19398/// document named `<NAME>`") rather than as a
19399/// receiver-widen → combinator → predicate chain.
19400///
19401/// Composition-symmetric to [`kube_name_is`]: the lifted predicate
19402/// answers "does *this* one document match name `<NAME>`?", the
19403/// lifted navigator answers "find the one document of name
19404/// `<NAME>` in *this list*?". Same axis, different arity — the two
19405/// call shapes emit-side test harnesses reach for when splitting
19406/// multi-doc CR emissions by per-CR identity. Peer of
19407/// [`find_by_kind`] (b73a13e) on the sibling `kind:` discriminator
19408/// axis: [`find_by_kind`] navigates by CR shape (there is exactly
19409/// one `Gateway` + one `HTTPRoute` per Aplicacao at V0); this navigates
19410/// by CR identity (there is one CNP per `(:de, :para)` fan-in
19411/// group, and the per-CNP identity is the
19412/// [`cilium_network_policy_name`]-composed edge label).
19413///
19414/// Every future per-CR-name multi-doc-navigator site (the future
19415/// `app-operator`'s per-Aplicacao CR-name join over emitted status
19416/// docs, MESH-COMPOSITION §III.2 #5; the M4 cross-cluster fan-out's
19417/// per-cluster `HelmRelease`-name split; the future per-`:contratos`
19418/// `CiliumClusterwideEnvoyConfig`-name filter over the sibling
19419/// L7-policy emission) reaches the same helper by construction, with
19420/// no inline `.iter().find(closure)` combinator chain and no drift
19421/// surface on the receiver-widen or combinator axes.
19422///
19423/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
19424#[must_use]
19425pub fn find_by_name<'a>(
19426 docs: &'a [serde_yaml::Value],
19427 name: &str,
19428) -> Option<&'a serde_yaml::Value> {
19429 docs.iter().find(|d| kube_name_is(d, name))
19430}
19431
19432/// Locate the first K8s CR YAML document in `docs` whose
19433/// `metadata.namespace` per-CR namespace-scoping axis equals `namespace`.
19434///
19435/// Composes on top of [`kube_namespace_is`] — same one-hop
19436/// `.get(KUBE_KEY_METADATA).and_then(get(KUBE_KEY_NAMESPACE))
19437/// .and_then(as_str) == Some(namespace)` predicate — and closes the
19438/// "find the first document scoped to a given namespace inside a multi-
19439/// doc mesh emission" navigator axis every future per-tenant / per-
19440/// cluster-namespace fan-out slicer reaches for to split the emitted
19441/// sequence by per-CR namespace-scoping before probing a per-CR body-
19442/// axis.
19443///
19444/// Composition-symmetric to [`kube_namespace_is`]: the lifted predicate
19445/// answers "does *this* one document match namespace `<NS>`?", the
19446/// lifted navigator answers "find the first document of namespace
19447/// `<NS>` in *this list*?". Same axis, different arity — the two call
19448/// shapes emit-side / operator-side harnesses reach for when splitting
19449/// multi-doc CR emissions by per-CR namespace-scoping. Peer of
19450/// [`find_by_name`] (092965d) on the sibling `metadata.name` identity
19451/// axis: [`find_by_name`] navigates by CR identity coordinate (there
19452/// is exactly one CR per unique `metadata.name` inside a scope);
19453/// [`find_by_namespace`] navigates by CR namespace-scoping coordinate
19454/// (there may be many CRs sharing a `metadata.namespace` — the "first
19455/// match" contract deliberately returns the first-emitted, matching
19456/// the sibling navigator's first-match contract on the identity axis).
19457///
19458/// This closes the three-arity closure on the `metadata.namespace`
19459/// per-CR namespace-scoping axis — accessor [`kube_namespace`]
19460/// (e18297b), predicate [`kube_namespace_is`], navigator
19461/// [`find_by_namespace`] — bringing it to structural parity with the
19462/// three-arity closure on the sibling identity axis: accessor
19463/// [`kube_name`] (c9cdecb), predicate [`kube_name_is`] (092965d),
19464/// navigator [`find_by_name`] (092965d). Together the two closures
19465/// bracket every accessor arity on the canonical
19466/// `metadata.{name, namespace}` per-CR coordinate pair the K8s
19467/// API-machinery pins as the two load-bearing coordinates every
19468/// namespaced `CustomResource` carries.
19469///
19470/// Every future per-CR-namespace multi-doc-navigator site (the M4
19471/// cross-cluster fan-out's per-tenant `HelmRelease` split by
19472/// `metadata.namespace`, MESH-COMPOSITION §III.2 #3; the future
19473/// `app-operator`'s per-Aplicacao `mesh.pleme.io/v1alpha1/Aplicacao`
19474/// CR namespace-scoping join over emitted status docs, §III.2 #5; the
19475/// future per-tenant CNP audit surface that locates the first emitted
19476/// CNP inside a given tenant's namespace-scoping slice) reaches this
19477/// same helper by construction, with no inline `.iter().find(closure)`
19478/// combinator chain and no drift surface on the receiver-widen or
19479/// combinator axes.
19480#[must_use]
19481pub fn find_by_namespace<'a>(
19482 docs: &'a [serde_yaml::Value],
19483 namespace: &str,
19484) -> Option<&'a serde_yaml::Value> {
19485 docs.iter().find(|d| kube_namespace_is(d, namespace))
19486}
19487
19488/// Upsert `new_entry` into a typed sequence of programs.yaml-shaped
19489/// entries by matching on `new_entry`'s `<name_key>` scalar — the
19490/// idempotent "replace-in-place if present, else append" contract
19491/// every writer-side aggregator overlay lands the same 11-line block
19492/// in front of. Returns `Ok(true)` when the entry was appended new,
19493/// `Ok(false)` when an existing entry with the same `<name_key>`
19494/// value was replaced in place (preserving position); returns
19495/// `on_missing_name()` when `new_entry` doesn't carry `<name_key>`
19496/// as a string scalar (the caller's own typed
19497/// [`crate::RenderError`]-shaped error surface, threaded through the
19498/// closure so this helper stays crate-agnostic).
19499///
19500/// Two identical-shape call sites collapse onto this helper — the
19501/// two [`caixa-flux`] writer-side upsert paths that both land a
19502/// programs.yaml entry into a `programs:` sequence differing only
19503/// on the outer navigation:
19504///
19505/// * [`caixa_flux::upsert_into_helmrelease_programs`][helm-up] —
19506/// the aggregator-HelmRelease shape, upserting into
19507/// `spec.values.programs[]` on a `HelmRelease` document;
19508/// * [`caixa_flux::upsert_into_programs_yaml`][yaml-up] — the
19509/// bare-values.yaml shape, upserting into `programs[]` at the
19510/// values.yaml root.
19511///
19512/// Until this lift landed both call sites re-inlined the same
19513/// verbatim 11-line block — extract-name-scalar-or-error, iterate
19514/// the sequence, replace-in-place-on-match else fall through to
19515/// push — with no compile-time link between the two: a rebrand on
19516/// either side (a per-entry match key rename beyond the currently-
19517/// lifted [`crate::FLEET_PROGRAMS_KEY_NAME`], the idempotency
19518/// contract's semantic reshaping — e.g. matching on
19519/// `(name, namespace)` for the M4 multi-namespace aggregator flow
19520/// once the `lareira-fleet-programs` chart admits per-entry
19521/// `namespace:` overrides, the return-value's `bool`-shape shift
19522/// once "replace" grows a merge-semantics axis) would silently
19523/// desynchronize the two writer-side paths — one path idempotently
19524/// upserts under the new contract while the other silently keeps
19525/// the old shape, and the failure surfaces at aggregator-apply
19526/// time as a duplicated / missing / mis-merged entry far from the
19527/// rebrand commit's source. Peer of the sibling render-side lifts
19528/// ([`single_field_overlay`], [`servico_m2_overlay`],
19529/// [`insert_first_seen`]) on the same "the same shape written
19530/// verbatim ≥ 2 times becomes a typed helper" trajectory THEORY.md
19531/// §I.3.5 promotes to a build-time concern.
19532///
19533/// The `name_key` axis stays parametric (rather than pinned to
19534/// [`crate::FLEET_PROGRAMS_KEY_NAME`] inside the helper) so a
19535/// future per-entry match on a different discriminator scalar (an
19536/// M4 `id:` axis promoted alongside `name:`, the future
19537/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-entry
19538/// `spec.selector` upsert path) reaches for the same helper with a
19539/// different key rather than re-inlining the loop. The closure-
19540/// shaped error surface (rather than a bare `Result<bool,
19541/// &'static str>` or an added typed error variant in this crate)
19542/// keeps every caller's own error enum authoritative — the
19543/// diagnostic remediation for a missing-name-scalar in a programs-
19544/// yaml entry rightly names the caller's aggregator schema
19545/// (`spec.values.programs[].name` for the `HelmRelease` shape,
19546/// `programs[].name` for the bare values.yaml shape), not this
19547/// generic helper.
19548///
19549/// [helm-up]: ../../caixa_flux/fn.upsert_into_helmrelease_programs.html
19550/// [yaml-up]: ../../caixa_flux/fn.upsert_into_programs_yaml.html
19551///
19552/// # Errors
19553///
19554/// Returns `on_missing_name()` when `new_entry.get(name_key)` is
19555/// not a [`serde_yaml::Value::String`] — the closure surfaces the
19556/// caller's own typed error variant naming the offending schema
19557/// axis. On success returns `Ok(true)` for a newly-appended entry,
19558/// `Ok(false)` for an in-place replacement.
19559pub fn upsert_named_entry<E>(
19560 arr: &mut Vec<serde_yaml::Value>,
19561 new_entry: serde_yaml::Value,
19562 name_key: &'static str,
19563 on_missing_name: impl FnOnce() -> E,
19564) -> Result<bool, E> {
19565 let new_name = match new_entry.get(name_key).and_then(|n| n.as_str()) {
19566 Some(s) => s.to_string(),
19567 None => return Err(on_missing_name()),
19568 };
19569 for slot in arr.iter_mut() {
19570 if slot.get(name_key).and_then(|n| n.as_str()) == Some(&new_name) {
19571 *slot = new_entry;
19572 return Ok(false);
19573 }
19574 }
19575 arr.push(new_entry);
19576 Ok(true)
19577}
19578
19579/// Render the M2 typed-slot YAML overlay for a Caixa: the camelCase
19580/// `(key, value)` fragments every per-Servico renderer
19581/// ([`caixa-helm`]'s values block, [`caixa-flux`]'s programs.yaml
19582/// entry) merges into its target with `or_insert` semantics so explicit
19583/// `spec.*` fields from the ComputeUnit YAML take precedence over the
19584/// manifest-derived overlay.
19585///
19586/// Keys (alphabetically ordered, since the return type is
19587/// [`BTreeMap`]) match the ComputeUnit / pleme-computeunit values
19588/// schema:
19589///
19590/// * [`M2_KEY_BEHAVIOR`] — present iff `caixa.behavior` is `Some`
19591/// and `BehaviorSpec::is_empty` returns `false`.
19592/// * [`M2_KEY_LIMITS`] — present iff `caixa.limits` is `Some` and
19593/// `LimitsSpec::is_empty` returns `false`.
19594/// * [`M2_KEY_UPGRADE_FROM`] — present iff `caixa.upgrade_from` is
19595/// non-empty.
19596///
19597/// An entirely empty M2 surface returns an empty map; the renderer
19598/// merges zero fragments and emits no extra keys (the per-renderer
19599/// "empty M2 slots do not appear" tests pin this invariant —
19600/// `caixa_helm::tests::empty_m2_slots_do_not_appear` and
19601/// `caixa_flux::tests::empty_m2_slots_do_not_appear_in_programs_yaml_entry`).
19602///
19603/// # Errors
19604///
19605/// Returns [`RenderError::Yaml`] if `serde_yaml::to_value` fails for
19606/// any of the typed M2 slot values. The prior inline block silently
19607/// substituted [`serde_yaml::Value::Null`] in this case, which renders
19608/// as e.g. `limits: null` — indistinguishable from "the author omitted
19609/// the slot" once it leaves the typed surface.
19610pub fn servico_m2_overlay(
19611 caixa: &Caixa,
19612) -> Result<BTreeMap<&'static str, serde_yaml::Value>, RenderError> {
19613 let mut out = BTreeMap::new();
19614 if let Some(limits) = caixa.limits() {
19615 if !limits.is_empty() {
19616 let v = serde_yaml::to_value(limits).map_err(|source| RenderError::Yaml {
19617 slot: M2_KEY_LIMITS,
19618 source,
19619 })?;
19620 out.insert(M2_KEY_LIMITS, v);
19621 }
19622 }
19623 if let Some(behavior) = caixa.behavior() {
19624 if !behavior.is_empty() {
19625 let v = serde_yaml::to_value(behavior).map_err(|source| RenderError::Yaml {
19626 slot: M2_KEY_BEHAVIOR,
19627 source,
19628 })?;
19629 out.insert(M2_KEY_BEHAVIOR, v);
19630 }
19631 }
19632 if !caixa.upgrade_from().is_empty() {
19633 let v = serde_yaml::to_value(caixa.upgrade_from()).map_err(|source| RenderError::Yaml {
19634 slot: M2_KEY_UPGRADE_FROM,
19635 source,
19636 })?;
19637 out.insert(M2_KEY_UPGRADE_FROM, v);
19638 }
19639 Ok(out)
19640}
19641
19642/// Compose the canonical per-Servico value-block splice every per-Servico
19643/// renderer applies to the target values / entry mapping — the two-step
19644/// sequence [`caixa_helm::build_values_yaml`] and
19645/// [`caixa_flux::programs_yaml_entry`] both re-derived inline before this
19646/// lift:
19647///
19648/// 1. Splice every string-keyed entry from the `ComputeUnit` YAML's
19649/// `spec.*` sub-mapping (routed through [`string_keyed_entries`],
19650/// preserving the source Mapping's insertion order).
19651/// 2. Overlay the M2 typed slots (routed through
19652/// [`servico_m2_overlay`], `BTreeMap` key-ordered) at every M2 key
19653/// not already claimed by step 1 — the `or_insert` precedence rule
19654/// the two prior inline call sites shared, promoted here to a
19655/// filtered append so the returned `Vec` is drop-in for a target
19656/// mapping whose insertion order is load-bearing (caixa-flux's
19657/// `serde_yaml::Mapping` preserves it; caixa-helm's `BTreeMap`
19658/// re-sorts by key, so both consumer shapes stay byte-identical
19659/// to their prior inline blocks under this lift).
19660///
19661/// Returns a `Vec<(String, serde_yaml::Value)>` in insertion order —
19662/// spec.* entries first (original ordering preserved), then the M2 slots
19663/// that weren't claimed by spec.* (in [`servico_m2_overlay`]'s canonical
19664/// BTreeMap-key ordering: `behavior` → `limits` → `upgradeFrom`).
19665/// Callers extend their target mapping by iterating the `Vec` and
19666/// inserting each pair with their own map type's canonical insert.
19667///
19668/// Until this lift landed the two prior inline blocks each carried the
19669/// same three-shape composition: `for (k, v) in
19670/// caixa_core::string_keyed_entries(spec) { <insert>(k, v.clone()); }`
19671/// followed by `for (key, value) in caixa_core::servico_m2_overlay(caixa)?
19672/// { <entry-and-or-insert>(key, value); }`. A future change to the
19673/// per-Servico splice / overlay composition — the M4 typed per-edge
19674/// policy overlay slot addition (MESH-COMPOSITION §III.2 #3), a change
19675/// to the spec.* / M2 precedence rule (e.g. reversing to "M2 wins on
19676/// collision" once per-Aplicacao operator overrides land), a
19677/// canonicalization pass on the merged key set (e.g. rejecting empty
19678/// string keys, casing-normalization on DNS-1123 labels) — would have
19679/// to be threaded through both renderers in lockstep or one would
19680/// silently diverge from the other on which keys it emitted and in
19681/// what order. Peer with the lifted [`servico_m2_overlay`] on the
19682/// per-Servico M2-overlay axis (10bf310 / 0e84fb9 on the sibling
19683/// upsert-loop / test-side probe axes) — completes the
19684/// "one canonical splice / overlay composition per typed axis"
19685/// discipline the M2 overlay lift established, now on the composed
19686/// spec.*+M2 axis every per-Servico renderer entry-point navigates.
19687///
19688/// # Errors
19689///
19690/// Propagates [`RenderError::Yaml`] from [`servico_m2_overlay`] when
19691/// `serde_yaml::to_value` fails for any typed M2 slot value — the same
19692/// error surface [`servico_m2_overlay`]'s docstring names.
19693pub fn servico_spec_and_m2_overlay_entries(
19694 caixa: &Caixa,
19695 spec: &serde_yaml::Value,
19696) -> Result<Vec<(String, serde_yaml::Value)>, RenderError> {
19697 let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
19698 let mut out: Vec<(String, serde_yaml::Value)> = Vec::new();
19699 for (k, v) in string_keyed_entries(spec) {
19700 seen.insert(k.to_string());
19701 out.push((k.to_string(), v.clone()));
19702 }
19703 for (key, value) in servico_m2_overlay(caixa)? {
19704 if !seen.contains(key) {
19705 out.push((key.to_string(), value));
19706 }
19707 }
19708 Ok(out)
19709}
19710
19711/// Bracket a typed `u32` axis with the "zero-floor + upper-cap" gate
19712/// pair every capped-`u32` `:politicas` / `:supervisor` / `:limits`
19713/// axis carries. Returns `on_zero()` when `value == 0`,
19714/// `on_cap_exceeded(value)` when `value > cap`, `Ok(())` otherwise.
19715///
19716/// The zero-floor arm strictly precedes the cap arm so a literal `0`
19717/// value surfaces the self-locating zero diagnostic (which every
19718/// per-axis error variant already documents an "omit the axis to
19719/// express no-bound" remediation for) rather than the misleading
19720/// `0 > cap` false-negative on the cap arm. Same ordering discipline
19721/// every existing per-axis inline `if value == 0 { … } if value > CAP
19722/// { … }` block already applies — this lift makes the ordering a
19723/// property of the helper, not a per-call-site convention six sites
19724/// re-derive.
19725///
19726/// Six identical-shape call sites collapse onto this helper:
19727///
19728/// * [`crate::AplicacaoSpec::validate_politicas`] on
19729/// `MeshPolicy::retries` (zero →
19730/// [`crate::AplicacaoError::PolicyRetriesZero`], cap →
19731/// [`crate::AplicacaoError::PolicyRetriesExceedsCap`],
19732/// cap = [`crate::POLICY_RETRIES_MAX`]),
19733/// `CircuitBreaker::max_failures` (zero →
19734/// [`crate::AplicacaoError::PolicyBreakerZeroFailures`], cap →
19735/// [`crate::AplicacaoError::PolicyBreakerMaxFailuresExceedsCap`],
19736/// cap = [`crate::POLICY_BREAKER_MAX_FAILURES_MAX`]), and
19737/// `RateLimit::rate` (zero →
19738/// [`crate::AplicacaoError::PolicyRateLimitZero`], cap →
19739/// [`crate::AplicacaoError::PolicyRateLimitExceedsCap`],
19740/// cap = [`crate::POLICY_RATE_LIMIT_MAX`]);
19741/// * [`crate::SupervisorSpec::validate`] on `max_restarts`
19742/// (zero → [`crate::SupervisorError::ZeroMaxRestarts`], cap →
19743/// [`crate::SupervisorError::MaxRestartsExceedsCap`],
19744/// cap = [`crate::SUPERVISOR_MAX_RESTARTS_MAX`]);
19745/// * [`crate::LimitsSpec::validate`] on `cpu`
19746/// (zero → [`crate::LimitsError::CpuZero`], cap →
19747/// [`crate::LimitsError::CpuExceedsCap`],
19748/// cap = [`crate::LIMITS_CPU_MILLICORES_MAX`]).
19749///
19750/// Peer to [`require_positive_bounded_u64`] on the `u64`-typed axes
19751/// ([`crate::LimitsSpec::fuel`]). Generic over the caller's error enum
19752/// so the same helper reaches every crate-level [`thiserror`] surface
19753/// — the six per-axis error variants remain the source of truth for
19754/// each axis's remediation prose; the helper only sequences the two
19755/// gate arms in canonical order and threads the value into the cap
19756/// arm's discriminator field.
19757///
19758/// # Errors
19759///
19760/// Returns `on_zero()` for `value == 0`; returns `on_cap_exceeded(value)`
19761/// for `value > cap`; returns `Ok(())` otherwise.
19762pub fn require_positive_bounded_u32<E>(
19763 value: u32,
19764 cap: u32,
19765 on_zero: impl FnOnce() -> E,
19766 on_cap_exceeded: impl FnOnce(u32) -> E,
19767) -> Result<(), E> {
19768 if value == 0 {
19769 return Err(on_zero());
19770 }
19771 if value > cap {
19772 return Err(on_cap_exceeded(value));
19773 }
19774 Ok(())
19775}
19776
19777/// Peer of [`require_positive_bounded_u32`] on the `u64`-typed axes.
19778/// Returns `on_zero()` when `value == 0`, `on_cap_exceeded(value)`
19779/// when `value > cap`, `Ok(())` otherwise. See
19780/// [`require_positive_bounded_u32`] for the ordering / lift rationale
19781/// (same "zero-floor arm strictly precedes cap arm so `0` surfaces
19782/// the self-locating diagnostic" discipline the peer helper documents).
19783///
19784/// The single existing call site is [`crate::LimitsSpec::validate`] on
19785/// `fuel` (zero → [`crate::LimitsError::FuelZero`], cap →
19786/// [`crate::LimitsError::FuelExceedsCap`], cap =
19787/// [`crate::LIMITS_FUEL_MAX`]). Lifted alongside its `u32` peer so
19788/// the two integer-typed axes on this discipline share one canonical
19789/// entry-point — a future `u64`-typed axis (a hypothetical
19790/// per-Aplicacao byte-budget cap, the M4 per-edge policy resolver's
19791/// byte-throughput axis) reaches for the same helper by construction.
19792///
19793/// # Errors
19794///
19795/// Returns `on_zero()` for `value == 0`; returns `on_cap_exceeded(value)`
19796/// for `value > cap`; returns `Ok(())` otherwise.
19797pub fn require_positive_bounded_u64<E>(
19798 value: u64,
19799 cap: u64,
19800 on_zero: impl FnOnce() -> E,
19801 on_cap_exceeded: impl FnOnce(u64) -> E,
19802) -> Result<(), E> {
19803 if value == 0 {
19804 return Err(on_zero());
19805 }
19806 if value > cap {
19807 return Err(on_cap_exceeded(value));
19808 }
19809 Ok(())
19810}
19811
19812/// Bracket a typed `u64` axis carrying a quantized value with the
19813/// "zero-floor + below-quantum floor + upper-cap + not-quantum-multiple"
19814/// four-arm gate every capped-and-quantized `u64` axis in the crate
19815/// carries. Returns `on_zero()` when `value == 0`,
19816/// `on_below_quantum(value)` when `value < quantum`,
19817/// `on_cap_exceeded(value)` when `value > cap`,
19818/// `on_not_quantum_multiple(value)` when `value % quantum != 0`,
19819/// `Ok(())` otherwise.
19820///
19821/// The four arms fire in canonical `zero → below-quantum → cap →
19822/// not-quantum-multiple` order, matching the discipline the pre-lift
19823/// inline block at [`crate::LimitsSpec::validate`]'s `:memory` axis
19824/// applied by hand across four sequential `if let Some(m) = self.memory()`
19825/// wrappers. Each arm strictly precedes the next: the zero-floor arm
19826/// precedes the below-quantum arm so `Some(0)` (a value the modulus arm
19827/// would silently accept because `0 % quantum == 0` and the below-quantum
19828/// arm would also flag because `0 < quantum` — two distinct diagnostics
19829/// for the same value) surfaces the self-locating zero diagnostic every
19830/// per-axis error variant already documents an "omit the axis to
19831/// express no-bound" remediation for; the below-quantum arm precedes
19832/// the cap arm so a sub-quantum value (which is *also* not a quantum
19833/// multiple by construction — the smallest positive quantum multiple
19834/// *is* `quantum`) surfaces the more actionable "raise to at least one
19835/// quantum" diagnostic first; the cap arm precedes the not-multiple
19836/// arm so a value that is both above-cap and sub-quantum-residue
19837/// surfaces the cap diagnostic first (the not-multiple remediation
19838/// would be misleading when the offending value already exceeds the
19839/// upper bracket — the canonical fix collapses both into "pin a
19840/// quantum-aligned value ≤ cap"), peer to the
19841/// [`require_positive_canonical_bounded_duration`] cap-precedes-not-
19842/// canonical ordering on the sibling typed-`Duration` axis.
19843///
19844/// One existing call site collapses onto this helper —
19845/// [`crate::LimitsSpec::validate`] on
19846/// [`crate::LimitsSpec::memory`] (zero →
19847/// [`crate::LimitsError::MemoryZero`], below-quantum →
19848/// [`crate::LimitsError::MemoryBelowWasm32Page`], cap →
19849/// [`crate::LimitsError::MemoryExceedsWasm32Cap`], not-multiple →
19850/// [`crate::LimitsError::MemoryNotPageMultiple`],
19851/// quantum = [`crate::LIMITS_MEMORY_WASM32_PAGE_BYTES`] (64 KiB),
19852/// cap = [`crate::LIMITS_MEMORY_WASM32_MAX_BYTES`] (4 GiB)) — the last
19853/// unlifted `:limits` axis on the four-axis `LimitsSpec::validate`
19854/// discipline. The three peer axes (`:fuel`, `:wall-clock`, `:cpu`)
19855/// each route through one substrate helper today
19856/// ([`require_positive_bounded_u64`],
19857/// [`require_positive_canonical_bounded_duration`],
19858/// [`require_positive_bounded_u32`]); after this lift `:memory` joins
19859/// them at the same altitude — every `LimitsSpec::validate` axis is
19860/// exactly one typed-helper dispatch, with the four-arm ordering
19861/// discipline promoted from per-site convention to structural contract
19862/// on the substrate primitive.
19863///
19864/// Peer to [`require_positive_bounded_u32`] /
19865/// [`require_positive_bounded_u64`] on the two-arm integer-typed
19866/// bracket axes and to [`require_positive_canonical_bounded_duration`]
19867/// on the three-arm typed-`Duration` bracket-and-quantize axis. Generic
19868/// over the caller's error enum so the same helper reaches every
19869/// crate-level [`thiserror`] surface — the four per-axis error variants
19870/// remain the source of truth for each axis's remediation prose; the
19871/// helper only sequences the four gate arms in canonical order and
19872/// threads the value into the below-quantum / cap / not-multiple arms'
19873/// discriminator fields.
19874///
19875/// PRIME DIRECTIVE promotion: the four-arm quantized-byte-cap cascade
19876/// is the natural u64 extension of the two-arm
19877/// [`require_positive_bounded_u64`] bracket the sibling `:fuel` axis
19878/// already routes through. Lifting it means a future quantized-byte-cap
19879/// axis reaching for the same discipline — a wasm64-target promotion
19880/// raising the wasm32 page and address-space bounds, a hypothetical
19881/// per-Aplicacao heap-max byte-cap, an operator-side page-aligned
19882/// byte-cap admitted by the M4 CR materializer's admission webhook —
19883/// lands as a thin four-closure wrapper rather than re-inlining the
19884/// same four-arm cascade with a fresh page-alignment convention.
19885///
19886/// # Errors
19887///
19888/// Returns `on_zero()` for `value == 0`; returns
19889/// `on_below_quantum(value)` for `value < quantum`; returns
19890/// `on_cap_exceeded(value)` for `value > cap`; returns
19891/// `on_not_quantum_multiple(value)` for `value % quantum != 0`;
19892/// returns `Ok(())` otherwise.
19893pub fn require_positive_quantum_multiple_bounded_u64<E>(
19894 value: u64,
19895 quantum: u64,
19896 cap: u64,
19897 on_zero: impl FnOnce() -> E,
19898 on_below_quantum: impl FnOnce(u64) -> E,
19899 on_cap_exceeded: impl FnOnce(u64) -> E,
19900 on_not_quantum_multiple: impl FnOnce(u64) -> E,
19901) -> Result<(), E> {
19902 if value == 0 {
19903 return Err(on_zero());
19904 }
19905 if value < quantum {
19906 return Err(on_below_quantum(value));
19907 }
19908 if value > cap {
19909 return Err(on_cap_exceeded(value));
19910 }
19911 if !value.is_multiple_of(quantum) {
19912 return Err(on_not_quantum_multiple(value));
19913 }
19914 Ok(())
19915}
19916
19917/// Bracket a typed `Duration` axis with the "zero-floor +
19918/// canonical-form + upper-cap" three-arm gate every typed-`Duration`
19919/// slot in the crate carries. Returns `on_zero()` when `value` is
19920/// `Duration::ZERO`, `on_not_canonical(value)` when `value` carries
19921/// sub-millisecond residue the shared
19922/// [`crate::supervisor::duration_codec`] cannot round-trip losslessly,
19923/// `on_cap_exceeded(value)` when `value > cap`, `Ok(())` otherwise.
19924///
19925/// The three arms fire in canonical `zero → not-canonical → cap` order,
19926/// matching the discipline every existing per-axis inline block already
19927/// applied by hand: the zero-floor arm precedes the canonical-form arm
19928/// so `Duration::ZERO` (whose `subsec_nanos() == 0` makes it accepted
19929/// by the canonical-form predicate) surfaces the self-locating zero
19930/// diagnostic — every per-axis zero variant already documents an
19931/// "omit the axis to express no-bound" remediation — rather than the
19932/// misleading no-op the canonical arm would return; the canonical-form
19933/// arm then precedes the cap arm so a `Duration` that is *both*
19934/// sub-millisecond and above-cap surfaces the more fundamental
19935/// round-trip-shape diagnostic first (the cap's `1ms..=<cap>`
19936/// remediation would be misleading when no integer-ms form of the
19937/// offending value exists). Same ordering discipline the peer
19938/// [`require_positive_bounded_u32`] applies on its two arms — this
19939/// lift makes the three-arm ordering a property of the helper, not a
19940/// per-call-site convention four sites re-derived by hand.
19941///
19942/// Four identical-shape call sites collapse onto this helper — one for
19943/// each typed-`Duration` slot in the crate:
19944///
19945/// * [`crate::AplicacaoSpec::validate`] on
19946/// [`crate::MeshPolicy::timeout`] (zero →
19947/// [`crate::AplicacaoError::PolicyTimeoutZero`], not-canonical →
19948/// [`crate::AplicacaoError::PolicyTimeoutNotCanonical`], cap →
19949/// [`crate::AplicacaoError::PolicyTimeoutExceedsCap`],
19950/// cap = [`crate::POLICY_TIMEOUT_MAX`]) and
19951/// [`crate::CircuitBreaker::window`] (zero →
19952/// [`crate::AplicacaoError::PolicyBreakerZeroWindow`],
19953/// not-canonical →
19954/// [`crate::AplicacaoError::PolicyBreakerWindowNotCanonical`],
19955/// cap → [`crate::AplicacaoError::PolicyBreakerWindowExceedsCap`],
19956/// cap = [`crate::POLICY_BREAKER_WINDOW_MAX`]);
19957/// * [`crate::LimitsSpec::validate`] on
19958/// [`crate::LimitsSpec::wall_clock`] (zero →
19959/// [`crate::LimitsError::WallClockZero`], not-canonical →
19960/// [`crate::LimitsError::WallClockNotCanonical`], cap →
19961/// [`crate::LimitsError::WallClockExceedsCap`],
19962/// cap = [`crate::LIMITS_WALL_CLOCK_MAX`]);
19963/// * [`crate::SupervisorSpec::validate`] on
19964/// [`crate::SupervisorSpec::restart_window`] (zero →
19965/// [`crate::SupervisorError::RestartWindowZero`], not-canonical →
19966/// [`crate::SupervisorError::RestartWindowNotCanonical`], cap →
19967/// [`crate::SupervisorError::RestartWindowExceedsCap`],
19968/// cap = [`crate::SUPERVISOR_RESTART_WINDOW_MAX`]).
19969///
19970/// Peer to [`require_positive_bounded_u32`] /
19971/// [`require_positive_bounded_u64`] on the integer-typed capped axes;
19972/// the four typed-`Duration` axes and the four typed-integer axes now
19973/// route through one helper each, so a future axis reaching for the
19974/// same discipline lands in exactly one place. Generic over the
19975/// caller's error enum so the same helper reaches every crate-level
19976/// [`thiserror`] surface — the ten per-axis error variants remain the
19977/// source of truth for each axis's remediation prose; the helper only
19978/// sequences the three gate arms in canonical order and threads the
19979/// value into the not-canonical / cap arms' discriminator fields.
19980///
19981/// # Errors
19982///
19983/// Returns `on_zero()` for `value.is_zero()`; returns
19984/// `on_not_canonical(value)` when `value` carries sub-millisecond
19985/// residue (`value.subsec_nanos() % 1_000_000 != 0`); returns
19986/// `on_cap_exceeded(value)` for `value > cap`; returns `Ok(())`
19987/// otherwise.
19988pub fn require_positive_canonical_bounded_duration<E>(
19989 value: std::time::Duration,
19990 cap: std::time::Duration,
19991 on_zero: impl FnOnce() -> E,
19992 on_not_canonical: impl FnOnce(std::time::Duration) -> E,
19993 on_cap_exceeded: impl FnOnce(std::time::Duration) -> E,
19994) -> Result<(), E> {
19995 if value.is_zero() {
19996 return Err(on_zero());
19997 }
19998 if !crate::supervisor::duration_codec::is_integer_millisecond_duration(value) {
19999 return Err(on_not_canonical(value));
20000 }
20001 if value > cap {
20002 return Err(on_cap_exceeded(value));
20003 }
20004 Ok(())
20005}
20006
20007/// Bracket a `:versao` requirement-string axis with the shared
20008/// "empty-first, then [`crate::parse_requirement`]" gate pair every
20009/// dep-shaped `:versao` slot carries. Returns `on_empty()` when
20010/// `versao.is_empty()`, `on_invalid(reason)` when
20011/// [`crate::parse_requirement`] rejects the non-empty input, `Ok(())`
20012/// otherwise.
20013///
20014/// The empty-first arm strictly precedes the parse arm so a literal
20015/// `""` value surfaces the self-locating empty diagnostic every
20016/// per-axis error variant already documents an "omit the axis to
20017/// express any-version" remediation for, rather than the misleading
20018/// parse-side no-op — [`crate::parse_requirement("")`][crate::parse_requirement]
20019/// hits `semver::VersionReq::parse("")` which returns
20020/// `Ok(VersionReq { comparators: [] })` (semantically identical to
20021/// [`semver::VersionReq::STAR`]), so without the empty-first arm an
20022/// authored blank `:versao "" ` would silently round-trip as an
20023/// implicit `"*"` — the same "silent widening" footgun the peer
20024/// [`require_positive_bounded_u32`] closes on its zero-floor arm.
20025///
20026/// The three existing call sites — [`crate::dep::Dep::validate`] on
20027/// [`crate::dep::Dep::versao`] (empty → [`crate::DepError::VersaoEmpty`],
20028/// invalid → [`crate::DepError::VersaoInvalid`]),
20029/// [`crate::AplicacaoSpec::validate_membros`] on
20030/// [`crate::aplicacao::Membro::versao`] (empty →
20031/// [`crate::AplicacaoError::MembroVersaoEmpty`], invalid →
20032/// [`crate::AplicacaoError::MembroVersaoInvalid`]), and
20033/// [`crate::SupervisorSpec::validate`] on
20034/// [`crate::supervisor::ChildSpec::versao`] (empty →
20035/// [`crate::SupervisorError::EmptyChildVersion`], invalid →
20036/// [`crate::SupervisorError::ChildVersaoInvalid`]) — each formerly
20037/// inlined this two-arm cascade verbatim. Lifting to one canonical
20038/// entry-point closes the drift footgun structurally: a future
20039/// widening of the accepted requirement-shape (a hypothetical
20040/// git-tag-prefix leniency, a per-axis strictness override, or the
20041/// M4 typed-resolver's `constraint:` axis on
20042/// [`ABSORPTION-ROADMAP.md`]'s per-resolver-step trajectory) reaches
20043/// every dep-shaped `:versao` consumer by one edit at this helper,
20044/// not a coordinated rewrite across three modules.
20045///
20046/// Peer of [`require_positive_bounded_u32`] /
20047/// [`require_positive_bounded_u64`] on the same closure-based
20048/// caller-error-variant discipline — the caller owns the enum
20049/// variant + its self-locating discriminator fields
20050/// (`nome`/`caixa`, `versao`), this helper only sequences the two
20051/// gate arms in canonical order and threads the parser's
20052/// `semver`-shaped reason into the invalid arm's `reason:` field.
20053///
20054/// # Errors
20055///
20056/// Returns `on_empty()` for `versao.is_empty()`; returns
20057/// `on_invalid(reason)` when [`crate::parse_requirement`] rejects
20058/// the non-empty input (the parser's `to_string()` output threaded
20059/// through as the invalid arm's `reason:`); returns `Ok(())`
20060/// otherwise.
20061pub fn require_valid_versao_requirement<E>(
20062 versao: &str,
20063 on_empty: impl FnOnce() -> E,
20064 on_invalid: impl FnOnce(String) -> E,
20065) -> Result<(), E> {
20066 if versao.is_empty() {
20067 return Err(on_empty());
20068 }
20069 if let Err(e) = crate::parse_requirement(versao) {
20070 return Err(on_invalid(e.to_string()));
20071 }
20072 Ok(())
20073}
20074
20075/// Bracket a K8s DNS-1123-label-shaped axis with the shared
20076/// "empty-first, then [`is_dns_1123_label`]" gate pair every Servico-
20077/// name reference slot carries. Returns `on_empty()` when
20078/// `value.is_empty()`, `on_invalid(reason)` when [`is_dns_1123_label`]
20079/// rejects the non-empty input, `Ok(())` otherwise.
20080///
20081/// The empty-first arm strictly precedes the shape arm so a literal
20082/// `""` value surfaces each per-axis error variant's narrower self-
20083/// locating `_Empty` diagnostic (`MembroCaixaEmpty`, `PlacementClusterEmpty`,
20084/// `EntradaParaEmpty`, `NomeEmpty`, `EmptyChildName`, `ModuleEmpty`, …)
20085/// rather than the shared predicate's generic "must not be empty" prose
20086/// — the same "misframed generic diagnostic" footgun the peer
20087/// [`require_valid_versao_requirement`] closes on its empty arm. The
20088/// invalid arm threads the predicate's parser-shaped reason verbatim
20089/// into the caller's `*Invalid { reason }` field so the author's
20090/// remediation prose (which specific violation — length / boundary /
20091/// character-class) flows through unchanged.
20092///
20093/// The eight existing call sites — [`crate::AplicacaoSpec`]'s five
20094/// name-shaped slots (`validate_membro_caixa` on `:membros :caixa`,
20095/// `validate_placement_cluster` on `:placement :clusters`,
20096/// `validate_placement_affinity` on `:placement :affinity`,
20097/// `validate_contrato_caixa` on `:contratos :de`/`:para`,
20098/// `validate_entrada_para` on `:entrada :para`),
20099/// [`crate::SupervisorSpec::validate`] on `:children :caixa`,
20100/// [`crate::manifest::Caixa::validate_nome`] on `:nome`, and
20101/// [`crate::upgrade::validate_module`] on `:upgrade-from :module` —
20102/// each formerly inlined this two-arm cascade verbatim. Lifting to one
20103/// canonical entry-point closes the drift footgun structurally: a
20104/// future widening of the accepted DNS-1123-label shape (a hypothetical
20105/// IDN-Punycode-accepting variant, a per-axis strictness override for
20106/// the M4 CR materializer's `spec.name` axes, or the future
20107/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's admission
20108/// webhook floor) reaches every name-shaped consumer by one edit at
20109/// this helper, not a coordinated rewrite across three modules.
20110///
20111/// Peer of [`require_valid_versao_requirement`] on the same closure-
20112/// based caller-error-variant discipline — the caller owns the enum
20113/// variant + its self-locating discriminator fields (`caixa`, `cluster`,
20114/// `affinity`, `nome`, `slot`, `kind`, `module`, …), this helper only
20115/// sequences the two gate arms in canonical order and threads the
20116/// predicate's shape-shaped reason into the invalid arm's `reason:`
20117/// field.
20118///
20119/// # Errors
20120///
20121/// Returns `on_empty()` for `value.is_empty()`; returns
20122/// `on_invalid(reason)` when [`is_dns_1123_label`] rejects the
20123/// non-empty input (the predicate's parser-shaped reason threaded
20124/// through as the invalid arm's `reason:`); returns `Ok(())` otherwise.
20125pub fn require_valid_dns_1123_label<E>(
20126 value: &str,
20127 on_empty: impl FnOnce() -> E,
20128 on_invalid: impl FnOnce(String) -> E,
20129) -> Result<(), E> {
20130 if value.is_empty() {
20131 return Err(on_empty());
20132 }
20133 if let Err(reason) = is_dns_1123_label(value) {
20134 return Err(on_invalid(reason));
20135 }
20136 Ok(())
20137}
20138
20139/// Bracket a sandboxed-relative `.lisp`-terminating path axis with the
20140/// shared "empty → absolute → parent-escape → non-`.lisp`-extension"
20141/// four-arm gate every author-supplied M2 tatara-lisp source-path slot
20142/// on the caixa surface carries. Delegates to
20143/// [`is_sandboxed_relative_path`] for the three structural arms and to
20144/// [`is_lisp_extension`] for the extension arm; returns each arm's
20145/// caller-owned error variant via the four `FnOnce` closures.
20146///
20147/// The arm ordering (`Empty → Absolute → ParentEscape → NonLisp`) is
20148/// canonical across every existing per-axis site — a path that is
20149/// *both* sandbox-escaping and non-`.lisp` surfaces the more
20150/// fundamental sandbox-shape diagnostic first (the `.lisp` remediation
20151/// would be misleading when the offending path can never resolve under
20152/// the caixa root anyway; the canonical fix collapses both into "pin a
20153/// relative `.lisp` path under the caixa root"). Same
20154/// smallest-scope-arm-fires-last posture the peer
20155/// [`require_positive_bounded_u32`] /
20156/// [`require_positive_canonical_bounded_duration`] chains follow on the
20157/// integer / duration axes, and the same posture every per-axis inline
20158/// pre-lift block already applied by hand
20159/// ([`crate::behavior::BehaviorError`]'s `EmptyPath` → `AbsolutePath`
20160/// → `ParentEscape` → `NonLispExtension` chain,
20161/// [`crate::upgrade::UpgradeError`]'s `EmptyScript` → `AbsoluteScript`
20162/// → `ParentEscapeScript` → `NonLispExtensionScript` chain).
20163///
20164/// Two identical-shape call sites collapse onto this helper — one for
20165/// each M2 typed path-slot the wasm-engine reads through
20166/// `tatara_lisp::read`:
20167///
20168/// * [`crate::behavior::BehaviorSpec::validate`] on
20169/// `:behavior :on-*` callback paths — every arm carries the slot
20170/// name verbatim through the closure's caller-side capture (empty
20171/// → [`crate::behavior::BehaviorError::EmptyPath`], absolute →
20172/// [`crate::behavior::BehaviorError::AbsolutePath`], parent-escape
20173/// → [`crate::behavior::BehaviorError::ParentEscape`], non-`.lisp`
20174/// → [`crate::behavior::BehaviorError::NonLispExtension`]);
20175/// * [`crate::upgrade::UpgradeInstruction::validate`]'s `StateChange`
20176/// arm on `:upgrade-from :state-change :script` (empty →
20177/// [`crate::upgrade::UpgradeError::EmptyScript`], absolute →
20178/// [`crate::upgrade::UpgradeError::AbsoluteScript`], parent-escape
20179/// → [`crate::upgrade::UpgradeError::ParentEscapeScript`],
20180/// non-`.lisp` →
20181/// [`crate::upgrade::UpgradeError::NonLispExtensionScript`]).
20182///
20183/// Peer of the sibling `require_positive_bounded_u32` /
20184/// `require_positive_bounded_u64` /
20185/// `require_positive_canonical_bounded_duration` /
20186/// `require_valid_versao_requirement` / `require_valid_dns_1123_label`
20187/// helpers on the same closure-based caller-error-variant discipline —
20188/// the caller owns the enum variant + its self-locating discriminator
20189/// fields (`slot`, `path`, `script`), this helper only sequences the
20190/// four gate arms in canonical order and invokes the caller's closure
20191/// on the offending arm.
20192///
20193/// PRIME DIRECTIVE promotion: the two-consumer duplication budget
20194/// (THEORY.md §I.3.5: "every recurring shape becomes a generator
20195/// before it becomes a pattern; every pattern becomes a library before
20196/// it becomes duplicated code. The duplication budget is zero.")
20197/// promotes the four-step cascade to a typed substrate-side helper on
20198/// the same trajectory the [`is_sandboxed_relative_path`] /
20199/// [`is_lisp_extension`] primitives already follow. A future third
20200/// consumer — the `:bibliotecas` per-entry tatara-lisp source-file
20201/// axis, the `:exe` `:kind Binario` entry-point axis, the M2.5
20202/// wasm-engine pre-warm hook axis, the future `mesh.pleme.io/v1alpha1/Caixa`
20203/// CR materializer's per-path validator — lands as a thin
20204/// four-closure wrapper rather than re-inlining the same four-arm
20205/// cascade.
20206///
20207/// # Errors
20208///
20209/// Returns `on_empty()` when `path` is empty; returns `on_absolute()`
20210/// when `path` is absolute; returns `on_parent_escape()` when `path`
20211/// carries a [`std::path::Component::ParentDir`] component anywhere;
20212/// returns `on_non_lisp()` when `path`'s terminating extension is not
20213/// exactly [`LISP_SOURCE_EXTENSION`]; returns `Ok(())` otherwise.
20214pub fn require_sandboxed_lisp_path<E>(
20215 path: &Path,
20216 on_empty: impl FnOnce() -> E,
20217 on_absolute: impl FnOnce() -> E,
20218 on_parent_escape: impl FnOnce() -> E,
20219 on_non_lisp: impl FnOnce() -> E,
20220) -> Result<(), E> {
20221 match is_sandboxed_relative_path(path) {
20222 Ok(()) => {}
20223 Err(PathShapeViolation::Empty) => return Err(on_empty()),
20224 Err(PathShapeViolation::Absolute) => return Err(on_absolute()),
20225 Err(PathShapeViolation::ParentEscape) => return Err(on_parent_escape()),
20226 }
20227 if !is_lisp_extension(path) {
20228 return Err(on_non_lisp());
20229 }
20230 Ok(())
20231}
20232
20233/// Bracket a per-list uniqueness gate with the shared "insert into
20234/// `seen`; caller-shaped `Err` on the second occurrence" gate every
20235/// declaration-order-preserving `Vec`-authored slot in caixa-core
20236/// carries. Delegates to [`std::collections::HashSet::insert`] verbatim
20237/// (which returns `true` on first insertion, `false` on repeat), then
20238/// invokes the caller's `on_duplicate` closure only on the duplicate
20239/// arm — keeping the hot path (the unique case) allocation-free.
20240///
20241/// The ten existing call sites — [`crate::AplicacaoSpec::validate`]'s
20242/// four per-list uniqueness gates (`:membros :caixa` →
20243/// [`crate::AplicacaoError::MembroDuplicate`], `:placement :clusters` →
20244/// [`crate::AplicacaoError::PlacementClusterDuplicate`],
20245/// `:entrada :paths` → [`crate::AplicacaoError::EntradaPathDuplicate`],
20246/// `:contratos` on the six-tuple typed-edge identity key →
20247/// [`crate::AplicacaoError::ContratoDuplicate`]),
20248/// [`crate::SupervisorSpec::validate`] on `:children :caixa`
20249/// ([`crate::SupervisorError::DuplicateChildCaixa`]),
20250/// [`crate::manifest::Caixa`]'s four per-list uniqueness gates
20251/// ([`crate::manifest::Caixa::validate_deps`] on `:deps` and `:deps-dev`
20252/// → [`crate::DepError::DuplicateNome`],
20253/// [`crate::manifest::Caixa::validate_code_paths`] on
20254/// `:bibliotecas`/`:exe`/`:servicos` →
20255/// [`crate::ManifestError::CodePathDuplicate`],
20256/// [`crate::manifest::Caixa::validate_etiquetas`] on `:etiquetas` →
20257/// [`crate::ManifestError::EtiquetaDuplicate`],
20258/// [`crate::manifest::Caixa::validate_autores`] on `:autores` →
20259/// [`crate::ManifestError::AutorDuplicate`]), and
20260/// [`crate::dep::Dep`]'s [`crate::DepError::CaracteristicaDuplicate`]
20261/// gate on `:caracteristicas` — each formerly inlined the same three-
20262/// line
20263/// ```ignore
20264/// if !seen.insert(key) {
20265/// return Err(<Variant> { … });
20266/// }
20267/// ```
20268/// shape by hand, differing only in the seen-set key type and the
20269/// caller's [`thiserror`] variant. Lifting to one canonical entry-point
20270/// closes the drift footgun structurally: a future tightening of the
20271/// per-list uniqueness discipline (a declaration-order pin on the
20272/// reported entry index, an instrumentation hook for the operator's
20273/// audit trail, the M4 CR materializer's admission-webhook per-list
20274/// invariant) reaches every consumer by one edit at this helper, not
20275/// a coordinated rewrite across every per-list gate in the crate. The
20276/// per-axis error variants remain the source of truth for each axis's
20277/// remediation prose — this helper only sequences the insert-and-check
20278/// pair.
20279///
20280/// Same set-not-multiset discipline every peer `Duplicate*` variant
20281/// documents. The typed key `K` is generic so both `&str`-shaped
20282/// callers (nine sites) and the tuple-shaped
20283/// [`crate::AplicacaoError::ContratoDuplicate`] typed-edge identity
20284/// carrier route through one helper; the caller owns the enum variant
20285/// + its self-locating discriminator fields, this helper only sequences
20286/// the insert-and-check pair in canonical `insert → on_duplicate` order.
20287/// Sibling to the peer `require_positive_bounded_*` /
20288/// `require_positive_canonical_bounded_duration` /
20289/// `require_valid_versao_requirement` / `require_valid_dns_1123_label`
20290/// helpers on the same closure-based caller-error-variant discipline.
20291///
20292/// # Errors
20293///
20294/// Returns `on_duplicate()` when `key` was already in `seen` (the
20295/// [`std::collections::HashSet::insert`] call returns `false`); returns
20296/// `Ok(())` otherwise.
20297pub fn insert_first_seen<K, E, S>(
20298 seen: &mut std::collections::HashSet<K, S>,
20299 key: K,
20300 on_duplicate: impl FnOnce() -> E,
20301) -> Result<(), E>
20302where
20303 K: std::hash::Hash + Eq,
20304 S: std::hash::BuildHasher,
20305{
20306 if seen.insert(key) {
20307 Ok(())
20308 } else {
20309 Err(on_duplicate())
20310 }
20311}
20312
20313/// Test-side pin that asserts a renderer-crate `pub use caixa_core::X;`
20314/// re-export shares both the byte value *and* the `&'static str`
20315/// allocation of its canonical `caixa_core::X` declaration — the
20316/// stronger predicate than a plain `assert_eq!` byte-equality check.
20317///
20318/// The canonical drift footgun this closes: a renderer crate silently
20319/// carries a sibling `pub const X: &str = "…";` (or a copy-pasted
20320/// `pub const X: &str = caixa_core::X;` shape whose right-hand side
20321/// materializes a fresh promoted-static allocation with the same
20322/// bytes) instead of `pub use caixa_core::X;`. A byte-only `assert_eq!`
20323/// on the value would pass — the strings are equal — but the two
20324/// declarations point at two different `&'static` allocations, so a
20325/// future canonical-side rebrand (`caixa_core::X` migrates from
20326/// `"foo"` to `"foo-v2"`) silently drifts the two apart, with the
20327/// apply-time symptom (the cluster-side CRD schema drops the malformed
20328/// axis, the operator's dispatch loop misses the renamed key, the
20329/// Cilium data plane silently reroutes past the renamed L4/L7 rule)
20330/// far from the drift commit's source. Byte-equality misses this
20331/// class of drift; static-data identity via [`std::ptr::eq`] catches
20332/// it structurally.
20333///
20334/// Lifted from the seventy-five per-`_re_export_points_at_caixa_core_
20335/// canonical` test bodies formerly inlined verbatim across
20336/// [`caixa-mesh`][mesh] (49 tests), [`caixa-flux`][flux] (21 tests),
20337/// and [`caixa-helm`][helm] (5 tests) — each formerly carried the same
20338/// two-arm `assert_eq!(<LOCAL>, caixa_core::<LOCAL>);` + `assert!(std
20339/// ::ptr::eq(<LOCAL>.as_ptr(), caixa_core::<LOCAL>.as_ptr()), "…must
20340/// be a re-export of caixa_core::…, not a sibling `pub const`…");`
20341/// pair by hand, differing only in the local `<LOCAL>` identifier the
20342/// diagnostic names. The lifted helper puts the canonical two-arm
20343/// gate in exactly one place so the next per-renderer re-export pin
20344/// (the future [`caixa-otel`] telemetry-pipeline renderer's per-CR
20345/// axis re-exports, the M4 [`mesh.pleme.io/v1alpha1/Aplicacao`] CR
20346/// materializer's per-spec-axis re-exports, the future per-Supervisor
20347/// reconciler's per-`:children` axis re-exports) lands on this
20348/// helper by construction rather than by copying the boilerplate.
20349///
20350/// Same trajectory as the sibling [`require_kind`] /
20351/// [`require_single_servico`] cross-renderer-shared-gate lifts on the
20352/// production-side axis; this closes the peer test-side re-export-
20353/// identity-gate axis.
20354///
20355/// # Panics
20356///
20357/// Panics via [`assert_eq!`] when the two byte-strings differ; panics
20358/// via [`assert!`] on the [`std::ptr::eq`] arm when the two share
20359/// bytes but point at different `&'static str` allocations. The
20360/// `name` argument names the local re-export for the failure message
20361/// so the diagnostic reads `KUBE_KEY_SPEC must be a re-export of
20362/// caixa_core::KUBE_KEY_SPEC, …` — pointing at the offending
20363/// re-export site, not just at the assertion.
20364///
20365/// [mesh]: https://docs.rs/caixa-mesh
20366/// [flux]: https://docs.rs/caixa-flux
20367/// [helm]: https://docs.rs/caixa-helm
20368pub fn assert_str_reexport_identity(name: &str, local: &'static str, canonical: &'static str) {
20369 assert_eq!(
20370 local, canonical,
20371 "{name} must byte-equal caixa_core::{name}"
20372 );
20373 assert!(
20374 std::ptr::eq(local.as_ptr(), canonical.as_ptr()),
20375 "{name} must be a re-export of caixa_core::{name}, \
20376 not a sibling `pub const` that happens to carry the same string \
20377 — drift between the two is the canonical footgun this lift closes"
20378 );
20379}
20380
20381/// Extension methods on [`serde_yaml::Mapping`] that lift the per-key
20382/// scalar-promotion boilerplate every K8s-artifact-emitter across
20383/// `caixa-mesh`, `caixa-flux`, `caixa-helm`, and `caixa-core::render`
20384/// carries: the canonical `mapping.insert(Value::String(key.into()),
20385/// value)` three-liner the schema-key axis of every emitted YAML
20386/// document tunnels a `&'static str` key axis-name through.
20387///
20388/// Five methods form the primitive quintuple — one per non-Null
20389/// primitive [`serde_yaml::Value`] variant the K8s-artifact-emit
20390/// surface actually reaches for as a leaf payload:
20391///
20392/// * [`Self::insert_str_key`] — insert with a `&str` key and any
20393/// fully-built [`serde_yaml::Value`]. The building block every
20394/// other renderer helper (`yaml_string_mapping`, `label_selector`,
20395/// `kube_resource_skeleton`, `single_field_overlay`) composes on
20396/// top of.
20397/// * [`Self::insert_string`] — insert with a `&str` key and an
20398/// `Into<String>` value that gets auto-promoted to
20399/// [`serde_yaml::Value::String`]. The string-scalar-valued-field
20400/// shape every schema-typed `apiVersion` / `kind` /
20401/// `metadata.namespace` / `port.protocol` / `hostname` /
20402/// `path.value` axis emission uses — collapses the two-step
20403/// `insert_str_key(K, Value::String(V.into()))` boilerplate onto
20404/// one direct call.
20405/// * [`Self::insert_number`] — insert with a `&str` key and an
20406/// `Into<serde_yaml::Number>` value that gets auto-promoted to
20407/// [`serde_yaml::Value::Number`]. The integer-scalar-valued-field
20408/// shape every schema-typed `port` / `targetPort` / `attempts` /
20409/// `maxFailures` / `hostPort` axis emission uses — collapses the
20410/// two-step `insert_str_key(K, Value::Number(N.into()))`
20411/// boilerplate onto one direct call.
20412/// * [`Self::insert_mapping`] — insert with a `&str` key and a
20413/// [`serde_yaml::Mapping`] value that gets auto-promoted to
20414/// [`serde_yaml::Value::Mapping`]. The nested-Mapping-valued-field
20415/// shape every schema-typed `metadata` / `spec` / `spec.rules[].path`
20416/// / `toPorts[].rules` sub-block emission uses — collapses the
20417/// two-step `insert_str_key(K, Value::Mapping(m))` boilerplate
20418/// onto one direct call.
20419/// * [`Self::insert_sequence`] — insert with a `&str` key and a
20420/// `Vec<serde_yaml::Value>` value that gets auto-promoted to
20421/// [`serde_yaml::Value::Sequence`]. The list-shape-valued-field
20422/// shape every schema-typed `spec.ingress[].fromEndpoints` /
20423/// `spec.ingress[].toPorts` / `spec.hostnames` / `spec.rules` list
20424/// emission uses — collapses the two-step
20425/// `insert_str_key(K, Value::Sequence(v))` boilerplate onto one
20426/// direct call.
20427///
20428/// A sibling method — [`Self::entry_str_key`] — closes the entry-API
20429/// twin of [`Self::insert_str_key`] on the same `&str → Value::String`
20430/// key-promotion axis: the [`serde_yaml::Mapping::entry`] method's
20431/// `Value` parameter demands the same `Value::String(<K>.into())`
20432/// wrapping every fresh-emit site's `insert_str_key` call closes, but
20433/// on the idempotent-upsert axis (where callers compose
20434/// `.or_insert(...)` / `.or_insert_with(...)` / `.and_modify(...)` /
20435/// `.or_default()` on the returned entry handle) rather than the
20436/// fresh-emit axis. Same key-promotion contract, different downstream
20437/// API surface — so a future rebrand of the promotion (e.g. to
20438/// [`serde_yaml::Value::Tagged`] under a K8s Server-Side-Apply typed-
20439/// field-ownership axis) reaches both fresh-emit and upsert sites
20440/// through one lift.
20441///
20442/// See each method's docstring for its compounding rationale.
20443pub trait MappingExt {
20444 /// Insert `(key, value)` into `self` with `key` promoted to a
20445 /// [`serde_yaml::Value::String`]. Returns the prior value at that
20446 /// key, mirroring [`serde_yaml::Mapping::insert`].
20447 ///
20448 /// The canonical shape ~48 call sites across the caixa-side
20449 /// renderer surface (`caixa-mesh` per-`CiliumNetworkPolicy` /
20450 /// `Gateway` / `HTTPRoute` construction, `caixa-flux` per-
20451 /// `GitRepository` / `HelmRelease` / `Kustomization` construction,
20452 /// `caixa-helm` per-`Chart.yaml` / `values.yaml` construction,
20453 /// `caixa-core::render` per-skeleton construction) previously
20454 /// carried inline as the three-line block
20455 /// `mapping.insert(serde_yaml::Value::String(<KEY>.into()),
20456 /// <VALUE>)` — three per-call boilerplate axes (`serde_yaml::` path
20457 /// re-quote, `Value::String(_)` promotion, `.into()` `&str → String`
20458 /// coercion) around a two-token semantic payload (`<KEY>`, `<VALUE>`).
20459 ///
20460 /// Lifting collapses the boilerplate into one method call the
20461 /// caller reads as intent (`mapping.insert_str_key(<KEY>, <VALUE>)`
20462 /// — "insert this schema key with this rendered value") rather
20463 /// than five hand-spelled positional artifacts. The next renderer
20464 /// to land — the per-`:politicas` `CiliumClusterwideEnvoyConfig`
20465 /// emitter (MESH-COMPOSITION §III.2 #3), the `app-operator`'s
20466 /// typed `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer (§III.2
20467 /// #5), the M4 cross-cluster fan-out's per-cluster `Service` /
20468 /// `HTTPRoute backendRefs` emission, the future `caixa-otel`
20469 /// OpenTelemetry-Collector pipeline emitter — gets the canonical
20470 /// key-scalar-promotion for free with one method call, instead of
20471 /// re-inlining the three-line block.
20472 ///
20473 /// Peer to the sibling render-side helpers on the
20474 /// [`serde_yaml::Value`]-construction surface:
20475 /// [`yaml_string_mapping`] (string→string mapping), [`label_selector`]
20476 /// (K8s `LabelSelector` shape), [`kube_resource_skeleton`] (K8s
20477 /// `apiVersion`+`kind`+`metadata` skeleton), [`single_field_overlay`]
20478 /// (`Option<T>` → single-key overlay). Each closes a distinct axis
20479 /// of the K8s-artifact-emit surface's "same shape, written N times"
20480 /// duplication; this one closes the per-key insert primitive the
20481 /// other four all compose on top of.
20482 fn insert_str_key(&mut self, key: &str, value: serde_yaml::Value) -> Option<serde_yaml::Value>;
20483
20484 /// Insert `(key, Value::String(value.into()))` into `self` — the
20485 /// string-scalar-valued-field emission shape that combines
20486 /// [`Self::insert_str_key`]'s `&str → Value::String` key promotion
20487 /// with an automatic `Value::String` promotion of an `Into<String>`
20488 /// value. Returns the prior value at that key, mirroring
20489 /// [`serde_yaml::Mapping::insert`].
20490 ///
20491 /// The canonical shape ~17 production call sites across the caixa-
20492 /// side renderer surface previously carried inline as the three-
20493 /// line block `mapping.insert_str_key(<KEY>,
20494 /// serde_yaml::Value::String(<VALUE>.into() | .clone() |
20495 /// .to_string()))` — the two-token semantic payload (`<KEY>`,
20496 /// `<VALUE>`) buried under three boilerplate axes (`serde_yaml::`
20497 /// path re-quote, `Value::String(_)` promotion, the
20498 /// `.into() | .clone() | .to_string()` `→ String` coercion).
20499 ///
20500 /// Sites lifted:
20501 ///
20502 /// * caixa-mesh's `programs_for_aplicacao` per-`:membros` entry
20503 /// (`FLEET_PROGRAMS_KEY_NAME` / `FLEET_PROGRAMS_KEY_VERSAO` /
20504 /// `FLEET_PROGRAMS_KEY_APLICACAO`);
20505 /// * caixa-mesh's `cilium_network_policies` per-`toPorts[]` port
20506 /// entry (`KUBE_KEY_PORT` / `KUBE_KEY_PROTOCOL`) and per-HTTP-
20507 /// rule `CILIUM_KEY_PATH` L7 predicate;
20508 /// * caixa-mesh's `gateway_routes` per-`Gateway` listener block
20509 /// (`GATEWAY_API_KEY_NAME` /
20510 /// [`crate::GATEWAY_API_KEY_HOSTNAME`] / `GATEWAY_API_KEY_PROTOCOL`)
20511 /// and `spec.gatewayClassName`;
20512 /// * caixa-mesh's `gateway_routes` per-`HTTPRoute` `parentRefs[]`
20513 /// name, per-rule `matches[].path.{type,value}` prefix-match, and
20514 /// per-rule `backendRefs[].name` backend-target;
20515 /// * caixa-flux's `programs_yaml_entry` per-entry `name` /
20516 /// `namespace` axes;
20517 /// * caixa-core `kube_resource_skeleton`'s `apiVersion` / `kind`
20518 /// scalar heads (the two production emit sites the prior
20519 /// `Value::String(_.to_string())` inline shape sat at).
20520 ///
20521 /// Lifting collapses the boilerplate into one method call the
20522 /// caller reads as intent (`mapping.insert_string(<KEY>, <VALUE>)`
20523 /// — "insert a string-scalar-typed field named `KEY` with rendered
20524 /// value `VALUE`") rather than four hand-spelled positional
20525 /// artifacts. The next renderer to land — the per-`:politicas`
20526 /// `CiliumClusterwideEnvoyConfig` emitter (whose per-policy string-
20527 /// scalar axes are `name` / `namespace` / `defaultAction`), the
20528 /// `app-operator`'s typed `mesh.pleme.io/v1alpha1/Aplicacao` CR
20529 /// materializer (per-`spec.selectors[]` `name` / per-`spec.gates[]`
20530 /// string-typed axes), the M4 cross-cluster fan-out's per-cluster
20531 /// `Service.spec.ports[].name` / `HTTPRoute.spec.rules[].filters[].
20532 /// requestHeaderModifier.set[].name` string-scalar emission, the
20533 /// future `caixa-otel` OpenTelemetry-Collector `pipelines.traces.
20534 /// receivers[].endpoint` string-scalar emission — gets the canonical
20535 /// string-scalar-valued-field shape for free with one method call,
20536 /// instead of re-inlining the three-token
20537 /// `Value::String(_.into() | .clone() | .to_string())` block.
20538 ///
20539 /// Peer to [`Self::insert_str_key`] on the sibling any-Value axis —
20540 /// the two together form the "one method call per emission axis"
20541 /// primitive pair the K8s-artifact-emit surface's "same shape,
20542 /// written N times" duplication (THEORY.md §I.3.5) collapses onto.
20543 fn insert_string<V: Into<String>>(&mut self, key: &str, value: V) -> Option<serde_yaml::Value>;
20544
20545 /// Insert `(key, Value::Number(value.into()))` into `self` — the
20546 /// integer-scalar-valued-field emission shape that combines
20547 /// [`Self::insert_str_key`]'s `&str → Value::String` key promotion
20548 /// with an automatic [`serde_yaml::Value::Number`] promotion of an
20549 /// `Into<serde_yaml::Number>` value. Returns the prior value at that
20550 /// key, mirroring [`serde_yaml::Mapping::insert`].
20551 ///
20552 /// The canonical shape 2 production call sites across `caixa-mesh`
20553 /// previously carried inline as the three-token block
20554 /// `mapping.insert_str_key(<KEY>, serde_yaml::Value::Number(<N>.into()))`
20555 /// — the two-token semantic payload (`<KEY>`, `<N>`) buried under
20556 /// three boilerplate axes (`serde_yaml::` path re-quote,
20557 /// `Value::Number(_)` promotion, the `<N>.into()` typed-integer →
20558 /// [`serde_yaml::Number`] coercion) around a numeric constant or
20559 /// typed field the caller already carries as `u16` / `u32` / `u64`.
20560 ///
20561 /// Sites lifted:
20562 ///
20563 /// * caixa-mesh's `gateway_routes` per-`Gateway` `spec.listeners[].port`
20564 /// external HTTP listener port (`KUBE_KEY_PORT` around the lifted
20565 /// [`crate::GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT`] `u16` const,
20566 /// cd60fde);
20567 /// * caixa-mesh's `gateway_routes` per-`HTTPRoute.spec.rules[].backendRefs[].port`
20568 /// backend-target Servico port (`KUBE_KEY_PORT` around the
20569 /// [`crate::AplicacaoSpec`]-side `entrada.port` `u16` field the
20570 /// `:entrada :port` typed slot flows through).
20571 ///
20572 /// Lifting collapses the boilerplate into one method call the
20573 /// caller reads as intent (`mapping.insert_number(<KEY>, <N>)` —
20574 /// "insert a numeric-scalar-typed field named `KEY` with the typed
20575 /// integer `N`") rather than three hand-spelled positional artifacts.
20576 /// The next renderer to land — the per-`:politicas`
20577 /// `CiliumClusterwideEnvoyConfig` emitter (whose per-policy
20578 /// integer-scalar axes are the Envoy circuit-breaker
20579 /// `maxRequests` / `maxPendingRequests` / `maxConnections` count
20580 /// fields and the Cilium ratelimit `requestPerUnit` field,
20581 /// MESH-COMPOSITION §III.2 #3), the `app-operator`'s typed
20582 /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer (per-`spec.
20583 /// selectors[]` integer-scored `weight` fields, §III.2 #5), the
20584 /// M4 cross-cluster fan-out's per-cluster
20585 /// `Service.spec.ports[].{port, targetPort, nodePort}` /
20586 /// `HTTPRoute.spec.rules[].backendRefs[].{port, weight}`
20587 /// integer-scalar emission, the future `caixa-otel`
20588 /// OpenTelemetry-Collector `service.pipelines.traces.receivers[].
20589 /// grpc.max_recv_msg_size_mib` integer-scalar emission — gets the
20590 /// canonical integer-scalar-valued-field shape for free with one
20591 /// method call, instead of re-inlining the three-token
20592 /// `Value::Number(_.into())` block.
20593 ///
20594 /// The `Into<serde_yaml::Number>` bound accepts every numeric
20595 /// primitive [`serde_yaml::Number`] declares `From` for
20596 /// (`i8`..=`i64`, `u8`..=`u64`, `f32`, `f64`) — the same coverage
20597 /// the two production sites reach through with their `u16` port
20598 /// fields and the same coverage every future numeric-scalar
20599 /// emission (the K8s `Service.spec.ports[].targetPort` `IntOrString`
20600 /// integer arm, the `HTTPRoute.spec.rules[].backendRefs[].weight`
20601 /// `int32` axis, the Envoy `maxRequests` `uint32` axis) reaches
20602 /// through with matching typed integer fields.
20603 ///
20604 /// Peer to [`Self::insert_string`] on the sibling string-scalar axis
20605 /// and to [`Self::insert_mapping`] / [`Self::insert_sequence`] on
20606 /// the sibling nested-Mapping / list-shape axes — the five together
20607 /// with [`Self::insert_str_key`] form the "one method call per
20608 /// emission axis" primitive quintuple the K8s-artifact-emit
20609 /// surface's "same shape, written N times" duplication (THEORY.md
20610 /// §I.3.5) collapses onto: `insert_str_key` for any-Value inserts,
20611 /// `insert_string` for the string-scalar-valued-field shape,
20612 /// `insert_number` for the integer-scalar-valued-field shape,
20613 /// `insert_mapping` for the nested-Mapping-valued-field shape,
20614 /// `insert_sequence` for the list-shape-valued-field shape.
20615 fn insert_number<N: Into<serde_yaml::Number>>(
20616 &mut self,
20617 key: &str,
20618 value: N,
20619 ) -> Option<serde_yaml::Value>;
20620
20621 /// Insert `(key, Value::Mapping(value))` into `self` — the
20622 /// nested-Mapping-valued-field emission shape that combines
20623 /// [`Self::insert_str_key`]'s `&str → Value::String` key promotion
20624 /// with an automatic [`serde_yaml::Value::Mapping`] promotion of a
20625 /// [`serde_yaml::Mapping`] value. Returns the prior value at that
20626 /// key, mirroring [`serde_yaml::Mapping::insert`].
20627 ///
20628 /// The canonical shape ~6 production call sites across the caixa-
20629 /// side renderer surface previously carried inline as the three-
20630 /// token block `mapping.insert_str_key(<KEY>,
20631 /// serde_yaml::Value::Mapping(<INNER>))` — a two-token semantic
20632 /// payload (`<KEY>`, `<INNER>`) buried under a two-axis boilerplate
20633 /// (`serde_yaml::` path re-quote, `Value::Mapping(_)` promotion)
20634 /// around a `Mapping` variable the caller already built.
20635 ///
20636 /// Sites lifted:
20637 ///
20638 /// * caixa-mesh's `cilium_network_policies` per-`toPorts[]`
20639 /// `rules:` L7-introspection sub-block (`KUBE_KEY_RULES` around
20640 /// the built `rules` Mapping);
20641 /// * caixa-mesh's `cilium_network_policies` per-`CiliumNetworkPolicy`
20642 /// `spec:` block (`KUBE_KEY_SPEC` around the built `policy_spec`
20643 /// Mapping);
20644 /// * caixa-mesh's `gateway_routes` per-`Gateway` `spec:` block
20645 /// (`KUBE_KEY_SPEC` around the built `g_spec` Mapping);
20646 /// * caixa-mesh's `gateway_routes` per-`HTTPRoute.spec.rules[]`
20647 /// `matches[].path:` sub-block (`GATEWAY_API_KEY_PATH` around the
20648 /// built `path_match` Mapping);
20649 /// * caixa-mesh's `gateway_routes` per-`HTTPRoute` `spec:` block
20650 /// (`KUBE_KEY_SPEC` around the built `r_spec` Mapping);
20651 /// * caixa-core's `kube_resource_skeleton` per-CR
20652 /// `metadata:` sub-block (`KUBE_KEY_METADATA` around the built
20653 /// `metadata_map` Mapping).
20654 ///
20655 /// Lifting collapses the boilerplate into one method call the
20656 /// caller reads as intent (`mapping.insert_mapping(<KEY>, <INNER>)`
20657 /// — "insert a nested-Mapping-typed sub-block named `KEY` with the
20658 /// built inner `INNER`") rather than three hand-spelled positional
20659 /// artifacts. The next renderer to land — the per-`:politicas`
20660 /// `CiliumClusterwideEnvoyConfig` emitter (whose per-policy
20661 /// nested-Mapping sub-blocks are `metadata:` / `spec:` /
20662 /// `spec.resources[]`), the `app-operator`'s typed
20663 /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer
20664 /// (per-`spec.selectors[]` and per-`spec.gates[]` sub-blocks), the
20665 /// M4 cross-cluster fan-out's per-cluster `Service.spec` /
20666 /// `HTTPRoute.spec` sub-block emission, the future `caixa-otel`
20667 /// OpenTelemetry-Collector per-pipeline `receivers:` /
20668 /// `processors:` / `exporters:` nested-Mapping emission — gets the
20669 /// canonical nested-Mapping-valued-field shape for free with one
20670 /// method call, instead of re-inlining the three-token
20671 /// `Value::Mapping(_)` promotion.
20672 ///
20673 /// Peer to [`Self::insert_string`] on the sibling scalar-value axis
20674 /// and [`Self::insert_sequence`] on the sibling list-shape axis —
20675 /// the four together with [`Self::insert_str_key`] form the "one
20676 /// method call per emission axis" primitive quadruple the K8s-
20677 /// artifact-emit surface's "same shape, written N times" duplication
20678 /// (THEORY.md §I.3.5) collapses onto: `insert_str_key` for any-Value
20679 /// inserts, `insert_string` for the string-scalar-valued-field
20680 /// shape, `insert_mapping` for the nested-Mapping-valued-field
20681 /// shape, `insert_sequence` for the list-shape-valued-field shape.
20682 fn insert_mapping(
20683 &mut self,
20684 key: &str,
20685 value: serde_yaml::Mapping,
20686 ) -> Option<serde_yaml::Value>;
20687
20688 /// Insert `(key, Value::Sequence(value))` into `self` — the
20689 /// list-shape-valued-field emission shape that combines
20690 /// [`Self::insert_str_key`]'s `&str → Value::String` key promotion
20691 /// with an automatic [`serde_yaml::Value::Sequence`] promotion of a
20692 /// pre-built `Vec<serde_yaml::Value>` value. Returns the prior
20693 /// value at that key, mirroring [`serde_yaml::Mapping::insert`].
20694 ///
20695 /// The canonical shape 4 production call sites across `caixa-mesh`
20696 /// previously carried inline as the three-token block
20697 /// `mapping.insert_str_key(<KEY>, serde_yaml::Value::Sequence(<VEC>))`
20698 /// — a two-token semantic payload (`<KEY>`, `<VEC>`) buried under a
20699 /// two-axis boilerplate (`serde_yaml::` path re-quote,
20700 /// `Value::Sequence(_)` promotion) around a `Vec<Value>` variable
20701 /// the caller already built.
20702 ///
20703 /// Sites lifted:
20704 ///
20705 /// * caixa-mesh's `cilium_network_policies` per-`CiliumNetworkPolicy`
20706 /// `spec.ingress[].fromEndpoints:` singleton-list (`CILIUM_KEY_FROM_ENDPOINTS`
20707 /// around a `vec![from_endpoint]` selector wrapper);
20708 /// * caixa-mesh's `cilium_network_policies` per-`CiliumNetworkPolicy`
20709 /// `spec.ingress[].toPorts:` list (`CILIUM_KEY_TO_PORTS` around the
20710 /// built `to_ports_seq` per-edge port-and-L7-rule vec);
20711 /// * caixa-mesh's `gateway_routes` per-`HTTPRoute` `spec.hostnames:`
20712 /// singleton-list (`GATEWAY_API_KEY_HOSTNAMES` around a
20713 /// `vec![Value::String(entrada.host…)]` host wrapper);
20714 /// * caixa-mesh's `gateway_routes` per-`HTTPRoute` `spec.rules:`
20715 /// list (`KUBE_KEY_RULES` around the built `rules` per-path
20716 /// match+backend+overlay vec).
20717 ///
20718 /// Lifting collapses the boilerplate into one method call the
20719 /// caller reads as intent (`mapping.insert_sequence(<KEY>, <VEC>)`
20720 /// — "insert a list-shape-typed sub-block named `KEY` with the built
20721 /// inner `VEC`") rather than three hand-spelled positional
20722 /// artifacts. The next renderer to land — the per-`:politicas`
20723 /// `CiliumClusterwideEnvoyConfig` emitter (whose per-policy
20724 /// list-shape sub-blocks are `spec.resources[]` / `spec.listeners[]`
20725 /// / `spec.virtualHosts[]`, MESH-COMPOSITION §III.2 #3), the
20726 /// `app-operator`'s typed `mesh.pleme.io/v1alpha1/Aplicacao` CR
20727 /// materializer (per-`spec.selectors[]` and per-`spec.gates[]`
20728 /// list-shape sub-blocks, §III.2 #5), the M4 cross-cluster fan-out's
20729 /// per-cluster `Service.spec.ports[]` /
20730 /// `HTTPRoute.spec.rules[].backendRefs[]` list emission, the future
20731 /// `caixa-otel` OpenTelemetry-Collector per-pipeline `receivers[]`
20732 /// / `processors[]` / `exporters[]` list emission — gets the
20733 /// canonical list-shape-valued-field shape for free with one method
20734 /// call, instead of re-inlining the three-token `Value::Sequence(_)`
20735 /// promotion.
20736 ///
20737 /// Peer to [`Self::insert_mapping`] on the sibling nested-Mapping
20738 /// axis and [`Self::insert_string`] on the sibling scalar-value axis
20739 /// — the four together with [`Self::insert_str_key`] form the "one
20740 /// method call per emission axis" primitive quadruple the K8s-
20741 /// artifact-emit surface's "same shape, written N times" duplication
20742 /// (THEORY.md §I.3.5) collapses onto: `insert_str_key` for any-Value
20743 /// inserts, `insert_string` for the string-scalar-valued-field
20744 /// shape, `insert_mapping` for the nested-Mapping-valued-field
20745 /// shape, `insert_sequence` for the list-shape-valued-field shape.
20746 ///
20747 /// Complementary to [`singleton_mapping_sequence`] on the peer
20748 /// singleton-list-shape axis: `singleton_mapping_sequence(m)` builds
20749 /// the sole-Mapping-element `Value::Sequence` payload;
20750 /// `insert_sequence(K, v)` inserts an already-built `Vec<Value>`
20751 /// payload under a schema key. A caller composing the two through
20752 /// [`Self::insert_singleton_mapping_sequence`] writes
20753 /// `mapping.insert_singleton_mapping_sequence(K, m)` for the
20754 /// singleton case (the sole element is a fresh Mapping); reach for
20755 /// `mapping.insert_sequence(K, v)` for the multi-element or
20756 /// non-Mapping-element case (the vec is built up per-iteration or
20757 /// wraps a non-Mapping scalar).
20758 fn insert_sequence(
20759 &mut self,
20760 key: &str,
20761 value: Vec<serde_yaml::Value>,
20762 ) -> Option<serde_yaml::Value>;
20763
20764 /// Insert `(key, Value::Sequence(vec![Value::Mapping(value)]))` into
20765 /// `self` — the singleton-Mapping-list-shape-valued-field emission
20766 /// shape that composes [`Self::insert_str_key`]'s
20767 /// `&str → Value::String` key promotion with the
20768 /// [`singleton_mapping_sequence`] helper's singleton-list wrap of a
20769 /// [`serde_yaml::Mapping`] payload. Returns the prior value at that
20770 /// key, mirroring [`serde_yaml::Mapping::insert`].
20771 ///
20772 /// The canonical shape 7 production call sites across `caixa-mesh`
20773 /// previously carried inline as the two-token composition
20774 /// `mapping.insert_str_key(<KEY>, singleton_mapping_sequence(<M>))`
20775 /// — a two-token semantic payload (`<KEY>`, `<M>`) buried under a
20776 /// two-symbol boilerplate (`insert_str_key(_, _)` +
20777 /// `singleton_mapping_sequence(_)`) that fully covers the axis: every
20778 /// site both wraps its per-call `Mapping` as the sole-element list
20779 /// value and inserts it under a schema key on an outer `Mapping`. A
20780 /// rebrand on either half — the outer key-scalar promotion axis
20781 /// migrating to a per-key typed `Value` variant, the singleton-list
20782 /// wrap migrating to a Server-Side-Apply-typed `Value::Tagged`
20783 /// per-CRD-list shape once K8s per-field ownership annotations reach
20784 /// the K8s Gateway API / Cilium NetworkPolicy CRD list schemas —
20785 /// would silently desynchronize one site while leaving the other six
20786 /// on the old shape.
20787 ///
20788 /// Sites lifted:
20789 ///
20790 /// * caixa-mesh's `cilium_network_policies` per-`toPorts[]` port
20791 /// entry `ports:` singleton-list (`CILIUM_KEY_PORTS` around the
20792 /// built `port_entry` Mapping);
20793 /// * caixa-mesh's `cilium_network_policies` per-`toPorts[]` L7
20794 /// `rules.http:` singleton-list (`CILIUM_KEY_HTTP` around the
20795 /// built `http_rule` Mapping);
20796 /// * caixa-mesh's `cilium_network_policies` per-`CiliumNetworkPolicy`
20797 /// `spec.ingress:` singleton-list (`CILIUM_KEY_INGRESS` around the
20798 /// built `ingress_rule` Mapping);
20799 /// * caixa-mesh's `gateway_routes` per-`Gateway` `spec.listeners:`
20800 /// singleton-list (`GATEWAY_API_KEY_LISTENERS` around the built
20801 /// `listener` Mapping);
20802 /// * caixa-mesh's `gateway_routes` per-`HTTPRoute.spec.rules[]`
20803 /// `matches:` singleton-list (`GATEWAY_API_KEY_MATCHES` around the
20804 /// built `match_entry` Mapping);
20805 /// * caixa-mesh's `gateway_routes` per-`HTTPRoute.spec.rules[]`
20806 /// `backendRefs:` singleton-list (`GATEWAY_API_KEY_BACKEND_REFS`
20807 /// around the built `backend_ref` Mapping);
20808 /// * caixa-mesh's `gateway_routes` per-`HTTPRoute`
20809 /// `spec.parentRefs:` singleton-list (`GATEWAY_API_KEY_PARENT_REFS`
20810 /// around the built `parent_ref` Mapping).
20811 ///
20812 /// Lifting collapses the two-symbol composition into one method call
20813 /// the caller reads as intent (`mapping.insert_singleton_mapping_sequence
20814 /// (<KEY>, <M>)` — "insert a singleton-Mapping-list-shape sub-block
20815 /// named `KEY` wrapping the built inner `M`") rather than two
20816 /// nested calls. Peer to [`Self::insert_sequence`] on the sibling
20817 /// multi-element or non-Mapping-element list-shape axis — the two
20818 /// together partition the list-shape-valued-field emission surface:
20819 /// [`Self::insert_singleton_mapping_sequence`] for the sole-Mapping-
20820 /// element case, [`Self::insert_sequence`] for every other case.
20821 ///
20822 /// The next renderer to land — the per-`:politicas`
20823 /// `CiliumClusterwideEnvoyConfig` emitter (whose singleton
20824 /// `spec.resources:[]` / `spec.listeners:[]` / `spec.virtualHosts:[]`
20825 /// Mapping-element blocks, MESH-COMPOSITION §III.2 #3, are exactly the
20826 /// singleton-Mapping-list shape), the `app-operator`'s typed
20827 /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer (per-single-
20828 /// selector / per-single-gate emission, §III.2 #5), the M4 cross-
20829 /// cluster fan-out's per-cluster singleton `Service.spec.ports[]` /
20830 /// `HTTPRoute.spec.rules[].backendRefs[]` sole-element emission, the
20831 /// future `caixa-otel` OpenTelemetry-Collector `pipelines.traces.
20832 /// receivers[]` singleton-receiver emission — gets the canonical
20833 /// singleton-Mapping-list-shape wrap+insert for free with one method
20834 /// call, instead of re-inlining the two-symbol composition.
20835 fn insert_singleton_mapping_sequence(
20836 &mut self,
20837 key: &str,
20838 value: serde_yaml::Mapping,
20839 ) -> Option<serde_yaml::Value>;
20840
20841 /// Entry-API sibling of [`Self::insert_str_key`] — mint the
20842 /// `Value::String(<KEY>.into())` key-promotion the underlying
20843 /// [`serde_yaml::Mapping::entry`] method's `Value` parameter
20844 /// demands, and return the entry-API's
20845 /// [`serde_yaml::mapping::Entry`] handle the caller composes
20846 /// `.or_insert(<V>)` / `.or_insert_with(<F>)` /
20847 /// `.and_modify(<F>)` / `.or_default()` on.
20848 ///
20849 /// The canonical shape 4 production call sites across `caixa-flux`
20850 /// previously carried inline as the three-token composition
20851 /// `mapping.entry(serde_yaml::Value::String(<KEY>.into()))` around
20852 /// a one-token semantic payload (the schema key axis-name). Every
20853 /// site immediately composes an `.or_insert(...)` on the returned
20854 /// [`serde_yaml::mapping::Entry`] handle — the pattern is the
20855 /// entry-API twin of the [`Self::insert_str_key`] pattern the
20856 /// ~48 fresh-emit sites already collapsed onto (23506b3).
20857 ///
20858 /// Sites lifted:
20859 ///
20860 /// * caixa-flux's `programs_yaml_entry` per-`servico_m2_overlay`
20861 /// key idempotent-upsert loop (`entry.entry(Value::String(
20862 /// <key>.to_string())).or_insert(<value>)` — one
20863 /// `.or_insert(...)` per `M2_KEY_LIMITS` / `M2_KEY_BEHAVIOR` /
20864 /// `M2_KEY_UPGRADE_FROM` axis, iterating the
20865 /// [`servico_m2_overlay`] `BTreeMap`);
20866 /// * caixa-flux's `upsert_into_helmrelease_programs` per-
20867 /// `HelmRelease.spec.values` upsert-if-absent (`FLUX_KEY_VALUES`
20868 /// around a default fresh `Value::Mapping`);
20869 /// * caixa-flux's `upsert_into_helmrelease_programs` per-
20870 /// `HelmRelease.spec.values.programs` upsert-if-absent
20871 /// (`FLEET_PROGRAMS_KEY_PROGRAMS` around a default fresh
20872 /// `Value::Sequence`);
20873 /// * caixa-flux's `upsert_into_programs_yaml` per-top-level
20874 /// `programs:` upsert-if-absent (`FLEET_PROGRAMS_KEY_PROGRAMS`
20875 /// around a default fresh `Value::Sequence` — the sibling of
20876 /// the `upsert_into_helmrelease_programs` site on the same
20877 /// key, one path deep in a HelmRelease `spec.values.` sub-tree,
20878 /// one path at the values.yaml root).
20879 ///
20880 /// Lifting collapses the three-token composition into one method
20881 /// call the caller reads as intent
20882 /// (`mapping.entry_str_key(<KEY>).or_insert(<DEFAULT>)` — "get the
20883 /// entry handle for this schema key and default it if missing")
20884 /// rather than four hand-spelled positional artifacts
20885 /// (`serde_yaml::` path re-quote, `Value::String(_)` promotion,
20886 /// the `.into() | .to_string()` `&str → String` coercion, plus the
20887 /// `.entry(_)` call itself). The next renderer to land — the
20888 /// per-`:politicas` `CiliumClusterwideEnvoyConfig` emitter (which
20889 /// upserts singleton `spec.resources:[]` / `spec.listeners:[]`
20890 /// blocks under an existing per-cluster overlay CR, MESH-COMPOSITION
20891 /// §III.2 #3), the `app-operator`'s typed
20892 /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer (which
20893 /// upserts `status.` sub-fields on partial reconciles, §III.2 #5),
20894 /// the M4 cross-cluster fan-out's per-cluster idempotent
20895 /// HelmRelease upsert — gets the canonical entry-API key-promotion
20896 /// for free with one method call, instead of re-inlining the
20897 /// three-token block.
20898 ///
20899 /// Peer to [`Self::insert_str_key`] on the sibling fresh-emit
20900 /// axis of the same `&str → Value::String` key-promotion — the
20901 /// two together partition the `Mapping`-write surface: entry-API
20902 /// for idempotent-upsert sites where the caller cares whether the
20903 /// prior value was present (`or_insert` / `and_modify` /
20904 /// `or_default` composition), insert-API for fresh-emit sites where
20905 /// the caller unconditionally writes a value and either drops or
20906 /// pattern-matches on the returned `Option<Value>` prior value.
20907 fn entry_str_key(&mut self, key: &str) -> serde_yaml::mapping::Entry<'_>;
20908
20909 /// Arity-0-or-1 twin of [`Self::insert_str_key`] — insert
20910 /// `(key, value.clone())` iff `value` is `Some`; leave `self`
20911 /// untouched iff `value` is `None`. Returns the prior value at that
20912 /// key when the insert fires (mirroring
20913 /// [`serde_yaml::Mapping::insert`]), and `None` otherwise (no insert
20914 /// happened, so no prior value can be surfaced).
20915 ///
20916 /// The canonical shape 3 production call sites across `caixa-mesh`
20917 /// previously carried inline as the three-line block
20918 /// `if let Some(<x>) = &<overlay> { <mapping>.insert_str_key(<KEY>,
20919 /// <x>.clone()); }` around a two-token semantic payload (the schema
20920 /// key axis-name + the `Option<Value>` overlay slot). Every site
20921 /// pairs a per-`:politicas` overlay [`single_field_overlay`] `Option
20922 /// <Value>` output with the same conditional-insert conditional —
20923 /// the arity-0-or-1 twin of [`Self::insert_str_key`]'s always-1
20924 /// arity on the per-`(:de, :para)` axis.
20925 ///
20926 /// Sites lifted:
20927 ///
20928 /// * caixa-mesh's `cilium_network_policies` per-ingress-rule
20929 /// `:politicas :mtls-required` mutual-auth overlay
20930 /// ([`crate::CILIUM_KEY_AUTHENTICATION`] around the
20931 /// `mtls_overlay` [`single_field_overlay`] output — the
20932 /// tristate `{mode: required | disabled}` block or the
20933 /// None-omit arm);
20934 /// * caixa-mesh's `gateway_routes` per-HTTPRoute-rule
20935 /// `:politicas :timeout` request-deadline overlay
20936 /// ([`crate::GATEWAY_API_KEY_TIMEOUTS`] around the
20937 /// `timeout_overlay` [`single_field_overlay`] output — the
20938 /// `{request: "<duration>"}` block or the None-omit arm);
20939 /// * caixa-mesh's `gateway_routes` per-HTTPRoute-rule
20940 /// `:politicas :retries` retry-attempt-cap overlay
20941 /// ([`crate::GATEWAY_API_KEY_RETRY`] around the
20942 /// `retry_overlay` [`single_field_overlay`] output — the
20943 /// `{attempts: <N>}` block or the None-omit arm).
20944 ///
20945 /// Lifting collapses the three-line block into one method call the
20946 /// caller reads as intent (`mapping.insert_str_key_if_some(<KEY>,
20947 /// <overlay>.as_ref())` — "insert this schema key if the overlay
20948 /// carried a value; else leave the key absent") rather than four
20949 /// hand-spelled positional artifacts (the `if let Some(_) = &_`
20950 /// destructure, the per-inner `.clone()`, the trailing brace, plus
20951 /// the `.insert_str_key(_)` call itself). The absent-overlay arm —
20952 /// which every [`MeshPolicy`] axis defaults to when the author
20953 /// leaves the typed slot unset (the `None` arm of the
20954 /// `Option<Value>` [`single_field_overlay`] output) — reads as the
20955 /// method's own `Option::None` branch, not a per-call-site inverted
20956 /// `if let Some` scaffold around a per-call-site clone.
20957 ///
20958 /// The next renderer to land — the per-`:politicas`
20959 /// `CiliumClusterwideEnvoyConfig` emitter (whose per-policy
20960 /// `authentication:` / `rateLimit:` / `circuitBreaker:` Option
20961 /// overlays, MESH-COMPOSITION §III.2 #3, thread through the same
20962 /// [`single_field_overlay`] `Option<Value>` axis the three lifted
20963 /// sites here already reach), the `app-operator`'s typed
20964 /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer (whose per-
20965 /// selector `status.` sub-field overlays are the same arity-0-or-1
20966 /// shape, §III.2 #5), the M4 cross-cluster fan-out's per-cluster
20967 /// `HTTPRoute.spec.rules[].filters[]` per-filter Option overlays
20968 /// (the same shape at the per-cluster axis) — gets the canonical
20969 /// arity-0-or-1 conditional-insert for free with one method call,
20970 /// instead of re-inlining the three-line `if let Some { clone;
20971 /// insert_str_key }` block.
20972 ///
20973 /// Peer to [`Self::insert_str_key`] on the always-1 arity axis
20974 /// (fresh-emit sites where the caller unconditionally writes a
20975 /// value) — the two together partition the fresh-emit surface
20976 /// exactly on the arity axis: [`Self::insert_str_key`] for
20977 /// unconditional writes, [`Self::insert_str_key_if_some`] for
20978 /// conditional writes gated on an `Option<Value>` upstream
20979 /// producer (the per-`:politicas` overlay
20980 /// [`single_field_overlay`] axis, and every future arity-0-or-1
20981 /// axis every future renderer's optional-slot machinery reaches
20982 /// through).
20983 ///
20984 /// The `Option<&Value>` shape (as opposed to an owned
20985 /// `Option<Value>`) lets the caller pass `overlay.as_ref()` on an
20986 /// owned `Option<Value>` the caller reuses across iterations of an
20987 /// outer per-`(:de, :para)` or per-rule loop — every lifted site
20988 /// consumes the overlay from a loop-outer binding into each of N
20989 /// per-iteration `Mapping`s, so the clone happens iff the insert
20990 /// fires (the None arm skips the clone entirely) and the outer
20991 /// binding stays available for the next iteration.
20992 fn insert_str_key_if_some(
20993 &mut self,
20994 key: &str,
20995 value: Option<&serde_yaml::Value>,
20996 ) -> Option<serde_yaml::Value>;
20997
20998 /// Fetch a `&mut serde_yaml::Mapping` at `key`, defaulting an empty
20999 /// [`serde_yaml::Mapping`] into place when the entry is absent.
21000 /// Returns `Some(&mut inner)` on the absent-key (fresh empty
21001 /// Mapping) and present-Mapping arms; `None` iff `key` holds a
21002 /// different [`serde_yaml::Value`] variant — a structural
21003 /// container-type mismatch the caller surfaces as its own
21004 /// domain-specific error (`Error::MissingField("spec.values must
21005 /// be a mapping")` for the caixa-flux Flux-HelmRelease overlay
21006 /// walker).
21007 ///
21008 /// The canonical shape 1 production call site in `caixa-flux`
21009 /// (`upsert_into_helmrelease_programs`'s per-`HelmRelease.spec.values`
21010 /// container-upsert on the way down to
21011 /// `spec.values.programs[]`) previously carried inline as a
21012 /// four-line block combining [`Self::entry_str_key`]'s entry-API
21013 /// key promotion (68d035e), an
21014 /// `.or_insert(Value::Mapping(Mapping::new()))` empty-Mapping
21015 /// default, and a `let Value::Mapping(inner) = _ else { Err(...) }`
21016 /// destructure — a two-token semantic payload (the schema key +
21017 /// the domain-specific type-mismatch diagnostic) buried under
21018 /// three boilerplate axes (`Value::Mapping(_)` variant promotion,
21019 /// `Mapping::new()` empty-container construction, the outer
21020 /// `let else` destructure). Peer to
21021 /// [`Self::entry_or_default_sequence`] on the sibling `Vec<Value>`-
21022 /// valued idempotent-container-upsert axis — the two together
21023 /// partition the entry-API-container-upsert surface exactly on the
21024 /// container-variant axis: [`Self::entry_or_default_mapping`] for
21025 /// nested-Mapping sub-blocks, [`Self::entry_or_default_sequence`]
21026 /// for list-shape sub-blocks.
21027 ///
21028 /// Sites lifted:
21029 ///
21030 /// * caixa-flux's `upsert_into_helmrelease_programs` per-
21031 /// `HelmRelease.spec.values` container-upsert
21032 /// (`FLUX_KEY_VALUES` around the default fresh
21033 /// `Value::Mapping`, on the way down to the nested
21034 /// `spec.values.programs[]` sequence).
21035 ///
21036 /// Lifting collapses the four-line block into one method call the
21037 /// caller reads as intent (`mapping.entry_or_default_mapping(<KEY>)
21038 /// .ok_or(<ERR>)?` — "give me the nested Mapping at this schema
21039 /// key, defaulting empty if absent, else surface my domain
21040 /// error") rather than five hand-spelled positional artifacts
21041 /// (`serde_yaml::` path re-quote, `Value::Mapping(_)` promotion,
21042 /// `Mapping::new()` construction, the entry-API `.or_insert(...)`
21043 /// call, plus the outer `let Value::Mapping(_) = _ else {}`
21044 /// destructure). The next renderer to land — the per-`:politicas`
21045 /// `CiliumClusterwideEnvoyConfig` emitter (whose per-cluster
21046 /// upsert walks
21047 /// `HelmRelease.spec.values.<library>.<:politicas-axis>`,
21048 /// idempotent-upserting nested-Mapping sub-blocks under each
21049 /// axis, MESH-COMPOSITION §III.2 #3), the `app-operator`'s typed
21050 /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer (which
21051 /// upserts `status.<axis>` nested-Mapping sub-blocks on partial
21052 /// reconciles, §III.2 #5), the M4 cross-cluster fan-out's
21053 /// per-cluster idempotent `HelmRelease.spec.values.<library>`
21054 /// container-upsert — gets the canonical entry-API-with-
21055 /// container-type-check for free with one method call, instead
21056 /// of re-inlining the four-line block.
21057 ///
21058 /// The default-empty-Mapping construction fires only on the
21059 /// absent-key arm (`.or_insert_with(...)` gates the closure on
21060 /// vacancy) — the present-key arm reuses the existing Mapping
21061 /// verbatim, so the caller's downstream writes on `&mut inner`
21062 /// compose with any prior overlay writes from earlier passes
21063 /// (the exact idempotent-upsert semantic the caixa-flux
21064 /// per-cluster `feira app deploy` write path depends on to
21065 /// preserve operator-pinned overlays across re-renders).
21066 fn entry_or_default_mapping(&mut self, key: &str) -> Option<&mut serde_yaml::Mapping>;
21067
21068 /// Fetch a `&mut Vec<serde_yaml::Value>` at `key`, defaulting an
21069 /// empty [`Vec<serde_yaml::Value>`] into place when the entry is
21070 /// absent. Returns `Some(&mut inner)` on the absent-key (fresh
21071 /// empty Sequence) and present-Sequence arms; `None` iff `key`
21072 /// holds a different [`serde_yaml::Value`] variant — a structural
21073 /// container-type mismatch the caller surfaces as its own
21074 /// domain-specific error (`Error::MissingField("programs must be
21075 /// a sequence")` for the caixa-flux fleet-programs upsert
21076 /// walkers).
21077 ///
21078 /// The canonical shape 2 production call sites in `caixa-flux`
21079 /// (`upsert_into_helmrelease_programs`'s per-
21080 /// `HelmRelease.spec.values.programs` container-upsert and
21081 /// `upsert_into_programs_yaml`'s top-level `programs:` container-
21082 /// upsert) previously carried inline as a four-line block
21083 /// combining [`Self::entry_str_key`]'s entry-API key promotion
21084 /// (68d035e), an `.or_insert(Value::Sequence(Vec::new()))`
21085 /// empty-Sequence default, and a `match _ { Value::Sequence(seq)
21086 /// => seq, _ => return Err(...) }` destructure — a two-token
21087 /// semantic payload (the schema key + the domain-specific
21088 /// type-mismatch diagnostic) buried under three boilerplate axes
21089 /// (`Value::Sequence(_)` variant promotion, `Vec::new()`
21090 /// empty-container construction, the outer `match` destructure).
21091 /// Peer to [`Self::entry_or_default_mapping`] on the sibling
21092 /// nested-Mapping-valued idempotent-container-upsert axis.
21093 ///
21094 /// Sites lifted:
21095 ///
21096 /// * caixa-flux's `upsert_into_helmrelease_programs` per-
21097 /// `HelmRelease.spec.values.programs` list-container-upsert
21098 /// (`FLEET_PROGRAMS_KEY_PROGRAMS` around the default fresh
21099 /// `Value::Sequence`, one path deep in a `HelmRelease`
21100 /// `spec.values.` sub-tree);
21101 /// * caixa-flux's `upsert_into_programs_yaml` per-top-level
21102 /// `programs:` list-container-upsert
21103 /// (`FLEET_PROGRAMS_KEY_PROGRAMS` around the default fresh
21104 /// `Value::Sequence` — the sibling of the
21105 /// `upsert_into_helmrelease_programs` site on the same key,
21106 /// one path at the values.yaml root).
21107 ///
21108 /// Lifting collapses the four-line block into one method call the
21109 /// caller reads as intent (`mapping.entry_or_default_sequence(<KEY>)
21110 /// .ok_or(<ERR>)?` — "give me the list at this schema key,
21111 /// defaulting empty if absent, else surface my domain error")
21112 /// rather than five hand-spelled positional artifacts
21113 /// (`serde_yaml::` path re-quote, `Value::Sequence(_)` promotion,
21114 /// `Vec::new()` construction, the entry-API `.or_insert(...)`
21115 /// call, plus the outer `match { Value::Sequence(_) => _, _ =>
21116 /// return Err(_) }` destructure). The next renderer to land — the
21117 /// per-`:politicas` `CiliumClusterwideEnvoyConfig` emitter
21118 /// (whose per-cluster upsert walks nested list-shape sub-blocks
21119 /// `spec.resources[]` / `spec.listeners[]` / `spec.virtualHosts[]`
21120 /// under existing operator-pinned overlay CRs, MESH-COMPOSITION
21121 /// §III.2 #3), the `app-operator`'s typed
21122 /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer (which
21123 /// upserts `status.selectors[]` / `status.gates[]` list-shape
21124 /// sub-blocks on partial reconciles, §III.2 #5), the M4 cross-
21125 /// cluster fan-out's per-cluster idempotent
21126 /// `HelmRelease.spec.values.programs` list-upsert — gets the
21127 /// canonical entry-API-with-container-type-check for free with
21128 /// one method call, instead of re-inlining the four-line block.
21129 ///
21130 /// The default-empty-Sequence construction fires only on the
21131 /// absent-key arm (`.or_insert_with(...)` gates the closure on
21132 /// vacancy) — the present-key arm reuses the existing Vec
21133 /// verbatim, so the caller's downstream `upsert_named_entry`
21134 /// (10bf310) call on `&mut inner` composes with any prior
21135 /// entries the emitter wrote on earlier passes (the exact
21136 /// idempotent-upsert semantic the `feira app deploy` per-cluster
21137 /// write path depends on to preserve prior `programs[]` entries
21138 /// across per-Servico rewrites).
21139 fn entry_or_default_sequence(&mut self, key: &str) -> Option<&mut Vec<serde_yaml::Value>>;
21140}
21141
21142impl MappingExt for serde_yaml::Mapping {
21143 #[inline]
21144 fn insert_str_key(&mut self, key: &str, value: serde_yaml::Value) -> Option<serde_yaml::Value> {
21145 self.insert(serde_yaml::Value::String(key.to_string()), value)
21146 }
21147
21148 #[inline]
21149 fn insert_string<V: Into<String>>(&mut self, key: &str, value: V) -> Option<serde_yaml::Value> {
21150 self.insert_str_key(key, serde_yaml::Value::String(value.into()))
21151 }
21152
21153 #[inline]
21154 fn insert_number<N: Into<serde_yaml::Number>>(
21155 &mut self,
21156 key: &str,
21157 value: N,
21158 ) -> Option<serde_yaml::Value> {
21159 self.insert_str_key(key, serde_yaml::Value::Number(value.into()))
21160 }
21161
21162 #[inline]
21163 fn insert_mapping(
21164 &mut self,
21165 key: &str,
21166 value: serde_yaml::Mapping,
21167 ) -> Option<serde_yaml::Value> {
21168 self.insert_str_key(key, serde_yaml::Value::Mapping(value))
21169 }
21170
21171 #[inline]
21172 fn insert_sequence(
21173 &mut self,
21174 key: &str,
21175 value: Vec<serde_yaml::Value>,
21176 ) -> Option<serde_yaml::Value> {
21177 self.insert_str_key(key, serde_yaml::Value::Sequence(value))
21178 }
21179
21180 #[inline]
21181 fn insert_singleton_mapping_sequence(
21182 &mut self,
21183 key: &str,
21184 value: serde_yaml::Mapping,
21185 ) -> Option<serde_yaml::Value> {
21186 self.insert_str_key(key, singleton_mapping_sequence(value))
21187 }
21188
21189 #[inline]
21190 fn entry_str_key(&mut self, key: &str) -> serde_yaml::mapping::Entry<'_> {
21191 self.entry(serde_yaml::Value::String(key.to_string()))
21192 }
21193
21194 #[inline]
21195 fn insert_str_key_if_some(
21196 &mut self,
21197 key: &str,
21198 value: Option<&serde_yaml::Value>,
21199 ) -> Option<serde_yaml::Value> {
21200 value.and_then(|v| self.insert_str_key(key, v.clone()))
21201 }
21202
21203 #[inline]
21204 fn entry_or_default_mapping(&mut self, key: &str) -> Option<&mut serde_yaml::Mapping> {
21205 match self
21206 .entry_str_key(key)
21207 .or_insert_with(|| serde_yaml::Value::Mapping(serde_yaml::Mapping::new()))
21208 {
21209 serde_yaml::Value::Mapping(m) => Some(m),
21210 _ => None,
21211 }
21212 }
21213
21214 #[inline]
21215 fn entry_or_default_sequence(&mut self, key: &str) -> Option<&mut Vec<serde_yaml::Value>> {
21216 match self
21217 .entry_str_key(key)
21218 .or_insert_with(|| serde_yaml::Value::Sequence(Vec::new()))
21219 {
21220 serde_yaml::Value::Sequence(s) => Some(s),
21221 _ => None,
21222 }
21223 }
21224}
21225
21226/// Extension methods for the [`Vec<serde_yaml::Value>`] emission
21227/// surface that the K8s-artifact-emit sites of `caixa-mesh` /
21228/// `caixa-flux` / `caixa-helm` / `caixa-core::render` build up as
21229/// `spec.ingress[]` / `spec.rules[]` / `spec.hostnames[]` / per-
21230/// programs.yaml-entry payloads before wrapping each vec as a
21231/// [`serde_yaml::Value::Sequence`] on an outer [`serde_yaml::Mapping`]
21232/// (via [`MappingExt::insert_sequence`]).
21233///
21234/// Peer to [`MappingExt`] on the sibling [`serde_yaml::Value`]-
21235/// construction surface: [`MappingExt`] closes the per-key-and-value
21236/// insert primitive every schema-key axis reaches through;
21237/// [`SequenceExt`] closes the per-list-element push primitive every
21238/// per-iteration append site reaches through when the built-up
21239/// [`serde_yaml::Value`] variant is uniform across a loop body (e.g.
21240/// every element is a fresh [`serde_yaml::Value::Mapping`], not a
21241/// heterogeneous mix of `Mapping` / `String` / `Sequence`).
21242///
21243/// Each method mints the same `Value::<Variant>(<payload>)` promotion
21244/// the caller would otherwise re-inline as
21245/// `vec.push(serde_yaml::Value::<Variant>(<payload>))` on every
21246/// iteration. Same variant-promotion contract as [`MappingExt`]'s
21247/// typed inserts, applied to the sequence-append axis instead of the
21248/// mapping-insert axis — so a future rebrand of the `Value` variant
21249/// wrapping (e.g. to a Server-Side-Apply-typed
21250/// [`serde_yaml::Value::Tagged`] per-list-element ownership axis)
21251/// reaches both `Mapping`-insert and `Vec<Value>`-push sites through
21252/// one lift.
21253pub trait SequenceExt {
21254 /// Append `Value::Mapping(value)` to `self` — the per-iteration
21255 /// append shape that combines a `Vec<serde_yaml::Value>::push`
21256 /// with an automatic [`serde_yaml::Value::Mapping`] promotion of a
21257 /// pre-built [`serde_yaml::Mapping`] element.
21258 ///
21259 /// The canonical shape 4 production call sites across `caixa-mesh`
21260 /// previously carried inline as the three-token block
21261 /// `<vec>.push(serde_yaml::Value::Mapping(<M>))` — a one-token
21262 /// semantic payload (the per-iteration `Mapping`) buried under a
21263 /// two-axis boilerplate (`serde_yaml::` path re-quote,
21264 /// `Value::Mapping(_)` promotion) around a `Mapping` variable the
21265 /// caller already built.
21266 ///
21267 /// Sites lifted:
21268 ///
21269 /// * caixa-mesh's `programs_for_aplicacao` per-`:membros`
21270 /// programs.yaml entry append (per-member entry `Mapping` →
21271 /// the fan-out `Vec<Value>`);
21272 /// * caixa-mesh's `cilium_network_policies` per-edge
21273 /// `spec.ingress[].toPorts[]` L4-and-L7 port-and-rule append
21274 /// (per-`(:de, :para)` group's per-edge `to_port` Mapping →
21275 /// the `to_ports_seq` Vec);
21276 /// * caixa-mesh's `cilium_network_policies` per-policy
21277 /// top-level CNP-document append (per-`(:de, :para)` group's
21278 /// built `policy` Mapping → the render-output `Vec<Value>`);
21279 /// * caixa-mesh's `gateway_routes` per-HTTPRoute-rule
21280 /// `spec.rules[]` append (per-path built `rule` Mapping → the
21281 /// `rules` Vec).
21282 ///
21283 /// Lifting collapses the three-token block into one method call
21284 /// the caller reads as intent (`<vec>.push_mapping(<M>)` —
21285 /// "append this built inner `M` as the next `Value::Mapping`
21286 /// element") rather than three hand-spelled positional artifacts
21287 /// (`serde_yaml::` path re-quote, `Value::Mapping(_)` promotion,
21288 /// plus the `.push(_)` call itself). Peer to
21289 /// [`MappingExt::insert_singleton_mapping_sequence`] on the
21290 /// singleton-Mapping-list-shape axis: [`Self::push_mapping`]
21291 /// builds up a multi-element `Vec<Value>` per iteration when the
21292 /// caller then calls [`MappingExt::insert_sequence`] to route the
21293 /// finished vec under a schema key;
21294 /// [`MappingExt::insert_singleton_mapping_sequence`] fuses the
21295 /// singleton wrap + the schema-key insert into one call when the
21296 /// caller has exactly one Mapping element to emit under a schema
21297 /// key.
21298 ///
21299 /// The next renderer to land — the per-`:politicas`
21300 /// `CiliumClusterwideEnvoyConfig` emitter (whose per-policy
21301 /// `spec.resources[]` / `spec.listeners[]` / `spec.virtualHosts[]`
21302 /// list-shape axes fan out multi-Mapping-element per iteration,
21303 /// MESH-COMPOSITION §III.2 #3), the `app-operator`'s typed
21304 /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer (per-
21305 /// `spec.selectors[]` / per-`spec.gates[]` multi-element append,
21306 /// §III.2 #5), the M4 cross-cluster fan-out's per-cluster
21307 /// multi-entry `Service.spec.ports[]` /
21308 /// `HTTPRoute.spec.rules[].backendRefs[]` list append, the future
21309 /// `caixa-otel` OpenTelemetry-Collector per-pipeline
21310 /// `receivers[]` / `processors[]` / `exporters[]` multi-element
21311 /// append — gets the canonical `Value::Mapping`-promoted append
21312 /// for free with one method call, instead of re-inlining the
21313 /// three-token `Value::Mapping(_)` promotion.
21314 fn push_mapping(&mut self, value: serde_yaml::Mapping);
21315}
21316
21317impl SequenceExt for Vec<serde_yaml::Value> {
21318 #[inline]
21319 fn push_mapping(&mut self, value: serde_yaml::Mapping) {
21320 self.push(serde_yaml::Value::Mapping(value));
21321 }
21322}
21323
21324#[cfg(test)]
21325mod tests {
21326 use super::*;
21327 use crate::{BehaviorSpec, CaixaKind, LimitsSpec, UpgradeFromEntry, UpgradeInstruction};
21328 use std::path::PathBuf;
21329 use std::time::Duration;
21330
21331 fn bare_servico() -> Caixa {
21332 Caixa {
21333 nome: "hello-rio".into(),
21334 versao: "0.1.0".into(),
21335 kind: CaixaKind::Servico,
21336 edicao: Some("2026".into()),
21337 descricao: None,
21338 repositorio: None,
21339 licenca: None,
21340 autores: vec![],
21341 etiquetas: vec![],
21342 deps: vec![],
21343 deps_dev: vec![],
21344 exe: vec![],
21345 bibliotecas: vec![],
21346 servicos: vec!["servicos/hello-rio.computeunit.yaml".into()],
21347 limits: None,
21348 behavior: None,
21349 upgrade_from: vec![],
21350 estrategia: None,
21351 max_restarts: None,
21352 restart_window: None,
21353 children: vec![],
21354 membros: vec![],
21355 contratos: vec![],
21356 politicas: None,
21357 placement: None,
21358 entrada: None,
21359 ci: None,
21360 }
21361 }
21362
21363 #[test]
21364 fn empty_caixa_returns_empty_overlay() {
21365 let overlay = servico_m2_overlay(&bare_servico()).unwrap();
21366 assert!(
21367 overlay.is_empty(),
21368 "a Caixa with no M2 slots emits zero overlay fragments"
21369 );
21370 }
21371
21372 #[test]
21373 fn empty_typed_specs_are_skipped_like_unset_ones() {
21374 // `Some(LimitsSpec::default())` (every axis None) and
21375 // `Some(BehaviorSpec::default())` (every callback None) must
21376 // round-trip identical to `None` — the is_empty()-skip
21377 // invariant the renderers' "empty M2 slots do not appear"
21378 // tests pinned inline before this lift.
21379 let mut c = bare_servico();
21380 c.limits = Some(LimitsSpec::default());
21381 c.behavior = Some(BehaviorSpec::default());
21382 let overlay = servico_m2_overlay(&c).unwrap();
21383 assert!(overlay.is_empty());
21384 }
21385
21386 #[test]
21387 fn limits_slot_appears_under_camelcase_key() {
21388 let mut c = bare_servico();
21389 c.limits = Some(LimitsSpec {
21390 memory: Some(64 * 1024 * 1024),
21391 fuel: Some(1_000_000),
21392 wall_clock: Some(Duration::from_secs(30)),
21393 cpu: Some(500),
21394 });
21395 let overlay = servico_m2_overlay(&c).unwrap();
21396 assert_eq!(overlay.len(), 1);
21397 let limits = overlay.get(M2_KEY_LIMITS).expect("limits key present");
21398 assert_eq!(
21399 limits.get(M2_LIMITS_KEY_MEMORY).and_then(|m| m.as_str()),
21400 Some("64MiB")
21401 );
21402 assert_eq!(
21403 limits
21404 .get(M2_LIMITS_KEY_WALL_CLOCK)
21405 .and_then(|m| m.as_str()),
21406 Some("30s")
21407 );
21408 }
21409
21410 #[test]
21411 fn behavior_slot_appears_under_camelcase_key() {
21412 let mut c = bare_servico();
21413 c.behavior = Some(BehaviorSpec {
21414 on_init: Some(PathBuf::from("lib/init.lisp")),
21415 on_call: Some(PathBuf::from("lib/handlers.lisp")),
21416 ..Default::default()
21417 });
21418 let overlay = servico_m2_overlay(&c).unwrap();
21419 let behavior = overlay.get(M2_KEY_BEHAVIOR).expect("behavior key present");
21420 assert_eq!(
21421 behavior
21422 .get(M2_BEHAVIOR_KEY_ON_INIT)
21423 .and_then(|v| v.as_str()),
21424 Some("lib/init.lisp")
21425 );
21426 assert_eq!(
21427 behavior
21428 .get(M2_BEHAVIOR_KEY_ON_CALL)
21429 .and_then(|v| v.as_str()),
21430 Some("lib/handlers.lisp")
21431 );
21432 }
21433
21434 #[test]
21435 fn upgrade_from_slot_appears_under_camelcase_key() {
21436 let mut c = bare_servico();
21437 c.upgrade_from = vec![UpgradeFromEntry {
21438 from: "0.0.9".into(),
21439 instructions: vec![UpgradeInstruction::LoadModule {
21440 module: "hello-rio".into(),
21441 }],
21442 }];
21443 let overlay = servico_m2_overlay(&c).unwrap();
21444 let upgrade = overlay
21445 .get(M2_KEY_UPGRADE_FROM)
21446 .expect("upgradeFrom key present");
21447 let arr = upgrade.as_sequence().expect("sequence");
21448 assert_eq!(arr.len(), 1);
21449 assert_eq!(
21450 arr[0]
21451 .get(M2_UPGRADE_FROM_KEY_FROM)
21452 .and_then(|v| v.as_str()),
21453 Some("0.0.9")
21454 );
21455 }
21456
21457 #[test]
21458 fn all_three_slots_appear_in_alphabetical_iteration_order() {
21459 // BTreeMap iteration is sorted by key — pin that the renderers
21460 // can rely on a deterministic iteration order, which feeds
21461 // into deterministic YAML output (the value-as-proof property
21462 // THEORY.md §V.2.7 "render determinism" requires).
21463 let mut c = bare_servico();
21464 c.limits = Some(LimitsSpec {
21465 memory: Some(64 * 1024 * 1024),
21466 ..Default::default()
21467 });
21468 c.behavior = Some(BehaviorSpec {
21469 on_init: Some(PathBuf::from("lib/init.lisp")),
21470 ..Default::default()
21471 });
21472 c.upgrade_from = vec![UpgradeFromEntry {
21473 from: "0.0.9".into(),
21474 instructions: vec![UpgradeInstruction::LoadModule {
21475 module: "hello-rio".into(),
21476 }],
21477 }];
21478 let overlay = servico_m2_overlay(&c).unwrap();
21479 let keys: Vec<_> = overlay.keys().copied().collect();
21480 assert_eq!(
21481 keys,
21482 vec![M2_KEY_BEHAVIOR, M2_KEY_LIMITS, M2_KEY_UPGRADE_FROM]
21483 );
21484 }
21485
21486 // ── servico_spec_and_m2_overlay_entries — composed splice ────────────
21487 //
21488 // The compound peer of `servico_m2_overlay` on the ComputeUnit-YAML
21489 // `spec.*` + M2-overlay axis: fuses the two prior inline for-loops
21490 // caixa-flux::programs_yaml_entry and caixa-helm::build_values_yaml
21491 // both carried around `string_keyed_entries` + `servico_m2_overlay`
21492 // into one canonical composition. The pins below bracket the shape
21493 // end-to-end (spec.* keys first + preserved-insertion-order, then M2
21494 // slots in BTreeMap-key order at every M2 key not already claimed by
21495 // spec.*).
21496
21497 fn cu_yaml_with_spec_fields(spec_yaml: &str) -> serde_yaml::Value {
21498 serde_yaml::from_str(&format!(
21499 "apiVersion: wasm.pleme.io/v1alpha1\nkind: ComputeUnit\nmetadata:\n name: hello-rio\nspec:\n{spec_yaml}"
21500 ))
21501 .unwrap()
21502 }
21503
21504 #[test]
21505 fn servico_spec_and_m2_overlay_entries_empty_caixa_and_empty_spec_yields_empty() {
21506 let cu = cu_yaml_with_spec_fields(" {}\n");
21507 let spec = cu.get(KUBE_KEY_SPEC).unwrap();
21508 let out = servico_spec_and_m2_overlay_entries(&bare_servico(), spec).unwrap();
21509 assert!(
21510 out.is_empty(),
21511 "empty spec + empty M2 surface yields zero entries \
21512 (both loops short-circuit vacuously)"
21513 );
21514 }
21515
21516 #[test]
21517 fn servico_spec_and_m2_overlay_entries_splices_spec_fields_in_source_insertion_order() {
21518 // The spec.* field-splice loop preserves the source YAML
21519 // Mapping's insertion order — caixa-flux's `serde_yaml::Mapping`
21520 // target reads this back verbatim, so a rebrand of the source
21521 // ComputeUnit YAML's field ordering must not silently reorder
21522 // the emitted programs.yaml entry.
21523 let cu = cu_yaml_with_spec_fields(
21524 " module:\n source: oci://ghcr.io/pleme-io/hello-rio:v0.1.0\n \
21525 trigger:\n service: {port: 8080}\n capabilities:\n - env\n",
21526 );
21527 let spec = cu.get(KUBE_KEY_SPEC).unwrap();
21528 let out = servico_spec_and_m2_overlay_entries(&bare_servico(), spec).unwrap();
21529 let keys: Vec<&str> = out.iter().map(|(k, _)| k.as_str()).collect();
21530 assert_eq!(
21531 keys,
21532 vec![
21533 COMPUTEUNIT_SPEC_KEY_MODULE,
21534 COMPUTEUNIT_SPEC_KEY_TRIGGER,
21535 COMPUTEUNIT_SPEC_KEY_CAPABILITIES,
21536 ],
21537 "spec.* keys must appear in source-Mapping insertion order",
21538 );
21539 }
21540
21541 #[test]
21542 fn servico_spec_and_m2_overlay_entries_appends_m2_slots_after_spec_in_canonical_key_order() {
21543 // Bracket the second-half of the composition — the M2 overlay
21544 // walk lands after the spec.* splice, in BTreeMap-key ordering
21545 // (behavior → limits → upgradeFrom).
21546 let cu = cu_yaml_with_spec_fields(
21547 " module:\n source: oci://ghcr.io/pleme-io/hello-rio:v0.1.0\n",
21548 );
21549 let spec = cu.get(KUBE_KEY_SPEC).unwrap();
21550 let mut c = bare_servico();
21551 c.limits = Some(LimitsSpec {
21552 memory: Some(64 * 1024 * 1024),
21553 ..Default::default()
21554 });
21555 c.behavior = Some(BehaviorSpec {
21556 on_init: Some(PathBuf::from("lib/init.lisp")),
21557 ..Default::default()
21558 });
21559 c.upgrade_from = vec![UpgradeFromEntry {
21560 from: "0.0.9".into(),
21561 instructions: vec![UpgradeInstruction::LoadModule {
21562 module: "hello-rio".into(),
21563 }],
21564 }];
21565 let out = servico_spec_and_m2_overlay_entries(&c, spec).unwrap();
21566 let keys: Vec<&str> = out.iter().map(|(k, _)| k.as_str()).collect();
21567 assert_eq!(
21568 keys,
21569 vec![
21570 COMPUTEUNIT_SPEC_KEY_MODULE,
21571 M2_KEY_BEHAVIOR,
21572 M2_KEY_LIMITS,
21573 M2_KEY_UPGRADE_FROM,
21574 ],
21575 "M2 slots must land after the spec.* splice, in canonical \
21576 BTreeMap key order",
21577 );
21578 }
21579
21580 #[test]
21581 fn servico_spec_and_m2_overlay_entries_or_insert_precedence_spec_wins_on_collision() {
21582 // The or_insert precedence rule the two prior inline blocks
21583 // shared: when the ComputeUnit YAML's `spec.*` sub-mapping
21584 // already carries the M2 slot's key (an author-authored
21585 // ComputeUnit `spec.limits` overriding the manifest-derived
21586 // `caixa.limits` overlay), the spec.* value stays and the M2
21587 // overlay's value is skipped. Regression-guards against a
21588 // future reversal ("M2 wins on collision") silently changing
21589 // the composition without an explicit slot-precedence flip at
21590 // the helper.
21591 let cu = cu_yaml_with_spec_fields(
21592 " limits:\n memory: from-spec\n module:\n source: oci://x\n",
21593 );
21594 let spec = cu.get(KUBE_KEY_SPEC).unwrap();
21595 let mut c = bare_servico();
21596 c.limits = Some(LimitsSpec {
21597 memory: Some(64 * 1024 * 1024),
21598 ..Default::default()
21599 });
21600 let out = servico_spec_and_m2_overlay_entries(&c, spec).unwrap();
21601 let limits_entries: Vec<&(String, serde_yaml::Value)> =
21602 out.iter().filter(|(k, _)| k == M2_KEY_LIMITS).collect();
21603 assert_eq!(
21604 limits_entries.len(),
21605 1,
21606 "on collision the M2 overlay's `limits` entry must be \
21607 filtered out — spec.* wins, and appears exactly once",
21608 );
21609 assert_eq!(
21610 limits_entries[0]
21611 .1
21612 .get(M2_LIMITS_KEY_MEMORY)
21613 .and_then(|v| v.as_str()),
21614 Some("from-spec"),
21615 "the surviving `limits` entry must carry the spec.* value, \
21616 not the manifest-derived M2 overlay's value",
21617 );
21618 }
21619
21620 #[test]
21621 fn servico_spec_and_m2_overlay_entries_short_circuits_on_non_mapping_spec() {
21622 // Sibling `string_keyed_entries` docstring pins the
21623 // non-Mapping short-circuit; extend it to the composed splice
21624 // — a spec that isn't a Mapping yields zero spec.* entries,
21625 // and only the M2 overlay contributes. Bracket-guard against a
21626 // future refactor that swaps `string_keyed_entries` for a
21627 // stricter parser silently dropping the M2 half too.
21628 let non_mapping_spec = serde_yaml::Value::String("not-a-mapping".into());
21629 let mut c = bare_servico();
21630 c.limits = Some(LimitsSpec {
21631 memory: Some(64 * 1024 * 1024),
21632 ..Default::default()
21633 });
21634 let out = servico_spec_and_m2_overlay_entries(&c, &non_mapping_spec).unwrap();
21635 let keys: Vec<&str> = out.iter().map(|(k, _)| k.as_str()).collect();
21636 assert_eq!(
21637 keys,
21638 vec![M2_KEY_LIMITS],
21639 "non-Mapping spec short-circuits the spec.* splice; the M2 \
21640 overlay still contributes its filled slots",
21641 );
21642 }
21643
21644 #[test]
21645 fn servico_spec_and_m2_overlay_entries_matches_hand_written_composition() {
21646 // Cross-check the lifted composition against the hand-written
21647 // two-loop shape the two prior inline blocks carried. A drift
21648 // between the helper and the inline composition would silently
21649 // emit a different key set / ordering / precedence at every
21650 // routed renderer — pin the equivalence so the helper stays a
21651 // drop-in replacement for both.
21652 let cu = cu_yaml_with_spec_fields(
21653 " module:\n source: oci://ghcr.io/pleme-io/hello-rio:v0.1.0\n \
21654 trigger:\n service: {port: 8080}\n",
21655 );
21656 let spec = cu.get(KUBE_KEY_SPEC).unwrap();
21657 let mut c = bare_servico();
21658 c.limits = Some(LimitsSpec {
21659 memory: Some(32 * 1024 * 1024),
21660 ..Default::default()
21661 });
21662 c.behavior = Some(BehaviorSpec {
21663 on_call: Some(PathBuf::from("lib/handlers.lisp")),
21664 ..Default::default()
21665 });
21666
21667 let via_helper = servico_spec_and_m2_overlay_entries(&c, spec).unwrap();
21668
21669 let mut via_inline: Vec<(String, serde_yaml::Value)> = Vec::new();
21670 let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
21671 for (k, v) in string_keyed_entries(spec) {
21672 seen.insert(k.to_string());
21673 via_inline.push((k.to_string(), v.clone()));
21674 }
21675 for (key, value) in servico_m2_overlay(&c).unwrap() {
21676 if !seen.contains(key) {
21677 via_inline.push((key.to_string(), value));
21678 }
21679 }
21680
21681 assert_eq!(
21682 via_helper, via_inline,
21683 "servico_spec_and_m2_overlay_entries must byte-equal the \
21684 hand-written two-loop composition (spec.* splice + M2 \
21685 overlay with or_insert precedence) the two prior inline \
21686 call sites carried",
21687 );
21688 }
21689
21690 #[test]
21691 fn pleme_label_consts_share_canonical_prefix() {
21692 // Single-source-of-truth invariant: every pleme-io label key
21693 // is `<PLEME_LABEL_PREFIX>/<axis>`. A future label-namespace
21694 // rebrand is a one-line PLEME_LABEL_PREFIX edit + this test
21695 // pins the contract that no other label leaks past the lift.
21696 for k in [LABEL_APLICACAO, LABEL_PROGRAM, LABEL_CONTRATO] {
21697 assert!(
21698 k.starts_with(PLEME_LABEL_PREFIX),
21699 "label key {k:?} must share the {PLEME_LABEL_PREFIX:?} prefix"
21700 );
21701 // Each label is `<prefix>/<axis>` — the suffix is non-empty
21702 // (the `/` separator is followed by the axis name).
21703 let suffix = k.strip_prefix(PLEME_LABEL_PREFIX).unwrap();
21704 assert!(suffix.starts_with('/'));
21705 assert!(suffix.len() > 1, "axis name must be non-empty for {k:?}");
21706 }
21707 }
21708
21709 #[test]
21710 fn pleme_label_consts_have_expected_canonical_values() {
21711 // Pin the actual string values so a typo in the lift can't
21712 // silently rebrand the whole pleme-io label namespace. These
21713 // strings are part of the cluster-side contract with the
21714 // lareira-fleet-programs chart + Cilium identity layer + Hubble
21715 // flow attribution; changing any of them is a coordinated
21716 // multi-repo migration, not an incidental edit.
21717 assert_eq!(PLEME_LABEL_PREFIX, "pleme.pleme.io");
21718 assert_eq!(LABEL_APLICACAO, "pleme.pleme.io/aplicacao");
21719 assert_eq!(LABEL_PROGRAM, "pleme.pleme.io/program");
21720 assert_eq!(LABEL_CONTRATO, "pleme.pleme.io/contrato");
21721 }
21722
21723 #[test]
21724 fn default_namespace_pins_canonical_value() {
21725 // Pin the actual string so a typo in this lift can't silently
21726 // rebrand the cluster-side namespace every renderer emits
21727 // into. The string is part of the cluster-side contract with
21728 // the lareira-fleet-programs aggregator chart, the per-cluster
21729 // CiliumNetworkPolicy `endpointSelector` namespace scope, the
21730 // Gateway / HTTPRoute apply namespace, and the future M4
21731 // `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's apply
21732 // namespace; changing it is a coordinated multi-repo migration
21733 // (the per-cluster k8s repo's namespaces, every
21734 // lareira-fleet-programs HelmRelease's targetNamespace, every
21735 // ComputeUnit's `metadata.namespace`), not an incidental edit.
21736 // Peer to `pleme_label_consts_have_expected_canonical_values`
21737 // on the canonical-string-value-pin axis for the
21738 // `PLEME_LABEL_PREFIX` / `LABEL_*` constants.
21739 assert_eq!(DEFAULT_NAMESPACE, "tatara-system");
21740 }
21741
21742 #[test]
21743 fn default_flux_system_namespace_pins_canonical_value() {
21744 // Pin the actual string so a typo in this lift can't silently
21745 // rebrand the FluxCD installation namespace the rendered
21746 // `kustomization.yaml`'s `metadata.namespace` /
21747 // `spec.sourceRef.name` axes consume. The string is part of the
21748 // cluster-side contract with the `flux bootstrap` pipeline (the
21749 // bootstrap convention names the `GitRepository` after the
21750 // installation namespace, so both axes are the same load-bearing
21751 // string), the `kustomize-controller` watch-window scope (a
21752 // drifted value sits outside the controller's watch window and
21753 // is never reconciled), and the per-cluster k8s repo's flux
21754 // bootstrap manifests; changing it is a coordinated multi-repo
21755 // migration, not an incidental edit. Peer to
21756 // `default_namespace_pins_canonical_value` on the
21757 // canonical-string-value-pin axis for the workload-side
21758 // [`DEFAULT_NAMESPACE`] constant.
21759 assert_eq!(DEFAULT_FLUX_SYSTEM_NAMESPACE, "flux-system");
21760 }
21761
21762 #[test]
21763 fn default_flux_system_namespace_is_a_valid_dns_1123_label() {
21764 // Cross-axis invariant: the FluxCD installation namespace lands
21765 // as `metadata.namespace` on every emitted `Kustomization`
21766 // resource and as `spec.sourceRef.name` (a K8s resource name
21767 // under the same DNS-1123 floor), and the K8s apiserver
21768 // enforces the DNS-1123 label rule on both. Pinning this here
21769 // means a future rebrand on the canonical lift can't silently
21770 // land a value the apiserver refuses at the *first*
21771 // `kustomization.yaml` apply against a cluster, far from the
21772 // rebrand commit's source — the typed [`is_dns_1123_label`]
21773 // floor rejects it at caixa-core build time on the canonical
21774 // lift, before any renderer consumes the value. Same shape as
21775 // `default_namespace_is_a_valid_dns_1123_label` on the
21776 // workload-side [`DEFAULT_NAMESPACE`] axis.
21777 assert!(
21778 is_dns_1123_label(DEFAULT_FLUX_SYSTEM_NAMESPACE).is_ok(),
21779 "DEFAULT_FLUX_SYSTEM_NAMESPACE {DEFAULT_FLUX_SYSTEM_NAMESPACE:?} must be a valid \
21780 DNS-1123 label — every K8s apiserver-side schema enforces \
21781 this rule on `metadata.namespace`"
21782 );
21783 }
21784
21785 #[test]
21786 fn default_flux_reconcile_interval_pins_canonical_value() {
21787 // Pin the actual string so a typo in this lift can't silently
21788 // rebrand the substrate-side default Flux v2 reconcile-poll
21789 // cadence duration scalar the substrate's per-caixa
21790 // `cluster_bundle` renderer seeds into every emitted per-caixa
21791 // Flux v2 CR (GitRepository / HelmRelease / Kustomization) at
21792 // its `spec.interval` axis when the operator doesn't pin a per-
21793 // caixa override. The string is part of the cluster-side
21794 // contract with the Flux v2 source-controller / helm-controller
21795 // / kustomize-controller trio: each controller's per-CR admission
21796 // gate parses the value via `metav1.ParseDuration` before
21797 // installing the per-CR watch, and the resulting cadence pins
21798 // the per-CR reconcile-freshness / cluster-load tradeoff every
21799 // substrate-side Flux v2 pipeline runs at. Changing this value
21800 // is a coordinated substrate-side reconcile-cadence promotion
21801 // (a `10m` → `5m` migration once lower-latency-poll optimizations
21802 // ship, a `10m` → `15m` migration on cost-optimized clusters
21803 // where per-CR source-controller poll cost outweighs the
21804 // reconcile-freshness gain), not an incidental edit. Peer to
21805 // `default_namespace_pins_canonical_value` and
21806 // `default_gateway_class_name_pins_canonical_value` on the
21807 // canonical-substrate-default-load-bearing-scalar pin surface.
21808 assert_eq!(DEFAULT_FLUX_RECONCILE_INTERVAL, "10m");
21809 }
21810
21811 #[test]
21812 fn default_flux_reconcile_interval_is_a_valid_metav1_duration_scalar() {
21813 // Cross-axis grammar invariant: the Flux v2 controller-side per-
21814 // CR admission gate parses the reconcile-poll cadence scalar via
21815 // `metav1.ParseDuration` before installing the per-CR watch. The
21816 // Go-duration-format grammar is non-empty, ASCII, and structured
21817 // as `<digits><unit>[<digits><unit>...]` where each unit is one
21818 // of `{ns, us, µs, ms, s, m, h}`. Pin a floor that catches the
21819 // canonical drift footguns — an empty scalar (`""` — admission
21820 // gate rejects), a non-ASCII-alphanumeric byte (`"10 m"` — the
21821 // whitespace defeats the parser), a missing-unit scalar (`"10"`
21822 // — the parser rejects for lack of a unit suffix), or a leading-
21823 // non-digit scalar (`"m10"` — the parser rejects for lack of a
21824 // leading magnitude). A future rebrand on the canonical lift
21825 // that lands a value outside the Go-duration-format grammar
21826 // would surface here at caixa-core build time on the canonical
21827 // lift, before any renderer consumes the value. Same shape as
21828 // `default_namespace_is_a_valid_dns_1123_label` /
21829 // `default_flux_system_namespace_is_a_valid_dns_1123_label` /
21830 // `default_gateway_class_name_is_a_valid_dns_1123_label` on the
21831 // peer canonical-substrate-default-grammar-floor surface.
21832 let v = DEFAULT_FLUX_RECONCILE_INTERVAL;
21833 assert!(
21834 !v.is_empty(),
21835 "DEFAULT_FLUX_RECONCILE_INTERVAL {v:?} must be non-empty \
21836 per the Flux v2 controller-side `metav1.ParseDuration` \
21837 admission gate"
21838 );
21839 assert!(
21840 v.chars().all(|c| c.is_ascii_alphanumeric()),
21841 "DEFAULT_FLUX_RECONCILE_INTERVAL {v:?} must be ASCII-\
21842 alphanumeric throughout per the Go-duration-format grammar \
21843 — no whitespace / separator bytes the `metav1.ParseDuration` \
21844 admission gate would reject"
21845 );
21846 let first = v.chars().next().expect("non-empty");
21847 assert!(
21848 first.is_ascii_digit(),
21849 "DEFAULT_FLUX_RECONCILE_INTERVAL {v:?} first byte {first:?} \
21850 must be an ASCII digit per the Go-duration-format grammar \
21851 — the leading magnitude precedes the unit suffix; a leading \
21852 non-digit defeats `metav1.ParseDuration`"
21853 );
21854 let last = v.chars().next_back().expect("non-empty");
21855 assert!(
21856 last.is_ascii_alphabetic() && last.is_ascii_lowercase(),
21857 "DEFAULT_FLUX_RECONCILE_INTERVAL {v:?} last byte {last:?} \
21858 must be an ASCII lowercase alphabetic unit suffix per the \
21859 Go-duration-format grammar — the trailing unit follows the \
21860 magnitude; an unterminated magnitude defeats \
21861 `metav1.ParseDuration`"
21862 );
21863 }
21864
21865 #[test]
21866 fn default_flux_chart_source_subpath_pins_canonical_value() {
21867 // Pin the actual scalar so a typo in this lift can't silently
21868 // rebrand the substrate-side default Flux v2
21869 // `HelmRelease.spec.chart.spec.chart` chart-directory-in-
21870 // GitRepository-source sub-path the substrate's per-caixa
21871 // `cluster_bundle` renderer seeds into every emitted per-caixa
21872 // `helmrelease.yaml` document. The value is part of the
21873 // cluster-side contract with the Flux v2 helm-controller (the
21874 // per-CR chart-open loop uses this to locate the
21875 // `Chart.yaml` + `values.yaml` pair inside the paired
21876 // GitRepository clone root); changing it is a coordinated
21877 // substrate-side chart-directory-in-git-source promotion
21878 // (a `"chart"` → `"charts"` migration on a per-caixa multi-chart
21879 // layout landing, a `"chart"` → `"helm"` migration on a
21880 // cross-language convention alignment, a `"chart"` → `"deploy"`
21881 // migration on a per-caixa-deploy-directory naming migration),
21882 // not an incidental edit. Peer to
21883 // `default_flux_reconcile_interval_pins_canonical_value` +
21884 // `flux_helmrelease_remediation_retries_default_pins_canonical_value`
21885 // on the canonical-Flux-v2-per-CR-substrate-default-scalar pin
21886 // surface.
21887 assert_eq!(DEFAULT_FLUX_CHART_SOURCE_SUBPATH, "chart");
21888 }
21889
21890 #[test]
21891 fn default_flux_chart_source_subpath_is_a_valid_relative_directory_scalar() {
21892 // Cross-axis grammar invariant: the Flux v2 source-controller
21893 // resolves the per-CR `HelmRelease.spec.chart.spec.chart` scalar
21894 // as a directory path relative to the paired `GitRepository`
21895 // clone root. Pin a floor that catches the canonical drift
21896 // footguns — an empty scalar (`""` — the source-controller-side
21897 // per-CR chart-open loop rejects for lack of a target directory),
21898 // a leading-separator scalar (`"/chart"` — the source-controller
21899 // rejects for the absolute-path shape breaking the relative-path
21900 // composition against the per-clone-root anchor), a non-ASCII
21901 // byte (a UTF-8 multi-byte name defeating the per-clone-root
21902 // filesystem name resolution on the source-controller pod's
21903 // filesystem layer), or a leading whitespace / dot byte (`" chart"`
21904 // / `".chart"` — surface as either a "directory not found" per-
21905 // CR error or, worse, a silent match against a hidden dot-file
21906 // sibling of the intended chart directory). A future rebrand on
21907 // the canonical lift that lands a value outside the grammar
21908 // would surface here at caixa-core build time on the canonical
21909 // lift, before any renderer consumes the value. Same shape as
21910 // `default_flux_reconcile_interval_is_a_valid_metav1_duration_scalar`
21911 // on the peer canonical-substrate-default-grammar-floor surface.
21912 let v = DEFAULT_FLUX_CHART_SOURCE_SUBPATH;
21913 assert!(
21914 !v.is_empty(),
21915 "DEFAULT_FLUX_CHART_SOURCE_SUBPATH {v:?} must be non-empty \
21916 per the Flux v2 source-controller-side per-CR chart-open \
21917 loop's requirement of a target directory"
21918 );
21919 assert!(
21920 v.is_ascii(),
21921 "DEFAULT_FLUX_CHART_SOURCE_SUBPATH {v:?} must be ASCII \
21922 throughout — a non-ASCII multi-byte name defeats the per-\
21923 clone-root filesystem name resolution on the source-\
21924 controller pod's filesystem layer"
21925 );
21926 let first = v.chars().next().expect("non-empty");
21927 assert!(
21928 !matches!(first, '/' | '.' | ' ' | '\t'),
21929 "DEFAULT_FLUX_CHART_SOURCE_SUBPATH {v:?} first byte {first:?} \
21930 must not be a leading separator (`/`), leading dot (`.`), or \
21931 leading whitespace — a leading separator breaks the relative-\
21932 path composition against the per-clone-root anchor, a leading \
21933 dot risks silent matches against hidden dot-file siblings, and \
21934 leading whitespace defeats the per-clone-root filesystem name \
21935 resolution"
21936 );
21937 }
21938
21939 #[test]
21940 fn flux_helmrelease_remediation_retries_default_pins_canonical_value() {
21941 // Pin the actual scalar so a typo in this lift can't silently
21942 // rebrand the substrate-side default Flux v2
21943 // `HelmRelease.spec.{install,upgrade}.remediation.retries` retry-
21944 // count ceiling the substrate's per-caixa `cluster_bundle`
21945 // renderer seeds into every emitted per-caixa `helmrelease.yaml`
21946 // document under both the install-path and the upgrade-path
21947 // remediation blocks. The value is part of the cluster-side
21948 // contract with the Flux v2 helm-controller (the per-CR
21949 // remediation loop uses this as the ceiling on the number of
21950 // Helm-install / Helm-upgrade re-attempts before the controller
21951 // marks the `HelmRelease` `Ready: False` and stops retrying);
21952 // changing it is a coordinated substrate-side retry-ceiling
21953 // promotion (a `3` → `5` migration once per-caixa idempotency
21954 // invariants tighten and higher-retry recovery from transient
21955 // apiserver / registry / oci-source flakes becomes safe, a `3` →
21956 // `1` migration on hardened per-caixa pipelines where a failed
21957 // apply should escalate to operator-attention rather than mask
21958 // under further retries), not an incidental edit. Peer to
21959 // `default_flux_reconcile_interval_pins_canonical_value` on the
21960 // canonical-Flux-v2-per-CR-substrate-default-scalar pin surface.
21961 assert_eq!(FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT, 3);
21962 }
21963
21964 #[test]
21965 fn flux_helmrelease_remediation_retries_default_is_a_bounded_positive_scalar() {
21966 // Cross-axis invariant: the Flux v2 `HelmRelease.spec.{install,
21967 // upgrade}.remediation.retries` OpenAPI schema types the field
21968 // as a signed 64-bit integer with a documented sentinel `-1`
21969 // meaning "retry indefinitely". The substrate opts out of the
21970 // unbounded-retry sentinel by declaring the canonical default as
21971 // a positive `u32` — the type itself rules out `-1` at
21972 // caixa-core build time, so a future rebrand on this lift cannot
21973 // silently land the "retry forever" sentinel by construction
21974 // (which would let a persistently-failing per-caixa chart apply
21975 // consume Flux v2 helm-controller reconcile-loop cycles
21976 // indefinitely, masking under further retries rather than
21977 // surfacing at the `HelmRelease.status.conditions[]` axis the
21978 // substrate's downstream reconciliation-topology consumer
21979 // watches). Pin the positive-scalar floor + a substrate-side
21980 // "sane retry ceiling" upper bound (the same 100-attempt hard
21981 // cap the peer `POLICY_RETRIES_MAX` per-`:politicas :retries`
21982 // axis carries; a substrate that seeds a per-CR default above
21983 // that ceiling is structurally a footgun by the same
21984 // "unbounded-retry masks the underlying failure" argument that
21985 // motivates the mesh-policy retries cap). Same shape as
21986 // `default_flux_reconcile_interval_is_a_valid_metav1_duration_scalar`
21987 // on the peer canonical-substrate-default-grammar-floor surface.
21988 let v = FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT;
21989 assert!(
21990 v > 0,
21991 "FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT {v} must be strictly \
21992 positive per the substrate's opt-out from the Flux v2 \
21993 `retries: -1` unbounded-retry sentinel — the `u32` type rules \
21994 out the sentinel, and a zero-retries default is structurally \
21995 a `remediation:` sub-block that never fires the retry path it \
21996 is declaring"
21997 );
21998 assert!(
21999 v <= 100,
22000 "FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT {v} must be within \
22001 the substrate's canonical retry-ceiling upper bound (100) — a \
22002 per-CR default above that ceiling silently masks the underlying \
22003 chart-apply failure under further retries rather than surfacing \
22004 it at the `HelmRelease.status.conditions[]` axis the substrate's \
22005 downstream reconciliation-topology consumer watches, the same \
22006 argument that motivates the peer `POLICY_RETRIES_MAX` per-\
22007 `:politicas :retries` axis cap"
22008 );
22009 }
22010
22011 #[test]
22012 fn flux_helmrelease_key_remediation_pins_canonical_value() {
22013 // Pin the actual string so a typo in this lift can't silently
22014 // rebrand the substrate-side Flux v2
22015 // `HelmRelease.spec.{install,upgrade}.remediation` sub-container-
22016 // axis key the substrate's per-caixa `cluster_bundle` renderer
22017 // seeds into every emitted per-caixa `helmrelease.yaml` document
22018 // at both the install-path + upgrade-path per-CR remediation
22019 // sub-block-header positions. The string is part of the cluster-
22020 // side contract with the Flux v2 helm-controller (the controller's
22021 // per-CR remediation loop reaches the retry-cap scalar through
22022 // this exact sub-container axis; a drifted sub-container-key
22023 // silently strips the entire per-path remediation block from the
22024 // emitted per-CR document, leaving the helm-controller to fall
22025 // back to the Flux v2 upstream defaults for the whole remediation
22026 // surface rather than the substrate's chosen ceiling, with no
22027 // diagnostic naming the container-axis-key-drift root cause).
22028 // Changing it is a coordinated Flux v3 CRD-schema-rebrand
22029 // migration alongside the upstream `helm-controller` deprecation
22030 // cycle (candidates like `recovery` / `retryPolicy` /
22031 // `errorHandling` that upstream Flux v3 roadmap floats in the
22032 // migration prose), not an incidental edit. Peer to
22033 // `flux_helmrelease_remediation_retries_default_pins_canonical_value`
22034 // on the sibling scalar-value half + the sibling
22035 // [`FLUX_HELMRELEASE_KEY_RETRIES`] leaf-scalar-key half of the
22036 // same per-path retry-cap declaration triple.
22037 assert_eq!(FLUX_HELMRELEASE_KEY_REMEDIATION, "remediation");
22038 }
22039
22040 #[test]
22041 fn flux_helmrelease_key_remediation_is_a_valid_dns_1123_label() {
22042 // Cross-axis invariant: every Flux v2 `HelmRelease` CRD-schema
22043 // sub-block-header key resolves through the K8s apiserver's
22044 // OpenAPI-schema-side identifier grammar, whose per-field key
22045 // axis is a subset of the DNS-1123-label grammar (lowercase
22046 // alphanumerics + hyphens, non-empty, ≤63 bytes). Pinning the
22047 // canonical `remediation` value against the typed
22048 // [`is_dns_1123_label`] floor rules out grammar drift on this
22049 // lift at caixa-core build time — a future rebrand landing a
22050 // value outside the DNS-1123-label subset (a leading digit, an
22051 // underscore, an uppercase byte, a `.` byte, or empty) would
22052 // surface here on the canonical lift, before any renderer
22053 // consumes the value and before any per-caixa Flux v2 CR reaches
22054 // the apiserver's OpenAPI-schema-side per-field admission gate.
22055 // Same shape as `default_gateway_class_name_is_a_valid_dns_1123_label`
22056 // on the peer canonical-CRD-schema-grammar-floor surface.
22057 assert!(
22058 is_dns_1123_label(FLUX_HELMRELEASE_KEY_REMEDIATION).is_ok(),
22059 "FLUX_HELMRELEASE_KEY_REMEDIATION {FLUX_HELMRELEASE_KEY_REMEDIATION:?} \
22060 must be a valid DNS-1123 label — every K8s apiserver-side \
22061 OpenAPI-schema-per-field-key axis is a subset of that grammar, \
22062 and the Flux v2 `HelmRelease` CRD schema is no exception"
22063 );
22064 }
22065
22066 #[test]
22067 fn flux_helmrelease_key_install_pins_canonical_value() {
22068 // Pin the actual string so a typo in this lift can't silently
22069 // rebrand the Flux v2 `HelmRelease.spec.install` per-CR helm-
22070 // action-phase discriminator parent-container-axis-key the
22071 // rendered `helmrelease.yaml` document mounts its per-CR first-
22072 // time chart apply phase-block under. The string is part of the
22073 // cluster-side contract with the upstream Flux v2 helm-
22074 // controller — the helm-controller's per-CR phase-dispatch loop
22075 // reaches the install-path phase block through this exact parent-
22076 // container axis; a drifted parent-container-key silently strips
22077 // the entire install-path phase block from the emitted per-CR
22078 // document, leaving the helm-controller to fall back to the Flux
22079 // v2 upstream defaults for the whole install-path phase surface
22080 // rather than the substrate's chosen per-CR install-path knob-set
22081 // (the `createNamespace` seeder never fires, the per-CR retry-cap
22082 // ceiling silently drops off the emitted document), with no
22083 // diagnostic naming the phase-discriminator-drift root cause.
22084 // Changing it is a coordinated Flux v3 CRD-schema-rebrand
22085 // migration alongside the upstream `helm-controller` deprecation
22086 // cycle (candidates like `initialize` / `apply` / `create` /
22087 // `first-run` that upstream Flux v3 roadmap floats in the
22088 // migration prose), not an incidental edit. Peer to
22089 // `flux_helmrelease_key_upgrade_pins_canonical_value` on the
22090 // sibling per-CR upgrade-path phase-discriminator parent-
22091 // container-axis-key half of the same per-CR helm-action-phase
22092 // discriminator parent-container-axis-key pair + the sibling
22093 // [`FLUX_HELMRELEASE_KEY_REMEDIATION`] sub-container-axis-key
22094 // hosted beneath both parent-container-axis-keys.
22095 assert_eq!(FLUX_HELMRELEASE_KEY_INSTALL, "install");
22096 }
22097
22098 #[test]
22099 fn flux_helmrelease_key_install_is_a_valid_dns_1123_label() {
22100 // Cross-axis invariant: every Flux v2 `HelmRelease` CRD-schema
22101 // sub-block-header key resolves through the K8s apiserver's
22102 // OpenAPI-schema-side identifier grammar, whose per-field key
22103 // axis is a subset of the DNS-1123-label grammar (lowercase
22104 // alphanumerics + hyphens, non-empty, ≤63 bytes). Pinning the
22105 // canonical `install` value against the typed
22106 // [`is_dns_1123_label`] floor rules out grammar drift on this
22107 // lift at caixa-core build time — a future rebrand landing a
22108 // value outside the DNS-1123-label subset (a leading digit, an
22109 // underscore, an uppercase byte, a `.` byte, or empty) would
22110 // surface here on the canonical lift, before any renderer
22111 // consumes the value and before any per-caixa Flux v2 CR reaches
22112 // the apiserver's OpenAPI-schema-side per-field admission gate.
22113 // Same shape as `flux_helmrelease_key_remediation_is_a_valid_
22114 // dns_1123_label` on the sibling per-CR sub-container-axis-key
22115 // grammar-floor surface.
22116 assert!(
22117 is_dns_1123_label(FLUX_HELMRELEASE_KEY_INSTALL).is_ok(),
22118 "FLUX_HELMRELEASE_KEY_INSTALL {FLUX_HELMRELEASE_KEY_INSTALL:?} \
22119 must be a valid DNS-1123 label — every K8s apiserver-side \
22120 OpenAPI-schema-per-field-key axis is a subset of that grammar, \
22121 and the Flux v2 `HelmRelease` CRD schema is no exception"
22122 );
22123 }
22124
22125 #[test]
22126 fn flux_helmrelease_key_upgrade_pins_canonical_value() {
22127 // Pin the actual string so a typo in this lift can't silently
22128 // rebrand the Flux v2 `HelmRelease.spec.upgrade` per-CR helm-
22129 // action-phase discriminator parent-container-axis-key the
22130 // rendered `helmrelease.yaml` document mounts its per-CR
22131 // subsequent-per-version chart re-apply phase-block under. The
22132 // string is part of the cluster-side contract with the upstream
22133 // Flux v2 helm-controller — the helm-controller's per-CR phase-
22134 // dispatch loop reaches the upgrade-path phase block through this
22135 // exact parent-container axis on every per-version chart re-apply
22136 // after the initial install-path phase completes; a drifted
22137 // parent-container-key silently strips the entire upgrade-path
22138 // phase block from the emitted per-CR document, leaving the
22139 // helm-controller to fall back to the Flux v2 upstream defaults
22140 // for the whole upgrade-path phase surface rather than the
22141 // substrate's chosen per-CR upgrade-path knob-set (the
22142 // `remediateLastFailure` toggle never fires, the per-CR retry-
22143 // cap ceiling silently drops off the emitted document), with no
22144 // diagnostic naming the phase-discriminator-drift root cause.
22145 // Changing it is a coordinated Flux v3 CRD-schema-rebrand
22146 // migration alongside the upstream `helm-controller` deprecation
22147 // cycle (candidates like `reapply` / `reconcile` / `update` /
22148 // `promote` that upstream Flux v3 roadmap floats in the
22149 // migration prose), not an incidental edit. Peer to
22150 // `flux_helmrelease_key_install_pins_canonical_value` on the
22151 // sibling per-CR install-path phase-discriminator parent-
22152 // container-axis-key half of the same per-CR helm-action-phase
22153 // discriminator parent-container-axis-key pair.
22154 assert_eq!(FLUX_HELMRELEASE_KEY_UPGRADE, "upgrade");
22155 }
22156
22157 #[test]
22158 fn flux_helmrelease_key_upgrade_is_a_valid_dns_1123_label() {
22159 // Cross-axis invariant: every Flux v2 `HelmRelease` CRD-schema
22160 // sub-block-header key resolves through the K8s apiserver's
22161 // OpenAPI-schema-side identifier grammar, whose per-field key
22162 // axis is a subset of the DNS-1123-label grammar. Pinning the
22163 // canonical `upgrade` value against the typed
22164 // [`is_dns_1123_label`] floor rules out grammar drift on this
22165 // lift at caixa-core build time. Peer to
22166 // `flux_helmrelease_key_install_is_a_valid_dns_1123_label` on
22167 // the sibling install-path phase-discriminator grammar-floor
22168 // surface + `flux_helmrelease_key_remediation_is_a_valid_dns_
22169 // 1123_label` on the sibling per-CR sub-container-axis-key
22170 // grammar-floor surface — same DNS-1123-label subset governs
22171 // every apiserver-side per-field-key axis, so every peer per-CR
22172 // sub-block-header lift carries the same grammar-floor pin.
22173 assert!(
22174 is_dns_1123_label(FLUX_HELMRELEASE_KEY_UPGRADE).is_ok(),
22175 "FLUX_HELMRELEASE_KEY_UPGRADE {FLUX_HELMRELEASE_KEY_UPGRADE:?} \
22176 must be a valid DNS-1123 label — every K8s apiserver-side \
22177 OpenAPI-schema-per-field-key axis is a subset of that grammar, \
22178 and the Flux v2 `HelmRelease` CRD schema is no exception"
22179 );
22180 }
22181
22182 #[test]
22183 fn flux_helmrelease_key_install_and_upgrade_stay_independent_axes() {
22184 // The two per-CR helm-action-phase discriminator parent-
22185 // container-axis-keys name distinct helm-controller-side phases
22186 // — install-path first-time chart apply vs upgrade-path per-
22187 // version chart re-apply — even though both host the same
22188 // sibling [`FLUX_HELMRELEASE_KEY_REMEDIATION`] sub-container-
22189 // axis-key beneath them. Pin that the two consts carry distinct
22190 // byte-sequences so a future rebrand on either arm can't
22191 // silently coalesce onto the peer arm (a
22192 // `FLUX_HELMRELEASE_KEY_INSTALL = "upgrade"` typo would flip
22193 // every substrate-side per-CR first-time chart apply phase
22194 // block onto the upgrade-path phase key silently — the install-
22195 // path becomes the upgrade-path at every emit site, and the
22196 // helm-controller reconciles both phase blocks under the same
22197 // parent-container-axis-key, silently dropping either the
22198 // install-path or the upgrade-path per-CR knob-set with no
22199 // diagnostic naming the phase-discriminator-coalesce root
22200 // cause). The per-CR helm-action-phase discriminator pair must
22201 // always resolve to distinct emitted parent-container-keys.
22202 assert_ne!(
22203 FLUX_HELMRELEASE_KEY_INSTALL, FLUX_HELMRELEASE_KEY_UPGRADE,
22204 "the per-CR install-path and upgrade-path helm-action-phase \
22205 discriminator parent-container-axis-keys must remain byte-\
22206 distinct — a coalesce onto one value silently drops either \
22207 the install-path or the upgrade-path per-CR knob-set from \
22208 every emitted `HelmRelease` document"
22209 );
22210 }
22211
22212 #[test]
22213 fn flux_helmrelease_key_remediate_last_failure_pins_canonical_value() {
22214 // Pin the actual string so a typo in this lift can't silently
22215 // rebrand the Flux v2 `HelmRelease.spec.upgrade.remediation
22216 // .remediateLastFailure` upgrade-path-only per-CR remediation-
22217 // toggle leaf-scalar-key the substrate's per-caixa `cluster_bundle`
22218 // renderer seeds to `true` into every emitted per-caixa
22219 // `helmrelease.yaml` document under the sibling
22220 // [`FLUX_HELMRELEASE_KEY_UPGRADE`] per-CR upgrade-path phase-
22221 // discriminator parent-container-axis-key's nested
22222 // [`FLUX_HELMRELEASE_KEY_REMEDIATION`] sub-container-axis-key. The
22223 // string is part of the cluster-side contract with the upstream
22224 // Flux v2 helm-controller — the controller's per-CR upgrade-path
22225 // remediation loop reaches the post-retry-exhaustion rollback
22226 // toggle through this exact leaf; a drifted leaf-scalar-key
22227 // silently strips the substrate's chosen post-retry-exhaustion
22228 // rollback semantic from every emitted per-caixa `HelmRelease`
22229 // document, leaving the helm-controller to leave every terminally-
22230 // failed upgrade in the failed state without rolling back to the
22231 // prior last-known-good release the substrate's "no chart apply
22232 // leaves a per-caixa CR in a stalled, unremediated state"
22233 // MESH-COMPOSITION.md §V guarantee mandates, with no diagnostic
22234 // naming the remediation-toggle-drift root cause. Changing it is
22235 // a coordinated Flux v3 CRD-schema-rebrand migration alongside
22236 // the upstream `helm-controller` deprecation cycle (candidates
22237 // like `rollbackOnFailure` / `remediateOnFailure` /
22238 // `recoverLastFailure` that upstream Flux v3 roadmap floats in
22239 // the migration prose), not an incidental edit. Peer to
22240 // `flux_helmrelease_key_retries_pins_canonical_value` on the
22241 // sibling per-CR retry-cap leaf-scalar-key half of the same
22242 // upgrade-path per-CR remediation block leaf-scalar-key pair.
22243 assert_eq!(
22244 FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE,
22245 "remediateLastFailure"
22246 );
22247 }
22248
22249 #[test]
22250 fn flux_helmrelease_key_remediate_last_failure_stays_independent_of_retries() {
22251 // The upgrade-path per-CR remediation block hosts two independent
22252 // leaf-scalar-key axes under the shared sibling
22253 // [`FLUX_HELMRELEASE_KEY_REMEDIATION`] sub-container-axis-key —
22254 // the per-CR retry-cap [`FLUX_HELMRELEASE_KEY_RETRIES`] (that
22255 // also sits under the install-path per-CR remediation block) and
22256 // the upgrade-path-only per-CR remediation-toggle
22257 // [`FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE`]. Pin that the
22258 // two consts carry byte-distinct sequences so a future rebrand
22259 // on either arm can't silently coalesce onto the peer arm (a
22260 // `FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE = "retries"` typo
22261 // would silently rebind the post-retry-exhaustion rollback
22262 // toggle onto the retry-cap ceiling axis at every emit site —
22263 // the helm-controller then reads the substrate's `true` seed as
22264 // an integer retry-cap `1` on the retry-cap axis instead of the
22265 // rollback-on-terminal-failure boolean, silently truncating the
22266 // per-CR upgrade-path retry budget and dropping the rollback
22267 // semantic entirely with no diagnostic naming the leaf-key-
22268 // coalesce root cause). The upgrade-path per-CR remediation
22269 // leaf-scalar-key pair must always resolve to distinct emitted
22270 // leaf-keys.
22271 assert_ne!(
22272 FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE, FLUX_HELMRELEASE_KEY_RETRIES,
22273 "the upgrade-path per-CR remediation retry-cap leaf-scalar-\
22274 key and remediation-toggle leaf-scalar-key must remain \
22275 byte-distinct — a coalesce onto one value silently rebinds \
22276 the post-retry-exhaustion rollback semantic onto the retry-\
22277 cap ceiling axis at every emit site"
22278 );
22279 }
22280
22281 #[test]
22282 fn flux_helmrelease_key_create_namespace_pins_canonical_value() {
22283 // Pin the actual string so a typo in this lift can't silently
22284 // rebrand the Flux v2 `HelmRelease.spec.install.createNamespace`
22285 // install-path-only per-CR namespace-seeder-toggle leaf-scalar-key
22286 // the substrate's per-caixa `cluster_bundle` renderer seeds to
22287 // `true` into every emitted per-caixa `helmrelease.yaml` document
22288 // under the sibling [`FLUX_HELMRELEASE_KEY_INSTALL`] per-CR
22289 // install-path phase-discriminator parent-container-axis-key. The
22290 // string is part of the cluster-side contract with the upstream
22291 // Flux v2 helm-controller — the controller's per-CR install-path
22292 // pre-apply loop reaches the target-namespace-seeder toggle
22293 // through this exact leaf; a drifted leaf-scalar-key silently
22294 // strips the substrate's chosen first-apply namespace-seeder
22295 // semantic from every emitted per-caixa `HelmRelease` document,
22296 // leaving the helm-controller to refuse every first-time per-caixa
22297 // chart apply against a fresh cluster whose target namespace has
22298 // not been pre-provisioned by an out-of-band pipeline the
22299 // substrate's "no per-caixa Servico apply is blocked on manual
22300 // namespace preprovisioning" MESH-COMPOSITION.md §V install-path-
22301 // fluency guarantee mandates, with no diagnostic naming the
22302 // seeder-toggle-drift root cause. Changing it is a coordinated
22303 // Flux v3 CRD-schema-rebrand migration alongside the upstream
22304 // `helm-controller` deprecation cycle (candidates like
22305 // `createTargetNamespace` / `seedNamespace` / `provisionNamespace`
22306 // that upstream Flux v3 roadmap floats in the migration prose),
22307 // not an incidental edit. Peer to
22308 // `flux_helmrelease_key_remediate_last_failure_pins_canonical_value`
22309 // on the sibling mirror-symmetric upgrade-path-only per-CR
22310 // remediation-toggle leaf-scalar-key half of the same install/
22311 // upgrade per-CR phase-specific toggle leaf-scalar-key pair.
22312 assert_eq!(FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE, "createNamespace");
22313 }
22314
22315 #[test]
22316 fn flux_helmrelease_key_create_namespace_stays_independent_of_remediate_last_failure() {
22317 // The per-CR install/upgrade phase blocks host two mirror-symmetric
22318 // phase-specific toggle leaf-scalar-key axes: the install-path-only
22319 // per-CR namespace-seeder-toggle
22320 // [`FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE`] under the sibling
22321 // [`FLUX_HELMRELEASE_KEY_INSTALL`] parent-container-axis-key (this
22322 // lift) and the upgrade-path-only per-CR remediation-toggle
22323 // [`FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE`] (96581b7) under the
22324 // sibling [`FLUX_HELMRELEASE_KEY_UPGRADE`] parent-container-axis-key.
22325 // Pin that the two consts carry byte-distinct sequences so a future
22326 // rebrand on either arm can't silently coalesce onto the peer arm
22327 // (a `FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE = "remediateLastFailure"`
22328 // typo would silently rebind the install-path namespace-seeder
22329 // toggle onto the upgrade-path per-CR remediation-toggle leaf at
22330 // every emit site — the helm-controller would then read the
22331 // substrate's `true` seed as a post-retry-exhaustion rollback opt-
22332 // in on the upgrade-path per-CR remediation axis instead of the
22333 // pre-apply namespace-seeder toggle, silently dropping the first-
22334 // apply namespace-seeder semantic entirely and misrouting the
22335 // install-path opt-in onto an upgrade-path axis where it never
22336 // fires with no diagnostic naming the leaf-key-coalesce root
22337 // cause). The install/upgrade per-CR phase-specific toggle leaf-
22338 // scalar-key pair must always resolve to distinct emitted leaf-
22339 // keys under mirror-symmetric parent-container-axis-keys.
22340 assert_ne!(
22341 FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE, FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE,
22342 "the install-path per-CR namespace-seeder-toggle leaf-scalar-\
22343 key and the upgrade-path per-CR remediation-toggle leaf-\
22344 scalar-key must remain byte-distinct — a coalesce onto one \
22345 value silently rebinds one phase's opt-in toggle onto the \
22346 peer phase's opt-in-toggle axis at every emit site, dropping \
22347 the phase-specific pre-apply / post-retry-exhaustion semantic \
22348 the substrate seeds on the coalesced arm"
22349 );
22350 }
22351
22352 #[test]
22353 fn flux_kustomization_key_prune_pins_canonical_value() {
22354 // Pin the actual string so a typo in this lift can't silently
22355 // rebrand the Flux v2 `Kustomization.spec.prune` per-CR garbage-
22356 // collection-toggle leaf-scalar-key the substrate's per-caixa
22357 // `cluster_bundle` renderer seeds to `true` into every emitted
22358 // per-caixa `kustomization.yaml` document at the top-level `spec`
22359 // position. The string is part of the cluster-side contract with
22360 // the upstream Flux v2 kustomize-controller — the controller's
22361 // per-CR reconcile loop reaches the sweep-what-you-removed toggle
22362 // through this exact leaf; a drifted leaf-scalar-key silently
22363 // strips the substrate's chosen sweep-what-you-removed semantic
22364 // from every emitted per-caixa `Kustomization` document, leaving
22365 // per-caixa resources the source manifest set previously
22366 // reconciled but no longer carries dangling in the cluster the
22367 // substrate's "the cluster's per-caixa live state converges to
22368 // the caixa's tatara-lisp source-of-truth on every reconcile —
22369 // resources the source no longer carries are swept by the
22370 // kustomize-controller, not left dangling" CAIXA-SDLC.md §V
22371 // author-to-live-convergence guarantee mandates, with no
22372 // diagnostic naming the toggle-drift root cause. Changing it is
22373 // a coordinated Flux v3 CRD-schema-rebrand migration alongside
22374 // the upstream `kustomize-controller` deprecation cycle
22375 // (candidates like `garbageCollect` / `sweep` / `pruneOrphaned`
22376 // / `deleteOrphans` that upstream Flux v3 roadmap floats in the
22377 // migration prose), not an incidental edit. Peer to
22378 // `flux_helmrelease_key_create_namespace_pins_canonical_value`
22379 // on the sibling co-resident per-caixa `HelmRelease` CR install-
22380 // path per-CR namespace-seeder-toggle leaf-scalar-key half of
22381 // the same per-caixa Flux-bundle per-CR-toggle leaf-scalar-key
22382 // surface.
22383 assert_eq!(FLUX_KUSTOMIZATION_KEY_PRUNE, "prune");
22384 }
22385
22386 #[test]
22387 fn flux_kustomization_key_prune_stays_independent_of_create_namespace() {
22388 // The per-caixa Flux bundle hosts two co-resident per-CR-toggle
22389 // leaf-scalar-key axes: the per-`Kustomization`-CR garbage-
22390 // collection-toggle [`FLUX_KUSTOMIZATION_KEY_PRUNE`] at the
22391 // top-level `spec` position (this lift) and the per-`HelmRelease`-
22392 // CR install-path namespace-seeder-toggle
22393 // [`FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE`] (ba9ab8b) under the
22394 // sibling [`FLUX_HELMRELEASE_KEY_INSTALL`] parent-container-axis-
22395 // key. Pin that the two consts carry byte-distinct sequences so
22396 // a future rebrand on either arm can't silently coalesce onto
22397 // the peer arm (a `FLUX_KUSTOMIZATION_KEY_PRUNE = "createNamespace"`
22398 // typo would silently rebind the Kustomization-CR garbage-
22399 // collection-toggle onto the HelmRelease-CR install-path
22400 // namespace-seeder-toggle leaf at every emit site — the
22401 // kustomize-controller would then read the substrate's `true`
22402 // seed at the drifted leaf-key rather than the canonical `prune`
22403 // axis, silently dropping the sweep-what-you-removed semantic
22404 // entirely and leaving per-caixa resources removed from the
22405 // source manifest set dangling in the cluster with no
22406 // diagnostic naming the leaf-key-coalesce root cause). The
22407 // per-`Kustomization`-CR garbage-collection-toggle and the
22408 // per-`HelmRelease`-CR install-path namespace-seeder-toggle must
22409 // always resolve to distinct emitted leaf-keys under their
22410 // respective co-resident per-CR spec surfaces.
22411 assert_ne!(
22412 FLUX_KUSTOMIZATION_KEY_PRUNE, FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE,
22413 "the per-`Kustomization`-CR garbage-collection-toggle leaf-\
22414 scalar-key and the per-`HelmRelease`-CR install-path \
22415 namespace-seeder-toggle leaf-scalar-key must remain byte-\
22416 distinct — a coalesce onto one value silently rebinds one \
22417 CR's opt-in toggle onto the peer CR's opt-in-toggle axis at \
22418 every emit site, dropping the per-CR-specific sweep-what-\
22419 you-removed / pre-apply-namespace-seeder semantic the \
22420 substrate seeds on the coalesced arm"
22421 );
22422 }
22423
22424 #[test]
22425 fn flux_kustomization_prune_default_pins_canonical_value() {
22426 // Pin the actual boolean so a rebrand on this lift can't silently
22427 // rebrand the Flux v2 `Kustomization.spec.prune` per-CR garbage-
22428 // collection-toggle scalar-value seed the substrate's per-caixa
22429 // `cluster_bundle` renderer threads into every emitted per-caixa
22430 // `kustomization.yaml` document under the sibling
22431 // [`FLUX_KUSTOMIZATION_KEY_PRUNE`] leaf-scalar-key axis. The
22432 // scalar is part of the cluster-side contract with the upstream
22433 // Flux v2 kustomize-controller — the controller's per-CR reconcile
22434 // loop reads the scalar under the sibling leaf-scalar-key axis
22435 // to decide whether to garbage-collect resources that were
22436 // previously reconciled by the CR but no longer appear in the
22437 // CR's current desired-state manifest set. Drift from the
22438 // canonical `true` seed to `false` silently drops the substrate's
22439 // chosen sweep-what-you-removed semantic from every emitted
22440 // per-caixa `Kustomization` document, leaving per-caixa resources
22441 // the source manifest set previously reconciled but no longer
22442 // carries dangling in the cluster the substrate's "the cluster's
22443 // per-caixa live state converges to the caixa's tatara-lisp
22444 // source-of-truth on every reconcile — resources the source no
22445 // longer carries are swept by the kustomize-controller, not left
22446 // dangling" CAIXA-SDLC.md §V author-to-live-convergence guarantee
22447 // mandates, with no diagnostic naming the toggle-drift root
22448 // cause. Changing it is a substrate-side policy migration
22449 // (candidates: `true` → `false` on a per-cluster class where a
22450 // human is expected to prune orphaned resources by hand once
22451 // per-cluster policy grows an operator-driven-cleanup mode; a
22452 // per-caixa opt-out slot the ABSORPTION-ROADMAP.md M4 typed-slot
22453 // trajectory adds once the substrate grows a `:kustomization
22454 // :prune` author-side toggle), not an incidental edit. Peer to
22455 // `flux_helmrelease_remediation_retries_default_pins_lifted_value`
22456 // on the sibling per-path per-CR HelmRelease remediation retry-
22457 // cap scalar-value default axis — that default names the per-
22458 // path per-CR remediation retry ceiling, and this default names
22459 // whether the per-CR reconcile loop sweeps orphaned resources at
22460 // all. Both are substrate-side policy choices the operator
22461 // inherits when the per-caixa `ClusterBundleOpts` doesn't pin an
22462 // override.
22463 assert!(FLUX_KUSTOMIZATION_PRUNE_DEFAULT);
22464 }
22465
22466 #[test]
22467 fn flux_kustomization_prune_default_pairs_with_lifted_leaf_key() {
22468 // Sibling-pair pin: the `(leaf-scalar-key, scalar-value)` per-CR
22469 // garbage-collection-toggle declaration lives at two lifted
22470 // `pub const` declarations —
22471 // [`FLUX_KUSTOMIZATION_KEY_PRUNE`] (8ec7917) on the key half
22472 // and [`FLUX_KUSTOMIZATION_PRUNE_DEFAULT`] on the value half.
22473 // Both halves must move together on any coordinated Flux v3
22474 // migration (a `garbageCollect: false` rename that rebrands the
22475 // leaf axis onto a new controller-side opt-in vs. the current
22476 // opt-out default; a leaf coalesce onto a peer per-CR toggle
22477 // that reroutes the substrate's canonical scalar seed onto an
22478 // unrelated axis), so a rebrand on either half without a
22479 // coordinated edit on the other would silently split the
22480 // substrate's canonical sweep-what-you-removed declaration —
22481 // the emit-site format-string would still thread the `{prune_key}`
22482 // named-arg through the lifted leaf-scalar-key but pair it with
22483 // a canonical `{prune_default}` that no longer reflects the
22484 // substrate-side semantic the leaf axis names. Pin the pair here
22485 // so a future edit that touches only the leaf-scalar-key half
22486 // or only the scalar-value default half surfaces at build time
22487 // rather than at reconcile time far from the source edit.
22488 // Confirms both consts carry their canonical wire representations
22489 // (`"prune"` byte-string on the leaf-scalar-key half; `true` on
22490 // the scalar-value default half) — the pair as-a-unit reads as
22491 // the substrate's chosen `prune: true` per-CR opt-in.
22492 assert_eq!(FLUX_KUSTOMIZATION_KEY_PRUNE, "prune");
22493 assert!(FLUX_KUSTOMIZATION_PRUNE_DEFAULT);
22494 }
22495
22496 #[test]
22497 fn flux_helmrelease_remediate_last_failure_default_pins_canonical_value() {
22498 // Pin the actual boolean so a rebrand on this lift can't silently
22499 // rebrand the Flux v2 `HelmRelease.spec.upgrade.remediation
22500 // .remediateLastFailure` upgrade-path-only per-CR remediation-toggle
22501 // scalar-value seed the substrate's per-caixa `cluster_bundle`
22502 // renderer threads into every emitted per-caixa `helmrelease.yaml`
22503 // document under the sibling
22504 // [`FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE`] leaf-scalar-key
22505 // axis. The scalar is part of the cluster-side contract with the
22506 // upstream Flux v2 helm-controller — the controller's per-CR
22507 // upgrade-path remediation loop reads the scalar under the sibling
22508 // leaf-scalar-key axis to decide whether to trigger the prior-
22509 // release rollback pipeline once the paired
22510 // [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] retry-cap ceiling
22511 // has been exhausted. Drift from the canonical `true` seed to
22512 // `false` silently drops the substrate's chosen post-retry-
22513 // exhaustion rollback semantic from every emitted per-caixa
22514 // `HelmRelease` document, leaving every terminally-failed upgrade
22515 // parked at `Ready: False` without rolling back to the prior last-
22516 // known-good release the substrate's "no chart apply leaves a
22517 // per-caixa CR in a stalled, unremediated state" MESH-COMPOSITION
22518 // .md §V guarantee mandates, with no diagnostic naming the
22519 // remediation-toggle-drift root cause. Changing it is a substrate-
22520 // side policy migration (candidates: `true` → `false` on a per-
22521 // cluster class where terminally-failed upgrades must escalate to
22522 // operator-attention rather than mask under an auto-rollback pipe-
22523 // line; a per-caixa opt-out slot the ABSORPTION-ROADMAP.md M4
22524 // typed-slot trajectory adds once the substrate grows a `:upgrade
22525 // :remediate-last-failure` author-side toggle), not an incidental
22526 // edit. Peer to `flux_kustomization_prune_default_pins_canonical_value`
22527 // on the sibling per-`Kustomization`-CR garbage-collection-toggle
22528 // scalar-value default axis — that default names whether the
22529 // per-CR `Kustomization` reconcile loop sweeps orphaned resources
22530 // at all, and this default names whether the per-CR `HelmRelease`
22531 // upgrade-path remediation loop rolls back to the prior last-
22532 // known-good release once the retry-cap ceiling is exhausted.
22533 // Both are substrate-side policy choices the operator inherits
22534 // when the per-caixa `ClusterBundleOpts` doesn't pin an override.
22535 assert!(FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT);
22536 }
22537
22538 #[test]
22539 fn flux_helmrelease_remediate_last_failure_default_pairs_with_lifted_leaf_key() {
22540 // Sibling-pair pin: the `(leaf-scalar-key, scalar-value)` per-CR
22541 // upgrade-path per-CR post-retry-exhaustion-rollback-toggle
22542 // declaration lives at two lifted `pub const` declarations —
22543 // [`FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE`] (96581b7) on the
22544 // key half and [`FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT`]
22545 // on the value half. Both halves must move together on any
22546 // coordinated Flux v3 migration (a `rollbackOnFailure: false`
22547 // rename that rebrands the leaf axis onto a new controller-side
22548 // opt-in vs. the current opt-in default; a leaf coalesce onto a
22549 // peer per-CR toggle that reroutes the substrate's canonical
22550 // scalar seed onto an unrelated axis), so a rebrand on either half
22551 // without a coordinated edit on the other would silently split the
22552 // substrate's canonical post-retry-exhaustion rollback declaration
22553 // — the emit-site format-string would still thread the
22554 // `{remediate_last_failure_key}` named-arg through the lifted
22555 // leaf-scalar-key but pair it with a canonical
22556 // `{remediate_last_failure_default}` that no longer reflects the
22557 // substrate-side semantic the leaf axis names. Pin the pair here
22558 // so a future edit that touches only the leaf-scalar-key half or
22559 // only the scalar-value default half surfaces at build time rather
22560 // than at reconcile time far from the source edit. Confirms both
22561 // consts carry their canonical wire representations
22562 // (`"remediateLastFailure"` byte-string on the leaf-scalar-key
22563 // half; `true` on the scalar-value default half) — the pair as-a-
22564 // unit reads as the substrate's chosen
22565 // `remediateLastFailure: true` per-CR opt-in.
22566 assert_eq!(
22567 FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE,
22568 "remediateLastFailure"
22569 );
22570 assert!(FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT);
22571 }
22572
22573 #[test]
22574 fn flux_helmrelease_create_namespace_default_pins_canonical_value() {
22575 // Pin the actual boolean so a rebrand on this lift can't silently
22576 // rebrand the Flux v2 `HelmRelease.spec.install.createNamespace`
22577 // install-path-only per-CR namespace-seeder-toggle scalar-value
22578 // seed the substrate's per-caixa `cluster_bundle` renderer threads
22579 // into every emitted per-caixa `helmrelease.yaml` document under
22580 // the sibling [`FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE`] leaf-
22581 // scalar-key axis. The scalar is part of the cluster-side contract
22582 // with the upstream Flux v2 helm-controller — the controller's
22583 // per-CR install-path pre-apply loop reads the scalar under the
22584 // sibling leaf-scalar-key axis to decide whether to first material-
22585 // ize the target namespace before the first-time chart apply.
22586 // Drift from the canonical `true` seed to `false` silently drops
22587 // the substrate's chosen first-apply namespace-seeder semantic
22588 // from every emitted per-caixa `HelmRelease` document, leaving
22589 // every first-time per-caixa chart apply against a fresh cluster
22590 // refused by the helm-controller because the target namespace was
22591 // not pre-provisioned by an out-of-band pipeline the substrate's
22592 // "no per-caixa Servico apply is blocked on manual namespace
22593 // preprovisioning" MESH-COMPOSITION.md §V install-path-fluency
22594 // guarantee mandates, with no diagnostic naming the seeder-toggle-
22595 // drift root cause. Changing it is a substrate-side policy
22596 // migration (candidates: `true` → `false` on hardened per-cluster
22597 // classes where namespace provisioning is an out-of-band operator
22598 // gate; a per-caixa opt-out slot the ABSORPTION-ROADMAP.md M4
22599 // typed-slot trajectory adds once the substrate grows a `:install
22600 // :create-namespace` author-side toggle), not an incidental edit.
22601 // Peer to `flux_helmrelease_remediate_last_failure_default_pins_canonical_value`
22602 // on the sibling mirror-symmetric upgrade-path-only per-CR
22603 // remediation-toggle scalar-value default axis — that default
22604 // names whether the per-CR `HelmRelease` upgrade-path remediation
22605 // loop rolls back to the prior last-known-good release once the
22606 // retry-cap ceiling is exhausted, and this default names whether
22607 // the per-CR `HelmRelease` install-path pre-apply loop materializes
22608 // the target namespace before the first-time chart apply. Both
22609 // are substrate-side policy choices the operator inherits when
22610 // the per-caixa `ClusterBundleOpts` doesn't pin an override, and
22611 // both close the mirror-symmetric install/upgrade per-CR phase-
22612 // specific toggle scalar-value default pair the peer leaf-scalar-
22613 // key pair [`FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE`] (ba9ab8b) /
22614 // [`FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE`] (96581b7)
22615 // already closed on the key half.
22616 assert!(FLUX_HELMRELEASE_CREATE_NAMESPACE_DEFAULT);
22617 }
22618
22619 #[test]
22620 fn flux_helmrelease_create_namespace_default_pairs_with_lifted_leaf_key() {
22621 // Sibling-pair pin: the `(leaf-scalar-key, scalar-value)` per-CR
22622 // install-path per-CR namespace-seeder-toggle declaration lives
22623 // at two lifted `pub const` declarations —
22624 // [`FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE`] (ba9ab8b) on the key
22625 // half and [`FLUX_HELMRELEASE_CREATE_NAMESPACE_DEFAULT`] on the
22626 // value half. Both halves must move together on any coordinated
22627 // Flux v3 migration (a `createTargetNamespace: false` rename that
22628 // rebrands the leaf axis onto a new controller-side opt-in vs.
22629 // the current opt-in default; a leaf coalesce onto a peer per-CR
22630 // toggle that reroutes the substrate's canonical scalar seed onto
22631 // an unrelated axis), so a rebrand on either half without a
22632 // coordinated edit on the other would silently split the substrate's
22633 // canonical first-apply namespace-seeder declaration — the emit-
22634 // site format-string would still thread the
22635 // `{create_namespace_key}` named-arg through the lifted leaf-
22636 // scalar-key but pair it with a canonical `{create_namespace_default}`
22637 // that no longer reflects the substrate-side semantic the leaf
22638 // axis names. Pin the pair here so a future edit that touches
22639 // only the leaf-scalar-key half or only the scalar-value default
22640 // half surfaces at build time rather than at reconcile time far
22641 // from the source edit. Confirms both consts carry their canonical
22642 // wire representations (`"createNamespace"` byte-string on the
22643 // leaf-scalar-key half; `true` on the scalar-value default half) —
22644 // the pair as-a-unit reads as the substrate's chosen
22645 // `createNamespace: true` per-CR opt-in.
22646 assert_eq!(FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE, "createNamespace");
22647 assert!(FLUX_HELMRELEASE_CREATE_NAMESPACE_DEFAULT);
22648 }
22649
22650 #[test]
22651 fn cluster_bundle_lareira_enabled_default_pins_canonical_value() {
22652 // Pin the actual boolean so a rebrand on this lift can't silently
22653 // rebrand the substrate-side default for the
22654 // `HelmRelease.spec.values.<library>.enabled` child-chart-
22655 // enablement toggle scalar the substrate's per-caixa
22656 // `cluster_bundle` renderer threads into every emitted per-caixa
22657 // `helmrelease.yaml` document under the sibling
22658 // [`HELM_VALUES_KEY_ENABLED`] leaf-scalar-key axis inside the
22659 // per-`{library_name}` values-overlay wrap. The scalar is the
22660 // substrate's chosen "force-on the child chart under the
22661 // cluster_bundle composition path" default — semantically
22662 // distinct from and inverse of the standalone
22663 // [`caixa_helm::RenderOpts`]::`enabled_default = false` seed
22664 // (which renders `enabled: false` in the per-caixa `values.yaml`
22665 // so cluster operators must opt each caixa in per-cluster); the
22666 // `cluster_bundle` composition path is the substrate-side
22667 // opt-in path where the operator has already asserted per-caixa
22668 // cluster-scoped ownership by materializing a per-caixa
22669 // GitRepository + HelmRelease + Kustomization trio, so the
22670 // overlay forces the child chart on by seeding `enabled: true`
22671 // under the `values.<library>` wrap. Drift from the canonical
22672 // `true` seed to `false` silently drops the substrate's chosen
22673 // force-on-under-composition semantic from every emitted
22674 // per-caixa `HelmRelease` document, leaving the paired
22675 // [`DEFAULT_LIBRARY_NAME`] child chart's `enabled: false`
22676 // per-chart default un-overridden — the Helm rendering pipeline
22677 // then no-ops every per-caixa lareira child chart at the
22678 // per-cluster `HelmRelease` apply step, with no diagnostic
22679 // naming the toggle-drift root cause. Peer to the sibling
22680 // `flux_helmrelease_create_namespace_default_pins_canonical_value`
22681 // (be1904b) / `flux_helmrelease_remediate_last_failure_default_pins_canonical_value`
22682 // (be1904b) / `flux_kustomization_prune_default_pins_canonical_value`
22683 // (ea857d8) on the peer canonical-Flux-v2-per-CR-substrate-
22684 // default surface — all four defaults are substrate-side policy
22685 // choices the operator inherits when the per-caixa
22686 // `ClusterBundleOpts` doesn't pin an override.
22687 assert!(CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT);
22688 }
22689
22690 #[test]
22691 fn cluster_bundle_lareira_enabled_default_pairs_with_lifted_leaf_key() {
22692 // Sibling-pair pin: the `(leaf-scalar-key, scalar-value)` per-
22693 // values-overlay child-chart-enablement-toggle declaration lives
22694 // at two lifted `pub const` declarations —
22695 // [`HELM_VALUES_KEY_ENABLED`] on the key half and
22696 // [`CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT`] on the value half.
22697 // Both halves must move together on any coordinated Helm 4
22698 // migration (an `on: true` rename that rebrands the leaf axis
22699 // onto a new controller-side opt-in vs. the current opt-in
22700 // default; a leaf coalesce onto a peer per-values-block toggle
22701 // that reroutes the substrate's canonical scalar seed onto an
22702 // unrelated axis), so a rebrand on either half without a
22703 // coordinated edit on the other would silently split the
22704 // substrate's canonical force-on-under-composition declaration —
22705 // the emit-site format-string would still thread the
22706 // `{enabled_key}` named-arg through the lifted leaf-scalar-key
22707 // but pair it with a canonical `{lareira_enabled_default}` that
22708 // no longer reflects the substrate-side semantic the leaf axis
22709 // names. Pin the pair here so a future edit that touches only
22710 // the leaf-scalar-key half or only the scalar-value default
22711 // half surfaces at build time rather than at apply time far
22712 // from the source edit. Confirms both consts carry their
22713 // canonical wire representations (`"enabled"` byte-string on
22714 // the leaf-scalar-key half; `true` on the scalar-value default
22715 // half) — the pair as-a-unit reads as the substrate's chosen
22716 // `enabled: true` per-values-overlay opt-in. Peer to
22717 // `flux_kustomization_prune_default_pairs_with_lifted_leaf_key`
22718 // (ea857d8) /
22719 // `flux_helmrelease_create_namespace_default_pairs_with_lifted_leaf_key`
22720 // (be1904b) /
22721 // `flux_helmrelease_remediate_last_failure_default_pairs_with_lifted_leaf_key`
22722 // (be1904b) on the sibling canonical-Flux-v2-per-CR-
22723 // substrate-default paired-halves surfaces.
22724 assert_eq!(HELM_VALUES_KEY_ENABLED, "enabled");
22725 assert!(CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT);
22726 }
22727
22728 #[test]
22729 fn standalone_lareira_enabled_default_pins_canonical_value() {
22730 // Pin the actual boolean so a rebrand on this lift can't silently
22731 // rebrand the substrate-side default for the
22732 // `values.<library>.enabled` child-chart-enablement toggle scalar
22733 // the substrate's per-caixa `caixa_helm::render_chart_for_servico`
22734 // renderer seeds into every emitted per-caixa `values.yaml`
22735 // document under the sibling [`HELM_VALUES_KEY_ENABLED`]
22736 // leaf-scalar-key axis inside the per-`{library_name}` wrap. The
22737 // scalar is the substrate's chosen "leave the child chart opted
22738 // out under the standalone per-chart path" default —
22739 // semantically distinct from and inverse of the composition
22740 // [`CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT`] seed (which renders
22741 // `enabled: true` in the per-cluster `HelmRelease` values-overlay
22742 // so the substrate force-ons the child chart at bundle
22743 // materialization time); the standalone per-chart path is the
22744 // substrate-side opt-out path where the operator has not yet
22745 // asserted per-caixa cluster-scoped ownership by materializing a
22746 // per-caixa GitRepository + HelmRelease + Kustomization trio, so
22747 // the per-chart `values.yaml` seeds `enabled: false` under the
22748 // `values.<library>` wrap and cluster operators must opt each
22749 // caixa in per-cluster. Drift from the canonical `false` seed to
22750 // `true` silently drops the substrate's chosen
22751 // opt-out-under-standalone semantic from every emitted per-caixa
22752 // `values.yaml` document, force-onning the paired
22753 // [`DEFAULT_LIBRARY_NAME`] child chart against the operator's
22754 // stated per-cluster opt-in convention — every rendered chart's
22755 // library-chart-side workload would come up on `helm template` /
22756 // `helm install` with no diagnostic naming the toggle-drift root
22757 // cause. Peer to `cluster_bundle_lareira_enabled_default_pins_canonical_value`
22758 // on the sibling composition-path `HelmRelease.spec.values.<library>.enabled`
22759 // scalar-value default surface — both defaults are substrate-side
22760 // policy choices the operator inherits when the per-caixa
22761 // `RenderOpts` / `ClusterBundleOpts` doesn't pin an override, and
22762 // together they close the mirror-symmetric standalone / composition
22763 // per-values-block child-chart-enablement-toggle scalar-value
22764 // default pair.
22765 assert!(!STANDALONE_LAREIRA_ENABLED_DEFAULT);
22766 }
22767
22768 #[test]
22769 fn standalone_lareira_enabled_default_pairs_with_lifted_leaf_key() {
22770 // Sibling-pair pin: the `(leaf-scalar-key, scalar-value)` per-
22771 // values-block child-chart-enablement-toggle declaration on the
22772 // standalone per-chart path lives at two lifted `pub const`
22773 // declarations — [`HELM_VALUES_KEY_ENABLED`] on the key half and
22774 // [`STANDALONE_LAREIRA_ENABLED_DEFAULT`] on the value half. Both
22775 // halves must move together on any coordinated Helm 4 migration
22776 // (an `on: false` rename that rebrands the leaf axis onto a new
22777 // controller-side opt-in vs. the current opt-out default; a leaf
22778 // coalesce onto a peer per-values-block toggle that reroutes the
22779 // substrate's canonical scalar seed onto an unrelated axis), so a
22780 // rebrand on either half without a coordinated edit on the other
22781 // would silently split the substrate's canonical
22782 // opt-out-under-standalone declaration — the emit-site block
22783 // insertion would still thread [`HELM_VALUES_KEY_ENABLED`] as the
22784 // key but pair it with a canonical `enabled_default` scalar-value
22785 // seed that no longer reflects the substrate-side semantic the
22786 // leaf axis names. Pin the pair here so a future edit that
22787 // touches only the leaf-scalar-key half or only the scalar-value
22788 // default half surfaces at build time rather than at apply time
22789 // far from the source edit. Confirms both consts carry their
22790 // canonical wire representations (`"enabled"` byte-string on the
22791 // leaf-scalar-key half; `false` on the scalar-value default half)
22792 // — the pair as-a-unit reads as the substrate's chosen
22793 // `enabled: false` per-values-block opt-out. Peer to
22794 // `cluster_bundle_lareira_enabled_default_pairs_with_lifted_leaf_key`
22795 // on the sibling composition-path
22796 // `HelmRelease.spec.values.<library>.enabled` scalar-value default
22797 // paired-halves surface — both `(key, value)` pairs share the same
22798 // [`HELM_VALUES_KEY_ENABLED`] leaf-scalar-key half but diverge on
22799 // the scalar-value half, which is exactly the mirror-symmetric
22800 // standalone / composition path-selection the two scalar-value
22801 // defaults name.
22802 assert_eq!(HELM_VALUES_KEY_ENABLED, "enabled");
22803 assert!(!STANDALONE_LAREIRA_ENABLED_DEFAULT);
22804 }
22805
22806 #[test]
22807 fn standalone_and_cluster_bundle_lareira_enabled_defaults_are_inverse_by_construction() {
22808 // Cross-const coherence pin: the two peer
22809 // per-values-block child-chart-enablement-toggle scalar-value
22810 // defaults on the standalone per-chart path
22811 // ([`STANDALONE_LAREIRA_ENABLED_DEFAULT`]) and the composition
22812 // per-cluster-`HelmRelease` values-overlay path
22813 // ([`CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT`]) name mirror-symmetric
22814 // inverse defaults on the same underlying
22815 // `values.<library>.enabled` sub-block axis: the standalone-path
22816 // default is `false` (opt-out — cluster operators must opt each
22817 // caixa in per-cluster) while the composition-path default is
22818 // `true` (opt-in — the substrate force-ons the child chart once
22819 // the operator has asserted per-caixa cluster-scoped ownership by
22820 // materializing a per-caixa GitRepository + HelmRelease +
22821 // Kustomization trio). The inversion is the substrate's chosen
22822 // author-to-live path-selection semantic — every consumer that
22823 // reads either default inherits the per-path opt-out / opt-in
22824 // decision by construction, so a future edit that accidentally
22825 // aligned the two defaults (both `false` on a substrate-wide
22826 // opt-out migration, both `true` on a substrate-wide opt-in
22827 // migration) would silently collapse the substrate's chosen
22828 // standalone-vs-composition path-selection semantic — the
22829 // per-chart `values.yaml` default and the per-cluster
22830 // `HelmRelease.spec.values.<library>.enabled` overlay default
22831 // would agree on the same enablement seed, and either the
22832 // standalone path would force-on the child chart against the
22833 // operator's per-cluster opt-in convention (both `true`) or the
22834 // composition path would leave the child chart opted-out against
22835 // the operator's per-caixa cluster-scoped ownership assertion
22836 // (both `false`). Pin the structural inversion here so a future
22837 // edit that touches only one of the two defaults surfaces at
22838 // caixa-core build time rather than at chart-apply time far from
22839 // the constant-drift source. Confirms the two `bool`s carry
22840 // distinct canonical wire representations — the pair as-a-unit
22841 // reads as the substrate's chosen mirror-symmetric author-to-live
22842 // path-selection semantic (standalone opt-out, composition
22843 // opt-in). Peer to the sibling pairwise-distinctness pins the
22844 // `M3_PLACEMENT_ESTRATEGIA_*` /
22845 // `M2_UPGRADE_INSTRUCTION_KIND_*` closed-set typed-enum
22846 // discriminator axes carry on the peer canonical-typed-enum-
22847 // discriminator distinctness surface.
22848 assert_ne!(
22849 STANDALONE_LAREIRA_ENABLED_DEFAULT, CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT,
22850 "STANDALONE_LAREIRA_ENABLED_DEFAULT and CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT \
22851 must remain inverse `bool`s — the standalone per-chart path defaults to \
22852 opt-out (`false`) and the composition per-cluster-HelmRelease values-overlay \
22853 path defaults to opt-in (`true`); collapsing the inversion silently \
22854 breaks the substrate's chosen mirror-symmetric author-to-live \
22855 path-selection semantic at chart-apply time far from the constant-\
22856 drift source."
22857 );
22858 }
22859
22860 #[test]
22861 fn flux_kustomization_key_path_pins_canonical_value() {
22862 // Pin the actual string so a typo in this lift can't silently
22863 // rebrand the Flux v2 `Kustomization.spec.path` per-CR source-
22864 // sub-tree leaf-scalar-key the substrate's per-caixa
22865 // `cluster_bundle` renderer seeds into every emitted per-caixa
22866 // `kustomization.yaml` document at the top-level `spec`
22867 // position. The string is part of the cluster-side contract
22868 // with the upstream Flux v2 kustomize-controller — the
22869 // controller's per-CR reconcile loop reaches the source-sub-
22870 // tree pointer through this exact leaf; a drifted leaf-scalar-
22871 // key silently unbinds every per-caixa `Kustomization` from
22872 // its paired per-caixa sub-tree of the pleme-io k8s repository
22873 // (the controller defaults to `./` when the CR omits the leaf,
22874 // pulling every unrelated cluster's manifests through the
22875 // wrong per-caixa `Kustomization`), with no diagnostic naming
22876 // the leaf-drift root cause. Changing it is a coordinated Flux
22877 // v3 CRD-schema-rebrand migration alongside the upstream
22878 // `kustomize-controller` deprecation cycle (candidates like
22879 // `sourcePath` / `manifestsPath` / `sourceRoot` upstream Flux
22880 // v3 roadmap floats), not an incidental edit. Peer to
22881 // `flux_kustomization_key_prune_pins_canonical_value` on the
22882 // sibling co-resident per-`Kustomization`-CR `spec.prune`
22883 // garbage-collection-toggle leaf-scalar-key half of the same
22884 // per-`Kustomization`-CR-spec surface.
22885 assert_eq!(FLUX_KUSTOMIZATION_KEY_PATH, "path");
22886 }
22887
22888 #[test]
22889 fn flux_kustomization_key_path_stays_independent_of_prune() {
22890 // The per-`Kustomization`-CR top-level `spec` surface hosts two
22891 // co-resident leaf-scalar-key axes: the per-CR source-sub-tree
22892 // pointer [`FLUX_KUSTOMIZATION_KEY_PATH`] (this lift) and the
22893 // per-CR garbage-collection-toggle [`FLUX_KUSTOMIZATION_KEY_PRUNE`]
22894 // (8ec7917). Pin that the two consts carry byte-distinct
22895 // sequences so a future rebrand on either arm can't silently
22896 // coalesce onto the peer arm (a
22897 // `FLUX_KUSTOMIZATION_KEY_PATH = "prune"` typo would silently
22898 // rebind the substrate's per-cluster / per-caixa sub-tree path
22899 // seed onto the garbage-collection-toggle leaf at every emit
22900 // site — the kustomize-controller would then read the
22901 // substrate's `./clusters/<cluster>/services/<name>` seed as a
22902 // boolean opt-in toggle, silently unbinding the per-caixa
22903 // `Kustomization` from its source-sub-tree entirely with no
22904 // diagnostic naming the leaf-key-coalesce root cause). The
22905 // per-`Kustomization`-CR source-sub-tree pointer and the per-
22906 // `Kustomization`-CR garbage-collection-toggle must always
22907 // resolve to distinct emitted leaf-keys under the same
22908 // top-level `spec` position.
22909 assert_ne!(
22910 FLUX_KUSTOMIZATION_KEY_PATH, FLUX_KUSTOMIZATION_KEY_PRUNE,
22911 "the per-`Kustomization`-CR source-sub-tree leaf-scalar-key \
22912 and the per-`Kustomization`-CR garbage-collection-toggle \
22913 leaf-scalar-key must remain byte-distinct — a coalesce \
22914 onto one value silently rebinds one axis onto the peer \
22915 axis at every emit site, dropping the source-sub-tree / \
22916 sweep-what-you-removed semantic the substrate seeds on the \
22917 coalesced arm"
22918 );
22919 }
22920
22921 #[test]
22922 fn flux_kustomization_key_timeout_pins_canonical_value() {
22923 // Pin the actual string so a typo in this lift can't silently
22924 // rebrand the Flux v2 `Kustomization.spec.timeout` per-CR
22925 // reconcile wall-clock cap leaf-scalar-key the substrate's per-
22926 // caixa `cluster_bundle` renderer seeds into every emitted per-
22927 // caixa `kustomization.yaml` document at the top-level `spec`
22928 // position. The string is part of the cluster-side contract
22929 // with the upstream Flux v2 kustomize-controller — the
22930 // controller's per-CR reconcile loop reaches the wall-clock cap
22931 // through this exact leaf; a drifted leaf-scalar-key silently
22932 // strips the substrate's chosen reconcile-ceiling from every
22933 // emitted per-caixa `Kustomization` document, letting the
22934 // controller fall back to the upstream Flux v2 controller-side
22935 // default cap rather than the substrate's per-caixa
22936 // idempotency-checkpoint-tuned ceiling, with no diagnostic
22937 // naming the timeout-drift root cause. Changing it is a
22938 // coordinated Flux v3 CRD-schema-rebrand migration alongside
22939 // the upstream `kustomize-controller` deprecation cycle, not
22940 // an incidental edit. Peer to
22941 // `flux_kustomization_key_path_pins_canonical_value` and
22942 // `flux_kustomization_key_prune_pins_canonical_value` on the
22943 // sibling co-resident per-`Kustomization`-CR spec surface
22944 // leaf-scalar-key axes.
22945 assert_eq!(FLUX_KUSTOMIZATION_KEY_TIMEOUT, "timeout");
22946 }
22947
22948 #[test]
22949 fn flux_kustomization_key_timeout_stays_independent_of_path_and_prune() {
22950 // The per-`Kustomization`-CR top-level `spec` surface hosts
22951 // three co-resident leaf-scalar-key axes: the per-CR reconcile
22952 // wall-clock cap [`FLUX_KUSTOMIZATION_KEY_TIMEOUT`] (this
22953 // lift), the per-CR source-sub-tree pointer
22954 // [`FLUX_KUSTOMIZATION_KEY_PATH`] (613d7ed), and the per-CR
22955 // garbage-collection-toggle [`FLUX_KUSTOMIZATION_KEY_PRUNE`]
22956 // (8ec7917). Pin that the three consts carry byte-distinct
22957 // sequences so a future rebrand on any one arm can't silently
22958 // coalesce onto a peer arm (a
22959 // `FLUX_KUSTOMIZATION_KEY_TIMEOUT = "path"` typo would silently
22960 // rebind the reconcile wall-clock cap onto the source-sub-tree
22961 // pointer leaf at every emit site — the kustomize-controller
22962 // would then parse the substrate's `./clusters/<c>/services/<n>`
22963 // seed as a `metav1.Duration` scalar and reject the per-CR
22964 // admission gate, with no diagnostic naming the leaf-key-
22965 // coalesce root cause). The per-`Kustomization`-CR reconcile
22966 // wall-clock cap, per-CR source-sub-tree pointer, and per-CR
22967 // garbage-collection-toggle must always resolve to distinct
22968 // emitted leaf-keys under the same top-level `spec` position.
22969 assert_ne!(
22970 FLUX_KUSTOMIZATION_KEY_TIMEOUT, FLUX_KUSTOMIZATION_KEY_PATH,
22971 "the per-`Kustomization`-CR reconcile wall-clock cap leaf-\
22972 scalar-key and the per-`Kustomization`-CR source-sub-tree \
22973 leaf-scalar-key must remain byte-distinct — a coalesce onto \
22974 one value silently rebinds one axis onto the peer axis at \
22975 every emit site, dropping the reconcile-ceiling / source-\
22976 sub-tree semantic the substrate seeds on the coalesced arm"
22977 );
22978 assert_ne!(
22979 FLUX_KUSTOMIZATION_KEY_TIMEOUT, FLUX_KUSTOMIZATION_KEY_PRUNE,
22980 "the per-`Kustomization`-CR reconcile wall-clock cap leaf-\
22981 scalar-key and the per-`Kustomization`-CR garbage-\
22982 collection-toggle leaf-scalar-key must remain byte-distinct \
22983 — a coalesce onto one value silently rebinds one axis onto \
22984 the peer axis at every emit site, dropping the reconcile-\
22985 ceiling / sweep-what-you-removed semantic the substrate \
22986 seeds on the coalesced arm"
22987 );
22988 }
22989
22990 #[test]
22991 fn default_flux_kustomization_timeout_pins_canonical_value() {
22992 // Pin the actual scalar so a typo in this lift can't silently
22993 // rebrand the substrate-side default Flux v2
22994 // `Kustomization.spec.timeout` reconcile wall-clock cap the
22995 // substrate's per-caixa `cluster_bundle` renderer seeds into
22996 // every emitted per-caixa `kustomization.yaml` document at the
22997 // top-level `spec` position. The value is part of the cluster-
22998 // side contract with the Flux v2 kustomize-controller (the
22999 // per-CR reconcile loop uses this as the ceiling on the wall-
23000 // clock time a single reconcile attempt is allowed to consume
23001 // before the controller marks the `Kustomization`
23002 // `Ready: False` and stops retrying); changing it is a
23003 // coordinated substrate-side reconcile-ceiling promotion (a
23004 // `5m` → `3m` migration on faster per-caixa idempotency-
23005 // checkpoint cadence, a `5m` → `10m` migration on larger per-
23006 // caixa manifest sets), not an incidental edit. Peer to
23007 // `default_flux_reconcile_interval_pins_canonical_value` and
23008 // `flux_helmrelease_remediation_retries_default_pins_canonical_value`
23009 // on the canonical-Flux-v2-per-CR-substrate-default-scalar pin
23010 // surface.
23011 assert_eq!(DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT, "5m");
23012 }
23013
23014 #[test]
23015 fn default_flux_kustomization_timeout_is_a_valid_metav1_duration_scalar() {
23016 // Cross-axis grammar invariant: the Flux v2 kustomize-
23017 // controller-side per-CR admission gate parses the reconcile
23018 // wall-clock cap scalar via `metav1.ParseDuration` before
23019 // installing the per-CR watch. The Go-duration-format grammar
23020 // is non-empty, ASCII, and structured as
23021 // `<digits><unit>[<digits><unit>...]` where each unit is one of
23022 // `{ns, us, µs, ms, s, m, h}`. Pin a floor that catches the
23023 // canonical drift footguns — an empty scalar (`""` — admission
23024 // gate rejects), a non-ASCII-alphanumeric byte (`"5 m"` — the
23025 // whitespace defeats the parser), a missing-unit scalar (`"5"`
23026 // — the parser rejects for lack of a unit suffix), or a
23027 // leading-non-digit scalar (`"m5"` — the parser rejects for
23028 // lack of a leading magnitude). A future rebrand on the
23029 // canonical lift that lands a value outside the Go-duration-
23030 // format grammar would surface here at caixa-core build time
23031 // on the canonical lift, before any renderer consumes the
23032 // value. Same shape as
23033 // `default_flux_reconcile_interval_is_a_valid_metav1_duration_scalar`
23034 // on the peer canonical-substrate-default-grammar-floor surface.
23035 let v = DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT;
23036 assert!(
23037 !v.is_empty(),
23038 "DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT {v:?} must be non-empty \
23039 per the Flux v2 controller-side `metav1.ParseDuration` \
23040 admission gate"
23041 );
23042 assert!(
23043 v.chars().all(|c| c.is_ascii_alphanumeric()),
23044 "DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT {v:?} must be ASCII-\
23045 alphanumeric throughout per the Go-duration-format grammar \
23046 — no whitespace / separator bytes the `metav1.ParseDuration` \
23047 admission gate would reject"
23048 );
23049 let first = v.chars().next().expect("non-empty");
23050 assert!(
23051 first.is_ascii_digit(),
23052 "DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT {v:?} first byte {first:?} \
23053 must be an ASCII digit per the Go-duration-format grammar \
23054 — the leading magnitude precedes the unit suffix; a leading \
23055 non-digit defeats `metav1.ParseDuration`"
23056 );
23057 let last = v.chars().next_back().expect("non-empty");
23058 assert!(
23059 last.is_ascii_alphabetic() && last.is_ascii_lowercase(),
23060 "DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT {v:?} last byte {last:?} \
23061 must be an ASCII lowercase alphabetic unit suffix per the \
23062 Go-duration-format grammar — the trailing unit follows the \
23063 magnitude; an unterminated magnitude defeats \
23064 `metav1.ParseDuration`"
23065 );
23066 }
23067
23068 #[test]
23069 fn default_gateway_class_name_pins_canonical_value() {
23070 // Pin the actual string so a typo in this lift can't silently
23071 // rebrand the substrate's chosen K8s Gateway API controller the
23072 // rendered `Gateway`'s `spec.gatewayClassName` axis binds to.
23073 // The string is part of the cluster-side contract with the Cilium
23074 // Gateway API implementation (the Cilium operator watches
23075 // `GatewayClass` objects whose `spec.controllerName` names the
23076 // Cilium reconciler; a drifted `spec.gatewayClassName` on the
23077 // emitted `Gateway` refers to a `GatewayClass` no controller
23078 // reconciles, and the `Gateway` sits at `Programmed: False`
23079 // with every attached `HTTPRoute` unbound), the same eBPF-identity
23080 // data plane the sibling `CiliumNetworkPolicy` renderer emits
23081 // policies against (the mesh-composition "one identity layer,
23082 // one data plane" invariant, MESH-COMPOSITION.md §V), and the
23083 // per-cluster GatewayClass fixture the operator-side install
23084 // pipeline provisions. Changing it is a coordinated multi-repo
23085 // migration (a substrate-side Gateway controller migration to
23086 // Envoy Gateway / Istio Gateway or any per-edition variant),
23087 // not an incidental edit. Peer to
23088 // `default_namespace_pins_canonical_value` and
23089 // `default_flux_system_namespace_pins_canonical_value` on the
23090 // canonical-substrate-default-resource-name-value-pin axis.
23091 assert_eq!(DEFAULT_GATEWAY_CLASS_NAME, "cilium");
23092 }
23093
23094 #[test]
23095 fn default_gateway_class_name_is_a_valid_dns_1123_label() {
23096 // Cross-axis invariant: the Gateway API `GatewayClass` is a
23097 // cluster-scoped K8s resource, and the K8s apiserver enforces
23098 // the DNS-1123 label rule on every cluster-scoped resource's
23099 // `metadata.name`. The emitted `Gateway`'s
23100 // `spec.gatewayClassName` axis references the `GatewayClass`
23101 // resource by that name — a drift to a value the apiserver
23102 // would refuse as a `GatewayClass.metadata.name` couldn't
23103 // resolve at reconcile time either, and the `Gateway`
23104 // Programmed condition never flips true. Pinning this here
23105 // means a future rebrand on the canonical lift can't silently
23106 // land a value the apiserver refuses at the *first* `Gateway`
23107 // apply against a cluster, far from the rebrand commit's
23108 // source — the typed [`is_dns_1123_label`] floor rejects it at
23109 // caixa-core build time on the canonical lift, before any
23110 // renderer consumes the value. Same shape as
23111 // `default_namespace_is_a_valid_dns_1123_label` and
23112 // `default_flux_system_namespace_is_a_valid_dns_1123_label` on
23113 // the peer canonical-DNS-1123-label-floor axes.
23114 assert!(
23115 is_dns_1123_label(DEFAULT_GATEWAY_CLASS_NAME).is_ok(),
23116 "DEFAULT_GATEWAY_CLASS_NAME {DEFAULT_GATEWAY_CLASS_NAME:?} must be a \
23117 valid DNS-1123 label — every K8s apiserver-side schema enforces \
23118 this rule on cluster-scoped `metadata.name` axes, and the \
23119 `Gateway.spec.gatewayClassName` axis resolves by that same rule"
23120 );
23121 }
23122
23123 #[test]
23124 fn flux_helmrelease_api_version_pins_canonical_value() {
23125 // Pin the actual string so a typo in this lift can't silently
23126 // rebrand the Flux v2 `HelmRelease` CRD group/version the rendered
23127 // `helmrelease.yaml` document declares + the rendered
23128 // `kustomization.yaml` document's `healthChecks[].apiVersion`
23129 // axis transitively references. The string is part of the
23130 // cluster-side contract with the Flux v2 `helm-controller` (the
23131 // controller watches the exact `helm.toolkit.fluxcd.io/v2`
23132 // group/version; a drifted value to a stale v2beta1 / v2beta2
23133 // lands the rendered `HelmRelease` outside the controller's
23134 // `Watches` and fails at apply time with "no kind 'HelmRelease'
23135 // is registered for version 'helm.toolkit.fluxcd.io/v2beta2'");
23136 // changing it is a coordinated Flux v3 migration alongside the
23137 // upstream `helm-controller` deprecation cycle, not an
23138 // incidental edit. Peer to `default_flux_system_namespace_pins_canonical_value`
23139 // on the canonical-Flux-CRD-axis-pin axis for the sibling
23140 // [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] constant.
23141 assert_eq!(FLUX_HELMRELEASE_API_VERSION, "helm.toolkit.fluxcd.io/v2");
23142 }
23143
23144 #[test]
23145 fn flux_helmrelease_api_version_carries_group_and_version_segments() {
23146 // Cross-axis invariant: a Kubernetes CRD `apiVersion` is a
23147 // `<group>/<version>` pair separated by exactly one `/` byte.
23148 // The group segment is a DNS-style multi-segment hostname
23149 // (`helm.toolkit.fluxcd.io`) and the version segment is a
23150 // Kubernetes API version label (`v2`, `v2beta1`, `v1alpha1` —
23151 // peer with the K8s API versioning convention upstream
23152 // documents). Pinning this here means a future rebrand on the
23153 // canonical lift can't silently land a malformed apiVersion
23154 // (no `/`, two `/`, empty group, empty version) that every
23155 // downstream YAML-aware deserializer would reject far from the
23156 // rebrand commit's source. The single-`/` invariant is the
23157 // load-bearing K8s API typed-discovery contract: a value the
23158 // apiserver's `RESTMapper` consults to resolve the CRD's
23159 // `RESTKind`.
23160 let v = FLUX_HELMRELEASE_API_VERSION;
23161 let parts: Vec<&str> = v.split('/').collect();
23162 assert_eq!(
23163 parts.len(),
23164 2,
23165 "FLUX_HELMRELEASE_API_VERSION {v:?} must split into exactly two \
23166 `/`-delimited segments (group/version) per the K8s CRD apiVersion \
23167 grammar — every downstream YAML-aware deserializer enforces this \
23168 shape"
23169 );
23170 assert!(
23171 !parts[0].is_empty(),
23172 "FLUX_HELMRELEASE_API_VERSION {v:?} group segment must be non-empty"
23173 );
23174 assert!(
23175 !parts[1].is_empty(),
23176 "FLUX_HELMRELEASE_API_VERSION {v:?} version segment must be non-empty"
23177 );
23178 assert!(
23179 parts[0].contains('.'),
23180 "FLUX_HELMRELEASE_API_VERSION {v:?} group segment {group:?} must be a \
23181 DNS-style multi-segment hostname (the canonical CRD-group convention \
23182 every K8s controller-runtime / kube-rs-aware client expects)",
23183 group = parts[0]
23184 );
23185 }
23186
23187 #[test]
23188 fn default_flux_helmrelease_api_version_matches_caixa_flux_test_fixtures() {
23189 // Cross-file drift pin: the four caixa-flux occurrences of
23190 // `helm.toolkit.fluxcd.io/v2` all consult the same canonical
23191 // constant, but the two `upsert_into_helmrelease_programs` test
23192 // fixtures (caixa-flux/src/lib.rs:928, 970) carry the value as
23193 // a static raw-string literal inside a `serde_yaml::from_str`
23194 // input (the YAML parser is the unit-under-test there, not the
23195 // rendering — the literals are intentionally not threaded
23196 // through the lift). This pin trips at caixa-core build time
23197 // if the canonical constant ever drifts past the literal the
23198 // caixa-flux test fixtures carry, so a future Flux v3 migration
23199 // surfaces here on the canonical-string axis rather than at the
23200 // first failing test fixture far from the rebrand commit. Peer
23201 // to the [`default_flux_system_namespace_pins_canonical_value`]
23202 // pin on the sibling Flux-namespace axis: both pin the canonical
23203 // string at the lift site so a future rebrand lands the
23204 // constant + every downstream reference + every test fixture in
23205 // one coordinated edit.
23206 assert_eq!(
23207 FLUX_HELMRELEASE_API_VERSION, "helm.toolkit.fluxcd.io/v2",
23208 "drift between FLUX_HELMRELEASE_API_VERSION and the \
23209 caixa-flux/src/lib.rs:928,970 test fixtures' literal values; \
23210 coordinate the migration across the const + every fixture in \
23211 one edit"
23212 );
23213 }
23214
23215 #[test]
23216 fn flux_gitrepository_api_version_pins_canonical_value() {
23217 // Pin the actual string so a typo in this lift can't silently
23218 // rebrand the Flux v2 `GitRepository` CRD group/version the rendered
23219 // `gitrepository.yaml` document declares. The string is part of the
23220 // cluster-side contract with the Flux v2 `source-controller` (the
23221 // controller watches the exact `source.toolkit.fluxcd.io/v1`
23222 // group/version; a drifted value to a stale v1beta1 / v1beta2 lands
23223 // the rendered `GitRepository` outside the controller's `Watches`
23224 // and fails at apply time with "no kind 'GitRepository' is
23225 // registered for version 'source.toolkit.fluxcd.io/v1beta2'");
23226 // changing it is a coordinated Flux v3 migration alongside the
23227 // upstream `source-controller` deprecation cycle, not an
23228 // incidental edit. Peer to
23229 // `flux_helmrelease_api_version_pins_canonical_value` on the
23230 // canonical-Flux-CRD-axis-pin axis for the sibling
23231 // [`FLUX_HELMRELEASE_API_VERSION`] constant.
23232 assert_eq!(
23233 FLUX_GITREPOSITORY_API_VERSION,
23234 "source.toolkit.fluxcd.io/v1"
23235 );
23236 }
23237
23238 #[test]
23239 fn flux_gitrepository_api_version_carries_group_and_version_segments() {
23240 // Cross-axis invariant: a Kubernetes CRD `apiVersion` is a
23241 // `<group>/<version>` pair separated by exactly one `/` byte.
23242 // The group segment is a DNS-style multi-segment hostname
23243 // (`source.toolkit.fluxcd.io`) and the version segment is a
23244 // Kubernetes API version label (`v1`, `v1beta1`, `v1alpha1` — peer
23245 // with the K8s API versioning convention upstream documents).
23246 // Pinning this here means a future rebrand on the canonical lift
23247 // can't silently land a malformed apiVersion (no `/`, two `/`,
23248 // empty group, empty version) that every downstream YAML-aware
23249 // deserializer would reject far from the rebrand commit's source.
23250 // The single-`/` invariant is the load-bearing K8s API typed-
23251 // discovery contract: a value the apiserver's `RESTMapper`
23252 // consults to resolve the CRD's `RESTKind`. Peer to
23253 // `flux_helmrelease_api_version_carries_group_and_version_segments`
23254 // on the sibling Flux-CRD-axis.
23255 let v = FLUX_GITREPOSITORY_API_VERSION;
23256 let parts: Vec<&str> = v.split('/').collect();
23257 assert_eq!(
23258 parts.len(),
23259 2,
23260 "FLUX_GITREPOSITORY_API_VERSION {v:?} must split into exactly two \
23261 `/`-delimited segments (group/version) per the K8s CRD apiVersion \
23262 grammar — every downstream YAML-aware deserializer enforces this \
23263 shape"
23264 );
23265 assert!(
23266 !parts[0].is_empty(),
23267 "FLUX_GITREPOSITORY_API_VERSION {v:?} group segment must be non-empty"
23268 );
23269 assert!(
23270 !parts[1].is_empty(),
23271 "FLUX_GITREPOSITORY_API_VERSION {v:?} version segment must be non-empty"
23272 );
23273 assert!(
23274 parts[0].contains('.'),
23275 "FLUX_GITREPOSITORY_API_VERSION {v:?} group segment {group:?} must be a \
23276 DNS-style multi-segment hostname (the canonical CRD-group convention \
23277 every K8s controller-runtime / kube-rs-aware client expects)",
23278 group = parts[0]
23279 );
23280 }
23281
23282 #[test]
23283 fn flux_gitrepository_and_helmrelease_api_versions_share_toolkit_fluxcd_io_root() {
23284 // Cross-axis invariant: every Flux v2 CRD group ends in the canonical
23285 // `.toolkit.fluxcd.io` root the upstream `fluxcd/flux2` project pins
23286 // for the source-/helm-/kustomize-/notification-controller triplet.
23287 // A future Flux v3 promotion that breaks the root suffix (forking
23288 // `source-controller` out of the toolkit group, for example) would
23289 // surface here as a coordinated cross-axis edit-point — both lifted
23290 // constants must move together to preserve the controller-triple
23291 // contract.
23292 const ROOT: &str = ".toolkit.fluxcd.io";
23293 let gr_group = FLUX_GITREPOSITORY_API_VERSION
23294 .split('/')
23295 .next()
23296 .expect("FLUX_GITREPOSITORY_API_VERSION has a group segment");
23297 let hr_group = FLUX_HELMRELEASE_API_VERSION
23298 .split('/')
23299 .next()
23300 .expect("FLUX_HELMRELEASE_API_VERSION has a group segment");
23301 assert!(
23302 gr_group.ends_with(ROOT),
23303 "FLUX_GITREPOSITORY_API_VERSION group {gr_group:?} must end with the \
23304 canonical Flux v2 `{ROOT}` root every controller in the triplet shares"
23305 );
23306 assert!(
23307 hr_group.ends_with(ROOT),
23308 "FLUX_HELMRELEASE_API_VERSION group {hr_group:?} must end with the \
23309 canonical Flux v2 `{ROOT}` root every controller in the triplet shares"
23310 );
23311 }
23312
23313 #[test]
23314 fn flux_kustomization_api_version_pins_canonical_value() {
23315 // Pin the actual string so a typo in this lift can't silently
23316 // rebrand the Flux v2 `Kustomization` CRD group/version the
23317 // rendered `kustomization.yaml` document declares. The string
23318 // is part of the cluster-side contract with the Flux v2
23319 // `kustomize-controller` (the controller watches the exact
23320 // `kustomize.toolkit.fluxcd.io/v1` group/version; a drifted
23321 // value to a stale v1beta1 / v1beta2 lands the rendered
23322 // `Kustomization` outside the controller's `Watches` and
23323 // fails at apply time with "no kind 'Kustomization' is
23324 // registered for version
23325 // 'kustomize.toolkit.fluxcd.io/v1beta2'"); changing it is a
23326 // coordinated Flux v3 migration alongside the upstream
23327 // `kustomize-controller` deprecation cycle, not an
23328 // incidental edit. Peer to
23329 // `flux_helmrelease_api_version_pins_canonical_value` /
23330 // `flux_gitrepository_api_version_pins_canonical_value` on
23331 // the canonical-Flux-CRD-axis-pin axis for the sibling
23332 // [`FLUX_HELMRELEASE_API_VERSION`] /
23333 // [`FLUX_GITREPOSITORY_API_VERSION`] constants — completes
23334 // the Flux v2 controller-triplet's per-CRD-axis pin set.
23335 assert_eq!(
23336 FLUX_KUSTOMIZATION_API_VERSION,
23337 "kustomize.toolkit.fluxcd.io/v1"
23338 );
23339 }
23340
23341 #[test]
23342 fn flux_kustomization_api_version_carries_group_and_version_segments() {
23343 // Cross-axis invariant: a Kubernetes CRD `apiVersion` is a
23344 // `<group>/<version>` pair separated by exactly one `/` byte.
23345 // The group segment is a DNS-style multi-segment hostname
23346 // (`kustomize.toolkit.fluxcd.io`) and the version segment is a
23347 // Kubernetes API version label (`v1`, `v1beta1`, `v1alpha1` —
23348 // peer with the K8s API versioning convention upstream
23349 // documents). Pinning this here means a future rebrand on the
23350 // canonical lift can't silently land a malformed apiVersion
23351 // (no `/`, two `/`, empty group, empty version) that every
23352 // downstream YAML-aware deserializer would reject far from the
23353 // rebrand commit's source. The single-`/` invariant is the
23354 // load-bearing K8s API typed-discovery contract: a value the
23355 // apiserver's `RESTMapper` consults to resolve the CRD's
23356 // `RESTKind`. Peer to
23357 // `flux_helmrelease_api_version_carries_group_and_version_segments`
23358 // / `flux_gitrepository_api_version_carries_group_and_version_segments`
23359 // on the sibling Flux-CRD-axis.
23360 let v = FLUX_KUSTOMIZATION_API_VERSION;
23361 let parts: Vec<&str> = v.split('/').collect();
23362 assert_eq!(
23363 parts.len(),
23364 2,
23365 "FLUX_KUSTOMIZATION_API_VERSION {v:?} must split into exactly two \
23366 `/`-delimited segments (group/version) per the K8s CRD apiVersion \
23367 grammar — every downstream YAML-aware deserializer enforces this \
23368 shape"
23369 );
23370 assert!(
23371 !parts[0].is_empty(),
23372 "FLUX_KUSTOMIZATION_API_VERSION {v:?} group segment must be non-empty"
23373 );
23374 assert!(
23375 !parts[1].is_empty(),
23376 "FLUX_KUSTOMIZATION_API_VERSION {v:?} version segment must be non-empty"
23377 );
23378 assert!(
23379 parts[0].contains('.'),
23380 "FLUX_KUSTOMIZATION_API_VERSION {v:?} group segment {group:?} must be a \
23381 DNS-style multi-segment hostname (the canonical CRD-group convention \
23382 every K8s controller-runtime / kube-rs-aware client expects)",
23383 group = parts[0]
23384 );
23385 }
23386
23387 #[test]
23388 fn flux_controller_triplet_api_versions_share_toolkit_fluxcd_io_root() {
23389 // Cross-axis triplet invariant: the Flux v2 controller triplet
23390 // (source-controller + helm-controller + kustomize-controller)
23391 // upstream all share the canonical `.toolkit.fluxcd.io` root.
23392 // The two-axis sibling pin
23393 // [`flux_gitrepository_and_helmrelease_api_versions_share_toolkit_fluxcd_io_root`]
23394 // enforces the invariant on the source-/helm- pair; this
23395 // pin extends it onto the kustomize-controller axis so a
23396 // future Flux v3 promotion that forks any single controller
23397 // out of the toolkit group surfaces as a coordinated
23398 // cross-axis edit-point across all three constants — the
23399 // controller triplet's CRD group/versions move together
23400 // upstream, and the lift discipline preserves that
23401 // movement at the typed substrate-side `&'static str`
23402 // surface.
23403 const ROOT: &str = ".toolkit.fluxcd.io";
23404 for (name, v) in [
23405 (
23406 "FLUX_GITREPOSITORY_API_VERSION",
23407 FLUX_GITREPOSITORY_API_VERSION,
23408 ),
23409 ("FLUX_HELMRELEASE_API_VERSION", FLUX_HELMRELEASE_API_VERSION),
23410 (
23411 "FLUX_KUSTOMIZATION_API_VERSION",
23412 FLUX_KUSTOMIZATION_API_VERSION,
23413 ),
23414 ] {
23415 let group = v
23416 .split('/')
23417 .next()
23418 .expect("Flux v2 CRD apiVersion has a group segment");
23419 assert!(
23420 group.ends_with(ROOT),
23421 "{name} group {group:?} must end with the canonical Flux v2 \
23422 `{ROOT}` root every controller in the source/helm/kustomize \
23423 triplet shares"
23424 );
23425 }
23426 }
23427
23428 #[test]
23429 fn flux_kind_git_repository_pins_canonical_value() {
23430 // Pin the actual string so a typo in this lift can't silently
23431 // rebrand the Flux v2 `GitRepository` CRD `kind` discriminator
23432 // the rendered Flux bundle's three `GitRepository`-naming axes
23433 // declare (gitrepository.yaml top-level kind, helmrelease.yaml
23434 // spec.chart.spec.sourceRef.kind, kustomization.yaml
23435 // spec.sourceRef.kind). The string is part of the cluster-side
23436 // contract with the Flux v2 `source-controller` — the
23437 // apiserver-side CRD resolution contract is the
23438 // `(apiVersion, kind)` tuple keyed against the registered
23439 // `CustomResourceDefinition`, so the kind half of the tuple is
23440 // exactly as load-bearing as the sibling
23441 // [`FLUX_GITREPOSITORY_API_VERSION`] apiVersion half. A drifted
23442 // value (e.g. an upstream Flux v3 rename to `GitSource`) lands
23443 // the rendered documents outside the source-controller's CRD
23444 // registration; changing it is a coordinated Flux v3 migration
23445 // alongside the upstream `source-controller` deprecation cycle,
23446 // not an incidental edit. Peer to
23447 // `flux_gitrepository_api_version_pins_canonical_value` on the
23448 // sibling apiVersion half of the same CRD-lookup tuple.
23449 assert_eq!(FLUX_KIND_GIT_REPOSITORY, "GitRepository");
23450 }
23451
23452 #[test]
23453 fn flux_kind_git_repository_carries_upper_camel_case_shape() {
23454 // Cross-axis invariant: a Kubernetes CRD `kind` discriminator is
23455 // an UpperCamelCase identifier per the K8s API conventions
23456 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds —
23457 // "Kinds are always UpperCamelCase"). Pinning the shape here
23458 // means a future rebrand on the canonical lift can't silently
23459 // land a malformed kind discriminator (snake_case, kebab-case,
23460 // lowercase, empty) that every downstream YAML-aware
23461 // deserializer would reject far from the rebrand commit's
23462 // source. The first-byte uppercase / rest-ASCII-alphanumeric
23463 // invariant is the load-bearing K8s API typed-discovery
23464 // contract: a value the apiserver's `RESTMapper` consults to
23465 // resolve the CRD's `RESTKind`. Peer to
23466 // `flux_gitrepository_api_version_carries_group_and_version_segments`
23467 // on the sibling apiVersion half of the same CRD-lookup tuple.
23468 let v = FLUX_KIND_GIT_REPOSITORY;
23469 assert!(
23470 !v.is_empty(),
23471 "FLUX_KIND_GIT_REPOSITORY {v:?} must be non-empty per the K8s API \
23472 UpperCamelCase kind discriminator grammar"
23473 );
23474 let first = v.chars().next().expect("non-empty");
23475 assert!(
23476 first.is_ascii_uppercase(),
23477 "FLUX_KIND_GIT_REPOSITORY {v:?} first byte {first:?} must be \
23478 ASCII-uppercase per the K8s API UpperCamelCase kind discriminator \
23479 grammar (Kinds are always UpperCamelCase)"
23480 );
23481 assert!(
23482 v.chars().all(|c| c.is_ascii_alphanumeric()),
23483 "FLUX_KIND_GIT_REPOSITORY {v:?} must be ASCII-alphanumeric \
23484 throughout per the K8s API kind discriminator grammar — no \
23485 snake_case, kebab-case, or whitespace bytes the apiserver-side \
23486 RESTMapper would reject"
23487 );
23488 }
23489
23490 #[test]
23491 fn flux_kind_helm_release_pins_canonical_value() {
23492 // Pin the actual string so a typo in this lift can't silently
23493 // rebrand the Flux v2 `HelmRelease` CRD `kind` discriminator
23494 // the rendered Flux bundle's two `HelmRelease`-naming axes
23495 // declare (helmrelease.yaml top-level kind, kustomization.yaml
23496 // spec.healthChecks[].kind). The string is part of the
23497 // cluster-side contract with the Flux v2 `helm-controller` —
23498 // the apiserver-side CRD resolution contract is the
23499 // `(apiVersion, kind)` tuple keyed against the registered
23500 // `CustomResourceDefinition`, so the kind half of the tuple is
23501 // exactly as load-bearing as the sibling
23502 // [`FLUX_HELMRELEASE_API_VERSION`] apiVersion half. A drifted
23503 // value (e.g. an upstream Flux v3 rename to `ChartRelease`)
23504 // lands the rendered documents outside the helm-controller's
23505 // CRD registration; changing it is a coordinated Flux v3
23506 // migration alongside the upstream `helm-controller`
23507 // deprecation cycle, not an incidental edit. Peer to
23508 // `flux_kind_git_repository_pins_canonical_value` on the
23509 // sibling Flux v2 source-controller CRD-`kind` axis.
23510 assert_eq!(FLUX_KIND_HELM_RELEASE, "HelmRelease");
23511 }
23512
23513 #[test]
23514 fn flux_kind_helm_release_carries_upper_camel_case_shape() {
23515 // Cross-axis invariant: a Kubernetes CRD `kind` discriminator is
23516 // an UpperCamelCase identifier per the K8s API conventions
23517 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds —
23518 // "Kinds are always UpperCamelCase"). Pinning the shape here
23519 // means a future rebrand on the canonical lift can't silently
23520 // land a malformed kind discriminator (snake_case, kebab-case,
23521 // lowercase, empty) that every downstream YAML-aware
23522 // deserializer would reject far from the rebrand commit's
23523 // source. The first-byte uppercase / rest-ASCII-alphanumeric
23524 // invariant is the load-bearing K8s API typed-discovery
23525 // contract: a value the apiserver's `RESTMapper` consults to
23526 // resolve the CRD's `RESTKind`. Peer to
23527 // `flux_kind_git_repository_carries_upper_camel_case_shape`
23528 // on the sibling Flux v2 source-controller CRD-`kind` axis.
23529 let v = FLUX_KIND_HELM_RELEASE;
23530 assert!(
23531 !v.is_empty(),
23532 "FLUX_KIND_HELM_RELEASE {v:?} must be non-empty per the K8s API \
23533 UpperCamelCase kind discriminator grammar"
23534 );
23535 let first = v.chars().next().expect("non-empty");
23536 assert!(
23537 first.is_ascii_uppercase(),
23538 "FLUX_KIND_HELM_RELEASE {v:?} first byte {first:?} must be \
23539 ASCII-uppercase per the K8s API UpperCamelCase kind discriminator \
23540 grammar (Kinds are always UpperCamelCase)"
23541 );
23542 assert!(
23543 v.chars().all(|c| c.is_ascii_alphanumeric()),
23544 "FLUX_KIND_HELM_RELEASE {v:?} must be ASCII-alphanumeric \
23545 throughout per the K8s API kind discriminator grammar — no \
23546 snake_case, kebab-case, or whitespace bytes the apiserver-side \
23547 RESTMapper would reject"
23548 );
23549 }
23550
23551 #[test]
23552 fn flux_kind_kustomization_pins_canonical_value() {
23553 // Pin the actual string so a typo in this lift can't silently
23554 // rebrand the Flux v2 `Kustomization` CRD `kind` discriminator
23555 // the rendered `kustomization.yaml`'s top-level `kind` axis
23556 // declares. The string is part of the cluster-side contract
23557 // with the Flux v2 `kustomize-controller` — the apiserver-side
23558 // CRD resolution contract is the `(apiVersion, kind)` tuple
23559 // keyed against the registered `CustomResourceDefinition`, so
23560 // the kind half of the tuple is exactly as load-bearing as the
23561 // sibling [`FLUX_KUSTOMIZATION_API_VERSION`] apiVersion half. A
23562 // drifted value (e.g. an upstream Flux v3 rename to
23563 // `KustomizationSet`) lands the rendered document outside the
23564 // kustomize-controller's CRD registration; changing it is a
23565 // coordinated Flux v3 migration alongside the upstream
23566 // `kustomize-controller` deprecation cycle, not an incidental
23567 // edit. Peer to
23568 // `flux_kind_git_repository_pins_canonical_value` /
23569 // `flux_kind_helm_release_pins_canonical_value` on the sibling
23570 // Flux v2 controller-triplet `kind`-axis surface — completes
23571 // the canonical-Flux-v2-CRD-kind-discriminator pin set across
23572 // the source-controller + helm-controller + kustomize-controller
23573 // triplet.
23574 assert_eq!(FLUX_KIND_KUSTOMIZATION, "Kustomization");
23575 }
23576
23577 #[test]
23578 fn flux_key_source_ref_pins_canonical_value() {
23579 // Pin the actual string so a typo in this lift can't silently
23580 // rebrand the Flux v2 per-`HelmRelease`/`Kustomization`
23581 // source-reference container-axis key the rendered
23582 // `helmrelease.yaml` (`spec.chart.spec.sourceRef`) +
23583 // `kustomization.yaml` (`spec.sourceRef`) documents mount the
23584 // per-CR `(kind, name, namespace)` reference triple under. The
23585 // string is part of the cluster-side contract with every
23586 // Flux-v2-conformant source-controller — the per-CR reconcile
23587 // loop keys off this exact container axis to source the
23588 // `(kind, name, namespace)` reference triple; a drifted value
23589 // (`"source_ref"` / `"source"` / `"sourceReference"` /
23590 // `"gitSourceRef"`) silently dangles both the HelmRelease's
23591 // chart resolution + the parent Kustomization's source
23592 // resolution at the Flux v2 source-controller's CRD
23593 // registration. Changing this value is a coordinated Flux v3
23594 // migration alongside the upstream `fluxcd/flux2` deprecation
23595 // cycle, not an incidental edit. Peer to
23596 // `flux_kind_git_repository_pins_canonical_value` /
23597 // `flux_kind_helm_release_pins_canonical_value` /
23598 // `flux_kind_kustomization_pins_canonical_value` on the sibling
23599 // per-CRD `kind`-axis surface — extends the canonical-Flux-v2-
23600 // load-bearing-string pin discipline from the per-CRD kind
23601 // discriminators onto the sibling per-CR source-reference
23602 // container-axis key both `cluster_bundle` renderers consume.
23603 assert_eq!(FLUX_KEY_SOURCE_REF, "sourceRef");
23604 }
23605
23606 #[test]
23607 fn flux_key_source_ref_carries_lower_camel_case_shape() {
23608 // Cross-axis invariant: the Flux v2 CRD field-naming convention
23609 // (inherited from the upstream K8s API conventions) admits
23610 // lowerCamelCase per-field keys — the source-reference
23611 // container-axis conforms to this on the leading-lowercase
23612 // `sourceRef` shape. Pinning the shape here means a future
23613 // rebrand on the canonical lift can't silently land a malformed
23614 // container-axis key (snake_case, kebab-case, UpperCamelCase,
23615 // empty) that the Flux v2 source-controller's per-CR reconcile
23616 // loop would reject at apply parse time far from the rebrand
23617 // commit's source. Peer to the sibling K8s-CR-lowerCamelCase-
23618 // per-field pin trajectory the sibling `KUBE_KEY_MATCH_LABELS`
23619 // / `GATEWAY_API_KEY_BACKEND_REFS` / `CILIUM_KEY_FROM_ENDPOINTS`
23620 // / `CILIUM_KEY_TO_PORTS` pins established on the sibling per-
23621 // K8s-CR-schema-field-name axes.
23622 let v = FLUX_KEY_SOURCE_REF;
23623 assert!(
23624 !v.is_empty(),
23625 "FLUX_KEY_SOURCE_REF {v:?} must be non-empty per the Flux v2 \
23626 CRD field-naming grammar"
23627 );
23628 let mut chars = v.chars();
23629 assert!(
23630 chars.next().is_some_and(|c| c.is_ascii_lowercase()),
23631 "FLUX_KEY_SOURCE_REF {v:?} must lead with an ASCII-lowercase \
23632 byte per the Flux v2 lowerCamelCase per-CR-field-key convention"
23633 );
23634 assert!(
23635 v.chars().all(|c| c.is_ascii_alphanumeric()),
23636 "FLUX_KEY_SOURCE_REF {v:?} must be ASCII-alphanumeric throughout \
23637 per the Flux v2 lowerCamelCase per-CR-field-key convention — \
23638 no `_` / `-` / `.` / whitespace bytes the Flux v2 source-\
23639 controller's per-CR reconcile loop would reject"
23640 );
23641 }
23642
23643 #[test]
23644 fn flux_key_values_pins_canonical_value() {
23645 // Pin the actual string so a typo in this lift can't silently
23646 // rebrand the Flux v2 per-`HelmRelease` values-override block-
23647 // body-axis key the rendered `helmrelease.yaml`'s `spec.values`
23648 // block declares. The string is part of the cluster-side
23649 // contract with the Flux v2 `helm-controller` — the per-CR
23650 // reconcile loop merges the per-cluster override YAML nested
23651 // under this exact block-body axis into the referenced chart's
23652 // `values.yaml` at Helm-render time; a drifted value
23653 // (`"Values"` / `"vals"` / `"chartValues"` / `"overrides"`)
23654 // silently routes the per-cluster overrides nowhere at Helm
23655 // render, and the workload comes up with the referenced
23656 // chart's admission-time defaults. Changing this value is a
23657 // coordinated Flux v3 migration alongside the upstream
23658 // `fluxcd/flux2` deprecation cycle, not an incidental edit.
23659 // Peer to `flux_key_source_ref_pins_canonical_value` on the
23660 // sibling Flux v2 per-CR container-axis-key surface — extends
23661 // the canonical-Flux-v2-load-bearing-string pin discipline from
23662 // the per-CR source-reference container-axis onto the sibling
23663 // per-`HelmRelease` values-override block-body-axis.
23664 assert_eq!(FLUX_KEY_VALUES, "values");
23665 }
23666
23667 #[test]
23668 fn flux_key_values_carries_lower_camel_case_shape() {
23669 // Cross-axis invariant: the Flux v2 CRD field-naming convention
23670 // (inherited from the upstream K8s API conventions) admits
23671 // lowerCamelCase per-field keys — the values-override block-
23672 // body axis conforms to this on the leading-lowercase `values`
23673 // shape (a single-word lowerCamelCase reduces to all-lowercase).
23674 // Pinning the shape here means a future rebrand on the
23675 // canonical lift can't silently land a malformed block-body-
23676 // axis key (snake_case, kebab-case, UpperCamelCase, empty) that
23677 // the Flux v2 helm-controller's per-CR reconcile loop would
23678 // reject at apply parse time far from the rebrand commit's
23679 // source. Peer to `flux_key_source_ref_carries_lower_camel_case_shape`
23680 // on the sibling Flux v2 per-CR container-axis-key surface.
23681 let v = FLUX_KEY_VALUES;
23682 assert!(
23683 !v.is_empty(),
23684 "FLUX_KEY_VALUES {v:?} must be non-empty per the Flux v2 \
23685 CRD field-naming grammar"
23686 );
23687 let mut chars = v.chars();
23688 assert!(
23689 chars.next().is_some_and(|c| c.is_ascii_lowercase()),
23690 "FLUX_KEY_VALUES {v:?} must lead with an ASCII-lowercase \
23691 byte per the Flux v2 lowerCamelCase per-CR-field-key convention"
23692 );
23693 assert!(
23694 v.chars().all(|c| c.is_ascii_alphanumeric()),
23695 "FLUX_KEY_VALUES {v:?} must be ASCII-alphanumeric throughout \
23696 per the Flux v2 lowerCamelCase per-CR-field-key convention — \
23697 no `_` / `-` / `.` / whitespace bytes the Flux v2 helm-\
23698 controller's per-CR reconcile loop would reject"
23699 );
23700 }
23701
23702 #[test]
23703 fn flux_key_chart_pins_canonical_value() {
23704 // Pin the actual string so a typo in this lift can't silently
23705 // rebrand the Flux v2 per-`HelmRelease` inline-chart-template
23706 // container-axis key the rendered `helmrelease.yaml`'s
23707 // `spec.chart` block declares. The string is part of the
23708 // cluster-side contract with the Flux v2 `helm-controller` —
23709 // the per-CR reconcile loop reads the nested
23710 // `HelmChartTemplate` sub-document (chart-name string,
23711 // source-of-truth reference triple, and reconcile cadence)
23712 // under this exact container axis to source the referenced
23713 // chart at Helm-render time; a drifted value (`"Chart"` /
23714 // `"chartTemplate"` / `"helmChart"` / `"chartRef"`) silently
23715 // dangles the whole chart-template resolution at the helm-
23716 // controller's CRD registration and the referenced chart
23717 // never resolves. Changing this value is a coordinated Flux
23718 // v3 migration alongside the upstream `fluxcd/flux2`
23719 // deprecation cycle, not an incidental edit. Peer to
23720 // `flux_key_source_ref_pins_canonical_value` /
23721 // `flux_key_values_pins_canonical_value` on the sibling Flux
23722 // v2 per-`HelmRelease` body-key surfaces — extends the
23723 // canonical-Flux-v2-load-bearing-string pin discipline from
23724 // the source-reference container-axis + values-override
23725 // block-body-axis onto the sibling chart-template container-
23726 // axis, completing the triplet of Flux v2 per-`HelmRelease`
23727 // `spec.*` body-key pin tests.
23728 assert_eq!(FLUX_KEY_CHART, "chart");
23729 }
23730
23731 #[test]
23732 fn flux_key_chart_carries_lower_camel_case_shape() {
23733 // Cross-axis invariant: the Flux v2 CRD field-naming
23734 // convention (inherited from the upstream K8s API
23735 // conventions) admits lowerCamelCase per-field keys — the
23736 // chart-template container-axis conforms to this on the
23737 // leading-lowercase `chart` shape (a single-word
23738 // lowerCamelCase reduces to all-lowercase). Pinning the shape
23739 // here means a future rebrand on the canonical lift can't
23740 // silently land a malformed container-axis key (snake_case,
23741 // kebab-case, UpperCamelCase, empty) that the Flux v2 helm-
23742 // controller's per-CR reconcile loop would reject at apply
23743 // parse time far from the rebrand commit's source. Peer to
23744 // `flux_key_source_ref_carries_lower_camel_case_shape` /
23745 // `flux_key_values_carries_lower_camel_case_shape` on the
23746 // sibling Flux v2 per-`HelmRelease` body-key surfaces.
23747 let v = FLUX_KEY_CHART;
23748 assert!(
23749 !v.is_empty(),
23750 "FLUX_KEY_CHART {v:?} must be non-empty per the Flux v2 \
23751 CRD field-naming grammar"
23752 );
23753 let mut chars = v.chars();
23754 assert!(
23755 chars.next().is_some_and(|c| c.is_ascii_lowercase()),
23756 "FLUX_KEY_CHART {v:?} must lead with an ASCII-lowercase \
23757 byte per the Flux v2 lowerCamelCase per-CR-field-key convention"
23758 );
23759 assert!(
23760 v.chars().all(|c| c.is_ascii_alphanumeric()),
23761 "FLUX_KEY_CHART {v:?} must be ASCII-alphanumeric throughout \
23762 per the Flux v2 lowerCamelCase per-CR-field-key convention — \
23763 no `_` / `-` / `.` / whitespace bytes the Flux v2 helm-\
23764 controller's per-CR reconcile loop would reject"
23765 );
23766 }
23767
23768 #[test]
23769 fn flux_helmchart_template_key_chart_pins_canonical_value() {
23770 // Pin the actual string so a typo in this lift can't silently
23771 // rebrand the Flux v2 `HelmChartTemplate.spec.chart` per-CR
23772 // chart-NAME reference leaf-scalar-axis key every caixa-flux-
23773 // emitted `HelmRelease` document nests inside the parent
23774 // `spec.chart.spec` sub-document. The helm-controller's
23775 // reconcile pipeline reads the chart-artifact name from this
23776 // exact leaf on every reconcile — a drifted `spec.chart.spec.Chart`
23777 // / `spec.chart.spec.chartRef` / `spec.chart.spec.chartName`
23778 // at the emission-side leaf key would silently land as a well-
23779 // formed but ignored `HelmChartTemplate.spec.*` extra property
23780 // the apiserver's CRD OpenAPI schema permits (arbitrary spec
23781 // extras) and the helm-controller would fail to resolve any
23782 // chart-artifact through the sibling `sourceRef` triple's
23783 // source at reconcile time — a non-self-locating "chart
23784 // 'unknown' not found in <source>" error far from the rebrand
23785 // commit's source `caixa.lisp` / the renderer's format-string
23786 // template. Peer to `flux_key_chart_pins_canonical_value` on
23787 // the sibling per-CR chart-template container-axis parent
23788 // this leaf-scalar-axis lift extends by descending one level
23789 // beneath, closing the substrate-side declaration the parent
23790 // container-axis lift docstring explicitly named as future
23791 // work.
23792 assert_eq!(FLUX_HELMCHART_TEMPLATE_KEY_CHART, "chart");
23793 }
23794
23795 #[test]
23796 fn flux_helmchart_template_key_chart_carries_lower_camel_case_shape() {
23797 // Cross-axis invariant: the Flux v2 CRD field-naming
23798 // convention (inherited from the upstream K8s API conventions)
23799 // admits lowerCamelCase per-field keys — the per-`HelmChartTemplate`
23800 // chart-NAME reference leaf-scalar-axis conforms to this on the
23801 // leading-lowercase `chart` shape (a single-word lowerCamelCase
23802 // reduces to all-lowercase). Pinning the shape here means a
23803 // future rebrand on the canonical lift can't silently land a
23804 // malformed leaf-scalar-axis key (snake_case, kebab-case,
23805 // UpperCamelCase, empty) that the Flux v2 helm-controller's
23806 // per-CR reconcile loop would reject at apply parse time far
23807 // from the rebrand commit's source. Peer to
23808 // `flux_key_chart_carries_lower_camel_case_shape` on the
23809 // sibling per-CR chart-template container-axis parent, and to
23810 // the deliberate axis-independence discipline the sibling
23811 // [`CILIUM_KEY_PATH`] / [`GATEWAY_API_KEY_PATH`] two-CRD-
23812 // groups-sharing-a-string re-exports established (two consts
23813 // spelling the same underlying string at distinct schema
23814 // axes stay sibling constants at the rustc symbol-name axis).
23815 let v = FLUX_HELMCHART_TEMPLATE_KEY_CHART;
23816 assert!(
23817 !v.is_empty(),
23818 "FLUX_HELMCHART_TEMPLATE_KEY_CHART {v:?} must be non-empty per \
23819 the Flux v2 CRD field-naming grammar"
23820 );
23821 let mut chars = v.chars();
23822 assert!(
23823 chars.next().is_some_and(|c| c.is_ascii_lowercase()),
23824 "FLUX_HELMCHART_TEMPLATE_KEY_CHART {v:?} must lead with an \
23825 ASCII-lowercase byte per the Flux v2 lowerCamelCase per-CR-\
23826 field-key convention"
23827 );
23828 assert!(
23829 v.chars().all(|c| c.is_ascii_alphanumeric()),
23830 "FLUX_HELMCHART_TEMPLATE_KEY_CHART {v:?} must be ASCII-\
23831 alphanumeric throughout per the Flux v2 lowerCamelCase per-CR-\
23832 field-key convention — no `_` / `-` / `.` / whitespace bytes \
23833 the Flux v2 helm-controller's per-CR reconcile loop would reject"
23834 );
23835 }
23836
23837 #[test]
23838 fn flux_helmchart_template_key_chart_and_flux_key_chart_stay_independent_axes() {
23839 // Cross-axis independence pin: both `FLUX_HELMCHART_TEMPLATE_KEY_CHART`
23840 // (`spec.chart.spec.chart` chart-NAME reference leaf-scalar-axis)
23841 // and the sibling `FLUX_KEY_CHART` (`spec.chart` per-CR chart-
23842 // template container-axis parent) spell the same underlying
23843 // `"chart"` string today but name distinct schema axes on the
23844 // same Flux v2 `HelmRelease` CRD group (a container-axis parent
23845 // vs a leaf-scalar grandchild inside it). Pin byte-equality of
23846 // each half against its own canonical declaration so a future
23847 // Flux v3 rebrand on either axis lands independently at the
23848 // rustc symbol-name axis rather than coalescing onto one
23849 // canonical declaration through a shared `&'static str`
23850 // allocation Rust's string interner would otherwise fuse.
23851 // Same axis-independence discipline the sibling
23852 // [`CILIUM_KEY_PATH`] (ef6114f) / [`GATEWAY_API_KEY_PATH`]
23853 // (9f45aa4) two-CRD-groups-sharing-a-string re-exports
23854 // established on the peer canonical-axis-independence surface.
23855 assert_eq!(FLUX_HELMCHART_TEMPLATE_KEY_CHART, "chart");
23856 assert_eq!(FLUX_KEY_CHART, "chart");
23857 assert_eq!(FLUX_HELMCHART_TEMPLATE_KEY_CHART, FLUX_KEY_CHART);
23858 }
23859
23860 #[test]
23861 fn flux_key_health_checks_pins_canonical_value() {
23862 // Pin the actual string so a typo in this lift can't silently
23863 // rebrand the Flux v2 per-`Kustomization` health-gate reference-
23864 // list container-axis key the rendered `kustomization.yaml`'s
23865 // `spec.healthChecks` block declares. The string is part of the
23866 // cluster-side contract with the Flux v2 `kustomize-controller`
23867 // — the per-CR reconcile loop reads the nested
23868 // `[]NamespacedObjectKindReference` list under this exact
23869 // container axis to gate the parent `Kustomization`'s
23870 // `Ready=True` transition on the referenced sibling
23871 // `HelmRelease` reaching its `HelmReleaseReady=True` condition;
23872 // a drifted value (`"HealthChecks"` / `"healthchecks"` /
23873 // `"healthcheck"` / `"health_checks"` / `"probes"`) silently
23874 // dangles the parent `Kustomization` at `Reconciling` forever
23875 // at the kustomize-controller's health-gate evaluation, and the
23876 // dependent per-cluster fleet-programs upsert chain never sees
23877 // `Ready=True`. Changing this value is a coordinated Flux v3
23878 // migration alongside the upstream `fluxcd/flux2` deprecation
23879 // cycle, not an incidental edit. Peer to
23880 // `flux_key_source_ref_pins_canonical_value` /
23881 // `flux_key_chart_pins_canonical_value` /
23882 // `flux_key_values_pins_canonical_value` on the sibling Flux v2
23883 // body-key surfaces — extends the canonical-Flux-v2-load-bearing-
23884 // string pin discipline from the per-`HelmRelease` triplet
23885 // (`spec.chart` + `spec.chart.spec.sourceRef` + `spec.values`)
23886 // onto the sibling per-`Kustomization` `spec.healthChecks`
23887 // reference-list container-axis, completing the quartet of Flux
23888 // v2 `spec.*` body-key pin tests.
23889 assert_eq!(FLUX_KEY_HEALTH_CHECKS, "healthChecks");
23890 }
23891
23892 #[test]
23893 fn flux_key_health_checks_carries_lower_camel_case_shape() {
23894 // Cross-axis invariant: the Flux v2 CRD field-naming convention
23895 // (inherited from the upstream K8s API conventions) admits
23896 // lowerCamelCase per-field keys — the per-`Kustomization`
23897 // health-gate reference-list container-axis conforms to this on
23898 // the leading-lowercase `healthChecks` shape. Pinning the shape
23899 // here means a future rebrand on the canonical lift can't
23900 // silently land a malformed container-axis key (snake_case,
23901 // kebab-case, UpperCamelCase, empty) that the Flux v2 kustomize-
23902 // controller's per-CR reconcile loop would reject at apply
23903 // parse time far from the rebrand commit's source. Peer to
23904 // `flux_key_source_ref_carries_lower_camel_case_shape` /
23905 // `flux_key_chart_carries_lower_camel_case_shape` /
23906 // `flux_key_values_carries_lower_camel_case_shape` on the
23907 // sibling Flux v2 body-key surfaces.
23908 let v = FLUX_KEY_HEALTH_CHECKS;
23909 assert!(
23910 !v.is_empty(),
23911 "FLUX_KEY_HEALTH_CHECKS {v:?} must be non-empty per the Flux \
23912 v2 CRD field-naming grammar"
23913 );
23914 let mut chars = v.chars();
23915 assert!(
23916 chars.next().is_some_and(|c| c.is_ascii_lowercase()),
23917 "FLUX_KEY_HEALTH_CHECKS {v:?} must lead with an ASCII-\
23918 lowercase byte per the Flux v2 lowerCamelCase per-CR-field-\
23919 key convention"
23920 );
23921 assert!(
23922 v.chars().all(|c| c.is_ascii_alphanumeric()),
23923 "FLUX_KEY_HEALTH_CHECKS {v:?} must be ASCII-alphanumeric \
23924 throughout per the Flux v2 lowerCamelCase per-CR-field-key \
23925 convention — no `_` / `-` / `.` / whitespace bytes the Flux \
23926 v2 kustomize-controller's per-CR reconcile loop would reject"
23927 );
23928 }
23929
23930 #[test]
23931 fn flux_key_interval_pins_canonical_value() {
23932 // Pin the actual string so a typo in this lift can't silently
23933 // rebrand the Flux v2 per-CR reconcile-poll cadence scalar-axis
23934 // key the rendered Flux bundle's three `spec.interval` scalars
23935 // declare — the shared axis-key the source-controller, helm-
23936 // controller, and kustomize-controller each read to schedule
23937 // their per-CR poll cycles off the sibling per-CR `apiVersion` +
23938 // `kind` registration. A drifted value (`"Interval"` / `"period"`
23939 // / `"cadence"` / `"pollInterval"` / `"reconcileInterval"`)
23940 // silently drops the per-CR reconcile schedule from all three
23941 // Flux controllers' per-CR watch registrations simultaneously —
23942 // the referenced Git source never re-polls / the referenced
23943 // chart never re-templates / the parent Kustomization never
23944 // re-applies at upstream drift, freezing the whole cluster's
23945 // per-`caixa` per-cluster bundle at the last-applied snapshot.
23946 // Changing this value is a coordinated Flux v3 migration
23947 // alongside the upstream `fluxcd/flux2` deprecation cycle, not
23948 // an incidental edit. Peer to
23949 // `flux_key_source_ref_pins_canonical_value` /
23950 // `flux_key_chart_pins_canonical_value` /
23951 // `flux_key_values_pins_canonical_value` /
23952 // `flux_key_health_checks_pins_canonical_value` on the sibling
23953 // Flux v2 per-CR body-key surfaces — extends the canonical-Flux-
23954 // v2-load-bearing-string pin discipline from the per-CR body-key
23955 // quartet onto the sibling cross-CR-shared reconcile-poll
23956 // cadence scalar-axis every Flux v2 controller reads.
23957 assert_eq!(FLUX_KEY_INTERVAL, "interval");
23958 }
23959
23960 #[test]
23961 fn flux_key_interval_carries_lower_camel_case_shape() {
23962 // Cross-axis invariant: the Flux v2 CRD field-naming convention
23963 // (inherited from the upstream K8s API conventions) admits
23964 // lowerCamelCase per-field keys — the per-CR reconcile-poll
23965 // cadence scalar-axis conforms to this on the leading-lowercase
23966 // `interval` shape. Pinning the shape here means a future rebrand
23967 // on the canonical lift can't silently land a malformed scalar-
23968 // axis key (snake_case, kebab-case, UpperCamelCase, empty) that
23969 // any of the three Flux v2 controllers' per-CR reconcile loops
23970 // would reject at apply parse time far from the rebrand commit's
23971 // source. Peer to `flux_key_source_ref_carries_lower_camel_case_shape`
23972 // / `flux_key_chart_carries_lower_camel_case_shape` /
23973 // `flux_key_values_carries_lower_camel_case_shape` /
23974 // `flux_key_health_checks_carries_lower_camel_case_shape` on the
23975 // sibling Flux v2 per-CR body-key surfaces.
23976 let v = FLUX_KEY_INTERVAL;
23977 assert!(
23978 !v.is_empty(),
23979 "FLUX_KEY_INTERVAL {v:?} must be non-empty per the Flux \
23980 v2 CRD field-naming grammar"
23981 );
23982 let mut chars = v.chars();
23983 assert!(
23984 chars.next().is_some_and(|c| c.is_ascii_lowercase()),
23985 "FLUX_KEY_INTERVAL {v:?} must lead with an ASCII-\
23986 lowercase byte per the Flux v2 lowerCamelCase per-CR-field-\
23987 key convention"
23988 );
23989 assert!(
23990 v.chars().all(|c| c.is_ascii_alphanumeric()),
23991 "FLUX_KEY_INTERVAL {v:?} must be ASCII-alphanumeric \
23992 throughout per the Flux v2 lowerCamelCase per-CR-field-key \
23993 convention — no `_` / `-` / `.` / whitespace bytes any of \
23994 the three Flux v2 controllers' per-CR reconcile loops would \
23995 reject"
23996 );
23997 }
23998
23999 #[test]
24000 fn flux_gitrepository_ref_key_tag_pins_canonical_value() {
24001 // Pin the actual string so a typo in this lift can't silently
24002 // rebrand the Flux v2 per-`GitRepository` `spec.ref.tag`
24003 // git-tag-selector scalar-axis key the rendered
24004 // `gitrepository.yaml` document declares on the tag-arm of the
24005 // FluxCD source-controller `spec.ref` discriminated-union axis.
24006 // A drifted value (`"Tag"` / `"gitTag"` / `"tagName"`) silently
24007 // dangles the tag-arm sub-block at the FluxCD source-controller's
24008 // CRD registration; the per-Servico clone never resolves at
24009 // reconcile time. Peer to
24010 // `flux_gitrepository_ref_key_branch_pins_canonical_value` /
24011 // `flux_gitrepository_ref_key_commit_pins_canonical_value` on
24012 // the sibling per-shape arms of the same discriminated-union
24013 // axis — closes the three-arm sub-selector-key trio the
24014 // FluxCD source-controller reads to bind the per-CR git-source
24015 // clone refspec.
24016 assert_eq!(FLUX_GITREPOSITORY_REF_KEY_TAG, "tag");
24017 }
24018
24019 #[test]
24020 fn flux_gitrepository_ref_key_branch_pins_canonical_value() {
24021 // Peer of `flux_gitrepository_ref_key_tag_pins_canonical_value`
24022 // on the branch-arm of the FluxCD source-controller
24023 // `GitRepository.spec.ref` discriminated-union axis.
24024 assert_eq!(FLUX_GITREPOSITORY_REF_KEY_BRANCH, "branch");
24025 }
24026
24027 #[test]
24028 fn flux_gitrepository_ref_key_commit_pins_canonical_value() {
24029 // Peer of `flux_gitrepository_ref_key_tag_pins_canonical_value`
24030 // on the commit-arm of the FluxCD source-controller
24031 // `GitRepository.spec.ref` discriminated-union axis.
24032 assert_eq!(FLUX_GITREPOSITORY_REF_KEY_COMMIT, "commit");
24033 }
24034
24035 #[test]
24036 fn flux_gitrepository_key_ref_pins_canonical_value() {
24037 // Bridge-arm pin: [`FLUX_GITREPOSITORY_KEY_REF`] resolves to
24038 // the canonical `"ref"` byte today — the exact YAML key the
24039 // FluxCD `source-controller` reads on every rendered
24040 // `GitRepository` document's `spec.ref` container-axis to
24041 // source the per-CR git-clone refspec discriminated-union
24042 // arm (`{tag, branch, commit}`). Pin the literal here (peer
24043 // with the sibling
24044 // [`flux_gitrepository_ref_key_tag_pins_canonical_value`] /
24045 // [`flux_gitrepository_ref_key_branch_pins_canonical_value`] /
24046 // [`flux_gitrepository_ref_key_commit_pins_canonical_value`]
24047 // per-shape arm sub-selector pins on the same `spec.ref`
24048 // sub-schema) so a future Flux v3 sub-schema rebrand on the
24049 // parent container-axis surfaces here as a coordinated edit-
24050 // point at the definition site rather than a silent apply-
24051 // time split between the writer-side template composer and
24052 // the aggregator's per-CR `RESTMapper` reader.
24053 assert_eq!(FLUX_GITREPOSITORY_KEY_REF, "ref");
24054 }
24055
24056 #[test]
24057 fn flux_gitrepository_key_url_pins_canonical_value() {
24058 // Bridge-arm pin: [`FLUX_GITREPOSITORY_KEY_URL`] resolves to
24059 // the canonical `"url"` byte today — the exact YAML key the
24060 // FluxCD `source-controller` reads on every rendered
24061 // `GitRepository` document's `spec.url` leaf-scalar-axis to
24062 // source the per-CR git-remote clone target. Pin the literal
24063 // here (peer with the sibling
24064 // [`flux_gitrepository_key_ref_pins_canonical_value`] on the
24065 // per-CR `spec.ref` container-axis surface) so a future Flux
24066 // v3 sub-schema rebrand on the URL axis (e.g. an upstream
24067 // `fluxcd/flux2` rename of `spec.url` to `spec.gitUrl` /
24068 // `spec.repository`) surfaces here as a coordinated edit-
24069 // point at the definition site rather than a silent apply-
24070 // time split between the writer-side template composer and
24071 // the source-controller's per-CR `RESTMapper` reader.
24072 assert_eq!(FLUX_GITREPOSITORY_KEY_URL, "url");
24073 }
24074
24075 #[test]
24076 fn flux_gitrepository_key_url_stays_independent_of_ref_and_api_version() {
24077 // Cross-axis peer-independence pin: the per-`GitRepository`-CRD
24078 // canonical-load-bearing-string surface carries three distinct
24079 // axes on the same CRD — `apiVersion`
24080 // ([`FLUX_GITREPOSITORY_API_VERSION`], the CRD-group/version
24081 // half of the `(apiVersion, kind)` apiserver-side CRD-lookup
24082 // tuple), `spec.ref`
24083 // ([`FLUX_GITREPOSITORY_KEY_REF`], the per-CR ref-selection
24084 // container-axis), and `spec.url`
24085 // ([`FLUX_GITREPOSITORY_KEY_URL`], the per-CR remote-repo-URL
24086 // leaf-scalar-axis). These three constants spell mutually
24087 // distinct schema axes on the same Flux v2 `source-controller`
24088 // CRD; pinning distinctness here means a future rebrand on
24089 // any one axis (a Flux v3 CRD-version bump, a `spec.ref`
24090 // container-axis rename, or a `spec.url` schema promotion)
24091 // surfaces as an edit on the corresponding canonical const
24092 // alone, without silently collapsing the three axes into one
24093 // edit-point at the rustc symbol-name axis.
24094 assert_ne!(FLUX_GITREPOSITORY_KEY_URL, FLUX_GITREPOSITORY_KEY_REF);
24095 assert_ne!(FLUX_GITREPOSITORY_KEY_URL, FLUX_GITREPOSITORY_API_VERSION);
24096 }
24097
24098 #[test]
24099 fn flux_gitrepository_ref_keys_all_carry_lower_camel_case_shape() {
24100 // Cross-axis invariant on all three arms of the FluxCD
24101 // source-controller `GitRepository.spec.ref` discriminated-union
24102 // axis: the Flux v2 CRD field-naming convention (inherited from
24103 // the upstream K8s API conventions) admits lowerCamelCase
24104 // per-field keys — `tag` / `branch` / `commit` all conform.
24105 // Pinning the shape here means a future rebrand on any of the
24106 // three canonical lifts can't silently land a malformed
24107 // sub-selector key (snake_case, kebab-case, UpperCamelCase,
24108 // empty) that the Flux v2 source-controller's per-CR reconcile
24109 // loop would reject at apply parse time. Peer to
24110 // `flux_key_interval_carries_lower_camel_case_shape` on the
24111 // sibling per-CR reconcile-poll-cadence scalar-axis key surface.
24112 for v in [
24113 FLUX_GITREPOSITORY_REF_KEY_TAG,
24114 FLUX_GITREPOSITORY_REF_KEY_BRANCH,
24115 FLUX_GITREPOSITORY_REF_KEY_COMMIT,
24116 ] {
24117 assert!(
24118 !v.is_empty(),
24119 "FLUX_GITREPOSITORY_REF_KEY_* {v:?} must be non-empty \
24120 per the Flux v2 CRD field-naming grammar"
24121 );
24122 let mut chars = v.chars();
24123 assert!(
24124 chars.next().is_some_and(|c| c.is_ascii_lowercase()),
24125 "FLUX_GITREPOSITORY_REF_KEY_* {v:?} must lead with an \
24126 ASCII-lowercase byte per the Flux v2 lowerCamelCase \
24127 per-CR-field-key convention"
24128 );
24129 assert!(
24130 v.chars().all(|c| c.is_ascii_alphanumeric()),
24131 "FLUX_GITREPOSITORY_REF_KEY_* {v:?} must be ASCII-\
24132 alphanumeric throughout per the Flux v2 lowerCamelCase \
24133 per-CR-field-key convention — no `_` / `-` / `.` / \
24134 whitespace bytes the Flux v2 source-controller's per-CR \
24135 reconcile loop would reject"
24136 );
24137 }
24138 }
24139
24140 #[test]
24141 fn flux_gitrepository_ref_keys_are_pairwise_distinct() {
24142 // The three arms of the FluxCD source-controller
24143 // `GitRepository.spec.ref` discriminated-union axis must remain
24144 // pairwise distinct — a hypothetical drift that collapsed two
24145 // sub-selector keys onto the same byte-string (e.g. an
24146 // accidental copy-paste making TAG and BRANCH both spell
24147 // `"tag"`) would silently reroute the per-shape emit at
24148 // `caixa_flux::GitRefSpec::ref_field_name` dispatch time and
24149 // dangle one arm's rendered `spec.ref` sub-block at cluster-
24150 // apply time. Pin the pairwise-distinctness here so the drift
24151 // fires at test time, not at cluster-apply time far from the
24152 // drift site.
24153 let keys = [
24154 FLUX_GITREPOSITORY_REF_KEY_TAG,
24155 FLUX_GITREPOSITORY_REF_KEY_BRANCH,
24156 FLUX_GITREPOSITORY_REF_KEY_COMMIT,
24157 ];
24158 for (i, a) in keys.iter().enumerate() {
24159 for b in keys.iter().skip(i + 1) {
24160 assert_ne!(
24161 a, b,
24162 "FLUX_GITREPOSITORY_REF_KEY_* arms must be pairwise \
24163 distinct (got a duplicate: {a:?})"
24164 );
24165 }
24166 }
24167 }
24168
24169 #[test]
24170 fn flux_kind_kustomization_carries_upper_camel_case_shape() {
24171 // Cross-axis invariant: a Kubernetes CRD `kind` discriminator is
24172 // an UpperCamelCase identifier per the K8s API conventions
24173 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds —
24174 // "Kinds are always UpperCamelCase"). Pinning the shape here
24175 // means a future rebrand on the canonical lift can't silently
24176 // land a malformed kind discriminator (snake_case, kebab-case,
24177 // lowercase, empty) that every downstream YAML-aware
24178 // deserializer would reject far from the rebrand commit's
24179 // source. The first-byte uppercase / rest-ASCII-alphanumeric
24180 // invariant is the load-bearing K8s API typed-discovery
24181 // contract: a value the apiserver's `RESTMapper` consults to
24182 // resolve the CRD's `RESTKind`. Peer to
24183 // `flux_kind_git_repository_carries_upper_camel_case_shape` /
24184 // `flux_kind_helm_release_carries_upper_camel_case_shape` on
24185 // the sibling Flux v2 controller-triplet `kind`-axis surface.
24186 let v = FLUX_KIND_KUSTOMIZATION;
24187 assert!(
24188 !v.is_empty(),
24189 "FLUX_KIND_KUSTOMIZATION {v:?} must be non-empty per the K8s API \
24190 UpperCamelCase kind discriminator grammar"
24191 );
24192 let first = v.chars().next().expect("non-empty");
24193 assert!(
24194 first.is_ascii_uppercase(),
24195 "FLUX_KIND_KUSTOMIZATION {v:?} first byte {first:?} must be \
24196 ASCII-uppercase per the K8s API UpperCamelCase kind discriminator \
24197 grammar (Kinds are always UpperCamelCase)"
24198 );
24199 assert!(
24200 v.chars().all(|c| c.is_ascii_alphanumeric()),
24201 "FLUX_KIND_KUSTOMIZATION {v:?} must be ASCII-alphanumeric \
24202 throughout per the K8s API kind discriminator grammar — no \
24203 snake_case, kebab-case, or whitespace bytes the apiserver-side \
24204 RESTMapper would reject"
24205 );
24206 }
24207
24208 #[test]
24209 fn gateway_api_api_version_pins_canonical_value() {
24210 // Pin the actual string so a typo in this lift can't silently
24211 // rebrand the K8s SIG-Network Gateway API CRD group/version
24212 // the rendered `Gateway` / `HTTPRoute` documents declare. The
24213 // string is part of the cluster-side contract with the
24214 // upstream Gateway-API-conformant gateway implementation
24215 // (Cilium, Istio, Envoy Gateway, NGINX, et al.): the
24216 // apiserver-side CRD-version registration watches the exact
24217 // `gateway.networking.k8s.io/v1` group/version; a drifted
24218 // value to a stale v1beta1 / v1alpha2 lands the rendered
24219 // `Gateway` / `HTTPRoute` outside the registration and fails
24220 // at apply time with "no kind 'Gateway' is registered for
24221 // version 'gateway.networking.k8s.io/v1beta1'"; changing it
24222 // is a coordinated Gateway API GA promotion alongside the
24223 // upstream SIG-Network deprecation cycle, not an incidental
24224 // edit. Peer to `flux_kustomization_api_version_pins_canonical_value`
24225 // / `flux_helmrelease_api_version_pins_canonical_value` /
24226 // `flux_gitrepository_api_version_pins_canonical_value` on
24227 // the canonical-K8s-CRD-axis-pin axis for the sibling
24228 // Flux v2 controller-triplet constants — extends the
24229 // canonical-string-pin discipline from the cluster-side
24230 // Flux v2 reconcile contract onto the cluster-side K8s
24231 // Gateway API ingress contract.
24232 assert_eq!(GATEWAY_API_API_VERSION, "gateway.networking.k8s.io/v1");
24233 }
24234
24235 #[test]
24236 fn gateway_api_api_version_carries_group_and_version_segments() {
24237 // Cross-axis invariant: a Kubernetes CRD `apiVersion` is a
24238 // `<group>/<version>` pair separated by exactly one `/` byte.
24239 // The group segment is a DNS-style multi-segment hostname
24240 // (`gateway.networking.k8s.io`) and the version segment is a
24241 // Kubernetes API version label (`v1`, `v1beta1`, `v1alpha2` —
24242 // peer with the K8s API versioning convention upstream
24243 // documents). Pinning this here means a future rebrand on the
24244 // canonical lift can't silently land a malformed apiVersion
24245 // (no `/`, two `/`, empty group, empty version) that every
24246 // downstream YAML-aware deserializer would reject far from the
24247 // rebrand commit's source. The single-`/` invariant is the
24248 // load-bearing K8s API typed-discovery contract: a value the
24249 // apiserver's `RESTMapper` consults to resolve the CRD's
24250 // `RESTKind`. Peer to
24251 // `flux_kustomization_api_version_carries_group_and_version_segments`
24252 // / `flux_helmrelease_api_version_carries_group_and_version_segments`
24253 // / `flux_gitrepository_api_version_carries_group_and_version_segments`
24254 // on the sibling Flux v2 controller-triplet CRD-axes.
24255 let v = GATEWAY_API_API_VERSION;
24256 let parts: Vec<&str> = v.split('/').collect();
24257 assert_eq!(
24258 parts.len(),
24259 2,
24260 "GATEWAY_API_API_VERSION {v:?} must split into exactly two \
24261 `/`-delimited segments (group/version) per the K8s CRD apiVersion \
24262 grammar — every downstream YAML-aware deserializer enforces this \
24263 shape"
24264 );
24265 assert!(
24266 !parts[0].is_empty(),
24267 "GATEWAY_API_API_VERSION {v:?} group segment must be non-empty"
24268 );
24269 assert!(
24270 !parts[1].is_empty(),
24271 "GATEWAY_API_API_VERSION {v:?} version segment must be non-empty"
24272 );
24273 assert!(
24274 parts[0].contains('.'),
24275 "GATEWAY_API_API_VERSION {v:?} group segment {group:?} must be a \
24276 DNS-style multi-segment hostname (the canonical CRD-group convention \
24277 every K8s controller-runtime / kube-rs-aware client expects)",
24278 group = parts[0]
24279 );
24280 }
24281
24282 #[test]
24283 fn cilium_api_version_pins_canonical_value() {
24284 // Pin the actual string so a typo in this lift can't silently
24285 // rebrand the Cilium CRD group/version the rendered
24286 // `CiliumNetworkPolicy` document declares. The string is part
24287 // of the cluster-side contract with the upstream Cilium
24288 // operator: the Cilium-operator-side CRD-version registration
24289 // watches the exact `cilium.io/v2` group/version; a drifted
24290 // value to a stale `v2alpha1` lands the rendered
24291 // `CiliumNetworkPolicy` outside the registration and fails at
24292 // apply time with "no kind 'CiliumNetworkPolicy' is registered
24293 // for version 'cilium.io/v2alpha1'"; changing it is a
24294 // coordinated Cilium-CRD promotion alongside the upstream
24295 // Cilium deprecation cycle, not an incidental edit. Peer to
24296 // `gateway_api_api_version_pins_canonical_value` /
24297 // `flux_kustomization_api_version_pins_canonical_value` /
24298 // `flux_helmrelease_api_version_pins_canonical_value` /
24299 // `flux_gitrepository_api_version_pins_canonical_value` on
24300 // the canonical-K8s-CRD-axis-pin axis for the sibling
24301 // K8s Gateway API + Flux v2 controller-triplet constants —
24302 // extends the canonical-string-pin discipline from the
24303 // cluster-side K8s Gateway API ingress + Flux v2 reconcile
24304 // contracts onto the cluster-side Cilium identity-based mesh
24305 // contract.
24306 assert_eq!(CILIUM_API_VERSION, "cilium.io/v2");
24307 }
24308
24309 #[test]
24310 fn cilium_api_version_carries_group_and_version_segments() {
24311 // Cross-axis invariant: a Kubernetes CRD `apiVersion` is a
24312 // `<group>/<version>` pair separated by exactly one `/` byte.
24313 // The group segment is a DNS-style hostname (`cilium.io`) and
24314 // the version segment is a Kubernetes API version label (`v2`,
24315 // `v2alpha1` — peer with the K8s API versioning convention
24316 // upstream documents). Pinning this here means a future rebrand
24317 // on the canonical lift can't silently land a malformed
24318 // apiVersion (no `/`, two `/`, empty group, empty version) that
24319 // every downstream YAML-aware deserializer would reject far
24320 // from the rebrand commit's source. The single-`/` invariant
24321 // is the load-bearing K8s API typed-discovery contract: a value
24322 // the apiserver's `RESTMapper` consults to resolve the CRD's
24323 // `RESTKind`. Peer to
24324 // `gateway_api_api_version_carries_group_and_version_segments`
24325 // / `flux_kustomization_api_version_carries_group_and_version_segments`
24326 // / `flux_helmrelease_api_version_carries_group_and_version_segments`
24327 // / `flux_gitrepository_api_version_carries_group_and_version_segments`
24328 // on the sibling K8s Gateway API + Flux v2 controller-triplet
24329 // CRD-axes.
24330 let v = CILIUM_API_VERSION;
24331 let parts: Vec<&str> = v.split('/').collect();
24332 assert_eq!(
24333 parts.len(),
24334 2,
24335 "CILIUM_API_VERSION {v:?} must split into exactly two \
24336 `/`-delimited segments (group/version) per the K8s CRD apiVersion \
24337 grammar — every downstream YAML-aware deserializer enforces this \
24338 shape"
24339 );
24340 assert!(
24341 !parts[0].is_empty(),
24342 "CILIUM_API_VERSION {v:?} group segment must be non-empty"
24343 );
24344 assert!(
24345 !parts[1].is_empty(),
24346 "CILIUM_API_VERSION {v:?} version segment must be non-empty"
24347 );
24348 assert!(
24349 parts[0].contains('.'),
24350 "CILIUM_API_VERSION {v:?} group segment {group:?} must be a \
24351 DNS-style hostname (the canonical CRD-group convention \
24352 every K8s controller-runtime / kube-rs-aware client expects)",
24353 group = parts[0]
24354 );
24355 }
24356
24357 #[test]
24358 fn cilium_kind_network_policy_pins_canonical_value() {
24359 // Pin the actual string so a typo in this lift can't silently
24360 // rebrand the Cilium-operator-side `CiliumNetworkPolicy` CRD
24361 // `kind` discriminator the rendered CNP document's top-level
24362 // `kind` axis declares. The string is part of the cluster-side
24363 // contract with the upstream Cilium operator — the apiserver-side
24364 // CRD resolution contract is the `(apiVersion, kind)` tuple
24365 // keyed against the registered `CustomResourceDefinition`, so
24366 // the kind half of the tuple is exactly as load-bearing as the
24367 // sibling [`CILIUM_API_VERSION`] apiVersion half. A drifted
24368 // value (e.g. an upstream rename to `CiliumNetworkPolicyV2`)
24369 // lands the rendered document outside the Cilium operator's
24370 // CRD registration; changing it is a coordinated Cilium-CRD
24371 // promotion alongside the upstream Cilium deprecation cycle,
24372 // not an incidental edit. Peer to
24373 // `flux_kind_kustomization_pins_canonical_value` /
24374 // `flux_kind_helm_release_pins_canonical_value` /
24375 // `flux_kind_git_repository_pins_canonical_value` on the
24376 // sibling cluster-side-CRD-`kind`-discriminator pin set —
24377 // extends the canonical-string-pin discipline from the Flux v2
24378 // controller-triplet `kind`-axis surface onto the Cilium-CRD
24379 // `kind`-axis surface, completing the per-Cilium-CRD
24380 // kind+apiVersion canonical-pin pair the M3 Aplicacao mesh
24381 // renderer's eBPF data-plane contract rests on.
24382 assert_eq!(CILIUM_KIND_NETWORK_POLICY, "CiliumNetworkPolicy");
24383 }
24384
24385 #[test]
24386 fn cilium_kind_network_policy_carries_upper_camel_case_shape() {
24387 // Cross-axis invariant: a Kubernetes CRD `kind` discriminator is
24388 // an UpperCamelCase identifier per the K8s API conventions
24389 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds —
24390 // "Kinds are always UpperCamelCase"). Pinning the shape here
24391 // means a future rebrand on the canonical lift can't silently
24392 // land a malformed kind discriminator (snake_case, kebab-case,
24393 // lowercase, empty) that every downstream YAML-aware
24394 // deserializer would reject far from the rebrand commit's
24395 // source. The first-byte uppercase / rest-ASCII-alphanumeric
24396 // invariant is the load-bearing K8s API typed-discovery
24397 // contract: a value the apiserver's `RESTMapper` consults to
24398 // resolve the CRD's `RESTKind`. Peer to
24399 // `flux_kind_kustomization_carries_upper_camel_case_shape` /
24400 // `flux_kind_helm_release_carries_upper_camel_case_shape` /
24401 // `flux_kind_git_repository_carries_upper_camel_case_shape` on
24402 // the sibling cluster-side-CRD-`kind`-discriminator surface.
24403 let v = CILIUM_KIND_NETWORK_POLICY;
24404 assert!(
24405 !v.is_empty(),
24406 "CILIUM_KIND_NETWORK_POLICY {v:?} must be non-empty per the K8s API \
24407 UpperCamelCase kind discriminator grammar"
24408 );
24409 let first = v.chars().next().expect("non-empty");
24410 assert!(
24411 first.is_ascii_uppercase(),
24412 "CILIUM_KIND_NETWORK_POLICY {v:?} first byte {first:?} must be \
24413 ASCII-uppercase per the K8s API UpperCamelCase kind discriminator \
24414 grammar (Kinds are always UpperCamelCase)"
24415 );
24416 assert!(
24417 v.chars().all(|c| c.is_ascii_alphanumeric()),
24418 "CILIUM_KIND_NETWORK_POLICY {v:?} must be ASCII-alphanumeric \
24419 throughout per the K8s API kind discriminator grammar — no \
24420 snake_case, kebab-case, or whitespace bytes the apiserver-side \
24421 RESTMapper would reject"
24422 );
24423 }
24424
24425 #[test]
24426 fn cilium_key_to_ports_pins_canonical_value() {
24427 // Pin the actual string so a typo in this lift can't silently
24428 // rebrand the Cilium CNP `spec.ingress[].toPorts[]` per-ingress-
24429 // rule port-set-container-axis key the rendered CNP document
24430 // mounts its per-port-set `{ports: […], rules: {…}}` list under.
24431 // The string is part of the cluster-side contract with the
24432 // upstream Cilium operator — the Cilium-operator-side per-CNP
24433 // L4/L7-dispatch pass keys off this axis to route the per-port
24434 // set through the eBPF data-plane's L4-allow (via `ports`) /
24435 // L7-dispatch (via nested `rules`) branches; a drifted value
24436 // (`"toport"` / `"toPort"` / `"targetPorts"`) at either the
24437 // production emitter or a downstream renderer's per-ingress-rule
24438 // port-set upsert silently emits a per-ingress-rule entry whose
24439 // port-set container the Cilium CRD schema validator drops as
24440 // unknown, and every intra-mesh `:contratos` flow the affected
24441 // CNP was authored to allow drops at the eBPF data-plane's
24442 // default-deny gate. Changing this value is a coordinated
24443 // Cilium-CRD promotion alongside the upstream Cilium project's
24444 // CRD schema-migration cycle, not an incidental edit. Peer to
24445 // `kube_key_rules_pins_canonical_value` (the nested
24446 // `spec.ingress[].toPorts[].rules` axis-key pin the L7-dispatch
24447 // container nests inside this port-set container's each entry)
24448 // on the sibling per-CNP-dispatch-axis pin set — completes the
24449 // per-CNP L4/L7-dispatch-container `(toPorts, rules)` pin pair
24450 // the M3 Aplicacao mesh renderer's eBPF data-plane contract
24451 // rests on.
24452 assert_eq!(CILIUM_KEY_TO_PORTS, "toPorts");
24453 }
24454
24455 #[test]
24456 fn cilium_key_to_ports_carries_lower_camel_case_shape() {
24457 // Cross-axis invariant: a Kubernetes CRD schema field name is a
24458 // lowerCamelCase identifier per the K8s API conventions
24459 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
24460 // "Field names should be lowercase camelCase") — first byte
24461 // ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
24462 // kebab-case or whitespace. Pinning the shape here means a
24463 // future rebrand on the canonical lift can't silently land a
24464 // malformed field-name discriminator (snake_case, kebab-case,
24465 // UpperCamelCase, empty) that the apiserver-side CRD schema
24466 // validator would reject far from the rebrand commit's source.
24467 // The first-byte lowercase / rest-ASCII-alphanumeric invariant
24468 // is the load-bearing K8s API typed-schema contract: a value
24469 // the apiserver-side OpenAPI schema validator consults to
24470 // resolve each CR-field's typed slot. Peer to the sibling
24471 // per-CNP `kind`-axis
24472 // `cilium_kind_network_policy_carries_upper_camel_case_shape`
24473 // pin — the UpperCamelCase K8s discriminator grammar governs
24474 // the top-level `kind` axis, the lowerCamelCase K8s field-name
24475 // grammar governs every nested schema-field axis (including
24476 // this per-ingress-rule port-set-container-axis key), same
24477 // convention distinct grammars.
24478 let v = CILIUM_KEY_TO_PORTS;
24479 assert!(
24480 !v.is_empty(),
24481 "CILIUM_KEY_TO_PORTS {v:?} must be non-empty per the K8s API \
24482 lowerCamelCase field-name grammar"
24483 );
24484 let first = v.chars().next().expect("non-empty");
24485 assert!(
24486 first.is_ascii_lowercase(),
24487 "CILIUM_KEY_TO_PORTS {v:?} first byte {first:?} must be \
24488 ASCII-lowercase per the K8s API lowerCamelCase field-name \
24489 grammar (field names are always lowerCamelCase)"
24490 );
24491 assert!(
24492 v.chars().all(|c| c.is_ascii_alphanumeric()),
24493 "CILIUM_KEY_TO_PORTS {v:?} must be ASCII-alphanumeric \
24494 throughout per the K8s API field-name grammar — no \
24495 snake_case, kebab-case, or whitespace bytes the apiserver-side \
24496 OpenAPI schema validator would reject"
24497 );
24498 }
24499
24500 #[test]
24501 fn cilium_key_endpoint_selector_pins_canonical_value() {
24502 // Pin the actual string so a typo in this lift can't silently
24503 // rebrand the Cilium CNP `spec.endpointSelector` destination-
24504 // identity-axis key the rendered CNP document mounts its
24505 // L3-target `LabelSelector` under. The string is part of the
24506 // cluster-side contract with the upstream Cilium operator —
24507 // the Cilium-operator-side per-CNP identity-resolution pass
24508 // keys off this axis to bind the emitted policy against its
24509 // destination workload identity via the K8s LabelSelector
24510 // schema; a drifted value (`"endpointselector"` /
24511 // `"endpointSelectors"` / `"endpoints"`) at either the
24512 // production emitter or a downstream renderer's per-CNP
24513 // destination-identity upsert silently emits a CNP whose
24514 // destination-identity axis the Cilium CRD schema validator
24515 // drops as unknown, and the policy binds against no
24516 // destination pods — every intra-mesh `:contratos` flow the
24517 // affected CNP was authored to allow drops at the eBPF
24518 // data-plane's default-deny gate. Changing this value is a
24519 // coordinated Cilium-CRD promotion alongside the upstream
24520 // Cilium project's CRD schema-migration cycle, not an
24521 // incidental edit. Peer to `cilium_key_to_ports_pins_\
24522 // canonical_value` (the per-ingress-rule port-set container
24523 // axis-key pin the L3-target selector pairs with under the
24524 // shared per-CNP-body schema) on the sibling per-CNP-body-axis
24525 // pin set — completes the per-CNP L3/L4/L7-triad
24526 // `(endpointSelector, ingress → toPorts → rules)` pin set the
24527 // M3 Aplicacao mesh renderer's eBPF data-plane contract rests
24528 // on.
24529 assert_eq!(CILIUM_KEY_ENDPOINT_SELECTOR, "endpointSelector");
24530 }
24531
24532 #[test]
24533 fn cilium_key_endpoint_selector_carries_lower_camel_case_shape() {
24534 // Cross-axis invariant: a Kubernetes CRD schema field name is a
24535 // lowerCamelCase identifier per the K8s API conventions
24536 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
24537 // "Field names should be lowercase camelCase") — first byte
24538 // ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
24539 // kebab-case or whitespace. Pinning the shape here means a
24540 // future rebrand on the canonical lift can't silently land a
24541 // malformed field-name discriminator (snake_case, kebab-case,
24542 // UpperCamelCase, empty) that the apiserver-side CRD schema
24543 // validator would reject far from the rebrand commit's source.
24544 // Peer to `cilium_key_to_ports_carries_lower_camel_case_shape`
24545 // on the sibling per-CNP-body-axis grammar-pin set — the
24546 // lowerCamelCase K8s field-name grammar governs every nested
24547 // schema-field axis (including this per-CNP destination-
24548 // identity-axis key), same convention.
24549 let v = CILIUM_KEY_ENDPOINT_SELECTOR;
24550 assert!(
24551 !v.is_empty(),
24552 "CILIUM_KEY_ENDPOINT_SELECTOR {v:?} must be non-empty per the K8s API \
24553 lowerCamelCase field-name grammar"
24554 );
24555 let first = v.chars().next().expect("non-empty");
24556 assert!(
24557 first.is_ascii_lowercase(),
24558 "CILIUM_KEY_ENDPOINT_SELECTOR {v:?} first byte {first:?} must be \
24559 ASCII-lowercase per the K8s API lowerCamelCase field-name \
24560 grammar (field names are always lowerCamelCase)"
24561 );
24562 assert!(
24563 v.chars().all(|c| c.is_ascii_alphanumeric()),
24564 "CILIUM_KEY_ENDPOINT_SELECTOR {v:?} must be ASCII-alphanumeric \
24565 throughout per the K8s API field-name grammar — no \
24566 snake_case, kebab-case, or whitespace bytes the apiserver-side \
24567 OpenAPI schema validator would reject"
24568 );
24569 }
24570
24571 #[test]
24572 fn cilium_key_ingress_pins_canonical_value() {
24573 // Pin the actual string so a typo in this lift can't silently
24574 // rebrand the Cilium CNP `spec.ingress[]` traffic-direction
24575 // container-axis key the rendered CNP document mounts its
24576 // permitted per-`(:de, :para)` inbound-ingress-rule list under.
24577 // The string is part of the cluster-side contract with the
24578 // upstream Cilium operator — the Cilium-operator-side per-CNP
24579 // L4/L7-dispatch pass keys off this axis to route the per-CNP
24580 // ingress-rule list through the eBPF data-plane's inbound-
24581 // traffic dispatch branch; a drifted value (`"Ingress"` /
24582 // `"ingressRules"` / `"inbound"`) at either the production
24583 // emitter or a downstream renderer's per-CNP traffic-direction
24584 // upsert silently emits a CNP whose ingress-rule list the
24585 // Cilium CRD schema validator drops as unknown, and every
24586 // intra-mesh `:contratos` flow the affected CNP was authored to
24587 // allow drops at the eBPF data-plane's default-deny gate.
24588 // Changing this value is a coordinated Cilium-CRD promotion
24589 // alongside the upstream Cilium project's CRD schema-migration
24590 // cycle, not an incidental edit. Peer to
24591 // `cilium_key_endpoint_selector_pins_canonical_value` (the
24592 // destination-identity axis-key pin the traffic-direction
24593 // container axis-key sits alongside under the shared per-CNP-
24594 // body schema) + `cilium_key_to_ports_pins_canonical_value`
24595 // (the per-ingress-rule port-set container axis-key pin the
24596 // traffic-direction axis nests) on the sibling per-CNP-body-
24597 // axis pin set — completes the per-CNP L3/L4/L7-triad
24598 // `(endpointSelector, ingress → toPorts → rules)` pin set the
24599 // M3 Aplicacao mesh renderer's eBPF data-plane contract rests
24600 // on.
24601 assert_eq!(CILIUM_KEY_INGRESS, "ingress");
24602 }
24603
24604 #[test]
24605 fn cilium_key_ingress_carries_lower_camel_case_shape() {
24606 // Cross-axis invariant: a Kubernetes CRD schema field name is a
24607 // lowerCamelCase identifier per the K8s API conventions
24608 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
24609 // "Field names should be lowercase camelCase") — first byte
24610 // ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
24611 // kebab-case or whitespace. Pinning the shape here means a
24612 // future rebrand on the canonical lift can't silently land a
24613 // malformed field-name discriminator (snake_case, kebab-case,
24614 // UpperCamelCase, empty) that the apiserver-side CRD schema
24615 // validator would reject far from the rebrand commit's source.
24616 // Peer to `cilium_key_endpoint_selector_carries_lower_camel_\
24617 // case_shape` / `cilium_key_to_ports_carries_lower_camel_case_\
24618 // shape` on the sibling per-CNP-body-axis grammar-pin set — the
24619 // lowerCamelCase K8s field-name grammar governs every nested
24620 // schema-field axis (including this per-CNP traffic-direction-
24621 // axis key), same convention.
24622 let v = CILIUM_KEY_INGRESS;
24623 assert!(
24624 !v.is_empty(),
24625 "CILIUM_KEY_INGRESS {v:?} must be non-empty per the K8s API \
24626 lowerCamelCase field-name grammar"
24627 );
24628 let first = v.chars().next().expect("non-empty");
24629 assert!(
24630 first.is_ascii_lowercase(),
24631 "CILIUM_KEY_INGRESS {v:?} first byte {first:?} must be \
24632 ASCII-lowercase per the K8s API lowerCamelCase field-name \
24633 grammar (field names are always lowerCamelCase)"
24634 );
24635 assert!(
24636 v.chars().all(|c| c.is_ascii_alphanumeric()),
24637 "CILIUM_KEY_INGRESS {v:?} must be ASCII-alphanumeric \
24638 throughout per the K8s API field-name grammar — no \
24639 snake_case, kebab-case, or whitespace bytes the apiserver-side \
24640 OpenAPI schema validator would reject"
24641 );
24642 }
24643
24644 #[test]
24645 fn cilium_key_from_endpoints_pins_canonical_value() {
24646 // Pin the actual string so a typo in this lift can't silently
24647 // rebrand the Cilium CNP `spec.ingress[].fromEndpoints[]`
24648 // identity-source selector-list-axis key the rendered CNP
24649 // document mounts its permitted-source `LabelSelector` list
24650 // under. The string is part of the cluster-side contract with
24651 // the upstream Cilium operator — the Cilium-operator-side per-
24652 // CNP identity-resolution pass keys off this axis to bind the
24653 // emitted ingress rule against the admitted source workload
24654 // identities via the K8s LabelSelector schema; a drifted value
24655 // (`"fromendpoints"` / `"fromEndPoint"` / `"sourceEndpoints"`)
24656 // at either the production emitter or a downstream renderer's
24657 // per-ingress-rule identity-source upsert silently emits a CNP
24658 // whose per-ingress-rule identity-source axis the Cilium CRD
24659 // schema validator drops as unknown, and the ingress rule
24660 // admits no source pods — every intra-mesh `:contratos` flow
24661 // the affected CNP was authored to allow drops at the eBPF
24662 // data-plane's default-deny gate. Changing this value is a
24663 // coordinated Cilium-CRD promotion alongside the upstream
24664 // Cilium project's CRD schema-migration cycle, not an
24665 // incidental edit. Peer to
24666 // `cilium_key_endpoint_selector_pins_canonical_value` (the
24667 // destination-identity axis-key pin the identity-source axis
24668 // structurally pairs with under the SPIFFE-identity-bound per-
24669 // CNP access-control contract) on the sibling per-CNP identity-
24670 // pair pin set — completes the per-CNP identity-pair
24671 // `(endpointSelector, fromEndpoints)` pin set the M3 Aplicacao
24672 // mesh renderer's eBPF data-plane contract rests on.
24673 assert_eq!(CILIUM_KEY_FROM_ENDPOINTS, "fromEndpoints");
24674 }
24675
24676 #[test]
24677 fn cilium_key_from_endpoints_carries_lower_camel_case_shape() {
24678 // Cross-axis invariant: a Kubernetes CRD schema field name is a
24679 // lowerCamelCase identifier per the K8s API conventions
24680 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
24681 // "Field names should be lowercase camelCase") — first byte
24682 // ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
24683 // kebab-case or whitespace. Pinning the shape here means a
24684 // future rebrand on the canonical lift can't silently land a
24685 // malformed field-name discriminator (snake_case, kebab-case,
24686 // UpperCamelCase, empty) that the apiserver-side CRD schema
24687 // validator would reject far from the rebrand commit's source.
24688 // Peer to `cilium_key_endpoint_selector_carries_lower_camel_\
24689 // case_shape` / `cilium_key_ingress_carries_lower_camel_case_\
24690 // shape` / `cilium_key_to_ports_carries_lower_camel_case_shape`
24691 // on the sibling per-CNP-body-axis grammar-pin set — the
24692 // lowerCamelCase K8s field-name grammar governs every nested
24693 // schema-field axis (including this per-ingress-rule identity-
24694 // source-axis key), same convention.
24695 let v = CILIUM_KEY_FROM_ENDPOINTS;
24696 assert!(
24697 !v.is_empty(),
24698 "CILIUM_KEY_FROM_ENDPOINTS {v:?} must be non-empty per the K8s API \
24699 lowerCamelCase field-name grammar"
24700 );
24701 let first = v.chars().next().expect("non-empty");
24702 assert!(
24703 first.is_ascii_lowercase(),
24704 "CILIUM_KEY_FROM_ENDPOINTS {v:?} first byte {first:?} must be \
24705 ASCII-lowercase per the K8s API lowerCamelCase field-name \
24706 grammar (field names are always lowerCamelCase)"
24707 );
24708 assert!(
24709 v.chars().all(|c| c.is_ascii_alphanumeric()),
24710 "CILIUM_KEY_FROM_ENDPOINTS {v:?} must be ASCII-alphanumeric \
24711 throughout per the K8s API field-name grammar — no \
24712 snake_case, kebab-case, or whitespace bytes the apiserver-side \
24713 OpenAPI schema validator would reject"
24714 );
24715 }
24716
24717 #[test]
24718 fn cilium_key_ports_pins_canonical_value() {
24719 // Pin the actual string so a typo in this lift can't silently
24720 // rebrand the Cilium CNP `spec.ingress[].toPorts[].ports[]`
24721 // per-`toPorts[]`-entry L4-port-tuple-list-container-axis key
24722 // the rendered CNP document mounts its per-port-set
24723 // `[{port, protocol}]` list under. The string is part of the
24724 // cluster-side contract with the upstream Cilium operator —
24725 // the Cilium-operator-side per-CNP L4-allow eBPF-program-
24726 // generation pass keys off this axis to source the per-port-set
24727 // `(port, protocol)` tuples the emitted ingress rule admits; a
24728 // drifted value (`"port"` / `"portList"` / `"L4Ports"`) at
24729 // either the production emitter or a downstream renderer's
24730 // per-`toPorts[]`-entry L4-port-tuple-list upsert silently
24731 // emits a per-`toPorts[]` entry whose L4-port-tuple-list-
24732 // container axis the Cilium CRD schema validator drops as
24733 // unknown, and the port-set admits no `(port, protocol)`
24734 // tuple — every intra-mesh `:contratos` flow the affected CNP
24735 // was authored to allow drops at the eBPF data-plane's
24736 // default-deny gate. Changing this value is a coordinated
24737 // Cilium-CRD promotion alongside the upstream Cilium project's
24738 // CRD schema-migration cycle, not an incidental edit. Peer to
24739 // `cilium_key_to_ports_pins_canonical_value` (the outer per-
24740 // ingress-rule port-set-container axis-key pin the L4 port-
24741 // tuple-list-container axis nests inside) on the sibling per-
24742 // CNP-dispatch-axis pin set — completes the per-CNP L4-half
24743 // `(toPorts, ports)` container-pair pin the M3 Aplicacao mesh
24744 // renderer's eBPF data-plane L4-allow contract rests on.
24745 assert_eq!(CILIUM_KEY_PORTS, "ports");
24746 }
24747
24748 #[test]
24749 fn cilium_key_ports_carries_lower_camel_case_shape() {
24750 // Cross-axis invariant: a Kubernetes CRD schema field name is a
24751 // lowerCamelCase identifier per the K8s API conventions
24752 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
24753 // "Field names should be lowercase camelCase") — first byte
24754 // ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
24755 // kebab-case or whitespace. Pinning the shape here means a
24756 // future rebrand on the canonical lift can't silently land a
24757 // malformed field-name discriminator (snake_case, kebab-case,
24758 // UpperCamelCase, empty) that the apiserver-side CRD schema
24759 // validator would reject far from the rebrand commit's source.
24760 // Peer to `cilium_key_endpoint_selector_carries_lower_camel_\
24761 // case_shape` / `cilium_key_ingress_carries_lower_camel_case_\
24762 // shape` / `cilium_key_to_ports_carries_lower_camel_case_shape`
24763 // / `cilium_key_from_endpoints_carries_lower_camel_case_shape`
24764 // on the sibling per-CNP-body-axis grammar-pin set — the
24765 // lowerCamelCase K8s field-name grammar governs every nested
24766 // schema-field axis (including this per-`toPorts[]`-entry L4-
24767 // port-tuple-list-container-axis key), same convention.
24768 let v = CILIUM_KEY_PORTS;
24769 assert!(
24770 !v.is_empty(),
24771 "CILIUM_KEY_PORTS {v:?} must be non-empty per the K8s API \
24772 lowerCamelCase field-name grammar"
24773 );
24774 let first = v.chars().next().expect("non-empty");
24775 assert!(
24776 first.is_ascii_lowercase(),
24777 "CILIUM_KEY_PORTS {v:?} first byte {first:?} must be \
24778 ASCII-lowercase per the K8s API lowerCamelCase field-name \
24779 grammar (field names are always lowerCamelCase)"
24780 );
24781 assert!(
24782 v.chars().all(|c| c.is_ascii_alphanumeric()),
24783 "CILIUM_KEY_PORTS {v:?} must be ASCII-alphanumeric \
24784 throughout per the K8s API field-name grammar — no \
24785 snake_case, kebab-case, or whitespace bytes the apiserver-side \
24786 OpenAPI schema validator would reject"
24787 );
24788 }
24789
24790 #[test]
24791 fn cilium_key_authentication_pins_canonical_value() {
24792 // Pin the actual string so a typo in this lift can't silently
24793 // rebrand the Cilium CNP `spec.ingress[].authentication`
24794 // per-ingress-rule mutual-auth-policy body-axis key the
24795 // rendered CNP document mounts its per-rule mTLS enforcement
24796 // block under. The string is part of the cluster-side
24797 // contract with the upstream Cilium operator — the Cilium-
24798 // operator-side per-CNP mutual-auth SPIFFE-handshake pipeline
24799 // keys off this axis to source the per-rule mTLS enforcement
24800 // mode (`required` vs `disabled`); a drifted value (`"auth"`
24801 // / `"mutualAuth"` / `"mtls"` / `"authPolicy"`) at either
24802 // the production emitter or a downstream renderer's per-
24803 // ingress-rule mutual-auth upsert silently emits a per-
24804 // `ingress[]` entry whose mutual-auth-axis the Cilium CRD
24805 // schema validator drops as unknown, and the ingress rule
24806 // falls back to the cluster-default authentication mode
24807 // (typically `"disabled"` — no mutual-auth enforcement)
24808 // silently bypassing the SPIFFE-identity-bound mTLS handshake
24809 // every intra-mesh `:contratos` flow the CNP was authored to
24810 // protect. Changing this value is a coordinated Cilium-CRD
24811 // promotion alongside the upstream Cilium project's CRD
24812 // schema-migration cycle, not an incidental edit. Peer to
24813 // `cilium_key_from_endpoints_pins_canonical_value` /
24814 // `cilium_key_to_ports_pins_canonical_value` (the sibling
24815 // per-ingress-rule-body-axis pins the mutual-auth axis pairs
24816 // with at the per-rule triple
24817 // `(fromEndpoints, toPorts, authentication)`) on the sibling
24818 // per-CNP-dispatch-axis pin set — completes the per-CNP per-
24819 // ingress-rule-body triple the M3 Aplicacao mesh renderer's
24820 // SPIFFE-identity-bound per-edge mTLS contract rests on.
24821 assert_eq!(CILIUM_KEY_AUTHENTICATION, "authentication");
24822 }
24823
24824 #[test]
24825 fn cilium_key_authentication_carries_lower_camel_case_shape() {
24826 // Cross-axis invariant: a Kubernetes CRD schema field name is a
24827 // lowerCamelCase identifier per the K8s API conventions
24828 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
24829 // "Field names should be lowercase camelCase") — first byte
24830 // ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
24831 // kebab-case or whitespace. Pinning the shape here means a
24832 // future rebrand on the canonical lift can't silently land a
24833 // malformed field-name discriminator (snake_case, kebab-case,
24834 // UpperCamelCase, empty) that the apiserver-side CRD schema
24835 // validator would reject far from the rebrand commit's source.
24836 // Peer to `cilium_key_endpoint_selector_carries_lower_camel_\
24837 // case_shape` / `cilium_key_ingress_carries_lower_camel_case_\
24838 // shape` / `cilium_key_to_ports_carries_lower_camel_case_shape`
24839 // / `cilium_key_from_endpoints_carries_lower_camel_case_shape`
24840 // / `cilium_key_ports_carries_lower_camel_case_shape` on the
24841 // sibling per-CNP-body-axis grammar-pin set — the
24842 // lowerCamelCase K8s field-name grammar governs every nested
24843 // schema-field axis (including this per-`ingress[]`-entry
24844 // mutual-auth-policy body-axis key), same convention.
24845 let v = CILIUM_KEY_AUTHENTICATION;
24846 assert!(
24847 !v.is_empty(),
24848 "CILIUM_KEY_AUTHENTICATION {v:?} must be non-empty per the K8s API \
24849 lowerCamelCase field-name grammar"
24850 );
24851 let first = v.chars().next().expect("non-empty");
24852 assert!(
24853 first.is_ascii_lowercase(),
24854 "CILIUM_KEY_AUTHENTICATION {v:?} first byte {first:?} must be \
24855 ASCII-lowercase per the K8s API lowerCamelCase field-name \
24856 grammar (field names are always lowerCamelCase)"
24857 );
24858 assert!(
24859 v.chars().all(|c| c.is_ascii_alphanumeric()),
24860 "CILIUM_KEY_AUTHENTICATION {v:?} must be ASCII-alphanumeric \
24861 throughout per the K8s API field-name grammar — no \
24862 snake_case, kebab-case, or whitespace bytes the apiserver-side \
24863 OpenAPI schema validator would reject"
24864 );
24865 }
24866
24867 #[test]
24868 fn cilium_key_mode_pins_canonical_value() {
24869 // Pin the actual string so a typo in this lift can't silently
24870 // rebrand the Cilium CNP `spec.ingress[].authentication.mode`
24871 // per-ingress-rule mutual-auth-mode-discriminator leaf-scalar-
24872 // axis key the rendered CNP document mounts its per-rule mTLS
24873 // enforcement mode value under. The string is part of the
24874 // cluster-side contract with the upstream Cilium operator —
24875 // the Cilium-operator-side per-CNP mutual-auth SPIFFE-handshake
24876 // pipeline reads this leaf axis to source the per-rule mTLS
24877 // enforcement mode value (`"required"` vs `"disabled"`); a
24878 // drifted key (`"policy"` / `"authMode"` / `"handshakeMode"`)
24879 // at either the production emitter or a downstream renderer's
24880 // per-ingress-rule mutual-auth-mode-leaf upsert silently emits
24881 // a per-`ingress[]` entry whose mutual-auth block's mode-
24882 // discriminator leaf-axis the Cilium CRD schema validator
24883 // drops as unknown, and the ingress rule falls back to the
24884 // cluster-default authentication mode (typically `"disabled"`
24885 // — no mutual-auth enforcement) silently bypassing the SPIFFE-
24886 // identity-bound mTLS handshake every intra-mesh `:contratos`
24887 // flow the CNP was authored to protect. Changing this value is
24888 // a coordinated Cilium-CRD promotion alongside the upstream
24889 // Cilium project's CRD schema-migration cycle, not an
24890 // incidental edit. Peer to
24891 // `cilium_key_authentication_pins_canonical_value` on the
24892 // sibling per-ingress-rule mutual-auth body-axis pin set —
24893 // completes the per-rule mutual-auth
24894 // `(authentication → mode)` body/leaf axis pin pair the M3
24895 // Aplicacao mesh renderer's SPIFFE-identity-bound per-edge
24896 // mTLS enforcement contract rests on. Byte-identical to the
24897 // sibling `:politicas :circuit-breaker (:window)` /
24898 // `:placement :estrategia` overlay mode-like axes today, but
24899 // semantically distinct: this const names the Cilium CRD's
24900 // per-authentication-block mode-discriminator leaf-axis key
24901 // (spelled per the Cilium project's CRD schema), so a future
24902 // rebrand on the Cilium CRD's per-authentication-block mode-
24903 // leaf axis lands at its own canonical const without coupling
24904 // the Cilium schema to any peer surface that happens to carry
24905 // the same byte.
24906 assert_eq!(CILIUM_KEY_MODE, "mode");
24907 }
24908
24909 #[test]
24910 fn cilium_key_mode_carries_lower_camel_case_shape() {
24911 // Cross-axis invariant: a Kubernetes CRD schema field name is a
24912 // lowerCamelCase identifier per the K8s API conventions
24913 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
24914 // "Field names should be lowercase camelCase") — first byte
24915 // ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
24916 // kebab-case or whitespace. Pinning the shape here means a
24917 // future rebrand on the canonical lift can't silently land a
24918 // malformed field-name discriminator (snake_case, kebab-case,
24919 // UpperCamelCase, empty) that the apiserver-side CRD schema
24920 // validator would reject far from the rebrand commit's source.
24921 // Peer to `cilium_key_authentication_carries_lower_camel_case_\
24922 // shape` on the sibling per-ingress-rule mutual-auth-body-axis
24923 // grammar-pin — the lowerCamelCase K8s field-name grammar
24924 // governs every nested schema-field axis (including this
24925 // per-authentication-block mode-discriminator leaf-axis key),
24926 // same convention.
24927 let v = CILIUM_KEY_MODE;
24928 assert!(
24929 !v.is_empty(),
24930 "CILIUM_KEY_MODE {v:?} must be non-empty per the K8s API \
24931 lowerCamelCase field-name grammar"
24932 );
24933 let first = v.chars().next().expect("non-empty");
24934 assert!(
24935 first.is_ascii_lowercase(),
24936 "CILIUM_KEY_MODE {v:?} first byte {first:?} must be \
24937 ASCII-lowercase per the K8s API lowerCamelCase field-name \
24938 grammar (field names are always lowerCamelCase)"
24939 );
24940 assert!(
24941 v.chars().all(|c| c.is_ascii_alphanumeric()),
24942 "CILIUM_KEY_MODE {v:?} must be ASCII-alphanumeric \
24943 throughout per the K8s API field-name grammar — no \
24944 snake_case, kebab-case, or whitespace bytes the apiserver-side \
24945 OpenAPI schema validator would reject"
24946 );
24947 }
24948
24949 #[test]
24950 fn cilium_key_http_pins_canonical_value() {
24951 // Pin the actual string so a typo in this lift can't silently
24952 // rebrand the Cilium CNP `spec.ingress[].toPorts[].rules.http`
24953 // per-`toPorts[]` L7-HTTP-rule-list-discriminator container-axis
24954 // key the rendered CNP document mounts its per-`toPorts[]` L7
24955 // URL-path-prefix predicate list under. The string is part of the
24956 // cluster-side contract with the upstream Cilium operator — the
24957 // Cilium-operator-side per-CNP L7 dispatch pipeline reads this
24958 // container axis to source the per-`toPorts[]` L7 URL-path-prefix
24959 // predicate list the ingress rule was authored to filter each
24960 // HTTP-shaped `:contratos` flow through; a drifted key (`"HTTP"` /
24961 // `"Http"` / `"httpRules"` / `"httpMatch"`) at either the
24962 // production emitter or a downstream renderer's per-`toPorts[]`
24963 // L7-rule-list-discriminator upsert silently emits a per-
24964 // `toPorts[]` entry whose L7-HTTP-rule-list-discriminator key the
24965 // Cilium CRD schema validator drops as unknown, and the per-
24966 // `toPorts[]` entry falls back to L4-only enforcement — no L7
24967 // URL-path predicate is applied — silently admitting every HTTP-
24968 // method / URL-path combination the ingress rule was authored to
24969 // filter to the exact path prefix set the typed `:contratos`
24970 // graph names at the L7 introspection axis. Changing this value
24971 // is a coordinated Cilium-CRD promotion alongside the upstream
24972 // Cilium project's CRD schema-migration cycle, not an incidental
24973 // edit. Peer to `cilium_key_mode_pins_canonical_value` /
24974 // `cilium_key_authentication_pins_canonical_value` on the
24975 // sibling per-ingress-rule mutual-auth body/leaf axis pin pair —
24976 // completes the per-`toPorts[]` L7-introspection
24977 // `(rules → http)` container/protocol-discriminator axis pin
24978 // pair the M3 Aplicacao mesh renderer's HTTP-shaped-`:contratos`
24979 // URL-path-prefix-filtering L7-enforcement contract rests on.
24980 // Byte-identical to the sibling `Gateway.spec.listeners[].name`
24981 // arbitrary-author-chosen listener-name today (`"http"` — the
24982 // author-chosen name for the substrate's V0 HTTP listener), but
24983 // semantically distinct: this const names the Cilium CRD's per-
24984 // `toPorts[]` L7-HTTP-rule-list-discriminator container-axis key
24985 // (spelled per the Cilium project's CRD schema), so a future
24986 // rebrand on the Cilium CRD's L7-HTTP-rule-list-discriminator
24987 // axis lands at its own canonical const without coupling the
24988 // Cilium schema to any peer surface that happens to carry the
24989 // same byte.
24990 assert_eq!(CILIUM_KEY_HTTP, "http");
24991 }
24992
24993 #[test]
24994 fn cilium_key_http_carries_lower_camel_case_shape() {
24995 // Cross-axis invariant: a Kubernetes CRD schema field name is a
24996 // lowerCamelCase identifier per the K8s API conventions
24997 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
24998 // "Field names should be lowercase camelCase") — first byte
24999 // ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
25000 // kebab-case or whitespace. Pinning the shape here means a
25001 // future rebrand on the canonical lift can't silently land a
25002 // malformed field-name discriminator (snake_case, kebab-case,
25003 // UpperCamelCase, empty) that the apiserver-side CRD schema
25004 // validator would reject far from the rebrand commit's source.
25005 // Peer to `cilium_key_mode_carries_lower_camel_case_shape` /
25006 // `cilium_key_authentication_carries_lower_camel_case_shape` on
25007 // the sibling per-ingress-rule mutual-auth-body/leaf-axis
25008 // grammar-pin set — the lowerCamelCase K8s field-name grammar
25009 // governs every nested schema-field axis (including this per-
25010 // `toPorts[]` L7-HTTP-rule-list-discriminator container-axis
25011 // key), same convention.
25012 let v = CILIUM_KEY_HTTP;
25013 assert!(
25014 !v.is_empty(),
25015 "CILIUM_KEY_HTTP {v:?} must be non-empty per the K8s API \
25016 lowerCamelCase field-name grammar"
25017 );
25018 let first = v.chars().next().expect("non-empty");
25019 assert!(
25020 first.is_ascii_lowercase(),
25021 "CILIUM_KEY_HTTP {v:?} first byte {first:?} must be \
25022 ASCII-lowercase per the K8s API lowerCamelCase field-name \
25023 grammar (field names are always lowerCamelCase)"
25024 );
25025 assert!(
25026 v.chars().all(|c| c.is_ascii_alphanumeric()),
25027 "CILIUM_KEY_HTTP {v:?} must be ASCII-alphanumeric \
25028 throughout per the K8s API field-name grammar — no \
25029 snake_case, kebab-case, or whitespace bytes the apiserver-side \
25030 OpenAPI schema validator would reject"
25031 );
25032 }
25033
25034 #[test]
25035 fn kube_key_type_pins_canonical_value() {
25036 // Pin the actual string so a typo in this lift can't silently
25037 // rebrand the K8s discriminated-union `type` scalar-discriminator
25038 // container-axis key every rendered CR mounts its per-position
25039 // discriminated-union type-value under. The string is part of the
25040 // cluster-side contract with every K8s apiserver-side OpenAPI
25041 // schema validator — the Gateway API v1 gateway-class-controller's
25042 // per-`HTTPRouteMatch` path-selection-predicate dispatch pass
25043 // reads this scalar-key to source the path-match-strategy
25044 // discriminator (the closed `PathMatchType` OpenAPI schema enum's
25045 // `{Exact, PathPrefix, RegularExpression}` set) the per-rule L7
25046 // URL-path-filtering was authored to bind — a drifted key
25047 // (`"Type"` / `"kind"` / `"discriminator"` / `"predicate"`) at
25048 // either the production emitter or a downstream renderer's per-
25049 // `HTTPRouteMatch` path-selection-predicate discriminator upsert
25050 // silently emits a per-match entry whose discriminator scalar-key
25051 // the Gateway API v1 `HTTPPathMatch` OpenAPI schema validator
25052 // drops as unknown, and the per-match entry falls back to the
25053 // schema-side default path-match-strategy — silently admitting
25054 // every URL-path prefix the ingress rule was authored to filter
25055 // to the exact predicate the typed `:entrada :paths` slot names
25056 // at the request-path-selection axis. Changing this value is a
25057 // coordinated K8s-API-conventions promotion alongside the
25058 // upstream sig-architecture per-version deprecation cycle, not
25059 // an incidental edit. Peer to
25060 // `cilium_key_http_pins_canonical_value` /
25061 // `cilium_key_mode_pins_canonical_value` /
25062 // `cilium_key_authentication_pins_canonical_value` on the
25063 // sibling per-CRD-body-axis pin set — extends the canonical-
25064 // string-pin discipline from the per-CRD-body-axis surfaces
25065 // onto the load-bearing nested K8s-discriminated-union-type-
25066 // scalar-discriminator axis every downstream apiserver-side
25067 // OpenAPI-schema-validator / gateway-class-controller consumer
25068 // of the rendered mesh bundle keys off before it can commit to
25069 // a per-match request-path-selection predicate.
25070 assert_eq!(KUBE_KEY_TYPE, "type");
25071 }
25072
25073 #[test]
25074 fn kube_key_type_carries_lower_camel_case_shape() {
25075 // Cross-axis invariant: a Kubernetes CRD schema field name is a
25076 // lowerCamelCase identifier per the K8s API conventions
25077 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
25078 // "Field names should be lowercase camelCase") — first byte
25079 // ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
25080 // kebab-case or whitespace. Pinning the shape here means a
25081 // future rebrand on the canonical lift can't silently land a
25082 // malformed field-name discriminator (snake_case, kebab-case,
25083 // UpperCamelCase, empty) that the apiserver-side CRD schema
25084 // validator would reject far from the rebrand commit's source.
25085 // Peer to `cilium_key_http_carries_lower_camel_case_shape` /
25086 // `cilium_key_mode_carries_lower_camel_case_shape` /
25087 // `cilium_key_authentication_carries_lower_camel_case_shape` on
25088 // the sibling per-CRD-body-axis grammar-pin set — the
25089 // lowerCamelCase K8s field-name grammar governs every nested
25090 // schema-field axis (including this K8s-discriminated-union-
25091 // type-scalar-discriminator axis), same convention.
25092 let v = KUBE_KEY_TYPE;
25093 assert!(
25094 !v.is_empty(),
25095 "KUBE_KEY_TYPE {v:?} must be non-empty per the K8s API \
25096 lowerCamelCase field-name grammar"
25097 );
25098 let first = v.chars().next().expect("non-empty");
25099 assert!(
25100 first.is_ascii_lowercase(),
25101 "KUBE_KEY_TYPE {v:?} first byte {first:?} must be \
25102 ASCII-lowercase per the K8s API lowerCamelCase field-name \
25103 grammar (field names are always lowerCamelCase)"
25104 );
25105 assert!(
25106 v.chars().all(|c| c.is_ascii_alphanumeric()),
25107 "KUBE_KEY_TYPE {v:?} must be ASCII-alphanumeric \
25108 throughout per the K8s API field-name grammar — no \
25109 snake_case, kebab-case, or whitespace bytes the apiserver-side \
25110 OpenAPI schema validator would reject"
25111 );
25112 }
25113
25114 #[test]
25115 fn gateway_api_kind_gateway_pins_canonical_value() {
25116 // Pin the actual string so a typo in this lift can't silently
25117 // rebrand the Gateway-API-conformant `Gateway` CRD `kind`
25118 // discriminator the rendered Gateway document's top-level
25119 // `kind` axis declares. The string is part of the cluster-side
25120 // contract with every Gateway-API-conformant gateway
25121 // implementation (Cilium, Istio, Envoy Gateway, NGINX) — the
25122 // apiserver-side CRD resolution contract is the
25123 // `(apiVersion, kind)` tuple keyed against the registered
25124 // `CustomResourceDefinition`, so the kind half of the tuple is
25125 // exactly as load-bearing as the sibling
25126 // [`GATEWAY_API_API_VERSION`] apiVersion half. A drifted value
25127 // (e.g. an upstream Gateway-API rebrand to `GatewayV1`) lands
25128 // the rendered document outside the apiserver-side CRD
25129 // registration; changing it is a coordinated Gateway-API
25130 // promotion alongside the upstream SIG-Network deprecation
25131 // cycle, not an incidental edit. Peer to
25132 // `cilium_kind_network_policy_pins_canonical_value` /
25133 // `flux_kind_kustomization_pins_canonical_value` /
25134 // `flux_kind_helm_release_pins_canonical_value` /
25135 // `flux_kind_git_repository_pins_canonical_value` on the
25136 // sibling cluster-side-CRD-`kind`-discriminator pin set —
25137 // extends the canonical-string-pin discipline from the
25138 // Cilium-CRD + Flux v2 controller-triplet `kind`-axis surfaces
25139 // onto the Gateway-API-CRD `kind`-axis surface, beginning the
25140 // per-Gateway-API-CRD kind+apiVersion canonical-pin pair the
25141 // M3 Aplicacao mesh renderer's external `:entrada` ingress
25142 // contract rests on.
25143 assert_eq!(GATEWAY_API_KIND_GATEWAY, "Gateway");
25144 }
25145
25146 #[test]
25147 fn gateway_api_kind_gateway_carries_upper_camel_case_shape() {
25148 // Cross-axis invariant: a Kubernetes CRD `kind` discriminator is
25149 // an UpperCamelCase identifier per the K8s API conventions
25150 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds —
25151 // "Kinds are always UpperCamelCase"). Pinning the shape here
25152 // means a future rebrand on the canonical lift can't silently
25153 // land a malformed kind discriminator (snake_case, kebab-case,
25154 // lowercase, empty) that every downstream YAML-aware
25155 // deserializer would reject far from the rebrand commit's
25156 // source. The first-byte uppercase / rest-ASCII-alphanumeric
25157 // invariant is the load-bearing K8s API typed-discovery
25158 // contract: a value the apiserver's `RESTMapper` consults to
25159 // resolve the CRD's `RESTKind`. Peer to
25160 // `cilium_kind_network_policy_carries_upper_camel_case_shape` /
25161 // `flux_kind_kustomization_carries_upper_camel_case_shape` /
25162 // `flux_kind_helm_release_carries_upper_camel_case_shape` /
25163 // `flux_kind_git_repository_carries_upper_camel_case_shape` on
25164 // the sibling cluster-side-CRD-`kind`-discriminator surface.
25165 let v = GATEWAY_API_KIND_GATEWAY;
25166 assert!(
25167 !v.is_empty(),
25168 "GATEWAY_API_KIND_GATEWAY {v:?} must be non-empty per the K8s API \
25169 UpperCamelCase kind discriminator grammar"
25170 );
25171 let first = v.chars().next().expect("non-empty");
25172 assert!(
25173 first.is_ascii_uppercase(),
25174 "GATEWAY_API_KIND_GATEWAY {v:?} first byte {first:?} must be \
25175 ASCII-uppercase per the K8s API UpperCamelCase kind discriminator \
25176 grammar (Kinds are always UpperCamelCase)"
25177 );
25178 assert!(
25179 v.chars().all(|c| c.is_ascii_alphanumeric()),
25180 "GATEWAY_API_KIND_GATEWAY {v:?} must be ASCII-alphanumeric \
25181 throughout per the K8s API kind discriminator grammar — no \
25182 snake_case, kebab-case, or whitespace bytes the apiserver-side \
25183 RESTMapper would reject"
25184 );
25185 }
25186
25187 #[test]
25188 fn gateway_api_kind_http_route_pins_canonical_value() {
25189 // Pin the actual string so a typo in this lift can't silently
25190 // rebrand the Gateway-API-conformant `HTTPRoute` CRD `kind`
25191 // discriminator the rendered HTTPRoute document's top-level
25192 // `kind` axis declares. The string is part of the cluster-side
25193 // contract with every Gateway-API-conformant gateway
25194 // implementation (Cilium, Istio, Envoy Gateway, NGINX) — the
25195 // apiserver-side CRD resolution contract is the
25196 // `(apiVersion, kind)` tuple keyed against the registered
25197 // `CustomResourceDefinition`, so the kind half of the tuple is
25198 // exactly as load-bearing as the sibling
25199 // [`GATEWAY_API_API_VERSION`] apiVersion half. A drifted value
25200 // (e.g. an upstream Gateway-API rebrand to `HTTPRouteV1`) lands
25201 // the rendered document outside the apiserver-side CRD
25202 // registration; changing it is a coordinated Gateway-API
25203 // promotion alongside the upstream SIG-Network deprecation
25204 // cycle, not an incidental edit. Peer to
25205 // `gateway_api_kind_gateway_pins_canonical_value` /
25206 // `cilium_kind_network_policy_pins_canonical_value` /
25207 // `flux_kind_kustomization_pins_canonical_value` /
25208 // `flux_kind_helm_release_pins_canonical_value` /
25209 // `flux_kind_git_repository_pins_canonical_value` on the
25210 // sibling cluster-side-CRD-`kind`-discriminator pin set —
25211 // completes the per-Gateway-API-CRD `kind`-axis canonical-pin
25212 // pair across the `(Gateway, HTTPRoute)` pair the renderer's
25213 // `gateway_routes` external `:entrada` ingress contract emits
25214 // together.
25215 assert_eq!(GATEWAY_API_KIND_HTTP_ROUTE, "HTTPRoute");
25216 }
25217
25218 #[test]
25219 fn gateway_api_kind_http_route_carries_upper_camel_case_shape() {
25220 // Cross-axis invariant: a Kubernetes CRD `kind` discriminator is
25221 // an UpperCamelCase identifier per the K8s API conventions
25222 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds —
25223 // "Kinds are always UpperCamelCase"). Acronyms like HTTP stay
25224 // ASCII-uppercase across the prefix per the same convention
25225 // (the K8s API Kinds for `HTTPRoute`, `TCPRoute`, `TLSRoute`,
25226 // `GRPCRoute` carry the full-uppercase protocol acronym).
25227 // Pinning the shape here means a future rebrand on the
25228 // canonical lift can't silently land a malformed kind
25229 // discriminator (snake_case, kebab-case, lowercase, empty)
25230 // that every downstream YAML-aware deserializer would reject
25231 // far from the rebrand commit's source. The first-byte
25232 // uppercase / rest-ASCII-alphanumeric invariant is the
25233 // load-bearing K8s API typed-discovery contract: a value the
25234 // apiserver's `RESTMapper` consults to resolve the CRD's
25235 // `RESTKind`. Peer to
25236 // `gateway_api_kind_gateway_carries_upper_camel_case_shape` /
25237 // `cilium_kind_network_policy_carries_upper_camel_case_shape` /
25238 // `flux_kind_kustomization_carries_upper_camel_case_shape` /
25239 // `flux_kind_helm_release_carries_upper_camel_case_shape` /
25240 // `flux_kind_git_repository_carries_upper_camel_case_shape` on
25241 // the sibling cluster-side-CRD-`kind`-discriminator surface.
25242 let v = GATEWAY_API_KIND_HTTP_ROUTE;
25243 assert!(
25244 !v.is_empty(),
25245 "GATEWAY_API_KIND_HTTP_ROUTE {v:?} must be non-empty per the K8s API \
25246 UpperCamelCase kind discriminator grammar"
25247 );
25248 let first = v.chars().next().expect("non-empty");
25249 assert!(
25250 first.is_ascii_uppercase(),
25251 "GATEWAY_API_KIND_HTTP_ROUTE {v:?} first byte {first:?} must be \
25252 ASCII-uppercase per the K8s API UpperCamelCase kind discriminator \
25253 grammar (Kinds are always UpperCamelCase)"
25254 );
25255 assert!(
25256 v.chars().all(|c| c.is_ascii_alphanumeric()),
25257 "GATEWAY_API_KIND_HTTP_ROUTE {v:?} must be ASCII-alphanumeric \
25258 throughout per the K8s API kind discriminator grammar — no \
25259 snake_case, kebab-case, or whitespace bytes the apiserver-side \
25260 RESTMapper would reject"
25261 );
25262 }
25263
25264 #[test]
25265 fn gateway_api_protocol_http_pins_canonical_value() {
25266 // Pin the actual string so a typo in this lift can't silently
25267 // rebrand the Gateway API v1 `ProtocolType` OpenAPI schema enum's
25268 // canonical `HTTP` listener-protocol value the rendered
25269 // `Gateway.spec.listeners[].protocol` scalar declares. The value
25270 // is part of the cluster-side contract with every Gateway-API-
25271 // conformant gateway implementation (Cilium, Istio, Envoy
25272 // Gateway, NGINX) — the gateway-class-controller's per-listener
25273 // bind loop keys off this exact byte-sequence to select the L7
25274 // parser + TLS termination strategy; the Gateway API v1
25275 // `ProtocolType` OpenAPI schema enum admits the closed set
25276 // `{"HTTP", "HTTPS", "TCP", "TLS", "UDP"}` verbatim, so a
25277 // drifted value (`"http"` / `"Http"` / `"HTTP/1.1"` / `"http/1.1"`)
25278 // lands the rendered `Gateway` outside the `ProtocolType` enum's
25279 // admitted set and every external `:entrada` HTTP flow drops at
25280 // the gateway-class-controller's admission gate. Changing this
25281 // value is a coordinated Gateway API `ProtocolType` promotion
25282 // alongside the upstream SIG-Network deprecation cycle, not an
25283 // incidental edit. Peer to
25284 // `gateway_api_kind_gateway_pins_canonical_value` /
25285 // `gateway_api_kind_http_route_pins_canonical_value` /
25286 // `default_gateway_class_name_pins_canonical_value` on the
25287 // sibling Gateway-API-CRD-`kind`-discriminator + Gateway-
25288 // controller-binding-scalar-value pin set — extends the pair
25289 // of `kind`-axis canonical-value pins across the
25290 // `(Gateway, HTTPRoute)` pair onto the sibling per-Gateway
25291 // `spec.listeners[].protocol` listener-protocol-scalar-value axis
25292 // the same `gateway_routes` external `:entrada` ingress emitter
25293 // carries.
25294 assert_eq!(GATEWAY_API_PROTOCOL_HTTP, "HTTP");
25295 }
25296
25297 #[test]
25298 fn gateway_api_protocol_http_carries_upper_case_shape() {
25299 // Cross-axis invariant: the Gateway API v1 `ProtocolType` OpenAPI
25300 // schema enum admits the closed set
25301 // `{"HTTP", "HTTPS", "TCP", "TLS", "UDP"}` — every admitted value
25302 // is ASCII-uppercase throughout per the upstream SIG-Network
25303 // Gateway API convention (see
25304 // https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.ProtocolType
25305 // — the admitted values are the transport / application-layer
25306 // protocol acronyms in their canonical uppercase form). Pinning
25307 // the shape here means a future rebrand on the canonical lift
25308 // can't silently land a malformed listener-protocol scalar
25309 // (lowercase `"http"`, mixed-case `"Http"`, dotted `"HTTP/1.1"`,
25310 // empty) that the K8s Gateway API v1 `ProtocolType` OpenAPI
25311 // schema enum would reject at admission time far from the
25312 // rebrand commit's source. The all-ASCII-uppercase invariant is
25313 // the load-bearing Gateway-API-implementation-side typed
25314 // listener-parser-selection contract: a value the gateway-
25315 // class-controller's per-listener bind loop selects the L7
25316 // parser + TLS termination strategy from.
25317 let v = GATEWAY_API_PROTOCOL_HTTP;
25318 assert!(
25319 !v.is_empty(),
25320 "GATEWAY_API_PROTOCOL_HTTP {v:?} must be non-empty per the \
25321 Gateway API v1 `ProtocolType` OpenAPI schema enum grammar"
25322 );
25323 assert!(
25324 v.chars().all(|c| c.is_ascii_uppercase()),
25325 "GATEWAY_API_PROTOCOL_HTTP {v:?} must be ASCII-uppercase \
25326 throughout per the Gateway API v1 `ProtocolType` OpenAPI \
25327 schema enum convention — no lowercase, mixed-case, dotted, \
25328 or whitespace bytes the gateway-class-controller's per-\
25329 listener bind loop would reject"
25330 );
25331 }
25332
25333 #[test]
25334 fn gateway_api_path_match_type_path_prefix_pins_canonical_value() {
25335 // Pin the actual string so a typo in this lift can't silently
25336 // rebrand the Gateway API v1 `PathMatchType` OpenAPI schema
25337 // enum's canonical `PathPrefix` per-`HTTPRouteMatch` path-
25338 // selection-predicate discriminator value the rendered
25339 // `HTTPRoute.spec.rules[].matches[].path.type` scalar declares.
25340 // The value is part of the cluster-side contract with every
25341 // Gateway-API-conformant gateway implementation (Cilium, Istio,
25342 // Envoy Gateway, NGINX) — the gateway-class-controller's
25343 // per-rule L7 dispatch loop keys off this exact byte-sequence
25344 // to select the request-path-selection predicate; the Gateway
25345 // API v1 `PathMatchType` OpenAPI schema enum admits the closed
25346 // set `{"Exact", "PathPrefix", "RegularExpression"}` verbatim,
25347 // so a drifted value (`"pathPrefix"` / `"path_prefix"` /
25348 // `"Prefix"` / `"path-prefix"`) lands the rendered `HTTPRoute`
25349 // outside the `PathMatchType` enum's admitted set and every
25350 // external `:entrada` path-filtered flow drops at the gateway-
25351 // class-controller's admission gate. Changing this value is a
25352 // coordinated Gateway API `PathMatchType` promotion alongside
25353 // the upstream SIG-Network deprecation cycle, not an incidental
25354 // edit. Peer to
25355 // `gateway_api_protocol_http_pins_canonical_value` /
25356 // `gateway_api_kind_gateway_pins_canonical_value` /
25357 // `gateway_api_kind_http_route_pins_canonical_value` /
25358 // `default_gateway_class_name_pins_canonical_value` on the
25359 // sibling Gateway-API-v1-OpenAPI-schema-enum-value +
25360 // Gateway-API-CRD-`kind`-discriminator + Gateway-controller-
25361 // binding-scalar-value pin set — extends the canonical-
25362 // Gateway-API-v1-OpenAPI-schema-enum-value single-sourcing
25363 // discipline the `ProtocolType.HTTP` pin established onto the
25364 // sibling `PathMatchType.PathPrefix` per-`HTTPRouteMatch`
25365 // path-selection-predicate discriminator the same
25366 // `gateway_routes` external `:entrada` ingress emitter carries
25367 // under the shared `HTTPRoute` body.
25368 assert_eq!(GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX, "PathPrefix");
25369 }
25370
25371 #[test]
25372 fn gateway_api_path_match_type_path_prefix_carries_upper_camel_case_shape() {
25373 // Cross-axis invariant: the Gateway API v1 `PathMatchType`
25374 // OpenAPI schema enum admits the closed set
25375 // `{"Exact", "PathPrefix", "RegularExpression"}` — every
25376 // admitted value is UpperCamelCase per the upstream SIG-Network
25377 // Gateway API convention (see
25378 // https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.PathMatchType
25379 // — the admitted values are the request-path-selection
25380 // predicate names in their canonical UpperCamelCase form,
25381 // matching the K8s API `Kinds are always UpperCamelCase`
25382 // convention the sibling `GATEWAY_API_KIND_*` discriminators
25383 // carry on the CRD-`kind`-axis surface). Pinning the shape
25384 // here means a future rebrand on the canonical lift can't
25385 // silently land a malformed path-match-type scalar (lowercase
25386 // `"pathprefix"`, snake_case `"path_prefix"`, kebab-case
25387 // `"path-prefix"`, empty) that the K8s Gateway API v1
25388 // `PathMatchType` OpenAPI schema enum would reject at
25389 // admission time far from the rebrand commit's source. The
25390 // first-byte uppercase / rest-ASCII-alphanumeric invariant is
25391 // the load-bearing Gateway-API-implementation-side typed
25392 // per-match request-path-selection-predicate-selection
25393 // contract: a value the gateway-class-controller's per-rule
25394 // L7 dispatch loop selects the request-path-predicate
25395 // evaluator from. Peer to
25396 // `gateway_api_kind_gateway_carries_upper_camel_case_shape` /
25397 // `gateway_api_kind_http_route_carries_upper_camel_case_shape`
25398 // on the sibling cluster-side-CRD-`kind`-discriminator
25399 // UpperCamelCase pin set — extends the canonical-K8s-API-
25400 // UpperCamelCase-typed-discriminator pin discipline the
25401 // `Kind` axis carries onto the sibling Gateway API v1
25402 // `PathMatchType` OpenAPI schema enum's per-value
25403 // UpperCamelCase surface (distinct from the sibling
25404 // Gateway API v1 `ProtocolType` OpenAPI schema enum's all-
25405 // ASCII-uppercase per-value convention the
25406 // `gateway_api_protocol_http_carries_upper_case_shape` pin
25407 // carries — the two peer Gateway-API-v1 OpenAPI schema
25408 // enum-value conventions do not collapse).
25409 let v = GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX;
25410 assert!(
25411 !v.is_empty(),
25412 "GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX {v:?} must be non-empty per \
25413 the Gateway API v1 `PathMatchType` OpenAPI schema enum grammar"
25414 );
25415 let first = v.chars().next().expect("non-empty");
25416 assert!(
25417 first.is_ascii_uppercase(),
25418 "GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX {v:?} first byte {first:?} \
25419 must be ASCII-uppercase per the Gateway API v1 `PathMatchType` \
25420 OpenAPI schema enum UpperCamelCase convention"
25421 );
25422 assert!(
25423 v.chars().all(|c| c.is_ascii_alphanumeric()),
25424 "GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX {v:?} must be ASCII-\
25425 alphanumeric throughout per the Gateway API v1 `PathMatchType` \
25426 OpenAPI schema enum UpperCamelCase convention — no snake_case, \
25427 kebab-case, or whitespace bytes the gateway-class-controller's \
25428 per-rule L7 dispatch loop would reject"
25429 );
25430 }
25431
25432 #[test]
25433 fn kube_protocol_tcp_pins_canonical_value() {
25434 // Pin the actual string so a typo in this lift can't silently
25435 // rebrand the K8s core `Protocol` OpenAPI schema enum's
25436 // canonical `TCP` L4-transport-protocol scalar value the
25437 // rendered `CiliumNetworkPolicy.spec.ingress[].toPorts[].ports[]
25438 // .protocol` scalar declares. The value is part of the cluster-
25439 // side contract with every K8s-core-`Protocol`-conformant CNI
25440 // + kube-proxy + eBPF-data-plane implementation (Cilium,
25441 // Calico, kube-proxy iptables/ipvs) — the CNI's per-CNP L4
25442 // dispatch pass keys off this exact byte-sequence to select
25443 // the per-tuple L4-transport-protocol predicate; the K8s core
25444 // `Protocol` OpenAPI schema enum admits the closed set
25445 // `{"TCP", "UDP", "SCTP"}` verbatim (see
25446 // https://kubernetes.io/docs/reference/generated/kubernetes-api/v1/#protocol-v1-core),
25447 // so a drifted value (`"tcp"` / `"Tcp"` / `"TCP/IP"` /
25448 // `"transport-tcp"`) lands the rendered `CiliumNetworkPolicy`
25449 // outside the `Protocol` enum's admitted set and every intra-
25450 // mesh `:contratos` L4-tuple-gated flow drops at the Cilium
25451 // operator's admission gate. Changing this value is a
25452 // coordinated K8s core `Protocol` promotion alongside the
25453 // upstream SIG-Network deprecation cycle, not an incidental
25454 // edit. Peer to
25455 // `gateway_api_protocol_http_pins_canonical_value` /
25456 // `gateway_api_path_match_type_path_prefix_pins_canonical_value`
25457 // on the sibling Gateway-API-v1-OpenAPI-schema-enum-value pin
25458 // set — extends the canonical-cluster-side-OpenAPI-schema-enum-
25459 // value single-sourcing discipline the Gateway-API v1
25460 // `ProtocolType.HTTP` / `PathMatchType.PathPrefix` pins
25461 // established onto the sibling K8s-core `Protocol.TCP` per-port-
25462 // tuple L4-transport-protocol-discriminator the
25463 // `cilium_network_policies` intra-mesh L4-tuple-gating emitter
25464 // carries under the shared `CiliumNetworkPolicy` body.
25465 assert_eq!(KUBE_PROTOCOL_TCP, "TCP");
25466 }
25467
25468 #[test]
25469 fn kube_protocol_tcp_carries_upper_case_shape() {
25470 // Cross-axis invariant: the K8s core `Protocol` OpenAPI schema
25471 // enum admits the closed set `{"TCP", "UDP", "SCTP"}` — every
25472 // admitted value is ASCII-uppercase throughout per the upstream
25473 // SIG-Network convention (the admitted values are the L4-
25474 // transport-protocol acronyms in their canonical uppercase form,
25475 // matching the sibling Gateway-API v1 `ProtocolType` OpenAPI
25476 // schema enum's `{"HTTP", "HTTPS", "TCP", "TLS", "UDP"}` all-
25477 // ASCII-uppercase convention the
25478 // `gateway_api_protocol_http_carries_upper_case_shape` pin
25479 // carries on the peer per-listener L7-parser-selection scalar
25480 // axis). Pinning the shape here means a future rebrand on the
25481 // canonical lift can't silently land a malformed L4-transport-
25482 // protocol scalar (lowercase `"tcp"`, mixed-case `"Tcp"`,
25483 // dotted `"TCP/IP"`, empty) that the K8s core `Protocol`
25484 // OpenAPI schema enum would reject at admission time far from
25485 // the rebrand commit's source. The all-ASCII-uppercase
25486 // invariant is the load-bearing K8s-core-`Protocol`-enum-side
25487 // typed L4-transport-selection contract: a value the CNI's per-
25488 // CNP L4 dispatch pass selects the per-tuple L4-transport-
25489 // protocol predicate from. Peer to
25490 // `gateway_api_protocol_http_carries_upper_case_shape` on the
25491 // sibling Gateway-API v1 `ProtocolType` OpenAPI schema enum's
25492 // all-ASCII-uppercase per-value convention pin set — the two
25493 // peer canonical-cluster-side-OpenAPI-schema-enum-value
25494 // uppercase conventions collapse on the shared `TCP` transport-
25495 // protocol acronym both `Protocol` enums admit at the closed-
25496 // set intersection.
25497 let v = KUBE_PROTOCOL_TCP;
25498 assert!(
25499 !v.is_empty(),
25500 "KUBE_PROTOCOL_TCP {v:?} must be non-empty per the K8s core \
25501 `Protocol` OpenAPI schema enum grammar"
25502 );
25503 assert!(
25504 v.chars().all(|c| c.is_ascii_uppercase()),
25505 "KUBE_PROTOCOL_TCP {v:?} must be ASCII-uppercase throughout \
25506 per the K8s core `Protocol` OpenAPI schema enum convention \
25507 — no lowercase, mixed-case, dotted, or whitespace bytes the \
25508 CNI's per-CNP L4 dispatch pass would reject"
25509 );
25510 }
25511
25512 #[test]
25513 fn cilium_auth_mode_required_pins_canonical_value() {
25514 // Pin the actual string so a typo in this lift can't silently
25515 // rebrand the Cilium `CiliumNetworkPolicy` `MutualAuthenticationMode`
25516 // OpenAPI schema enum's `required` mTLS-mandatory scalar-value the
25517 // rendered CNP's `spec.ingress[].authentication.mode` leaf declares
25518 // under the `:mtls-required t` affirmative arm of the typed
25519 // `:politicas :mtls-required` tristate. The value is part of the
25520 // cluster-side contract with the Cilium-agent-side per-rule mutual-
25521 // auth-block schema validator — the agent's per-rule dispatch loop
25522 // keys off this exact byte-sequence to select the SPIFFE-identity-
25523 // handshake-mandatory enforcement policy; the Cilium CNP
25524 // `MutualAuthenticationMode` OpenAPI schema enum admits the closed
25525 // set `{"required", "disabled", "test-always-fail"}` verbatim (the
25526 // `test-always-fail` arm is a Cilium-side debugging surface, not
25527 // author-reachable), so a drifted value (`"Required"` /
25528 // `"REQUIRED"` / `"mandatory"` / `"mtls-required"`) lands the
25529 // rendered `CiliumNetworkPolicy` outside the
25530 // `MutualAuthenticationMode` enum's admitted set and every intra-
25531 // mesh `:contratos` flow the CNP was authored to protect with per-
25532 // edge SPIFFE-identity-bound mutual-auth silently bypasses the
25533 // handshake at the Cilium data-plane's default-authentication mode
25534 // (typically also "disabled" today, but environment-divergent —
25535 // take effect) with no field naming the mTLS-mandatory-scalar-value-
25536 // drift root cause. Changing this value is a coordinated Cilium
25537 // CNP `MutualAuthenticationMode` promotion alongside the Cilium
25538 // project's periodic CRD schema-migration passes, not an
25539 // incidental edit. Peer to
25540 // `gateway_api_protocol_http_pins_canonical_value` /
25541 // `gateway_api_path_match_type_path_prefix_pins_canonical_value` /
25542 // `kube_protocol_tcp_pins_canonical_value` on the sibling
25543 // canonical-cluster-side-OpenAPI-schema-enum-value pin set —
25544 // extends the canonical-cluster-side-OpenAPI-schema-enum-value
25545 // single-sourcing discipline the Gateway-API v1 `ProtocolType.HTTP`
25546 // / `PathMatchType.PathPrefix` / K8s-core `Protocol.TCP` pins
25547 // established onto the sibling Cilium-CNP-side
25548 // `MutualAuthenticationMode.required` per-rule mTLS-mandatory
25549 // scalar-value the `cilium_network_policies` per-edge SPIFFE-
25550 // identity-bound mutual-auth emitter carries under the shared
25551 // `CiliumNetworkPolicy` body.
25552 assert_eq!(CILIUM_AUTH_MODE_REQUIRED, "required");
25553 }
25554
25555 #[test]
25556 fn cilium_auth_mode_disabled_pins_canonical_value() {
25557 // Peer to `cilium_auth_mode_required_pins_canonical_value` on the
25558 // `Some(false)` opt-out arm of the same
25559 // `MutualAuthenticationMode` OpenAPI schema enum: pin the actual
25560 // string so a typo can't silently rebrand the Cilium `disabled`
25561 // mTLS-skipped scalar-value the rendered CNP's per-rule authn-
25562 // block declares under the explicit `:mtls-required nil` opt-out
25563 // (distinct from the `None` slot-absent arm the renderer maps to
25564 // omit-the-block-entirely). A drifted value (`"Disabled"` /
25565 // `"DISABLED"` / `"off"` / `"skip"`) lands outside the
25566 // `MutualAuthenticationMode` OpenAPI schema enum's admitted set;
25567 // the author's explicit-opt-out intent silently collapses onto the
25568 // cluster-default authentication mode with no field naming the
25569 // mTLS-skipped-scalar-value-drift root cause. Peer to
25570 // `cilium_auth_mode_required_pins_canonical_value` on the
25571 // affirmative arm of the same enum — completes the per-authn-block
25572 // `(mode → {required, disabled})` author-reachable-scalar-value-
25573 // pair single-sourcing the M3 Aplicacao mesh renderer's SPIFFE-
25574 // identity-bound per-edge mTLS enforcement + explicit-opt-out
25575 // contract rests on across the two arms of the `:politicas
25576 // :mtls-required` tristate.
25577 assert_eq!(CILIUM_AUTH_MODE_DISABLED, "disabled");
25578 }
25579
25580 #[test]
25581 fn cilium_auth_modes_carry_lower_case_shape() {
25582 // Cross-axis invariant: the Cilium CNP `MutualAuthenticationMode`
25583 // OpenAPI schema enum admits the closed set `{"required",
25584 // "disabled", "test-always-fail"}` — every admitted value is
25585 // ASCII-lowercase throughout per the Cilium-project convention
25586 // (distinct from the sibling K8s-core `Protocol.TCP` /
25587 // Gateway-API-v1 `ProtocolType.HTTP` all-ASCII-uppercase
25588 // convention the `kube_protocol_tcp_carries_upper_case_shape` /
25589 // `gateway_api_protocol_http_carries_upper_case_shape` pins carry
25590 // on the sibling per-listener L7-parser-selection scalar axis, and
25591 // distinct from the sibling Gateway-API-v1
25592 // `PathMatchType.PathPrefix` UpperCamelCase convention the
25593 // `gateway_api_path_match_type_path_prefix_carries_upper_camel_case_shape`
25594 // pin carries on the sibling per-match request-path-selection
25595 // scalar axis — the Cilium CNP `MutualAuthenticationMode` enum
25596 // grammar does not collapse with either sibling cluster-side
25597 // OpenAPI schema enum's per-value casing convention). Pinning the
25598 // shape here means a future rebrand on either lifted value can't
25599 // silently land a malformed mode-discriminator scalar (uppercase
25600 // `"REQUIRED"` / `"DISABLED"`, UpperCamelCase `"Required"` /
25601 // `"Disabled"`, mixed-case, whitespace) that the Cilium CNP
25602 // `MutualAuthenticationMode` OpenAPI schema enum would reject at
25603 // admission time far from the rebrand commit's source.
25604 for v in [CILIUM_AUTH_MODE_REQUIRED, CILIUM_AUTH_MODE_DISABLED] {
25605 assert!(
25606 !v.is_empty(),
25607 "{v:?} must be non-empty per the Cilium CNP \
25608 `MutualAuthenticationMode` OpenAPI schema enum grammar"
25609 );
25610 assert!(
25611 v.chars().all(|c| c.is_ascii_lowercase()),
25612 "{v:?} must be ASCII-lowercase throughout per the Cilium \
25613 CNP `MutualAuthenticationMode` OpenAPI schema enum \
25614 convention — no uppercase, UpperCamelCase, or whitespace \
25615 bytes the Cilium-agent-side per-rule mutual-auth-block \
25616 schema validator would reject"
25617 );
25618 }
25619 }
25620
25621 #[test]
25622 fn cilium_auth_modes_are_distinct() {
25623 // Pin the `MutualAuthenticationMode` enum's per-arm distinctness
25624 // at type-check time: the two author-reachable arms of the typed
25625 // `:politicas :mtls-required` tristate must not collapse onto the
25626 // same scalar-value byte-sequence. A future rebrand that landed
25627 // both lifted constants on the same string (e.g. both `"required"`
25628 // through a copy-paste typo, or both aliased through a shared
25629 // helper) would silently erase the tristate's affirmative /
25630 // explicit-opt-out distinction at the emit boundary — the
25631 // renderer would emit the same scalar under both the `Some(true)`
25632 // and `Some(false)` arms of the closure the
25633 // `single_field_overlay(spec.politicas.mtls_required,
25634 // CILIUM_KEY_MODE, |required| …)` call site carries, collapsing
25635 // the two author intents onto a single Cilium-side enforcement
25636 // policy with no field naming the collapse root cause. Peer to
25637 // the two `cilium_auth_mode_{required,disabled}_pins_canonical_
25638 // value` per-arm pins — completes the per-arm distinctness pin
25639 // set on the closed author-reachable subset of the enum.
25640 assert_ne!(
25641 CILIUM_AUTH_MODE_REQUIRED, CILIUM_AUTH_MODE_DISABLED,
25642 "the two author-reachable arms of the `:mtls-required` \
25643 tristate must land distinct `MutualAuthenticationMode` \
25644 scalar-values"
25645 );
25646 }
25647
25648 #[test]
25649 fn cilium_auth_mode_bijection_dispatches_tristate_arms_onto_scalar_values() {
25650 // Pin the `bool → &'static str` projection every consumer of the
25651 // Cilium `MutualAuthenticationMode` closed-set enum's author-
25652 // reachable scalar-value pair reaches through: `true` (the
25653 // `Some(true)` mTLS-mandatory arm of the typed `:politicas
25654 // :mtls-required` tristate) maps to [`CILIUM_AUTH_MODE_REQUIRED`],
25655 // `false` (the `Some(false)` explicit-opt-out arm) maps to
25656 // [`CILIUM_AUTH_MODE_DISABLED`]. One projection body, both arms of
25657 // the tristate's non-`None` value-space, so a future per-arm
25658 // reassignment (e.g. an upstream Cilium v3 schema swap of the
25659 // `required` ↔ `disabled` scalars, or a per-arm renaming of the
25660 // mTLS-mandatory scalar from `required` to `enforced` / `strict`
25661 // / `mandatory`) lands at the two consts + this projection body
25662 // — not at the caixa-mesh production emitter's closure body and
25663 // the caixa-core `single_field_overlay_threads_typed_value_
25664 // through_closure` generic-helper pin's closure body independently.
25665 // Pin the per-arm round-trip so a future refactor that inverts
25666 // the bool → arm mapping (or collapses one arm) surfaces here
25667 // rather than silently letting a Cilium data-plane pod either
25668 // enforce mTLS where the author asked for skip or skip it where
25669 // the author asked for enforce.
25670 assert_eq!(cilium_auth_mode(true), CILIUM_AUTH_MODE_REQUIRED);
25671 assert_eq!(cilium_auth_mode(false), CILIUM_AUTH_MODE_DISABLED);
25672 // The two arms cover distinct value-space entries — a regression
25673 // that collapses them onto the same scalar surfaces here. Peer
25674 // to `cilium_auth_modes_are_distinct` (the per-arm distinctness
25675 // pin at the const-declaration axis) — this test extends the
25676 // pin onto the projection body axis, so both the raw consts and
25677 // the projection's per-arm dispatch preserve the tristate's
25678 // author-intent distinction end-to-end.
25679 assert_ne!(
25680 cilium_auth_mode(true),
25681 cilium_auth_mode(false),
25682 "cilium_auth_mode must project the two tristate arms onto \
25683 distinct `MutualAuthenticationMode` value-space entries — \
25684 a collapsed-arm regression would silently render both \
25685 `:mtls-required t` and `:mtls-required nil` identically at \
25686 the cluster artifact",
25687 );
25688 }
25689
25690 #[test]
25691 fn gateway_api_key_parent_refs_pins_canonical_value() {
25692 // Pin the actual string so a typo in this lift can't silently
25693 // rebrand the Gateway API `HTTPRoute` parent-Gateway-binding
25694 // container-axis key the rendered HTTPRoute document mounts its
25695 // per-route `[{name}]` parent-Gateway attachment list under. The
25696 // string is part of the cluster-side contract with every
25697 // Gateway-API-conformant gateway implementation (Cilium, Istio,
25698 // Envoy Gateway, NGINX) — the Gateway-API-implementation-side
25699 // per-HTTPRoute reconcile loop keys off this axis to source the
25700 // per-route parent-Gateway attachment list the route is bound
25701 // to; a drifted value (`"parentRef"` / `"parents"` /
25702 // `"parentGateways"`) at either the production emitter or a
25703 // downstream renderer's per-HTTPRoute parent-Gateway-binding
25704 // upsert silently emits an `HTTPRoute` whose parent-Gateway-
25705 // binding axis the Gateway API CRD schema validator drops as
25706 // unknown — the route lands unattached to any Gateway, and
25707 // every external `:entrada` flow the HTTPRoute was authored to
25708 // accept drops at the Gateway API implementation's per-Gateway
25709 // HTTP-listener fan-in with no field naming the parent-Gateway-
25710 // binding-drift root cause. Changing this value is a
25711 // coordinated Gateway API promotion alongside the upstream
25712 // SIG-Network Gateway API deprecation cycle, not an incidental
25713 // edit. Peer to `cilium_key_ports_pins_canonical_value` /
25714 // `cilium_key_from_endpoints_pins_canonical_value` /
25715 // `cilium_key_endpoint_selector_pins_canonical_value` /
25716 // `cilium_key_ingress_pins_canonical_value` /
25717 // `cilium_key_to_ports_pins_canonical_value` on the sibling
25718 // per-CNP-body-axis pin set — begins the per-Gateway-API-
25719 // HTTPRoute-body-axis canonical-string-pin set (`parentRefs`,
25720 // future `hostnames`) the M3 Aplicacao mesh renderer's external
25721 // `:entrada` ingress contract rests on across the Gateway API
25722 // HTTPRoute-side per-route body-shape.
25723 assert_eq!(GATEWAY_API_KEY_PARENT_REFS, "parentRefs");
25724 }
25725
25726 #[test]
25727 fn gateway_api_key_parent_refs_carries_lower_camel_case_shape() {
25728 // Cross-axis invariant: a Kubernetes CRD schema field name is a
25729 // lowerCamelCase identifier per the K8s API conventions
25730 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
25731 // "Field names should be lowercase camelCase") — first byte
25732 // ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
25733 // kebab-case or whitespace. Pinning the shape here means a
25734 // future rebrand on the canonical lift can't silently land a
25735 // malformed field-name discriminator (snake_case, kebab-case,
25736 // UpperCamelCase, empty) that the apiserver-side CRD schema
25737 // validator would reject far from the rebrand commit's source.
25738 // Peer to `cilium_key_ports_carries_lower_camel_case_shape` /
25739 // `cilium_key_from_endpoints_carries_lower_camel_case_shape` /
25740 // `cilium_key_endpoint_selector_carries_lower_camel_case_shape`
25741 // / `cilium_key_ingress_carries_lower_camel_case_shape` /
25742 // `cilium_key_to_ports_carries_lower_camel_case_shape` on the
25743 // sibling per-CNP-body-axis grammar-pin set — the lowerCamelCase
25744 // K8s field-name grammar governs every nested schema-field axis
25745 // (including this per-HTTPRoute parent-Gateway-binding-
25746 // container-axis key), same convention.
25747 let v = GATEWAY_API_KEY_PARENT_REFS;
25748 assert!(
25749 !v.is_empty(),
25750 "GATEWAY_API_KEY_PARENT_REFS {v:?} must be non-empty per the K8s API \
25751 lowerCamelCase field-name grammar"
25752 );
25753 let first = v.chars().next().expect("non-empty");
25754 assert!(
25755 first.is_ascii_lowercase(),
25756 "GATEWAY_API_KEY_PARENT_REFS {v:?} first byte {first:?} must be \
25757 ASCII-lowercase per the K8s API lowerCamelCase field-name \
25758 grammar (field names are always lowerCamelCase)"
25759 );
25760 assert!(
25761 v.chars().all(|c| c.is_ascii_alphanumeric()),
25762 "GATEWAY_API_KEY_PARENT_REFS {v:?} must be ASCII-alphanumeric \
25763 throughout per the K8s API field-name grammar — no \
25764 snake_case, kebab-case, or whitespace bytes the apiserver-side \
25765 OpenAPI schema validator would reject"
25766 );
25767 }
25768
25769 #[test]
25770 fn gateway_api_key_backend_refs_pins_canonical_value() {
25771 // Pin the actual string so a typo in this lift can't silently
25772 // rebrand the Gateway API `HTTPRoute` per-rule backend-destination
25773 // container-axis key the rendered HTTPRoute document mounts its
25774 // per-rule `[{name, port}]` backend fan-out list under. The
25775 // string is part of the cluster-side contract with every
25776 // Gateway-API-conformant gateway implementation (Cilium, Istio,
25777 // Envoy Gateway, NGINX) — the Gateway-API-implementation-side
25778 // per-rule L7 dispatch loop keys off this axis to source the
25779 // per-rule backend list the request is forwarded to; a drifted
25780 // value (`"backendRef"` / `"backends"` / `"forwardTo"`) at
25781 // either the production emitter or a downstream renderer's
25782 // per-rule backend-destination upsert silently emits an
25783 // `HTTPRoute` whose per-rule backend fan-out axis the Gateway
25784 // API CRD schema validator drops as unknown — no backend is
25785 // picked at the per-rule L7 dispatch, and every external
25786 // `:entrada` request the rule was authored to route drops at
25787 // the gateway-class-controller's per-rule reconcile with no
25788 // field naming the backend-destination-drift root cause.
25789 // Changing this value is a coordinated Gateway API promotion
25790 // alongside the upstream SIG-Network Gateway API deprecation
25791 // cycle, not an incidental edit. Peer to
25792 // `gateway_api_key_parent_refs_pins_canonical_value` on the
25793 // sibling per-HTTPRoute-body-axis canonical-string-pin surface
25794 // — extends the per-Gateway-API-HTTPRoute-body-axis pin set
25795 // (`parentRefs`, `backendRefs`, future `hostnames`) the M3
25796 // Aplicacao mesh renderer's external `:entrada` ingress
25797 // contract rests on across the Gateway API HTTPRoute-side per-
25798 // route body-shape.
25799 assert_eq!(GATEWAY_API_KEY_BACKEND_REFS, "backendRefs");
25800 }
25801
25802 #[test]
25803 fn gateway_api_key_backend_refs_carries_lower_camel_case_shape() {
25804 // Cross-axis invariant: a Kubernetes CRD schema field name is a
25805 // lowerCamelCase identifier per the K8s API conventions
25806 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
25807 // "Field names should be lowercase camelCase") — first byte
25808 // ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
25809 // kebab-case or whitespace. Pinning the shape here means a
25810 // future rebrand on the canonical lift can't silently land a
25811 // malformed field-name discriminator (snake_case, kebab-case,
25812 // UpperCamelCase, empty) that the apiserver-side CRD schema
25813 // validator would reject far from the rebrand commit's source.
25814 // Peer to `gateway_api_key_parent_refs_carries_lower_camel_case_shape`
25815 // on the sibling per-HTTPRoute-body-axis grammar-pin surface —
25816 // the lowerCamelCase K8s field-name grammar governs every
25817 // nested schema-field axis (including this per-rule backend-
25818 // destination-container-axis key), same convention.
25819 let v = GATEWAY_API_KEY_BACKEND_REFS;
25820 assert!(
25821 !v.is_empty(),
25822 "GATEWAY_API_KEY_BACKEND_REFS {v:?} must be non-empty per the K8s API \
25823 lowerCamelCase field-name grammar"
25824 );
25825 let first = v.chars().next().expect("non-empty");
25826 assert!(
25827 first.is_ascii_lowercase(),
25828 "GATEWAY_API_KEY_BACKEND_REFS {v:?} first byte {first:?} must be \
25829 ASCII-lowercase per the K8s API lowerCamelCase field-name \
25830 grammar (field names are always lowerCamelCase)"
25831 );
25832 assert!(
25833 v.chars().all(|c| c.is_ascii_alphanumeric()),
25834 "GATEWAY_API_KEY_BACKEND_REFS {v:?} must be ASCII-alphanumeric \
25835 throughout per the K8s API field-name grammar — no \
25836 snake_case, kebab-case, or whitespace bytes the apiserver-side \
25837 OpenAPI schema validator would reject"
25838 );
25839 }
25840
25841 #[test]
25842 fn gateway_api_key_matches_pins_canonical_value() {
25843 // Pin the actual string so a typo in this lift can't silently
25844 // rebrand the Gateway API `HTTPRoute` per-rule route-match
25845 // container-axis key the rendered HTTPRoute document mounts
25846 // its per-rule `[{path: {type, value}}]` route-match fan-out
25847 // list under. The string is part of the cluster-side contract
25848 // with every Gateway-API-conformant gateway implementation
25849 // (Cilium, Istio, Envoy Gateway, NGINX) — the Gateway-API-
25850 // implementation-side per-rule L7 dispatch loop keys off this
25851 // axis to source the per-rule request-selection predicate the
25852 // incoming request line + headers + query must satisfy for
25853 // the rule's backend fan-out to apply; a drifted value
25854 // (`"match"` / `"routeMatches"` / `"predicates"`) at either
25855 // the production emitter or a downstream renderer's per-rule
25856 // route-match upsert silently emits an `HTTPRoute` whose per-
25857 // rule request-selection axis the Gateway API CRD schema
25858 // validator drops as unknown — the per-rule predicate
25859 // degrades to the wildcard match at the gateway-class-
25860 // controller's per-rule reconcile, the rule matches every
25861 // request unconditionally, and every external `:entrada` path
25862 // filter the rule was authored to enforce drops with no field
25863 // naming the route-match-drift root cause. Changing this
25864 // value is a coordinated Gateway API promotion alongside the
25865 // upstream SIG-Network Gateway API deprecation cycle, not an
25866 // incidental edit. Peer to
25867 // `gateway_api_key_backend_refs_pins_canonical_value` /
25868 // `gateway_api_key_parent_refs_pins_canonical_value` on the
25869 // sibling per-HTTPRoute-body-axis canonical-string-pin
25870 // surface — completes the per-rule top-level-axis pin set
25871 // (`matches`, `backendRefs`, `timeouts`, `retry`) the M3
25872 // Aplicacao mesh renderer's external `:entrada` ingress
25873 // contract rests on across the Gateway API HTTPRoute per-rule
25874 // body-shape.
25875 assert_eq!(GATEWAY_API_KEY_MATCHES, "matches");
25876 }
25877
25878 #[test]
25879 fn gateway_api_key_matches_carries_lower_camel_case_shape() {
25880 // Cross-axis invariant: a Kubernetes CRD schema field name is a
25881 // lowerCamelCase identifier per the K8s API conventions
25882 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
25883 // "Field names should be lowercase camelCase") — first byte
25884 // ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
25885 // kebab-case or whitespace. Pinning the shape here means a
25886 // future rebrand on the canonical lift can't silently land a
25887 // malformed field-name discriminator (snake_case, kebab-case,
25888 // UpperCamelCase, empty) that the apiserver-side CRD schema
25889 // validator would reject far from the rebrand commit's source.
25890 // Peer to `gateway_api_key_backend_refs_carries_lower_camel_case_shape`
25891 // / `gateway_api_key_parent_refs_carries_lower_camel_case_shape`
25892 // on the sibling per-HTTPRoute-body-axis grammar-pin surface —
25893 // the lowerCamelCase K8s field-name grammar governs every
25894 // nested schema-field axis (including this per-rule route-
25895 // match-container-axis key), same convention.
25896 let v = GATEWAY_API_KEY_MATCHES;
25897 assert!(
25898 !v.is_empty(),
25899 "GATEWAY_API_KEY_MATCHES {v:?} must be non-empty per the K8s API \
25900 lowerCamelCase field-name grammar"
25901 );
25902 let first = v.chars().next().expect("non-empty");
25903 assert!(
25904 first.is_ascii_lowercase(),
25905 "GATEWAY_API_KEY_MATCHES {v:?} first byte {first:?} must be \
25906 ASCII-lowercase per the K8s API lowerCamelCase field-name \
25907 grammar (field names are always lowerCamelCase)"
25908 );
25909 assert!(
25910 v.chars().all(|c| c.is_ascii_alphanumeric()),
25911 "GATEWAY_API_KEY_MATCHES {v:?} must be ASCII-alphanumeric \
25912 throughout per the K8s API field-name grammar — no \
25913 snake_case, kebab-case, or whitespace bytes the apiserver-side \
25914 OpenAPI schema validator would reject"
25915 );
25916 }
25917
25918 #[test]
25919 fn gateway_api_key_gateway_class_name_pins_canonical_value() {
25920 // Pin the actual string so a typo in this lift can't silently
25921 // rebrand the Gateway API `Gateway` per-Gateway controller-
25922 // binding scalar-axis key the rendered Gateway document
25923 // mounts its per-Gateway `GatewayClass.metadata.name`
25924 // reference under. The string is part of the cluster-side
25925 // contract with every Gateway-API-conformant gateway
25926 // implementation (Cilium, Istio, Envoy Gateway, NGINX) —
25927 // the Gateway-API-implementation-side per-Gateway reconcile
25928 // loop keys off this axis to source the `GatewayClass`
25929 // reference the per-Gateway controller-name-lookup dispatch
25930 // resolves; a drifted value (`"gatewayClass"` /
25931 // `"className"` / `"gatewayClassRef"`) at the production
25932 // emitter silently emits a `Gateway` whose controller-binding
25933 // scalar-axis the Gateway API CRD schema validator drops as
25934 // unknown — no `GatewayClass` is resolved, no `controllerName`
25935 // is looked up, and every external `:entrada` flow the
25936 // Gateway was authored to accept drops at the gateway-class-
25937 // controller's per-Gateway reconcile with no field naming
25938 // the controller-binding-drift root cause. Changing this
25939 // value is a coordinated Gateway API promotion alongside
25940 // the upstream SIG-Network Gateway API deprecation cycle,
25941 // not an incidental edit. Peer to
25942 // `gateway_api_key_listeners_pins_canonical_value` /
25943 // `gateway_api_key_hostname_pins_canonical_value` on the
25944 // sibling per-Gateway-body-axis canonical-string-pin
25945 // surface — completes the per-Gateway-body-axis top-level-
25946 // axis pin set (`gatewayClassName`, `listeners`) the M3
25947 // Aplicacao mesh renderer's external `:entrada` ingress
25948 // contract rests on. Sibling of the peer
25949 // `default_gateway_class_name_pins_canonical_value` on the
25950 // canonical-Gateway-API-`(key, value)`-pair-lift surface
25951 // this lift closes the KEY half of.
25952 assert_eq!(GATEWAY_API_KEY_GATEWAY_CLASS_NAME, "gatewayClassName");
25953 }
25954
25955 #[test]
25956 fn gateway_api_key_gateway_class_name_carries_lower_camel_case_shape() {
25957 // Cross-axis invariant: a Kubernetes CRD schema field name is a
25958 // lowerCamelCase identifier per the K8s API conventions
25959 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
25960 // "Field names should be lowercase camelCase") — first byte
25961 // ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
25962 // kebab-case or whitespace. Pinning the shape here means a
25963 // future rebrand on the canonical lift can't silently land a
25964 // malformed field-name discriminator (snake_case, kebab-case,
25965 // UpperCamelCase, empty) that the apiserver-side CRD schema
25966 // validator would reject far from the rebrand commit's source.
25967 // Peer to `gateway_api_key_listeners_carries_lower_camel_case_shape`
25968 // / `gateway_api_key_matches_carries_lower_camel_case_shape`
25969 // on the sibling per-Gateway / per-HTTPRoute-body-axis
25970 // grammar-pin surface — the lowerCamelCase K8s field-name
25971 // grammar governs every nested schema-field axis (including
25972 // this per-Gateway controller-binding scalar-axis key), same
25973 // convention.
25974 let v = GATEWAY_API_KEY_GATEWAY_CLASS_NAME;
25975 assert!(
25976 !v.is_empty(),
25977 "GATEWAY_API_KEY_GATEWAY_CLASS_NAME {v:?} must be non-empty per the K8s API \
25978 lowerCamelCase field-name grammar"
25979 );
25980 let first = v.chars().next().expect("non-empty");
25981 assert!(
25982 first.is_ascii_lowercase(),
25983 "GATEWAY_API_KEY_GATEWAY_CLASS_NAME {v:?} first byte {first:?} must be \
25984 ASCII-lowercase per the K8s API lowerCamelCase field-name \
25985 grammar (field names are always lowerCamelCase)"
25986 );
25987 assert!(
25988 v.chars().all(|c| c.is_ascii_alphanumeric()),
25989 "GATEWAY_API_KEY_GATEWAY_CLASS_NAME {v:?} must be ASCII-alphanumeric \
25990 throughout per the K8s API field-name grammar — no \
25991 snake_case, kebab-case, or whitespace bytes the apiserver-side \
25992 OpenAPI schema validator would reject"
25993 );
25994 }
25995
25996 #[test]
25997 fn gateway_api_key_path_pins_canonical_value() {
25998 // Pin the actual string so a typo in this lift can't silently
25999 // rebrand the Gateway API `HTTPRoute` per-`HTTPRouteMatch`
26000 // path-matcher container-axis key the rendered HTTPRoute
26001 // document mounts its per-match `{type, value}` path-selection
26002 // predicate under. The string is part of the cluster-side
26003 // contract with every Gateway-API-conformant gateway
26004 // implementation (Cilium, Istio, Envoy Gateway, NGINX) — the
26005 // Gateway-API-implementation-side per-rule L7 dispatch loop
26006 // keys off this axis to source the per-match request-path-
26007 // selection predicate the incoming request line's `:path`
26008 // pseudo-header must satisfy under a `type` discriminator of
26009 // `Exact | PathPrefix | RegularExpression`; a drifted value
26010 // (`"pathMatch"` / `"prefix"` / `"url"`) at the production
26011 // emitter silently emits an `HTTPRoute` whose per-match path-
26012 // selection axis the Gateway API CRD schema validator drops
26013 // as unknown — the per-match path predicate degrades to the
26014 // wildcard match at the gateway-class-controller's per-rule
26015 // reconcile, the rule matches every request path
26016 // unconditionally, and every external `:entrada` path filter
26017 // the rule was authored to enforce drops with no field
26018 // naming the path-matcher-drift root cause. Changing this
26019 // value is a coordinated Gateway API promotion alongside the
26020 // upstream SIG-Network Gateway API deprecation cycle, not an
26021 // incidental edit. Peer to
26022 // `gateway_api_key_matches_pins_canonical_value` /
26023 // `gateway_api_key_backend_refs_pins_canonical_value` on the
26024 // sibling per-HTTPRoute-body-axis canonical-string-pin
26025 // surface — nests the per-Gateway-API-HTTPRoute-per-rule-
26026 // body-axis pin set (`matches`, `backendRefs`, `timeouts`,
26027 // `retry`) one level deeper onto the per-`HTTPRouteMatch`
26028 // body-axis surface the M3 Aplicacao mesh renderer's external
26029 // `:entrada` ingress contract rests on.
26030 assert_eq!(GATEWAY_API_KEY_PATH, "path");
26031 }
26032
26033 #[test]
26034 fn gateway_api_key_path_carries_lower_camel_case_shape() {
26035 // Cross-axis invariant: a Kubernetes CRD schema field name is a
26036 // lowerCamelCase identifier per the K8s API conventions
26037 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
26038 // "Field names should be lowercase camelCase") — first byte
26039 // ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
26040 // kebab-case or whitespace. Pinning the shape here means a
26041 // future rebrand on the canonical lift can't silently land a
26042 // malformed field-name discriminator (snake_case, kebab-case,
26043 // UpperCamelCase, empty) that the apiserver-side CRD schema
26044 // validator would reject far from the rebrand commit's source.
26045 // Peer to `gateway_api_key_matches_carries_lower_camel_case_shape`
26046 // / `gateway_api_key_backend_refs_carries_lower_camel_case_shape`
26047 // on the sibling per-HTTPRoute-body-axis grammar-pin surface —
26048 // the lowerCamelCase K8s field-name grammar governs every
26049 // nested schema-field axis (including this per-`HTTPRouteMatch`
26050 // path-matcher-container-axis key), same convention.
26051 let v = GATEWAY_API_KEY_PATH;
26052 assert!(
26053 !v.is_empty(),
26054 "GATEWAY_API_KEY_PATH {v:?} must be non-empty per the K8s API \
26055 lowerCamelCase field-name grammar"
26056 );
26057 let first = v.chars().next().expect("non-empty");
26058 assert!(
26059 first.is_ascii_lowercase(),
26060 "GATEWAY_API_KEY_PATH {v:?} first byte {first:?} must be \
26061 ASCII-lowercase per the K8s API lowerCamelCase field-name \
26062 grammar (field names are always lowerCamelCase)"
26063 );
26064 assert!(
26065 v.chars().all(|c| c.is_ascii_alphanumeric()),
26066 "GATEWAY_API_KEY_PATH {v:?} must be ASCII-alphanumeric \
26067 throughout per the K8s API field-name grammar — no \
26068 snake_case, kebab-case, or whitespace bytes the apiserver-side \
26069 OpenAPI schema validator would reject"
26070 );
26071 }
26072
26073 #[test]
26074 fn gateway_api_key_value_pins_canonical_value() {
26075 // Pin the actual string so a typo in this lift can't silently
26076 // rebrand the Gateway API `HTTPPathMatch` scalar-payload axis
26077 // key the rendered `HTTPRoute` document mounts its per-match
26078 // request-path-selection scalar payload under. The string is
26079 // part of the cluster-side contract with every Gateway-API-
26080 // conformant gateway implementation (Cilium, Istio, Envoy
26081 // Gateway, NGINX) — the Gateway-API-implementation-side per-
26082 // rule L7 dispatch loop keys off this axis to source the
26083 // per-match request-path string that the sibling `type`
26084 // discriminator (Exact | PathPrefix | RegularExpression) is
26085 // applied against; a drifted value (`"path"` / `"prefix"` /
26086 // `"pattern"` / `"expression"`) at the production emitter
26087 // silently emits an `HTTPRoute` whose per-match request-path
26088 // scalar the Gateway API CRD schema validator drops as
26089 // unknown — the per-match path predicate degrades to the
26090 // wildcard match at the gateway-class-controller's per-rule
26091 // reconcile, the rule matches every request path
26092 // unconditionally, and every external `:entrada` path filter
26093 // the rule was authored to enforce drops with no field
26094 // naming the `HTTPPathMatch`-scalar-payload-drift root cause.
26095 // Changing this value is a coordinated Gateway API promotion
26096 // alongside the upstream SIG-Network Gateway API deprecation
26097 // cycle, not an incidental edit. Peer to
26098 // `gateway_api_key_path_pins_canonical_value` on the sibling
26099 // per-`HTTPRouteMatch`-body-axis canonical-string-pin surface
26100 // — nests the per-Gateway-API-HTTPRoute-per-match-body-axis
26101 // pin set (`path` container-axis, `value` scalar-payload key)
26102 // one level deeper onto the per-`HTTPPathMatch` body-axis
26103 // surface the M3 Aplicacao mesh renderer's external `:entrada`
26104 // ingress contract rests on.
26105 assert_eq!(GATEWAY_API_KEY_VALUE, "value");
26106 }
26107
26108 #[test]
26109 fn gateway_api_key_value_carries_lower_camel_case_shape() {
26110 // Cross-axis invariant: a Kubernetes CRD schema field name is
26111 // a lowerCamelCase identifier per the K8s API conventions
26112 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
26113 // "Field names should be lowercase camelCase") — first byte
26114 // ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
26115 // kebab-case or whitespace. Pinning the shape here means a
26116 // future rebrand on the canonical lift can't silently land a
26117 // malformed field-name discriminator (snake_case, kebab-case,
26118 // UpperCamelCase, empty) that the apiserver-side CRD schema
26119 // validator would reject far from the rebrand commit's source.
26120 // Peer to `gateway_api_key_path_carries_lower_camel_case_shape`
26121 // on the sibling per-`HTTPRouteMatch`-body-axis grammar-pin
26122 // surface — the lowerCamelCase K8s field-name grammar governs
26123 // every nested schema-field axis (including this per-
26124 // `HTTPPathMatch` scalar-payload-axis key), same convention.
26125 let v = GATEWAY_API_KEY_VALUE;
26126 assert!(
26127 !v.is_empty(),
26128 "GATEWAY_API_KEY_VALUE {v:?} must be non-empty per the K8s API \
26129 lowerCamelCase field-name grammar"
26130 );
26131 let first = v.chars().next().expect("non-empty");
26132 assert!(
26133 first.is_ascii_lowercase(),
26134 "GATEWAY_API_KEY_VALUE {v:?} first byte {first:?} must be \
26135 ASCII-lowercase per the K8s API lowerCamelCase field-name \
26136 grammar (field names are always lowerCamelCase)"
26137 );
26138 assert!(
26139 v.chars().all(|c| c.is_ascii_alphanumeric()),
26140 "GATEWAY_API_KEY_VALUE {v:?} must be ASCII-alphanumeric \
26141 throughout per the K8s API field-name grammar — no \
26142 snake_case, kebab-case, or whitespace bytes the apiserver-side \
26143 OpenAPI schema validator would reject"
26144 );
26145 }
26146
26147 #[test]
26148 fn gateway_api_key_value_distinct_from_gateway_api_key_path() {
26149 // Cross-axis invariant: the `HTTPPathMatch` scalar-payload key
26150 // (`value`) and its parent-container-axis key (`path`) name
26151 // *distinct* Gateway-API-side schema fields — the parent is a
26152 // container that hangs off the per-`HTTPRouteMatch`
26153 // `matches[]` entry, the child is the scalar payload that
26154 // rides inside the parent's `{type, value}` two-axis body.
26155 // Under the sibling K8s API conventions grammar
26156 // (`gateway_api_key_value_carries_lower_camel_case_shape` /
26157 // `gateway_api_key_path_carries_lower_camel_case_shape`) both
26158 // are ASCII-lowerCamelCase identifiers, so a same-shape
26159 // grammar-pin alone doesn't prevent a future rebrand from
26160 // silently collapsing the two axes onto the same string —
26161 // pinning inequality here surfaces that footgun at exactly
26162 // this build-time lift instead of at apply time as an
26163 // `HTTPRoute` whose per-match `path` container-body is
26164 // structurally malformed (`{path: <str>, path: <str>}` — the
26165 // apiserver's OpenAPI schema validator drops the whole match
26166 // block, the per-match path predicate degrades to the
26167 // wildcard match at the gateway-class-controller's per-rule
26168 // reconcile, the rule matches every request path
26169 // unconditionally, and every external `:entrada` path filter
26170 // the rule was authored to enforce drops with no field
26171 // naming the container/scalar-collapse root cause).
26172 assert_ne!(
26173 GATEWAY_API_KEY_VALUE, GATEWAY_API_KEY_PATH,
26174 "GATEWAY_API_KEY_VALUE ({GATEWAY_API_KEY_VALUE:?}) must not \
26175 collapse onto GATEWAY_API_KEY_PATH ({GATEWAY_API_KEY_PATH:?}) \
26176 — the two name distinct Gateway API `HTTPPathMatch` axes \
26177 (parent container vs. inner scalar payload) that must \
26178 remain independently addressable in the emitted \
26179 `HTTPRoute` per-match body"
26180 );
26181 }
26182
26183 #[test]
26184 fn gateway_api_key_listeners_pins_canonical_value() {
26185 // Pin the actual string so a typo in this lift can't silently
26186 // rebrand the Gateway API `Gateway` per-listener-set container-
26187 // axis key the rendered Gateway document mounts its per-Gateway
26188 // `[{name, port, protocol, hostname}]` L7-listener fan-out list
26189 // under. The string is part of the cluster-side contract with
26190 // every Gateway-API-conformant gateway implementation (Cilium,
26191 // Istio, Envoy Gateway, NGINX) — the Gateway-API-implementation-
26192 // side per-Gateway reconcile loop keys off this axis to source
26193 // the per-Gateway L7-listener fan-out the external `:entrada`
26194 // flow the Gateway was authored to accept lands on; a drifted
26195 // value (`"listener"` / `"listen"` / `"servers"`) at either the
26196 // production emitter or a downstream renderer's per-Gateway L7-
26197 // listener-set upsert silently emits a `Gateway` whose L7-
26198 // listener-set axis the Gateway API CRD schema validator drops
26199 // as unknown — no listener is opened, and every external
26200 // `:entrada` flow drops at the gateway-class-controller's per-
26201 // Gateway reconcile with no field naming the L7-listener-set-
26202 // drift root cause. Changing this value is a coordinated
26203 // Gateway API promotion alongside the upstream SIG-Network
26204 // Gateway API deprecation cycle, not an incidental edit. Peer
26205 // to `gateway_api_key_parent_refs_pins_canonical_value` /
26206 // `gateway_api_key_backend_refs_pins_canonical_value` on the
26207 // sibling per-Gateway-API-CRD-body-axis canonical-string-pin
26208 // surface — extends the per-Gateway-API-CRD-body-axis pin set
26209 // (`parentRefs`, `backendRefs`, `listeners`, future
26210 // `hostnames`) the M3 Aplicacao mesh renderer's external
26211 // `:entrada` ingress contract rests on across the Gateway API
26212 // CRD-side body-shape.
26213 assert_eq!(GATEWAY_API_KEY_LISTENERS, "listeners");
26214 }
26215
26216 #[test]
26217 fn gateway_api_key_listeners_carries_lower_camel_case_shape() {
26218 // Cross-axis invariant: a Kubernetes CRD schema field name is a
26219 // lowerCamelCase identifier per the K8s API conventions
26220 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
26221 // "Field names should be lowercase camelCase") — first byte
26222 // ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
26223 // kebab-case or whitespace. Pinning the shape here means a
26224 // future rebrand on the canonical lift can't silently land a
26225 // malformed field-name discriminator (snake_case, kebab-case,
26226 // UpperCamelCase, empty) that the apiserver-side CRD schema
26227 // validator would reject far from the rebrand commit's source.
26228 // Peer to `gateway_api_key_parent_refs_carries_lower_camel_case_shape`
26229 // / `gateway_api_key_backend_refs_carries_lower_camel_case_shape`
26230 // on the sibling per-Gateway-API-CRD-body-axis grammar-pin
26231 // surface — the lowerCamelCase K8s field-name grammar governs
26232 // every nested schema-field axis (including this per-Gateway
26233 // L7-listener-set-container-axis key), same convention.
26234 let v = GATEWAY_API_KEY_LISTENERS;
26235 assert!(
26236 !v.is_empty(),
26237 "GATEWAY_API_KEY_LISTENERS {v:?} must be non-empty per the K8s API \
26238 lowerCamelCase field-name grammar"
26239 );
26240 let first = v.chars().next().expect("non-empty");
26241 assert!(
26242 first.is_ascii_lowercase(),
26243 "GATEWAY_API_KEY_LISTENERS {v:?} first byte {first:?} must be \
26244 ASCII-lowercase per the K8s API lowerCamelCase field-name \
26245 grammar (field names are always lowerCamelCase)"
26246 );
26247 assert!(
26248 v.chars().all(|c| c.is_ascii_alphanumeric()),
26249 "GATEWAY_API_KEY_LISTENERS {v:?} must be ASCII-alphanumeric \
26250 throughout per the K8s API field-name grammar — no \
26251 snake_case, kebab-case, or whitespace bytes the apiserver-side \
26252 OpenAPI schema validator would reject"
26253 );
26254 }
26255
26256 #[test]
26257 fn gateway_api_key_hostname_pins_canonical_value() {
26258 // Pin the actual string so a typo in this lift can't silently
26259 // rebrand the Gateway API `Gateway` per-listener DNS-host-
26260 // discriminator axis key the rendered Gateway document mounts
26261 // each listener's virtual-host filter under. The string is part
26262 // of the cluster-side contract with every Gateway-API-conformant
26263 // gateway implementation (Cilium, Istio, Envoy Gateway, NGINX) —
26264 // the Gateway-API-implementation-side per-listener SNI /
26265 // `Host:`-header dispatch loop keys off this axis to source the
26266 // per-listener virtual-host filter each listener's inbound
26267 // traffic is scoped against; a drifted value (`"host"` /
26268 // `"vhost"` / `"serverName"`) at either the production emitter
26269 // or a downstream renderer's per-listener DNS-host-discriminator
26270 // upsert silently emits a `Gateway` whose per-listener virtual-
26271 // host filter axis the Gateway API CRD schema validator drops as
26272 // unknown — the listener accepts traffic on the wildcard host
26273 // rather than the typed `:entrada :host` the Aplicacao author
26274 // declared, and every external `:entrada` flow the listener was
26275 // authored to accept lands on the wrong virtual-host filter with
26276 // no field naming the DNS-host-discriminator-drift root cause.
26277 // Changing this value is a coordinated Gateway API promotion
26278 // alongside the upstream SIG-Network Gateway API deprecation
26279 // cycle, not an incidental edit. Peer to
26280 // `gateway_api_key_listeners_pins_canonical_value` /
26281 // `gateway_api_key_parent_refs_pins_canonical_value` /
26282 // `gateway_api_key_backend_refs_pins_canonical_value` on the
26283 // sibling per-Gateway-API-CRD-body-axis canonical-string-pin
26284 // surface — nests the per-Gateway-API-CRD-body-axis pin
26285 // discipline one level deeper onto the sibling per-listener
26286 // body-axis surface, extending the per-Gateway-API-CRD-body-
26287 // axis pin set (`parentRefs`, `backendRefs`, `listeners`,
26288 // `hostname`, future `hostnames`) the M3 Aplicacao mesh
26289 // renderer's external `:entrada` ingress contract rests on
26290 // across the Gateway API CRD-side body-shape.
26291 assert_eq!(GATEWAY_API_KEY_HOSTNAME, "hostname");
26292 }
26293
26294 #[test]
26295 fn gateway_api_key_hostname_carries_lower_camel_case_shape() {
26296 // Cross-axis invariant: a Kubernetes CRD schema field name is a
26297 // lowerCamelCase identifier per the K8s API conventions
26298 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
26299 // "Field names should be lowercase camelCase") — first byte
26300 // ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
26301 // kebab-case or whitespace. Pinning the shape here means a
26302 // future rebrand on the canonical lift can't silently land a
26303 // malformed field-name discriminator (snake_case, kebab-case,
26304 // UpperCamelCase, empty) that the apiserver-side CRD schema
26305 // validator would reject far from the rebrand commit's source.
26306 // Peer to `gateway_api_key_listeners_carries_lower_camel_case_shape`
26307 // / `gateway_api_key_parent_refs_carries_lower_camel_case_shape`
26308 // / `gateway_api_key_backend_refs_carries_lower_camel_case_shape`
26309 // on the sibling per-Gateway-API-CRD-body-axis grammar-pin
26310 // surface — the lowerCamelCase K8s field-name grammar governs
26311 // every nested schema-field axis (including this per-listener
26312 // DNS-host-discriminator-axis key), same convention.
26313 let v = GATEWAY_API_KEY_HOSTNAME;
26314 assert!(
26315 !v.is_empty(),
26316 "GATEWAY_API_KEY_HOSTNAME {v:?} must be non-empty per the K8s API \
26317 lowerCamelCase field-name grammar"
26318 );
26319 let first = v.chars().next().expect("non-empty");
26320 assert!(
26321 first.is_ascii_lowercase(),
26322 "GATEWAY_API_KEY_HOSTNAME {v:?} first byte {first:?} must be \
26323 ASCII-lowercase per the K8s API lowerCamelCase field-name \
26324 grammar (field names are always lowerCamelCase)"
26325 );
26326 assert!(
26327 v.chars().all(|c| c.is_ascii_alphanumeric()),
26328 "GATEWAY_API_KEY_HOSTNAME {v:?} must be ASCII-alphanumeric \
26329 throughout per the K8s API field-name grammar — no \
26330 snake_case, kebab-case, or whitespace bytes the apiserver-side \
26331 OpenAPI schema validator would reject"
26332 );
26333 }
26334
26335 #[test]
26336 fn gateway_api_key_hostnames_pins_canonical_value() {
26337 // Pin the actual string so a typo in this lift can't silently
26338 // rebrand the Gateway API `HTTPRoute` spec-level DNS-host-filter
26339 // axis key the rendered HTTPRoute document mounts each route's
26340 // per-route virtual-host filter list under. The string is part
26341 // of the cluster-side contract with every Gateway-API-conformant
26342 // gateway implementation (Cilium, Istio, Envoy Gateway, NGINX) —
26343 // the Gateway-API-implementation-side per-route SNI /
26344 // `Host:`-header dispatch loop keys off this axis to source the
26345 // per-route virtual-host filter list each route's inbound
26346 // traffic is scoped against; a drifted value (`"hosts"` /
26347 // `"vhosts"` / `"serverNames"`) at either the production emitter
26348 // or a downstream renderer's per-route DNS-host-filter upsert
26349 // silently emits an `HTTPRoute` whose per-route virtual-host
26350 // filter axis the Gateway API CRD schema validator drops as
26351 // unknown — the route accepts traffic on every host the parent
26352 // Gateway's listener accepts rather than the typed `:entrada
26353 // :host` the Aplicacao author declared, and every external
26354 // `:entrada` flow the route was authored to accept lands on the
26355 // wildcard virtual-host filter with no field naming the DNS-
26356 // host-filter-drift root cause. Changing this value is a
26357 // coordinated Gateway API promotion alongside the upstream
26358 // SIG-Network Gateway API deprecation cycle, not an incidental
26359 // edit. Peer to
26360 // `gateway_api_key_hostname_pins_canonical_value` /
26361 // `gateway_api_key_listeners_pins_canonical_value` /
26362 // `gateway_api_key_parent_refs_pins_canonical_value` /
26363 // `gateway_api_key_backend_refs_pins_canonical_value` on the
26364 // sibling per-Gateway-API-CRD-body-axis canonical-string-pin
26365 // surface — closes the per-Gateway-API-CRD `HTTPRoute` per-route
26366 // body-axis pin pair across the singular / plural DNS-host
26367 // discriminator surface (`hostname` at the parent-Gateway per-
26368 // listener discriminator + `hostnames` at the child HTTPRoute
26369 // per-route filter list), so both halves of the DNS-host-
26370 // discriminator convention across the `(Gateway, HTTPRoute)`
26371 // pair the M3 Aplicacao mesh renderer's external `:entrada`
26372 // ingress contract emits together now carry one lifted
26373 // canonical-string pin apiece.
26374 assert_eq!(GATEWAY_API_KEY_HOSTNAMES, "hostnames");
26375 }
26376
26377 #[test]
26378 fn gateway_api_key_hostnames_carries_lower_camel_case_shape() {
26379 // Cross-axis invariant: a Kubernetes CRD schema field name is a
26380 // lowerCamelCase identifier per the K8s API conventions
26381 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
26382 // "Field names should be lowercase camelCase") — first byte
26383 // ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
26384 // kebab-case or whitespace. Pinning the shape here means a
26385 // future rebrand on the canonical lift can't silently land a
26386 // malformed field-name discriminator (snake_case, kebab-case,
26387 // UpperCamelCase, empty) that the apiserver-side CRD schema
26388 // validator would reject far from the rebrand commit's source.
26389 // Peer to `gateway_api_key_hostname_carries_lower_camel_case_shape`
26390 // / `gateway_api_key_listeners_carries_lower_camel_case_shape`
26391 // / `gateway_api_key_parent_refs_carries_lower_camel_case_shape`
26392 // / `gateway_api_key_backend_refs_carries_lower_camel_case_shape`
26393 // on the sibling per-Gateway-API-CRD-body-axis grammar-pin
26394 // surface — the lowerCamelCase K8s field-name grammar governs
26395 // every nested schema-field axis (including this per-route DNS-
26396 // host-filter-axis key), same convention.
26397 let v = GATEWAY_API_KEY_HOSTNAMES;
26398 assert!(
26399 !v.is_empty(),
26400 "GATEWAY_API_KEY_HOSTNAMES {v:?} must be non-empty per the K8s API \
26401 lowerCamelCase field-name grammar"
26402 );
26403 let first = v.chars().next().expect("non-empty");
26404 assert!(
26405 first.is_ascii_lowercase(),
26406 "GATEWAY_API_KEY_HOSTNAMES {v:?} first byte {first:?} must be \
26407 ASCII-lowercase per the K8s API lowerCamelCase field-name \
26408 grammar (field names are always lowerCamelCase)"
26409 );
26410 assert!(
26411 v.chars().all(|c| c.is_ascii_alphanumeric()),
26412 "GATEWAY_API_KEY_HOSTNAMES {v:?} must be ASCII-alphanumeric \
26413 throughout per the K8s API field-name grammar — no \
26414 snake_case, kebab-case, or whitespace bytes the apiserver-side \
26415 OpenAPI schema validator would reject"
26416 );
26417 }
26418
26419 #[test]
26420 fn gateway_api_key_timeouts_pins_canonical_value() {
26421 // Pin the actual string so a typo in this lift can't silently
26422 // rebrand the Gateway API `HTTPRoute` per-rule request-timeout-
26423 // policy body-axis key the rendered HTTPRoute document mounts
26424 // each rule's per-rule `:politicas :timeout` overlay under. The
26425 // string is part of the cluster-side contract with every
26426 // Gateway-API-conformant gateway implementation (Cilium, Istio,
26427 // Envoy Gateway, NGINX) — the Gateway-API-implementation-side
26428 // per-rule request-dispatch loop keys off this axis to source
26429 // the per-rule wall-clock deadline each accepted request is
26430 // bounded against; a drifted value (`"timeout"` (singular) /
26431 // `"timeoutPolicy"` / `"deadlines"`) at either the production
26432 // emitter or a downstream renderer's per-rule timeout-policy
26433 // upsert silently emits an `HTTPRoute` whose per-rule request-
26434 // timeout policy axis the Gateway API CRD schema validator
26435 // drops as unknown — the route accepts every inbound request
26436 // with no per-rule wall-clock deadline (the "no infinite
26437 // blocking" guarantee MESH-COMPOSITION.md §V mandates for every
26438 // rendered per-`:politicas` mesh-composition edge silently
26439 // regresses to the pre-overlay unbounded-request semantic), and
26440 // every external `:entrada` flow the route was authored to
26441 // bound by the typed `:politicas :timeout` slot runs to
26442 // whatever backend deadline the resolved backend's downstream
26443 // infrastructure picks with no field naming the per-rule-
26444 // timeout-policy-drift root cause. Changing this value is a
26445 // coordinated Gateway API promotion alongside the upstream
26446 // SIG-Network Gateway API deprecation cycle, not an incidental
26447 // edit. Peer to
26448 // `gateway_api_key_hostnames_pins_canonical_value` /
26449 // `gateway_api_key_hostname_pins_canonical_value` /
26450 // `gateway_api_key_listeners_pins_canonical_value` /
26451 // `gateway_api_key_parent_refs_pins_canonical_value` /
26452 // `gateway_api_key_backend_refs_pins_canonical_value` on the
26453 // sibling per-Gateway-API-CRD-body-axis canonical-string-pin
26454 // surface — extends the per-Gateway-API-`HTTPRoute` per-rule
26455 // body-axis pin set (`backendRefs`, future per-rule sibling
26456 // axes) onto the load-bearing per-rule request-timeout-policy
26457 // axis the M3 Aplicacao mesh renderer's per-`:politicas
26458 // :timeout` overlay lands under.
26459 assert_eq!(GATEWAY_API_KEY_TIMEOUTS, "timeouts");
26460 }
26461
26462 #[test]
26463 fn gateway_api_key_timeouts_carries_lower_camel_case_shape() {
26464 // Cross-axis invariant: a Kubernetes CRD schema field name is a
26465 // lowerCamelCase identifier per the K8s API conventions
26466 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
26467 // "Field names should be lowercase camelCase") — first byte
26468 // ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
26469 // kebab-case or whitespace. Pinning the shape here means a
26470 // future rebrand on the canonical lift can't silently land a
26471 // malformed field-name discriminator (snake_case, kebab-case,
26472 // UpperCamelCase, empty) that the apiserver-side CRD schema
26473 // validator would reject far from the rebrand commit's source.
26474 // Peer to `gateway_api_key_hostnames_carries_lower_camel_case_shape`
26475 // / `gateway_api_key_hostname_carries_lower_camel_case_shape`
26476 // / `gateway_api_key_listeners_carries_lower_camel_case_shape`
26477 // / `gateway_api_key_parent_refs_carries_lower_camel_case_shape`
26478 // / `gateway_api_key_backend_refs_carries_lower_camel_case_shape`
26479 // on the sibling per-Gateway-API-CRD-body-axis grammar-pin
26480 // surface — the lowerCamelCase K8s field-name grammar governs
26481 // every nested schema-field axis (including this per-rule
26482 // request-timeout-policy-axis key), same convention.
26483 let v = GATEWAY_API_KEY_TIMEOUTS;
26484 assert!(
26485 !v.is_empty(),
26486 "GATEWAY_API_KEY_TIMEOUTS {v:?} must be non-empty per the K8s API \
26487 lowerCamelCase field-name grammar"
26488 );
26489 let first = v.chars().next().expect("non-empty");
26490 assert!(
26491 first.is_ascii_lowercase(),
26492 "GATEWAY_API_KEY_TIMEOUTS {v:?} first byte {first:?} must be \
26493 ASCII-lowercase per the K8s API lowerCamelCase field-name \
26494 grammar (field names are always lowerCamelCase)"
26495 );
26496 assert!(
26497 v.chars().all(|c| c.is_ascii_alphanumeric()),
26498 "GATEWAY_API_KEY_TIMEOUTS {v:?} must be ASCII-alphanumeric \
26499 throughout per the K8s API field-name grammar — no \
26500 snake_case, kebab-case, or whitespace bytes the apiserver-side \
26501 OpenAPI schema validator would reject"
26502 );
26503 }
26504
26505 #[test]
26506 fn gateway_api_key_retry_pins_canonical_value() {
26507 // Pin the actual string so a typo in this lift can't silently
26508 // rebrand the Gateway API `HTTPRoute` per-rule retry-policy
26509 // body-axis key the rendered HTTPRoute document mounts each
26510 // rule's per-rule `:politicas :retries` overlay under. The
26511 // string is part of the cluster-side contract with every
26512 // Gateway-API-conformant gateway implementation (Cilium, Istio,
26513 // Envoy Gateway, NGINX) — the Gateway-API-implementation-side
26514 // per-rule request-dispatch loop keys off this axis to source
26515 // the per-rule retry budget each failed backend attempt count
26516 // is bounded against; a drifted value (`"retries"` (plural) /
26517 // `"retryPolicy"` / `"budget"`) at either the production
26518 // emitter or a downstream renderer's per-rule retry-policy
26519 // upsert silently emits an `HTTPRoute` whose per-rule retry-
26520 // budget axis the Gateway API CRD schema validator drops as
26521 // unknown — the route accepts every inbound request with no
26522 // per-rule retry budget (the "no infinite retrying without
26523 // bound" guarantee MESH-COMPOSITION.md §V mandates for every
26524 // rendered per-`:politicas` mesh-composition edge silently
26525 // regresses to the pre-overlay unbounded-retry semantic), and
26526 // every external `:entrada` flow the route was authored to cap
26527 // by the typed `:politicas :retries` slot runs to whatever
26528 // retry policy the resolved backend's downstream infrastructure
26529 // picks with no field naming the per-rule-retry-policy-drift
26530 // root cause. Changing this value is a coordinated Gateway API
26531 // promotion alongside the upstream SIG-Network Gateway API
26532 // deprecation cycle, not an incidental edit. Peer to
26533 // `gateway_api_key_timeouts_pins_canonical_value` /
26534 // `gateway_api_key_hostnames_pins_canonical_value` /
26535 // `gateway_api_key_hostname_pins_canonical_value` /
26536 // `gateway_api_key_listeners_pins_canonical_value` /
26537 // `gateway_api_key_parent_refs_pins_canonical_value` /
26538 // `gateway_api_key_backend_refs_pins_canonical_value` on the
26539 // sibling per-Gateway-API-CRD-body-axis canonical-string-pin
26540 // surface — closes the per-Gateway-API-`HTTPRoute`-per-rule
26541 // `:politicas` overlay axis pair (`timeouts` for `:politicas
26542 // :timeout`, `retry` for `:politicas :retries`) both
26543 // MESH-COMPOSITION.md §V "no infinite blocking / no infinite
26544 // retrying" guarantees rest on.
26545 assert_eq!(GATEWAY_API_KEY_RETRY, "retry");
26546 }
26547
26548 #[test]
26549 fn gateway_api_key_retry_carries_lower_camel_case_shape() {
26550 // Cross-axis invariant: a Kubernetes CRD schema field name is a
26551 // lowerCamelCase identifier per the K8s API conventions
26552 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
26553 // "Field names should be lowercase camelCase") — first byte
26554 // ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
26555 // kebab-case or whitespace. Pinning the shape here means a
26556 // future rebrand on the canonical lift can't silently land a
26557 // malformed field-name discriminator (snake_case, kebab-case,
26558 // UpperCamelCase, empty) that the apiserver-side CRD schema
26559 // validator would reject far from the rebrand commit's source.
26560 // Peer to `gateway_api_key_timeouts_carries_lower_camel_case_shape`
26561 // / `gateway_api_key_hostnames_carries_lower_camel_case_shape`
26562 // / `gateway_api_key_hostname_carries_lower_camel_case_shape`
26563 // / `gateway_api_key_listeners_carries_lower_camel_case_shape`
26564 // / `gateway_api_key_parent_refs_carries_lower_camel_case_shape`
26565 // / `gateway_api_key_backend_refs_carries_lower_camel_case_shape`
26566 // on the sibling per-Gateway-API-CRD-body-axis grammar-pin
26567 // surface — the lowerCamelCase K8s field-name grammar governs
26568 // every nested schema-field axis (including this per-rule
26569 // retry-policy-axis key), same convention.
26570 let v = GATEWAY_API_KEY_RETRY;
26571 assert!(
26572 !v.is_empty(),
26573 "GATEWAY_API_KEY_RETRY {v:?} must be non-empty per the K8s API \
26574 lowerCamelCase field-name grammar"
26575 );
26576 let first = v.chars().next().expect("non-empty");
26577 assert!(
26578 first.is_ascii_lowercase(),
26579 "GATEWAY_API_KEY_RETRY {v:?} first byte {first:?} must be \
26580 ASCII-lowercase per the K8s API lowerCamelCase field-name \
26581 grammar (field names are always lowerCamelCase)"
26582 );
26583 assert!(
26584 v.chars().all(|c| c.is_ascii_alphanumeric()),
26585 "GATEWAY_API_KEY_RETRY {v:?} must be ASCII-alphanumeric \
26586 throughout per the K8s API field-name grammar — no \
26587 snake_case, kebab-case, or whitespace bytes the apiserver-side \
26588 OpenAPI schema validator would reject"
26589 );
26590 }
26591
26592 #[test]
26593 fn gateway_api_key_attempts_pins_canonical_value() {
26594 // Pin the actual string so a typo in this lift can't silently
26595 // rebrand the Gateway API `HTTPRoute` per-rule retry-policy
26596 // `attempts` leaf scalar-key the rendered HTTPRoute document
26597 // mounts each rule's per-rule `:politicas :retries` typed `u32`
26598 // attempt count under. The string is part of the cluster-side
26599 // contract with every Gateway-API-conformant gateway
26600 // implementation (Cilium, Istio, Envoy Gateway, NGINX) — the
26601 // Gateway-API-implementation-side per-rule request-dispatch
26602 // loop keys off this leaf to source the per-rule retry attempt
26603 // budget each failed backend attempt count is bounded against;
26604 // a drifted value (`"attempt"` (singular) / `"count"` /
26605 // `"tries"` / `"maxAttempts"`) at either the production
26606 // emitter or a downstream renderer's per-rule retry-attempts
26607 // upsert silently emits an `HTTPRoute` whose per-rule retry-
26608 // attempts leaf the Gateway API CRD schema validator drops as
26609 // unknown — the retry sub-shape parses as an empty
26610 // `HTTPRouteRetry` with the typed `u32` attempt count silently
26611 // discarded, the route accepts every inbound request with no
26612 // per-rule retry budget (the "no infinite retrying without
26613 // bound" guarantee MESH-COMPOSITION.md §V mandates for every
26614 // rendered per-`:politicas` mesh-composition edge silently
26615 // regresses to the pre-overlay unbounded-retry semantic), and
26616 // every external `:entrada` flow the route was authored to cap
26617 // by the typed `:politicas :retries` slot runs to whatever
26618 // retry policy the resolved backend's downstream infrastructure
26619 // picks with no field naming the per-rule-retry-attempts-leaf-
26620 // key-drift root cause. Changing this value is a coordinated
26621 // Gateway API promotion alongside the upstream SIG-Network
26622 // Gateway API deprecation cycle, not an incidental edit. Peer
26623 // to `gateway_api_key_retry_pins_canonical_value` /
26624 // `gateway_api_key_timeouts_pins_canonical_value` /
26625 // `gateway_api_key_hostnames_pins_canonical_value` /
26626 // `gateway_api_key_hostname_pins_canonical_value` /
26627 // `gateway_api_key_listeners_pins_canonical_value` /
26628 // `gateway_api_key_parent_refs_pins_canonical_value` /
26629 // `gateway_api_key_backend_refs_pins_canonical_value` on the
26630 // sibling per-Gateway-API-CRD-body-axis canonical-string-pin
26631 // surface — closes the parent-leaf axis pair (`retry`
26632 // container + `attempts` leaf) both MESH-COMPOSITION.md §V
26633 // "no infinite retrying" guarantees rest on, one nesting
26634 // level deeper than the parent per-rule retry-policy
26635 // container axis (`retry`).
26636 assert_eq!(GATEWAY_API_KEY_ATTEMPTS, "attempts");
26637 }
26638
26639 #[test]
26640 fn gateway_api_key_attempts_carries_lower_camel_case_shape() {
26641 // Cross-axis invariant: a Kubernetes CRD schema field name is a
26642 // lowerCamelCase identifier per the K8s API conventions
26643 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
26644 // "Field names should be lowercase camelCase") — first byte
26645 // ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
26646 // kebab-case or whitespace. Pinning the shape here means a
26647 // future rebrand on the canonical lift can't silently land a
26648 // malformed field-name discriminator (snake_case, kebab-case,
26649 // UpperCamelCase, empty) that the apiserver-side CRD schema
26650 // validator would reject far from the rebrand commit's source.
26651 // Peer to `gateway_api_key_retry_carries_lower_camel_case_shape`
26652 // / `gateway_api_key_timeouts_carries_lower_camel_case_shape`
26653 // / `gateway_api_key_hostnames_carries_lower_camel_case_shape`
26654 // / `gateway_api_key_hostname_carries_lower_camel_case_shape`
26655 // / `gateway_api_key_listeners_carries_lower_camel_case_shape`
26656 // / `gateway_api_key_parent_refs_carries_lower_camel_case_shape`
26657 // / `gateway_api_key_backend_refs_carries_lower_camel_case_shape`
26658 // on the sibling per-Gateway-API-CRD-body-axis grammar-pin
26659 // surface — the lowerCamelCase K8s field-name grammar governs
26660 // every nested schema-field axis (including this per-rule
26661 // retry-attempts-leaf-key), same convention.
26662 let v = GATEWAY_API_KEY_ATTEMPTS;
26663 assert!(
26664 !v.is_empty(),
26665 "GATEWAY_API_KEY_ATTEMPTS {v:?} must be non-empty per the K8s API \
26666 lowerCamelCase field-name grammar"
26667 );
26668 let first = v.chars().next().expect("non-empty");
26669 assert!(
26670 first.is_ascii_lowercase(),
26671 "GATEWAY_API_KEY_ATTEMPTS {v:?} first byte {first:?} must be \
26672 ASCII-lowercase per the K8s API lowerCamelCase field-name \
26673 grammar (field names are always lowerCamelCase)"
26674 );
26675 assert!(
26676 v.chars().all(|c| c.is_ascii_alphanumeric()),
26677 "GATEWAY_API_KEY_ATTEMPTS {v:?} must be ASCII-alphanumeric \
26678 throughout per the K8s API field-name grammar — no \
26679 snake_case, kebab-case, or whitespace bytes the apiserver-side \
26680 OpenAPI schema validator would reject"
26681 );
26682 }
26683
26684 #[test]
26685 fn gateway_api_key_request_pins_canonical_value() {
26686 // Pin the actual string so a typo in this lift can't silently
26687 // rebrand the Gateway API `HTTPRoute` per-rule request-timeout-
26688 // policy `request` leaf scalar-key the rendered HTTPRoute
26689 // document mounts each rule's per-rule `:politicas :timeout`
26690 // typed K8s-duration string under. The string is part of the
26691 // cluster-side contract with every Gateway-API-conformant
26692 // gateway implementation (Cilium, Istio, Envoy Gateway, NGINX)
26693 // — the Gateway-API-implementation-side per-rule request-
26694 // dispatch loop keys off this leaf to source the per-rule
26695 // request wall-clock deadline each inbound request is bounded
26696 // against; a drifted value (`"deadline"` / `"requestTimeout"`
26697 // / `"timeout"` / `"upstreamRequest"`) at either the production
26698 // emitter or a downstream renderer's per-rule request-deadline
26699 // upsert silently emits an `HTTPRoute` whose per-rule request-
26700 // deadline leaf the Gateway API CRD schema validator drops as
26701 // unknown — the timeouts sub-shape parses as an empty
26702 // `HTTPRouteTimeouts` with the typed duration silently
26703 // discarded, the route accepts every inbound request with no
26704 // per-rule request deadline (the "no infinite blocking"
26705 // guarantee MESH-COMPOSITION.md §V mandates for every rendered
26706 // per-`:politicas` mesh-composition edge silently regresses to
26707 // the pre-overlay unbounded-blocking semantic), and every
26708 // external `:entrada` flow the route was authored to cap by
26709 // the typed `:politicas :timeout` slot runs to whatever
26710 // request-deadline the resolved backend's downstream
26711 // infrastructure picks with no field naming the per-rule-
26712 // request-deadline-leaf-key-drift root cause. Changing this
26713 // value is a coordinated Gateway API promotion alongside the
26714 // upstream SIG-Network Gateway API deprecation cycle, not an
26715 // incidental edit. Peer to
26716 // `gateway_api_key_attempts_pins_canonical_value` /
26717 // `gateway_api_key_retry_pins_canonical_value` /
26718 // `gateway_api_key_timeouts_pins_canonical_value` /
26719 // `gateway_api_key_hostnames_pins_canonical_value` /
26720 // `gateway_api_key_hostname_pins_canonical_value` /
26721 // `gateway_api_key_listeners_pins_canonical_value` /
26722 // `gateway_api_key_parent_refs_pins_canonical_value` /
26723 // `gateway_api_key_backend_refs_pins_canonical_value` on the
26724 // sibling per-Gateway-API-CRD-body-axis canonical-string-pin
26725 // surface — closes the second parent-leaf axis pair
26726 // (`timeouts` container + `request` leaf) both
26727 // MESH-COMPOSITION.md §V "no infinite blocking / no infinite
26728 // retrying" guarantees rest on, sibling to the parent-leaf
26729 // pair (`retry` container + `attempts` leaf) closed in
26730 // e2e136b.
26731 assert_eq!(GATEWAY_API_KEY_REQUEST, "request");
26732 }
26733
26734 #[test]
26735 fn gateway_api_key_request_carries_lower_camel_case_shape() {
26736 // Cross-axis invariant: a Kubernetes CRD schema field name is a
26737 // lowerCamelCase identifier per the K8s API conventions
26738 // (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
26739 // "Field names should be lowercase camelCase") — first byte
26740 // ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
26741 // kebab-case or whitespace. Pinning the shape here means a
26742 // future rebrand on the canonical lift can't silently land a
26743 // malformed field-name discriminator (snake_case, kebab-case,
26744 // UpperCamelCase, empty) that the apiserver-side CRD schema
26745 // validator would reject far from the rebrand commit's source.
26746 // Peer to `gateway_api_key_attempts_carries_lower_camel_case_shape`
26747 // / `gateway_api_key_retry_carries_lower_camel_case_shape`
26748 // / `gateway_api_key_timeouts_carries_lower_camel_case_shape`
26749 // / `gateway_api_key_hostnames_carries_lower_camel_case_shape`
26750 // / `gateway_api_key_hostname_carries_lower_camel_case_shape`
26751 // / `gateway_api_key_listeners_carries_lower_camel_case_shape`
26752 // / `gateway_api_key_parent_refs_carries_lower_camel_case_shape`
26753 // / `gateway_api_key_backend_refs_carries_lower_camel_case_shape`
26754 // on the sibling per-Gateway-API-CRD-body-axis grammar-pin
26755 // surface — the lowerCamelCase K8s field-name grammar governs
26756 // every nested schema-field axis (including this per-rule
26757 // request-deadline-leaf-key), same convention.
26758 let v = GATEWAY_API_KEY_REQUEST;
26759 assert!(
26760 !v.is_empty(),
26761 "GATEWAY_API_KEY_REQUEST {v:?} must be non-empty per the K8s API \
26762 lowerCamelCase field-name grammar"
26763 );
26764 let first = v.chars().next().expect("non-empty");
26765 assert!(
26766 first.is_ascii_lowercase(),
26767 "GATEWAY_API_KEY_REQUEST {v:?} first byte {first:?} must be \
26768 ASCII-lowercase per the K8s API lowerCamelCase field-name \
26769 grammar (field names are always lowerCamelCase)"
26770 );
26771 assert!(
26772 v.chars().all(|c| c.is_ascii_alphanumeric()),
26773 "GATEWAY_API_KEY_REQUEST {v:?} must be ASCII-alphanumeric \
26774 throughout per the K8s API field-name grammar — no \
26775 snake_case, kebab-case, or whitespace bytes the apiserver-side \
26776 OpenAPI schema validator would reject"
26777 );
26778 }
26779
26780 #[test]
26781 fn default_namespace_is_a_valid_dns_1123_label() {
26782 // Cross-axis invariant: the default namespace lands as
26783 // `metadata.namespace` on every emitted K8s object across every
26784 // renderer, and the K8s apiserver enforces the DNS-1123 label
26785 // rule on every `metadata.namespace`. Pinning this here means
26786 // a future rebrand on the canonical `DEFAULT_NAMESPACE`
26787 // declaration can't silently land a value the apiserver
26788 // refuses at the *first* renderer to apply against a cluster,
26789 // far from the rebrand commit's source — the typed
26790 // [`is_dns_1123_label`] floor rejects it at caixa-core build
26791 // time on the canonical lift, before any renderer consumes
26792 // the value. Same trajectory as `:membros :caixa` /
26793 // `:placement :clusters` / `:contratos :de`/`:para` /
26794 // `:entrada :para` / `:placement :affinity` (dfd4902 — the
26795 // five typed-identifier axes on the Aplicacao surface that
26796 // already land on this same `is_dns_1123_label` floor at
26797 // their respective validate gates), now extended onto the
26798 // canonical-namespace-default axis the renderers share.
26799 assert!(
26800 is_dns_1123_label(DEFAULT_NAMESPACE).is_ok(),
26801 "DEFAULT_NAMESPACE {DEFAULT_NAMESPACE:?} must be a valid \
26802 DNS-1123 label — every K8s apiserver-side schema enforces \
26803 this rule on `metadata.namespace`"
26804 );
26805 }
26806
26807 #[test]
26808 fn helm_chart_api_version_pins_canonical_value() {
26809 // Pin the actual string so a typo in this lift can't silently
26810 // rebrand the Helm 3 chart-schema apiVersion the rendered
26811 // `lareira-<nome>` `Chart.yaml` document declares at its
26812 // top-level `apiVersion` axis. The string is part of the
26813 // Helm-side contract with the Helm 3 chart-schema parser:
26814 // `helm dependency build` / `helm lint` / `helm template`
26815 // all resolve the chart under the Helm 3 v2 schema (permitting
26816 // top-level `dependencies:`); a drifted value to the legacy
26817 // Helm 2 `"v1"` schema (the pre-Helm-3 chart schema every
26818 // upstream Helm-3-migration doc names) silently reroutes the
26819 // rendered Chart.yaml through the Helm 2 parser, where the
26820 // top-level `dependencies:` block is unknown and the chart's
26821 // dep on the `pleme-computeunit` library chart never resolves
26822 // — `helm dependency build` reports "no requirements found"
26823 // and every `helm template` / `helm install` emits an empty
26824 // release (no ComputeUnit / Service / ScaledObject resources
26825 // land) far from the source caixa.lisp / the renderer's
26826 // `build_chart_yaml` call site. Changing it is a coordinated
26827 // Helm 4 chart-schema migration alongside the upstream Helm
26828 // chart-schema deprecation cycle, not an incidental edit.
26829 // Peer to `flux_helmrelease_api_version_pins_canonical_value`
26830 // / `flux_gitrepository_api_version_pins_canonical_value` /
26831 // `flux_kustomization_api_version_pins_canonical_value` /
26832 // `gateway_api_api_version_pins_canonical_value` /
26833 // `cilium_api_version_pins_canonical_value` on the sibling
26834 // cluster-side-CRD-apiVersion-pin set — those pin the K8s
26835 // apiserver-side `(apiVersion, kind)` `RESTMapper` contract,
26836 // this one pins the Helm-side chart-schema-parser contract
26837 // that gates every rendered `lareira-<nome>` chart's
26838 // dependency resolution before any K8s resource lands.
26839 assert_eq!(HELM_CHART_API_VERSION, "v2");
26840 }
26841
26842 #[test]
26843 fn helm_chart_api_version_carries_helm_3_chart_schema_shape() {
26844 // Cross-axis invariant: the Helm 3 chart-schema apiVersion is
26845 // a bare `v<digit>` version label (unlike the K8s CRD
26846 // apiVersion — `<group>/<version>` — the sibling
26847 // FLUX_HELMRELEASE_API_VERSION / GATEWAY_API_API_VERSION /
26848 // CILIUM_API_VERSION lifts pin). The Helm-side chart-schema
26849 // grammar carries no group prefix at all — the value is
26850 // parsed as a plain schema-version discriminator against the
26851 // Helm binary's built-in schema table (Helm 2 recognizes
26852 // `"v1"`, Helm 3 recognizes both `"v1"` for legacy compat
26853 // and `"v2"` for its native schema). Pinning the shape here
26854 // means a future rebrand on the canonical lift can't silently
26855 // land a K8s-CRD-shaped `group/version` value (e.g. an
26856 // accidental copy-paste from the sibling FLUX / GATEWAY /
26857 // CILIUM constants) that the Helm chart-schema parser would
26858 // fail to recognize at `helm dependency build` /
26859 // `helm lint` / `helm template` time. The `v<digit>+`
26860 // invariant is the load-bearing Helm-side chart-schema
26861 // typed-discovery contract: a value the Helm binary's
26862 // chart-schema resolver consults to select the schema
26863 // parser that reads the rest of the document. Peer to
26864 // `flux_kind_helm_release_carries_upper_camel_case_shape`
26865 // (which pins the K8s `RESTMapper` kind-grammar shape) —
26866 // both close the "the shape of the lifted schema-version
26867 // discriminator is grammatical, not just a byte-equal string"
26868 // discipline at the lift site.
26869 let v = HELM_CHART_API_VERSION;
26870 assert!(
26871 !v.is_empty(),
26872 "HELM_CHART_API_VERSION {v:?} must be non-empty per the Helm \
26873 chart-schema apiVersion grammar"
26874 );
26875 assert!(
26876 !v.contains('/'),
26877 "HELM_CHART_API_VERSION {v:?} must not contain `/` — the Helm-side \
26878 chart-schema apiVersion is a bare `v<digit>` label with no group \
26879 prefix, unlike the K8s CRD `<group>/<version>` shape the sibling \
26880 FLUX_HELMRELEASE_API_VERSION / GATEWAY_API_API_VERSION / \
26881 CILIUM_API_VERSION lifts carry"
26882 );
26883 let bytes = v.as_bytes();
26884 assert_eq!(
26885 bytes[0], b'v',
26886 "HELM_CHART_API_VERSION {v:?} must start with `v` per the Helm \
26887 chart-schema apiVersion grammar (`v1` for the legacy schema, \
26888 `v2` for the Helm 3 schema — every accepted value the Helm \
26889 binary's chart-schema resolver knows carries the `v` prefix)"
26890 );
26891 assert!(
26892 bytes.len() >= 2,
26893 "HELM_CHART_API_VERSION {v:?} must be at least 2 bytes (`v` + \
26894 at least one digit) per the Helm chart-schema apiVersion \
26895 grammar"
26896 );
26897 assert!(
26898 bytes[1..].iter().all(u8::is_ascii_digit),
26899 "HELM_CHART_API_VERSION {v:?} bytes after the leading `v` must be \
26900 ASCII digits per the Helm chart-schema apiVersion grammar — \
26901 no dots, no hyphens, no whitespace, no non-digit bytes the \
26902 Helm binary's chart-schema resolver would reject"
26903 );
26904 }
26905
26906 #[test]
26907 fn helm_chart_type_application_pins_canonical_value() {
26908 // Pin the actual string so a typo in this lift can't silently
26909 // rebrand the Helm 3 chart-schema `type` field's canonical
26910 // `application` per-chart-kind discriminator scalar-value the
26911 // rendered `lareira-<nome>` chart's Chart.yaml `type:` axis
26912 // declares. The value is part of the cluster-side contract with
26913 // Helm's per-release install-shape dispatch loop — the Helm
26914 // chart-schema pins the per-chart-kind axis to the closed set
26915 // `{"application", "library"}` (see
26916 // https://helm.sh/docs/topics/charts/#chart-types), so a drifted
26917 // value (`"Application"` / `"APPLICATION"` / `"app"` /
26918 // `"workload"`) lands the rendered `lareira-<nome>` chart outside
26919 // the schema's admitted set, and Helm's chart-schema parser
26920 // silently treats the unrecognized value as the default
26921 // `application` shape (masking the schema violation with no
26922 // process-log drift-signal); worse, an accidental collapse onto
26923 // the sibling `"library"` shape lands `lareira-<nome>` in the
26924 // dependency-only install-shape Helm refuses to install directly
26925 // ("Error: library charts cannot be installed"), dropping every
26926 // per-Servico `helm install` / `helm upgrade` release cycle with
26927 // no field naming the chart-kind-drift root cause. Changing this
26928 // value is a coordinated Helm chart-schema promotion alongside
26929 // the upstream Helm project's per-schema deprecation cycle, not
26930 // an incidental edit. Peer to
26931 // `helm_chart_api_version_pins_canonical_value` /
26932 // `kube_protocol_tcp_pins_canonical_value` /
26933 // `gateway_api_protocol_http_pins_canonical_value` /
26934 // `cilium_auth_mode_required_pins_canonical_value` on the
26935 // sibling canonical-Helm-chart-schema-axis + canonical-cluster-
26936 // side-OpenAPI-schema-enum-value pin sets — pivots the
26937 // canonical-enum-value single-sourcing discipline from the K8s-
26938 // CR-side surfaces onto the Helm-chart-schema-enum-value axis
26939 // every rendered Chart.yaml carries at its per-chart-kind
26940 // discriminator field.
26941 assert_eq!(HELM_CHART_TYPE_APPLICATION, "application");
26942 }
26943
26944 #[test]
26945 fn helm_chart_type_application_carries_lowercase_shape() {
26946 // Cross-axis invariant: the Helm 3 chart-schema `type` field
26947 // admits the closed set `{"application", "library"}` — every
26948 // admitted value is all-ASCII-lowercase throughout per the
26949 // upstream Helm project's per-enum-value naming convention
26950 // (distinct from the sibling K8s-core `Protocol` OpenAPI schema
26951 // enum's all-ASCII-uppercase per-value convention the
26952 // `kube_protocol_tcp_carries_upper_case_shape` pin carries, and
26953 // distinct from the sibling Gateway-API v1 `PathMatchType`
26954 // OpenAPI schema enum's UpperCamelCase per-value convention the
26955 // `gateway_api_path_match_type_path_prefix_carries_upper_camel_case_shape`
26956 // pin carries — the three peer canonical-cluster-side-schema-
26957 // enum-value conventions do not collapse). Same all-ASCII-
26958 // lowercase shape as the sibling Cilium `MutualAuthenticationMode`
26959 // enum-values the peer `cilium_auth_mode_required_carries_lowercase_shape`
26960 // / `cilium_auth_mode_disabled_carries_lowercase_shape` pins
26961 // enshrine — the two peer canonical-cluster-side-schema-enum-
26962 // value all-lowercase conventions collapse on the shared byte-
26963 // shape convention Helm and Cilium happen to share (independent
26964 // upstream projects, coincidental convention agreement).
26965 //
26966 // Pinning the shape here means a future rebrand on the canonical
26967 // lift can't silently land a malformed per-chart-kind scalar
26968 // (uppercase `"APPLICATION"`, mixed-case `"Application"`, empty)
26969 // that the Helm chart-schema parser would silently treat as the
26970 // default `application` shape (masking the drift with no
26971 // process-log signal).
26972 let v = HELM_CHART_TYPE_APPLICATION;
26973 assert!(
26974 !v.is_empty(),
26975 "HELM_CHART_TYPE_APPLICATION {v:?} must be non-empty per the \
26976 Helm 3 chart-schema `type` field grammar"
26977 );
26978 assert!(
26979 v.chars().all(|c| c.is_ascii_lowercase()),
26980 "HELM_CHART_TYPE_APPLICATION {v:?} must be ASCII-lowercase \
26981 throughout per the Helm 3 chart-schema per-chart-kind \
26982 discriminator naming convention — no uppercase, mixed-case, \
26983 or whitespace bytes the Helm chart-schema parser would \
26984 silently treat as the default `application` shape (masking \
26985 the drift with no process-log signal)"
26986 );
26987 }
26988
26989 #[test]
26990 fn helm_chart_type_library_pins_canonical_value() {
26991 // Pin the sibling closed-set arm of the Helm 3 chart-schema
26992 // `type` field's admitted set `{"application", "library"}` (see
26993 // https://helm.sh/docs/topics/charts/#chart-types). A drift on
26994 // this const's value (an `"Library"` / `"LIBRARY"` /
26995 // `"library-chart"` / `"lib"` typo, an accidental collapse onto
26996 // the sibling [`HELM_CHART_TYPE_APPLICATION`] shape) would land
26997 // a future per-Aplicacao library-chart emitter — the trajectory
26998 // item the [`HELM_CHART_TYPE_APPLICATION`] docstring names as
26999 // the natural next consumer of this const — outside the Helm
27000 // chart-schema's admitted set, with the same silent-collapse-
27001 // onto-`application`-default failure mode the peer
27002 // [`HELM_CHART_TYPE_APPLICATION`] pin's docstring enumerates on
27003 // the sibling closed-set arm (Helm's chart-schema parser
27004 // silently treats an unrecognized `type:` value as the default
27005 // `application` shape, so the misdeclared library chart installs
27006 // as an application chart instead of surfacing the schema
27007 // violation). Peer of
27008 // `helm_chart_type_application_pins_canonical_value` on the
27009 // sibling closed-set arm — the two pins together enshrine the
27010 // full closed set at the substrate-side canonical surface, and
27011 // the paired
27012 // `helm_chart_type_application_and_library_are_distinct` pin
27013 // (below) enforces the two arms never accidentally converge on
27014 // the same byte-shape.
27015 assert_eq!(HELM_CHART_TYPE_LIBRARY, "library");
27016 }
27017
27018 #[test]
27019 fn helm_chart_type_library_carries_lowercase_shape() {
27020 // Cross-axis invariant: the Helm 3 chart-schema `type` field
27021 // admits the closed set `{"application", "library"}` — every
27022 // admitted value is all-ASCII-lowercase throughout per the
27023 // upstream Helm project's per-enum-value naming convention.
27024 // Same all-ASCII-lowercase shape the peer
27025 // `helm_chart_type_application_carries_lowercase_shape` pin
27026 // enshrines on the sibling closed-set arm — the two pins
27027 // together enforce the shape-convention across the full
27028 // canonical-Helm-chart-schema-per-chart-kind-discriminator
27029 // closed set.
27030 //
27031 // Pinning the shape here means a future rebrand on the canonical
27032 // lift can't silently land a malformed per-chart-kind scalar
27033 // (uppercase `"LIBRARY"`, mixed-case `"Library"`, empty) that
27034 // the Helm chart-schema parser would silently treat as the
27035 // default `application` shape (masking the drift with no
27036 // process-log signal, and installing the misdeclared library
27037 // chart as an application chart instead of surfacing the
27038 // schema violation at chart-consumption time).
27039 let v = HELM_CHART_TYPE_LIBRARY;
27040 assert!(
27041 !v.is_empty(),
27042 "HELM_CHART_TYPE_LIBRARY {v:?} must be non-empty per the \
27043 Helm 3 chart-schema `type` field grammar"
27044 );
27045 assert!(
27046 v.chars().all(|c| c.is_ascii_lowercase()),
27047 "HELM_CHART_TYPE_LIBRARY {v:?} must be ASCII-lowercase \
27048 throughout per the Helm 3 chart-schema per-chart-kind \
27049 discriminator naming convention — no uppercase, mixed-case, \
27050 or whitespace bytes the Helm chart-schema parser would \
27051 silently treat as the default `application` shape (masking \
27052 the drift with no process-log signal)"
27053 );
27054 }
27055
27056 #[test]
27057 fn helm_chart_type_application_and_library_are_distinct() {
27058 // Structural distinctness invariant on the closed-set pair the
27059 // Helm 3 chart-schema `type` field admits (`{"application",
27060 // "library"}`). The two arms name distinct per-chart-kind
27061 // install shapes at the substrate-side Helm dispatch — an
27062 // `application`-typed chart installs into a namespace as a
27063 // workload while a `library`-typed chart is dependency-only
27064 // and Helm refuses to install it directly ("Error: library
27065 // charts cannot be installed") — so a future rebrand that
27066 // accidentally collapsed the two consts onto the same
27067 // byte-shape would land every consumer of one arm on the
27068 // sibling's install semantic by construction: a rendered
27069 // `lareira-<nome>` (application) chart that silently emitted
27070 // `type: library` would drop every per-Servico
27071 // `helm install` / `helm upgrade` release cycle with no field
27072 // naming the chart-kind-drift root cause, and (symmetrically)
27073 // a future per-Aplicacao library chart emitting
27074 // `type: application` would be install-able as a workload
27075 // when the substrate's install-shape dispatch expects it to
27076 // fail with the library-charts-cannot-be-installed diagnostic.
27077 // Pinning the distinctness here means a hypothetical future
27078 // edit that accidentally converges the two arms (a copy-paste
27079 // rebrand at one lift that stops at the peer const declaration,
27080 // a substrate-wide vocabulary shift that lands one arm without
27081 // its paired peer) surfaces at caixa-core build time rather
27082 // than as a chart-install-shape drift far from the source
27083 // commit. Same "closed-set arms are byte-distinct by
27084 // construction" discipline the peer
27085 // [`crate::CILIUM_AUTH_MODE_REQUIRED`] /
27086 // [`crate::CILIUM_AUTH_MODE_DISABLED`] pair carries on the
27087 // sibling two-arm Cilium `MutualAuthenticationMode` OpenAPI
27088 // enum closed set.
27089 assert_ne!(
27090 HELM_CHART_TYPE_APPLICATION, HELM_CHART_TYPE_LIBRARY,
27091 "HELM_CHART_TYPE_APPLICATION ({HELM_CHART_TYPE_APPLICATION:?}) and \
27092 HELM_CHART_TYPE_LIBRARY ({HELM_CHART_TYPE_LIBRARY:?}) must remain \
27093 byte-distinct — the two arms name the two install shapes of the \
27094 Helm 3 chart-schema `type` field's closed set {{\"application\", \
27095 \"library\"}} and every substrate-side consumer that dispatches \
27096 on the per-chart-kind axis relies on the two byte-shapes \
27097 distinguishing the workload-install-shape arm from the \
27098 dependency-only-install-shape arm"
27099 );
27100 }
27101
27102 #[test]
27103 fn helm_chart_key_api_version_pins_canonical_value() {
27104 // Pin the actual byte-string so a typo in this lift can't
27105 // silently rebrand the Helm 3 `Chart.yaml` top-level chart-
27106 // schema-apiVersion YAML axis-key the rendered `lareira-<nome>`
27107 // chart declares. The string is part of the substrate-side
27108 // contract with Helm's chart-schema parser at
27109 // `helm dependency build` / `helm lint` / `helm template` /
27110 // `helm install` time: the parser looks up the per-chart
27111 // chart-schema-apiVersion scalar under exactly this top-level
27112 // YAML key (Helm's chart-schema treats a missing `apiVersion:`
27113 // top-level scalar as an "apiVersion is required" hard error,
27114 // and Helm 3's chart-schema-version-router silently defaults
27115 // an unrecognized top-level apiVersion-carrier key to Helm 2
27116 // parsing shape). A drift on this const's value (an accidental
27117 // collapse onto `"ApiVersion"` / `"apiversion"` /
27118 // `"schemaVersion"` / the empty string) would silently reroute
27119 // the rendered `Chart.yaml` through the wrong chart-schema
27120 // parser at `helm dependency build` / `helm lint` /
27121 // `helm template` time. Peer to
27122 // `helm_chart_api_version_pins_canonical_value` on the sibling
27123 // axis-value canonical pin — completes the per-Chart.yaml
27124 // chart-schema-apiVersion axis's `(key, value)` canonical-pin
27125 // pair at the substrate.
27126 assert_eq!(HELM_CHART_KEY_API_VERSION, "apiVersion");
27127 }
27128
27129 #[test]
27130 fn helm_chart_key_api_version_matches_kube_key_api_version() {
27131 // Load-bearing byte-shape coincidence between the Helm 3
27132 // `Chart.yaml` top-level chart-schema-apiVersion YAML axis-key
27133 // ([`HELM_CHART_KEY_API_VERSION`]) and the K8s-CR top-level
27134 // per-CR schema-apiVersion YAML axis-key ([`KUBE_KEY_API_VERSION`])
27135 // — Helm inherits the K8s CR top-level shape verbatim (see
27136 // https://helm.sh/docs/topics/charts/#the-chartyaml-file), so
27137 // every consumer that navigates a Chart.yaml top-level mapping
27138 // by the schema-apiVersion key and every consumer that
27139 // navigates a K8s CR top-level mapping by the schema-apiVersion
27140 // key both read the byte-identical `"apiVersion"` key. The two
27141 // axes are structurally-independent schema surfaces (the Helm 3
27142 // chart-schema top-level shape vs. the K8s apiserver-side CR
27143 // top-level shape), so the substrate carries two distinct
27144 // `pub const` symbols; this pin makes the byte-shape
27145 // coincidence load-bearing rather than accidental so a future
27146 // K8s-side rebrand at [`KUBE_KEY_API_VERSION`] (or a Helm-side
27147 // rebrand at [`HELM_CHART_KEY_API_VERSION`]) that dropped the
27148 // byte-identity would fail the pin at substrate-build time
27149 // rather than as a silent Helm-chart-schema-parser rejection
27150 // at `helm lint` / `helm template` time far from the drift
27151 // site. Complementary to the sibling
27152 // [`helm_chart_key_type_is_byte_distinct_from_kube_key_kind`]
27153 // pin — that peer asserts the per-chart-kind discriminator key
27154 // pair is byte-distinct across the two schema surfaces (the
27155 // Chart.yaml `type:` axis vs. the K8s CR `kind:` axis), and
27156 // this pin asserts the per-schema-apiVersion axis-key pair is
27157 // byte-identical across the two schema surfaces; together the
27158 // two pins cover the full independence-map of the top-level
27159 // discriminator axes at the two schema surfaces.
27160 assert_eq!(
27161 HELM_CHART_KEY_API_VERSION, KUBE_KEY_API_VERSION,
27162 "HELM_CHART_KEY_API_VERSION ({HELM_CHART_KEY_API_VERSION:?}) \
27163 must remain byte-identical to KUBE_KEY_API_VERSION \
27164 ({KUBE_KEY_API_VERSION:?}) — Helm 3 inherits the K8s CR \
27165 top-level schema-apiVersion YAML-axis-key byte-shape \
27166 verbatim, and every downstream consumer that navigates a \
27167 `Chart.yaml` / K8s CR top-level mapping by the schema-\
27168 apiVersion key reads the byte-identical `\"apiVersion\"` \
27169 key; a drift on either side silently reroutes the \
27170 consumer through a schema-parser rejection far from the \
27171 drift site"
27172 );
27173 }
27174
27175 #[test]
27176 fn helm_chart_key_type_pins_canonical_value() {
27177 // Pin the actual byte-string so a typo in this lift can't silently
27178 // rebrand the Helm 3 `Chart.yaml` top-level per-chart-kind
27179 // discriminator YAML axis-key the rendered `lareira-<nome>` chart
27180 // declares. The string is part of the substrate-side contract with
27181 // Helm's chart-schema parser at `helm dependency build` /
27182 // `helm lint` / `helm template` / `helm install` time: the parser
27183 // looks up the per-chart-kind discriminator scalar under exactly
27184 // this top-level YAML key, and a drift on this const's value
27185 // (an accidental collapse onto `"Type"` / `"chartType"` /
27186 // `"kind"`, or the empty string) would silently reroute the
27187 // rendered `Chart.yaml` through the schema-shape-defaulting arm
27188 // of Helm's parser (unknown top-level keys default the
27189 // per-chart-kind axis to `application` with no process-log
27190 // signal). Peer to
27191 // `helm_chart_type_application_pins_canonical_value` /
27192 // `helm_chart_type_library_pins_canonical_value` on the sibling
27193 // axis-value canonical pin pair — completes the per-Chart.yaml
27194 // per-chart-kind discriminator axis's `(key, value-set)`
27195 // canonical-pin trio at the substrate.
27196 assert_eq!(HELM_CHART_KEY_TYPE, "type");
27197 }
27198
27199 #[test]
27200 fn helm_chart_key_type_is_byte_distinct_from_kube_key_kind() {
27201 // Structural distinctness invariant: the Helm 3 `Chart.yaml`
27202 // top-level per-chart-kind YAML axis-key
27203 // ([`HELM_CHART_KEY_TYPE`]) and the K8s CR top-level per-CRD
27204 // kind-discriminator YAML axis-key ([`KUBE_KEY_KIND`]) name
27205 // two structurally-independent axes at two structurally-
27206 // independent schema surfaces — the Helm-side chart-schema
27207 // top-level shape and the K8s-apiserver-side CR top-level
27208 // shape — and every substrate-side renderer that emits or
27209 // navigates a `Chart.yaml` vs. a K8s CR YAML relies on the
27210 // two byte-shapes distinguishing the two schema-surfaces at
27211 // its top-level mapping-key resolution. A hypothetical future
27212 // rebrand that accidentally aliased [`HELM_CHART_KEY_TYPE`]
27213 // at [`KUBE_KEY_KIND`]'s canonical would collapse the
27214 // per-Chart.yaml per-chart-kind discriminator axis onto the
27215 // K8s-CR per-CRD kind-discriminator axis at every consumer,
27216 // and Helm's chart-schema parser would silently drop the
27217 // rebranded key (top-level `kind:` is not part of the Helm 3
27218 // chart-schema's admitted set — the parser silently ignores
27219 // it, defaulting the per-chart-kind axis to `application`
27220 // with no process-log signal). Same "byte-distinct axis-keys
27221 // at structurally-independent schema surfaces" discipline the
27222 // peer [`CILIUM_KEY_PATH`] / [`GATEWAY_API_KEY_PATH`]
27223 // (ef6114f / 9f45aa4) pair carries on the sibling Cilium-CRD-
27224 // vs.-Gateway-API-per-HTTPRouteMatch path-matcher axis
27225 // independence — extends the discipline from the two K8s-CR-
27226 // side path-matcher schemas onto the Helm-side vs. K8s-side
27227 // top-level discriminator-key axis pair.
27228 assert_ne!(
27229 HELM_CHART_KEY_TYPE, KUBE_KEY_KIND,
27230 "HELM_CHART_KEY_TYPE ({HELM_CHART_KEY_TYPE:?}) and \
27231 KUBE_KEY_KIND ({KUBE_KEY_KIND:?}) name the top-level \
27232 discriminator keys of two structurally-independent schema \
27233 surfaces (the Helm 3 chart-schema and the K8s apiserver-side \
27234 CR schema) and must remain byte-distinct — a collapse \
27235 silently reroutes the per-Chart.yaml per-chart-kind axis \
27236 through the K8s-CR-shape-defaulting arm of Helm's parser"
27237 );
27238 }
27239
27240 #[test]
27241 fn helm_chart_key_app_version_pins_canonical_value() {
27242 // Pin the actual byte-string so a typo in this lift can't silently
27243 // rebrand the Helm 3 `Chart.yaml` top-level per-chart-app-version
27244 // YAML axis-key the rendered `lareira-<nome>` chart declares.
27245 // The string is part of the substrate-side contract with Helm's
27246 // chart-schema parser + every downstream chart-consumer that
27247 // routes the underlying-application-version display onto the
27248 // rendered chart's per-app-version field (Artifact Hub's per-
27249 // chart-search index, `helm search` / `helm show chart` operator
27250 // surfaces, the OCI-artifact-labels emitter every chart-publish
27251 // pipeline exports). A drift on this const's value (`"AppVersion"`
27252 // / `"applicationVersion"` / `"appversion"` / the empty string)
27253 // would silently drop the underlying-application-version field
27254 // from the parsed chart-metadata shape at every downstream
27255 // consumer, with no process-log signal at the substrate-side
27256 // emitter site. The `appVersion:` camelCase byte-shape is the
27257 // load-bearing Helm chart-schema per-app-version YAML axis-key
27258 // grammar the upstream Helm project pins. Peer to
27259 // `helm_chart_key_type_pins_canonical_value` on the sibling
27260 // per-Chart.yaml top-level YAML axis-key canonical pin surface —
27261 // completes the per-Chart.yaml top-level YAML axis-key
27262 // canonical-pin trio at the substrate for the three serde-
27263 // rename-literal-only axes on [`caixa_helm::ChartYaml`] (the
27264 // third top-level axis-key `apiVersion` lands under the peer
27265 // [`HELM_CHART_KEY_API_VERSION`] pin whose byte-shape coincides
27266 // with [`KUBE_KEY_API_VERSION`] by Helm's design decision to
27267 // inherit the K8s CR top-level shape verbatim — the paired
27268 // `helm_chart_key_api_version_matches_kube_key_api_version`
27269 // pin makes the coincidence load-bearing rather than
27270 // accidental).
27271 assert_eq!(HELM_CHART_KEY_APP_VERSION, "appVersion");
27272 }
27273
27274 #[test]
27275 fn helm_chart_key_app_version_is_byte_distinct_from_helm_chart_key_version() {
27276 // Structural distinctness invariant on the per-Chart.yaml top-
27277 // level version-axis-key pair. The Helm 3 chart-schema pins two
27278 // structurally-distinct version YAML axis-keys at the top-level
27279 // of every `Chart.yaml`:
27280 //
27281 // - `version:` — the chart's own SemVer (incremented per
27282 // release of the chart itself)
27283 // - `appVersion:` — the underlying application's version
27284 // (the version the containerized workload the chart
27285 // installs advertises)
27286 //
27287 // At the caixa-helm renderer both YAML axes today draw from the
27288 // caixa's `:versao` at `build_chart_yaml` (a caixa's per-caixa
27289 // BLAKE3-closure identity binds chart + wasm-binary at exactly
27290 // one release axis), but the Helm 3 chart-schema pins the two
27291 // top-level YAML keys distinctly regardless — every downstream
27292 // Helm-consumer (Artifact Hub's per-chart index, `helm search` /
27293 // `helm show chart` surfaces) routes the two version-axis
27294 // scalars onto distinct display fields. A hypothetical future
27295 // rebrand that accidentally aliased [`HELM_CHART_KEY_APP_VERSION`]
27296 // at the sibling per-Chart.yaml top-level `version:` key
27297 // (`"version"`) would collapse the two YAML axes at the
27298 // renderer's ChartYaml serialization, and Helm's chart-schema
27299 // parser would silently read the app-version scalar under the
27300 // chart-own-SemVer axis (the last `version:` key wins in
27301 // `serde_yaml`'s emitted mapping under this drift), overwriting
27302 // the chart's own SemVer at every downstream chart-consumer.
27303 // Same "byte-distinct version-axis keys at the same schema
27304 // surface" discipline the peer [`FLEET_PROGRAMS_KEY_VERSAO`] /
27305 // [`FLEET_PROGRAMS_KEY_NAME`] pair carries on the sibling
27306 // per-fleet-programs-entry axis pair — extends the discipline
27307 // from the per-fleet-programs-entry key-pair onto the per-
27308 // Chart.yaml top-level version-axis-key pair.
27309 assert_ne!(
27310 HELM_CHART_KEY_APP_VERSION, "version",
27311 "HELM_CHART_KEY_APP_VERSION ({HELM_CHART_KEY_APP_VERSION:?}) \
27312 must remain byte-distinct from the sibling per-Chart.yaml \
27313 top-level chart-own-SemVer `version:` key — a collapse \
27314 silently overwrites the chart's own SemVer at every \
27315 downstream Helm chart-consumer"
27316 );
27317 }
27318
27319 #[test]
27320 fn helm_chart_key_dependencies_pins_canonical_value() {
27321 // Pin the actual byte-string so a typo in this lift can't silently
27322 // rebrand the Helm 3 `Chart.yaml` top-level per-chart dependency-
27323 // list YAML axis-key the rendered `lareira-<nome>` chart declares.
27324 // The string is part of the substrate-side contract with Helm's
27325 // chart-schema parser — every rendered chart's `dependencies:`
27326 // list-container mounts under this exact byte-shape, and Helm's
27327 // per-dep resolver at `helm dependency build` / `helm dependency
27328 // update` time consumes the per-entry sub-mapping tetrad only if
27329 // the top-level list-container key matches this canonical shape.
27330 // A drift on this const's value (`"Dependencies"` / `"deps"` /
27331 // `"chartDependencies"` / `"depends"` / the empty string) would
27332 // silently drop the entire per-chart dep list from the parsed
27333 // chart-metadata shape, and every rendered `lareira-<nome>`
27334 // chart's install would fail with `template: no template ...
27335 // associated with template ...` far from the drift site with
27336 // no field naming the top-level-list-key-drift root cause. Peer
27337 // to [`helm_chart_key_type_pins_canonical_value`] /
27338 // [`helm_chart_key_app_version_pins_canonical_value`] /
27339 // [`helm_chart_key_api_version_pins_canonical_value`] on the
27340 // sibling per-Chart.yaml top-level YAML axis-key canonical-pin
27341 // surface — extends the per-Chart.yaml top-level YAML axis-key
27342 // canonical-pin trio those pins established onto the fourth
27343 // top-level axis-key at the substrate, the parent list-container
27344 // whose already-lifted per-`dependencies[]`-entry sub-mapping
27345 // tetrad ([`HELM_CHART_DEPENDENCY_KEY_NAME`] /
27346 // [`HELM_CHART_DEPENDENCY_KEY_VERSION`] /
27347 // [`HELM_CHART_DEPENDENCY_KEY_REPOSITORY`] /
27348 // [`HELM_CHART_DEPENDENCY_KEY_ALIAS`]) mounts one level down.
27349 assert_eq!(HELM_CHART_KEY_DEPENDENCIES, "dependencies");
27350 }
27351
27352 #[test]
27353 fn helm_chart_key_dependencies_is_byte_distinct_from_per_dep_sub_mapping_tetrad() {
27354 // Structural distinctness invariant on the per-Chart.yaml
27355 // `dependencies:` parent list-container axis-key vs. the four
27356 // already-lifted per-entry sub-mapping keys mounted one level
27357 // down. The parent+children pair spans two schema-nested YAML
27358 // levels — the top-level `dependencies:` list-container and
27359 // the per-entry sub-mapping `{name, version, repository,
27360 // alias}` — and Helm's chart-schema parser navigates them as
27361 // two structurally-independent axes: a collapse of the parent
27362 // axis-key onto any child (e.g. an accidental future rebrand
27363 // that renamed the [`HELM_CHART_KEY_DEPENDENCIES`] value to
27364 // `"name"` or `"version"`) would either drop the entire per-
27365 // chart dep list at the top-level parse (the child scalar
27366 // silently masks the parent list-container the schema expects)
27367 // or read the top-level list under a scalar-shaped axis-key
27368 // and reject the chart at `helm lint` with a shape mismatch
27369 // far from the drift site. Same "parent list-container
27370 // axis-key must remain byte-distinct from every child sub-
27371 // mapping axis-key" discipline the peer
27372 // [`SUPERVISOR_KEY_CHILDREN`] parent axis-key already carries
27373 // against the sibling [`SUPERVISOR_CHILD_KEY_CAIXA`] /
27374 // [`SUPERVISOR_CHILD_KEY_VERSAO`] / [`SUPERVISOR_CHILD_KEY_RESTART`]
27375 // per-entry sub-mapping triad on the M2 typed
27376 // `:supervisor :children` surface — extends the discipline onto
27377 // the Helm 3 `Chart.yaml` per-chart-dependency-list surface.
27378 for child in [
27379 HELM_CHART_DEPENDENCY_KEY_NAME,
27380 HELM_CHART_DEPENDENCY_KEY_VERSION,
27381 HELM_CHART_DEPENDENCY_KEY_REPOSITORY,
27382 HELM_CHART_DEPENDENCY_KEY_ALIAS,
27383 ] {
27384 assert_ne!(
27385 HELM_CHART_KEY_DEPENDENCIES, child,
27386 "HELM_CHART_KEY_DEPENDENCIES \
27387 ({HELM_CHART_KEY_DEPENDENCIES:?}) must remain \
27388 byte-distinct from every per-`dependencies[]`-entry \
27389 sub-mapping key ({child:?}) — a collapse silently \
27390 orphans the parent list-container at `helm lint` / \
27391 `helm dependency build` time"
27392 );
27393 }
27394 }
27395
27396 #[test]
27397 fn helm_chart_dependency_key_tetrad_pins_canonical_values() {
27398 // Byte-string pin on the per-`dependencies[]`-entry sub-mapping
27399 // YAML axis-key tetrad the Helm 3 chart-schema pins for every
27400 // per-dep entry the substrate emits under the top-level
27401 // `dependencies:` list at every rendered `lareira-<nome>`
27402 // Chart.yaml. The four axis-keys name the four load-bearing
27403 // per-dep sub-mapping fields Helm's per-dep resolver consumes
27404 // at `helm dependency build` / `helm dependency update` time:
27405 // `name` (the Helm-registry chart name), `version` (the SemVer-
27406 // range constraint), `repository` (the registry URL to fetch
27407 // from), and `alias` (the per-dep values wrap-key override).
27408 // A drift on any const's value (a typo on this lift, a case
27409 // flip to `"Name"` / `"Version"` / `"Repository"` / `"Alias"`,
27410 // an accidental collapse onto a sibling axis-key) would
27411 // silently rebrand the wire key at the `caixa_helm::ChartYaml`
27412 // emitter site — Helm's chart-schema parser silently drops
27413 // the drifted per-dep sub-mapping field, and the per-dep
27414 // resolver falls back to the parsed-shape defaults
27415 // (`""` / wildcard `*` / "no repository defined") at
27416 // `helm dependency build` time far from the drift site. Peer
27417 // to [`supervisor_child_key_tetrad_pins_canonical_values`] on
27418 // the sibling per-`:children` sub-mapping tetrad (ef912df) and
27419 // [`entrada_key_tetrad_pins_canonical_values`] on the sibling
27420 // per-`:entrada` sub-mapping tetrad (a3d6162).
27421 assert_eq!(HELM_CHART_DEPENDENCY_KEY_NAME, "name");
27422 assert_eq!(HELM_CHART_DEPENDENCY_KEY_VERSION, "version");
27423 assert_eq!(HELM_CHART_DEPENDENCY_KEY_REPOSITORY, "repository");
27424 assert_eq!(HELM_CHART_DEPENDENCY_KEY_ALIAS, "alias");
27425 }
27426
27427 #[test]
27428 fn helm_chart_dependency_key_name_matches_kube_key_name() {
27429 // Load-bearing byte-shape coincidence between the Helm 3
27430 // Chart.yaml per-`dependencies[]`-entry sub-mapping name key
27431 // ([`HELM_CHART_DEPENDENCY_KEY_NAME`]) and the K8s CR
27432 // per-`metadata` sub-mapping name key ([`KUBE_KEY_NAME`]) —
27433 // Helm inherits the K8s CR body-key vocabulary at every schema
27434 // surface it consumes (chart-metadata top-level, per-CR
27435 // install-payload, per-dep dependency-list). The two axes are
27436 // structurally-independent schema surfaces (the Helm 3
27437 // chart-schema per-dep entry vs. the K8s apiserver-side CR
27438 // metadata block) whose byte-shapes happen to coincide today;
27439 // this pin makes the byte-shape coincidence load-bearing
27440 // rather than accidental so a future K8s-side rebrand at
27441 // [`KUBE_KEY_NAME`] (or a Helm-side rebrand at
27442 // [`HELM_CHART_DEPENDENCY_KEY_NAME`]) that dropped the
27443 // byte-identity would fail the pin at substrate-build time
27444 // rather than as a silent Helm-per-dep-resolver drop at
27445 // `helm dependency build` time far from the drift site. Same
27446 // discipline as the peer
27447 // [`helm_chart_key_api_version_matches_kube_key_api_version`]
27448 // pin on the sibling top-level chart-schema-apiVersion axis
27449 // (cc44e4b) — extends the axis-key byte-identity coincidence
27450 // discipline from the per-Chart.yaml top-level shape onto the
27451 // per-`dependencies[]`-entry sub-mapping shape.
27452 assert_eq!(
27453 HELM_CHART_DEPENDENCY_KEY_NAME, KUBE_KEY_NAME,
27454 "HELM_CHART_DEPENDENCY_KEY_NAME ({HELM_CHART_DEPENDENCY_KEY_NAME:?}) \
27455 must remain byte-identical to KUBE_KEY_NAME ({KUBE_KEY_NAME:?}) — \
27456 Helm 3 inherits the K8s CR body-key vocabulary at every schema \
27457 surface, and every downstream consumer that navigates a per-dep \
27458 sub-mapping / a K8s CR metadata block by the `name` key reads the \
27459 byte-identical `\"name\"` key; a drift on either side silently \
27460 reroutes the consumer through a schema-parser drop far from the \
27461 drift site"
27462 );
27463 }
27464
27465 #[test]
27466 fn helm_chart_readme_filename_pins_canonical_value() {
27467 // Pin the actual byte-string so a typo on the canonical lift
27468 // can't silently rebrand the third leg of the per-`lareira-<nome>`
27469 // chart-directory `{Chart.yaml, values.yaml, README.md}`
27470 // canonical-per-chart-directory-filename axis triple. Peer to
27471 // the sibling
27472 // [`HELM_CHART_YAML_FILENAME`] / [`HELM_VALUES_YAML_FILENAME`]
27473 // canonical filename axes — the two schema-load-bearing halves
27474 // of the triple the sibling
27475 // [`HELM_VALUES_YAML_FILENAME`] docstring's closing paragraph
27476 // explicitly names as the pair that needed the third-leg
27477 // (`README.md`) filename half to close the discipline across
27478 // every `ChartFile` the [`caixa_helm::render_chart_for_servico`]
27479 // emitter's `ChartDir::files` vec carries. A drifted per-chart
27480 // readme filename value would surface downstream as GitHub /
27481 // Artifact Hub / any per-chart README-surfacing UI silently
27482 // falling back to "no README available" for the rendered
27483 // `lareira-<nome>` chart — the chart lists with no per-chart
27484 // elevator pitch or install instructions far from the drift
27485 // commit's source, with no field naming the readme-filename-
27486 // drift root cause. Same pin discipline as the peer
27487 // canonical-Helm-per-chart-directory-filename axes.
27488 assert_eq!(HELM_CHART_README_FILENAME, "README.md");
27489 }
27490
27491 #[test]
27492 fn helm_chart_readme_filename_carries_readme_dot_md_shape() {
27493 // Cross-axis invariant: the per-`lareira-<nome>`-chart-directory
27494 // human-facing readme filename carries the `.md` Markdown
27495 // extension the [`caixa_helm::build_readme`] emitter's Markdown-
27496 // shaped body targets — a drift to `.txt` / `.rst` /
27497 // extensionless / a per-fork rename would silently reroute the
27498 // rendered readme through a downstream tool that reads by
27499 // extension for its Markdown renderer (GitHub's per-repo README
27500 // surfacer, Artifact Hub's per-chart README surfacer, every
27501 // per-chart-directory `find . -name README.md` navigator any
27502 // downstream tooling might use). Peer to the sibling
27503 // [`HELM_CHART_YAML_FILENAME`] / [`HELM_VALUES_YAML_FILENAME`]
27504 // schema-load-bearing filename halves — the two YAML halves
27505 // carry the `.yaml` extension per Helm's per-chart-schema
27506 // convention; the readme half carries the `.md` extension per
27507 // the substrate's per-chart human-facing convention. Distinct
27508 // per-half schema conventions do not collapse on the shared
27509 // `<name>.<ext>` shape gate.
27510 let v = HELM_CHART_README_FILENAME;
27511 assert!(
27512 !v.is_empty(),
27513 "HELM_CHART_README_FILENAME {v:?} must be non-empty per the \
27514 per-`lareira-<nome>`-chart-directory readme-file axis"
27515 );
27516 assert!(
27517 v.ends_with(".md"),
27518 "HELM_CHART_README_FILENAME {v:?} must carry the `.md` \
27519 Markdown extension per the substrate's per-chart human-\
27520 facing readme convention — a drifted extension (`.txt` / \
27521 `.rst` / extensionless) would silently reroute downstream \
27522 tooling's Markdown renderer (GitHub's per-repo README \
27523 surfacer, Artifact Hub's per-chart README surfacer) to a \
27524 non-Markdown fallback path"
27525 );
27526 }
27527
27528 // ── lareira-<nome> chart-name prefix lift ──────────────────────
27529 //
27530 // The lift pins the substrate-wide `lareira-` chart-name prefix
27531 // as the single source of truth every per-Servico renderer
27532 // (caixa-helm, caixa-flux, caixa-tatara) reaches for, peer to the
27533 // [`DEFAULT_NAMESPACE`] (a085b26) lift on the canonical-namespace
27534 // axis. Pinning the prefix value, the helper's
27535 // construction-shape, and the DNS-1123-label round-trip for the
27536 // canonical-fixture input forms the structural floor every future
27537 // renderer consumer inherits by construction.
27538
27539 #[test]
27540 fn lareira_chart_name_prefix_pins_canonical_value() {
27541 // Pin the actual string value so a typo on the canonical lift
27542 // can't silently rebrand the substrate's per-Servico Helm chart
27543 // namespace. The string is part of the contract with the OCI
27544 // chart-publishing pipeline (`oci://<registry>/lareira-<nome>`),
27545 // the per-cluster HelmRelease `chart:` field (which Flux
27546 // resolves through the OCI ref), and the historical
27547 // `pleme-io/helmworks/charts/lareira-<name>/` source tree
27548 // layout (caixa-helm/src/lib.rs:7); changing it is a
27549 // coordinated multi-repo migration, not an incidental edit.
27550 // Peer to `default_namespace_pins_canonical_value` on the
27551 // canonical-string-value-pin axis for the
27552 // `DEFAULT_NAMESPACE` constant.
27553 assert_eq!(LAREIRA_CHART_NAME_PREFIX, "lareira-");
27554 }
27555
27556 #[test]
27557 fn lareira_chart_name_composes_prefix_and_nome() {
27558 // Pin the helper's construction shape — the chart name is the
27559 // prefix concatenated with the caixa's `:nome` verbatim, with
27560 // no intermediate hyphen, no path separator, no trimming. Pin
27561 // the canonical hello-rio fixture (the in-tree
27562 // `caixa-helm` test fixture at caixa-helm/src/lib.rs:431
27563 // already asserts `dir.name == "lareira-hello-rio"`, which
27564 // this helper now derives) and a peer fixture
27565 // (`checkout-aplicacao` member) to sweep the typical author
27566 // surface.
27567 assert_eq!(lareira_chart_name("hello-rio"), "lareira-hello-rio");
27568 assert_eq!(lareira_chart_name("cart"), "lareira-cart");
27569 assert_eq!(lareira_chart_name("worker"), "lareira-worker");
27570 }
27571
27572 #[test]
27573 fn lareira_chart_name_starts_with_prefix() {
27574 // Cross-axis invariant: every output of the helper begins with
27575 // the lifted prefix verbatim — a future refactor that
27576 // accidentally introduced a different prefix-application
27577 // shape (e.g. `format!("{nome}-lareira")` transposition, or a
27578 // `to_uppercase()` case fold) would surface here. The
27579 // structural pin holds for the empty `:nome` shape too
27580 // (a value `validate_nome` rejects upstream, but the helper
27581 // itself imposes no shape on the input).
27582 for nome in ["hello-rio", "cart", "worker", "a", ""] {
27583 let chart = lareira_chart_name(nome);
27584 assert!(
27585 chart.starts_with(LAREIRA_CHART_NAME_PREFIX),
27586 "lareira_chart_name({nome:?}) = {chart:?} must start with the lifted prefix \
27587 {LAREIRA_CHART_NAME_PREFIX:?}"
27588 );
27589 }
27590 }
27591
27592 #[test]
27593 fn lareira_chart_name_round_trips_through_dns_1123_for_validated_nome() {
27594 // Cross-axis invariant: every `:nome` past
27595 // [`Caixa::validate_nome`] (6c992f8) is a valid DNS-1123 label,
27596 // and the prepended `lareira-` segment is itself a valid
27597 // DNS-1123 label prefix (lowercase ASCII + hyphen with a
27598 // terminating-hyphen continuation). The composition therefore
27599 // round-trips through [`is_dns_1123_label`] for every
27600 // `:nome` whose joint length with the prefix stays ≤ 63 bytes
27601 // (the DNS-1123 label cap). The canonical author surface sits
27602 // far below that cap (the in-tree fixtures range from
27603 // `"a"` = 9-byte chart name to `"checkout"` = 16 bytes, with
27604 // the cap admitting up to 55-byte `:nome` values). Pin the
27605 // round-trip for the canonical-fixture set so a future renderer
27606 // that lands the helper's output verbatim as a K8s
27607 // `metadata.name` (caixa-helm's `ChartDir.name`,
27608 // caixa-flux's HelmRelease `chart:` field, caixa-tatara's
27609 // `release_name`) inherits the apiserver-valid floor by
27610 // construction.
27611 for nome in ["hello-rio", "cart", "worker", "checkout", "a"] {
27612 let chart = lareira_chart_name(nome);
27613 assert!(
27614 is_dns_1123_label(&chart).is_ok(),
27615 "lareira_chart_name({nome:?}) = {chart:?} must be a valid DNS-1123 label"
27616 );
27617 }
27618 }
27619
27620 #[test]
27621 fn lareira_chart_name_prefix_is_a_valid_dns_1123_segment_continuation() {
27622 // The lifted prefix is one substring of the rendered chart
27623 // name; pin its grammar so a future rebrand can't land a
27624 // value that would invalidate the joint DNS-1123 label
27625 // structurally. The prefix must:
27626 // - be lowercase ASCII alphanumeric + hyphen (the DNS-1123
27627 // accepted set), so its bytes don't widen the joint
27628 // accepted set;
27629 // - end with a hyphen (so the concatenation slot doesn't
27630 // accidentally merge with the leading character of the
27631 // `:nome` it precedes).
27632 assert!(
27633 LAREIRA_CHART_NAME_PREFIX
27634 .bytes()
27635 .all(|b| b.is_ascii_lowercase() || b.is_ascii_digit() || b == b'-'),
27636 "LAREIRA_CHART_NAME_PREFIX {LAREIRA_CHART_NAME_PREFIX:?} must use only DNS-1123-label \
27637 bytes (lowercase ASCII alphanumeric + hyphen)"
27638 );
27639 assert!(
27640 LAREIRA_CHART_NAME_PREFIX.ends_with('-'),
27641 "LAREIRA_CHART_NAME_PREFIX {LAREIRA_CHART_NAME_PREFIX:?} must end with `-` so \
27642 concatenation with the caixa's `:nome` produces a hyphenated joint label"
27643 );
27644 }
27645
27646 // ── is_lareira_chart_name_shape — joint-length budget on `:nome` ─────
27647 //
27648 // The canonical [`lareira_chart_name`] helper's own doc comment
27649 // (f7320d7) explicitly defers: "the M4 admission webhook will pin
27650 // the joint-length invariant when it lands". These tests land it
27651 // at the manifest-validate layer instead — the predicate consults
27652 // [`lareira_chart_name`] + [`is_dns_1123_label`] (no third primitive)
27653 // so a future rebrand of either axis re-derives the budget
27654 // mechanically and the test suite re-pins through the same lifts.
27655
27656 #[test]
27657 fn lareira_chart_name_nome_max_len_pins_arithmetic() {
27658 // Pin the arithmetic so a future shift in either input axis
27659 // surfaces here. The const is mechanically derived from
27660 // [`DNS_1123_LABEL_MAX_LEN`] (63 — the K8s apiserver cap every
27661 // chart-name-derived `metadata.name` inherits) minus
27662 // [`LAREIRA_CHART_NAME_PREFIX`].len() (8 — the canonical
27663 // chart-name prefix the lift f7320d7 made structural). The
27664 // landing value: 55 bytes the caixa's `:nome` may itself
27665 // occupy under the joint chart-name cap.
27666 assert_eq!(LAREIRA_CHART_NAME_NOME_MAX_LEN, 55);
27667 assert_eq!(
27668 LAREIRA_CHART_NAME_NOME_MAX_LEN,
27669 DNS_1123_LABEL_MAX_LEN - LAREIRA_CHART_NAME_PREFIX.len()
27670 );
27671 }
27672
27673 #[test]
27674 fn is_lareira_chart_name_shape_accepts_canonical_fixtures() {
27675 // Positive control: every in-tree fixture `:nome` (caixa-helm,
27676 // caixa-flux, caixa-mesh, caixa-tatara tests, the
27677 // checkout-aplicacao example) sits far below the cap. The
27678 // predicate must not regress this baseline shape.
27679 for nome in [
27680 "hello-rio",
27681 "cart",
27682 "worker",
27683 "checkout",
27684 "a",
27685 "akeyless-attest",
27686 ] {
27687 is_lareira_chart_name_shape(nome).unwrap_or_else(|e| {
27688 panic!("canonical :nome {nome:?} must pass chart-name budget, got {e:?}")
27689 });
27690 }
27691 }
27692
27693 #[test]
27694 fn is_lareira_chart_name_shape_accepts_nome_at_budget() {
27695 // Boundary-accepting case at the 55-byte cap — the joint
27696 // chart name is exactly 63 bytes, the DNS-1123 label cap.
27697 let at_cap = "a".repeat(LAREIRA_CHART_NAME_NOME_MAX_LEN);
27698 assert_eq!(at_cap.len(), LAREIRA_CHART_NAME_NOME_MAX_LEN);
27699 is_lareira_chart_name_shape(&at_cap).unwrap();
27700 assert_eq!(lareira_chart_name(&at_cap).len(), DNS_1123_LABEL_MAX_LEN);
27701 }
27702
27703 #[test]
27704 fn is_lareira_chart_name_shape_rejects_nome_one_over_budget() {
27705 // Fail-before-pass-after pin: 56 bytes is the smallest `:nome`
27706 // length that overflows the joint chart-name cap. The inner
27707 // [`is_dns_1123_label`] check accepts it (56 ≤ 63), so prior
27708 // to this gate it silently passed `Caixa::validate_nome` and
27709 // surfaced as a `helm lint` / apiserver rejection on the
27710 // rendered chart name far from the source caixa.lisp.
27711 let over = "a".repeat(LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
27712 let err = is_lareira_chart_name_shape(&over).unwrap_err();
27713 assert!(
27714 err.contains("63") && err.contains("64") && err.contains("55"),
27715 "diagnostic must name the DNS-1123 cap (63), the actual chart-name length (64), \
27716 and the per-`:nome` budget (55), got {err:?}"
27717 );
27718 assert!(
27719 err.contains("lareira-"),
27720 "diagnostic must name the canonical prefix verbatim, got {err:?}"
27721 );
27722 }
27723
27724 #[test]
27725 fn is_lareira_chart_name_shape_diagnostic_carries_offending_chart_name() {
27726 // The rendered chart name appears verbatim in the diagnostic
27727 // so the author sees exactly the string the apiserver would
27728 // have rejected — no re-derivation required to grep the source.
27729 let over = "x".repeat(LAREIRA_CHART_NAME_NOME_MAX_LEN + 5);
27730 let err = is_lareira_chart_name_shape(&over).unwrap_err();
27731 let expected_chart = lareira_chart_name(&over);
27732 assert!(
27733 err.contains(&expected_chart),
27734 "diagnostic must carry the rendered chart name {expected_chart:?} verbatim, \
27735 got {err:?}"
27736 );
27737 }
27738
27739 #[test]
27740 fn is_lareira_chart_name_shape_composes_through_canonical_helper() {
27741 // Cross-axis invariant: the predicate is defined exactly as
27742 // `is_dns_1123_label(lareira_chart_name(nome))` for the length
27743 // arm — no inline `format!("lareira-{nome}")` shape duplicating
27744 // the canonical lift. Pinning this composition closes the
27745 // drift footgun where a future predicate refactor re-inlines
27746 // the prefix-and-`:nome` concatenation and diverges from the
27747 // canonical helper. Sweep across the boundary so both sides
27748 // (accept + reject) consult the same helper.
27749 for delta in 0..=2usize {
27750 let nome = "z".repeat(LAREIRA_CHART_NAME_NOME_MAX_LEN.saturating_sub(delta));
27751 let predicate_ok = is_lareira_chart_name_shape(&nome).is_ok();
27752 let canonical_ok = is_dns_1123_label(&lareira_chart_name(&nome)).is_ok();
27753 assert_eq!(
27754 predicate_ok,
27755 canonical_ok,
27756 "predicate / canonical-composition divergence for :nome of len {} \
27757 (predicate_ok = {predicate_ok}, canonical_ok = {canonical_ok})",
27758 nome.len()
27759 );
27760 }
27761 }
27762
27763 // ── OCI chart-ref composer — `oci://<registry>/lareira-<nome>` ───────
27764 //
27765 // Peer to the `lareira_chart_name` composer above on the sibling
27766 // OCI-artifact-reference axis. Until this lift landed the
27767 // `caixa-tatara`'s `derive_chart_ref` carried an inline
27768 // `format!("oci://{registry}/{chart}")` — a 2-axis composition
27769 // (the `oci://` scheme prefix + the `lareira-<nome>` chart name)
27770 // whose byte-shape had no compile-time link to the historical doc
27771 // comments across `caixa-core`, `caixa-flux`, `caixa-helm`, and
27772 // `caixa-tatara` promising the same shape. Pin the const, the
27773 // composition equation, and the byte-shape against the prior
27774 // inline `format!` so a future composer-internal drift fires at
27775 // test time.
27776
27777 #[test]
27778 fn oci_scheme_prefix_pins_canonical_value() {
27779 // Pin the actual string value so a typo on the canonical lift
27780 // can't silently rebrand the substrate's OCI-artifact-reference
27781 // scheme. The string is part of the contract with the Helm 3
27782 // OCI storage protocol (`helm push chart.tgz oci://…`,
27783 // `helm registry login <registry>`, `helm install release
27784 // oci://…`) and the FluxCD `HelmRepository` `type: oci` source
27785 // (Flux source-controller keys off this literal on the OCI
27786 // path); changing it is a coordinated multi-repo migration,
27787 // not an incidental edit. Peer to
27788 // [`lareira_chart_name_prefix_pins_canonical_value`] on the
27789 // sibling canonical-string-value-pin axis.
27790 assert_eq!(OCI_SCHEME_PREFIX, "oci://");
27791 }
27792
27793 #[test]
27794 fn oci_chart_ref_pins_byte_shape_against_prior_inline_format() {
27795 // Byte-shape pin against the prior inline
27796 // `format!("oci://{registry}/{chart}")` at
27797 // caixa-tatara/src/lib.rs:202 (where `chart` was itself
27798 // `lareira_chart_name(caixa.nome.as_str())`). Any future
27799 // composer-internal drift on either axis (the `oci://` scheme
27800 // prefix, the `/` scheme-authority separator, the composition
27801 // with `lareira_chart_name`) surfaces here as a byte-shape
27802 // regression rather than at cluster-apply time far from the
27803 // drift site.
27804 assert_eq!(
27805 oci_chart_ref("ghcr.io/pleme-io/charts", "akeyless-attest"),
27806 "oci://ghcr.io/pleme-io/charts/lareira-akeyless-attest"
27807 );
27808 assert_eq!(
27809 oci_chart_ref("ghcr.io/pleme-io", "hello-rio"),
27810 "oci://ghcr.io/pleme-io/lareira-hello-rio"
27811 );
27812 }
27813
27814 #[test]
27815 fn oci_chart_ref_composes_through_canonical_helpers() {
27816 // Structural composition equation: the OCI chart-ref is
27817 // exactly `{OCI_SCHEME_PREFIX}{registry}/{lareira_chart_name(nome)}`
27818 // — no inline `"oci://"` scheme literal, no inline
27819 // `format!("lareira-{}", nome)` prefix duplication. Pinning
27820 // this composition closes the drift footgun where a future
27821 // composer refactor re-inlines either axis and diverges from
27822 // its canonical source of truth. Sweep across the canonical
27823 // fixture set so the composition holds for the same `:nome`
27824 // values every peer per-Servico renderer consults.
27825 for (registry, nome) in [
27826 ("ghcr.io/pleme-io/charts", "hello-rio"),
27827 ("ghcr.io/pleme-io", "cart"),
27828 ("registry.example.com", "worker"),
27829 ("localhost:5000", "checkout"),
27830 ] {
27831 let composed = oci_chart_ref(registry, nome);
27832 let expected = format!("{OCI_SCHEME_PREFIX}{registry}/{}", lareira_chart_name(nome));
27833 assert_eq!(
27834 composed, expected,
27835 "oci_chart_ref({registry:?}, {nome:?}) must equal the canonical composition \
27836 through OCI_SCHEME_PREFIX + lareira_chart_name"
27837 );
27838 }
27839 }
27840
27841 #[test]
27842 fn oci_chart_ref_starts_with_scheme_prefix() {
27843 // Cross-axis invariant: every output of the composer begins
27844 // with the lifted scheme prefix verbatim — a future refactor
27845 // that accidentally introduced a different scheme (e.g. a
27846 // `https://` transposition, or a scheme-authority separator
27847 // drift) would surface here. Peer to
27848 // [`lareira_chart_name_starts_with_prefix`] on the sibling
27849 // per-composer prefix-anchoring axis.
27850 for (registry, nome) in [
27851 ("ghcr.io/pleme-io/charts", "hello-rio"),
27852 ("ghcr.io/pleme-io", "cart"),
27853 ("localhost:5000", "a"),
27854 ] {
27855 let composed = oci_chart_ref(registry, nome);
27856 assert!(
27857 composed.starts_with(OCI_SCHEME_PREFIX),
27858 "oci_chart_ref({registry:?}, {nome:?}) = {composed:?} must start with the lifted \
27859 prefix {OCI_SCHEME_PREFIX:?}"
27860 );
27861 }
27862 }
27863
27864 #[test]
27865 fn oci_chart_ref_contains_lareira_chart_name_verbatim() {
27866 // Cross-axis invariant: every output of the composer contains
27867 // the canonical `lareira_chart_name(nome)` output verbatim as
27868 // its trailing segment — a future refactor that accidentally
27869 // introduced a case fold, a hyphen-collapse, or a different
27870 // prefix-application shape would surface here. Structurally
27871 // pins that the OCI chart-ref path and the peer per-Servico
27872 // renderer chart-name path (caixa-helm's `ChartDir.name`,
27873 // caixa-flux's `HelmRelease` `chart:` field) both reach for
27874 // the same canonical `lareira_chart_name` helper's output.
27875 for (registry, nome) in [
27876 ("ghcr.io/pleme-io/charts", "hello-rio"),
27877 ("ghcr.io/pleme-io", "cart"),
27878 ] {
27879 let composed = oci_chart_ref(registry, nome);
27880 let chart = lareira_chart_name(nome);
27881 assert!(
27882 composed.ends_with(&chart),
27883 "oci_chart_ref({registry:?}, {nome:?}) = {composed:?} must end with the canonical \
27884 lareira_chart_name({nome:?}) = {chart:?}"
27885 );
27886 }
27887 }
27888
27889 // ── Flux Kustomization source-sub-tree composer ───────────────────────
27890 //
27891 // Peer to the `oci_chart_ref` / `cilium_network_policy_name` /
27892 // `gateway_api_http_route_name` composers above on the sibling
27893 // canonical-load-bearing-scalar-that-consumers-key-off axis. Until
27894 // this lift landed the two-axis composition
27895 // (`./clusters/<cluster>/services/<nome>`) sat as an inline
27896 // `format!` template at the sole `caixa-flux::cluster_bundle`
27897 // `kustomization.yaml` production emit site plus a mirror-symmetric
27898 // inline `format!` at its paired test-fixture navigation site — no
27899 // compile-time link between the two sites and no compile-time link
27900 // ahead of the second production-emit occurrence the M4
27901 // `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
27902 // `Kustomization` synthesis will surface. Pin the byte-shape, the
27903 // composition equation, and the sub-tree-scope invariants against
27904 // the prior inline `format!` so a future composer-internal drift
27905 // fires at test time.
27906
27907 #[test]
27908 fn flux_kustomization_source_subtree_pins_byte_shape_against_prior_inline_format() {
27909 // Byte-shape pin against the prior inline
27910 // `format!("./clusters/{cluster}/services/{name}")` at
27911 // caixa-flux/src/lib.rs (both the `cluster_bundle`
27912 // `kustomization.yaml` `spec.path` production emit site and the
27913 // paired `cluster_bundle_kustomization_path_pins_lifted_sub_tree`
27914 // test-fixture navigation site). Any future composer-internal
27915 // drift on either axis (the `./clusters/` per-cluster prefix,
27916 // the `/services/` per-caixa infix, the trailing per-caixa
27917 // suffix, the composition order) surfaces here as a byte-shape
27918 // regression rather than at cluster-apply time far from the
27919 // drift site.
27920 assert_eq!(
27921 flux_kustomization_source_subtree("rio", "hello-rio"),
27922 "./clusters/rio/services/hello-rio"
27923 );
27924 assert_eq!(
27925 flux_kustomization_source_subtree("paris", "cart"),
27926 "./clusters/paris/services/cart"
27927 );
27928 assert_eq!(
27929 flux_kustomization_source_subtree("tokyo", "checkout"),
27930 "./clusters/tokyo/services/checkout"
27931 );
27932 }
27933
27934 #[test]
27935 fn flux_kustomization_source_subtree_starts_with_relative_clusters_prefix() {
27936 // Structural invariant: every output starts with the canonical
27937 // `./clusters/` per-cluster-prefix half of the sub-tree seed.
27938 // The leading `./` scopes the emit to the GitRepository root
27939 // (the kustomize-controller keys the per-CR reconcile loop off
27940 // the GitRepository the paired `sourceRef` names, so the sub-
27941 // tree seed must resolve relative to the GitRepository root,
27942 // not an absolute filesystem path). The `clusters/` component
27943 // scopes the emit to the paired cluster's manifest set under
27944 // the pleme-io k8s repository's canonical directory-tree
27945 // layout.
27946 for (cluster, nome) in [
27947 ("rio", "hello-rio"),
27948 ("paris", "cart"),
27949 ("tokyo", "checkout"),
27950 ] {
27951 let sub = flux_kustomization_source_subtree(cluster, nome);
27952 assert!(
27953 sub.starts_with("./clusters/"),
27954 "flux_kustomization_source_subtree({cluster:?}, {nome:?}) = {sub:?} must start \
27955 with the canonical `./clusters/` GitRepository-root-relative per-cluster prefix"
27956 );
27957 }
27958 }
27959
27960 #[test]
27961 fn flux_kustomization_source_subtree_contains_paired_cluster_and_nome() {
27962 // Cross-axis invariant: every output contains the paired
27963 // `<cluster>` and `<nome>` scalars verbatim, at their canonical
27964 // per-cluster / per-caixa sub-tree positions. A future
27965 // composer-internal drift that accidentally case-folded, hyphen-
27966 // collapsed, or transposed either axis (`./clusters/rio/services/hello-rio`
27967 // → `./clusters/hello-rio/services/rio` under a swapped
27968 // composition, `./clusters/Rio/services/HelloRio` under an
27969 // accidental case fold) would surface here as a structural
27970 // regression rather than at cluster-apply time far from the
27971 // drift site.
27972 for (cluster, nome) in [
27973 ("rio", "hello-rio"),
27974 ("paris", "cart"),
27975 ("tokyo", "checkout"),
27976 ("us-east-1", "worker"),
27977 ] {
27978 let sub = flux_kustomization_source_subtree(cluster, nome);
27979 assert!(
27980 sub.contains(&format!("/clusters/{cluster}/")),
27981 "flux_kustomization_source_subtree({cluster:?}, {nome:?}) = {sub:?} must carry \
27982 the paired `<cluster>` scalar under its canonical per-cluster sub-tree position"
27983 );
27984 assert!(
27985 sub.ends_with(&format!("/services/{nome}")),
27986 "flux_kustomization_source_subtree({cluster:?}, {nome:?}) = {sub:?} must end with \
27987 the paired `/services/<nome>` per-caixa sub-tree suffix"
27988 );
27989 }
27990 }
27991
27992 #[test]
27993 fn flux_kustomization_source_subtree_distinct_across_clusters_and_nomes() {
27994 // Uniqueness invariant: two distinct `(cluster, nome)` inputs
27995 // resolve to two distinct `spec.path` scalars. A composer-
27996 // internal drift that accidentally coalesced either axis onto
27997 // a constant (dropping `<cluster>` or `<nome>` from the emit)
27998 // would silently collapse two per-cluster / per-caixa
27999 // `Kustomization` CRs onto the same reconcile-target sub-tree,
28000 // routing two distinct manifest sets through the same apply
28001 // loop with no diagnostic naming the coalesce root cause.
28002 let a = flux_kustomization_source_subtree("rio", "hello-rio");
28003 let b = flux_kustomization_source_subtree("paris", "hello-rio");
28004 let c = flux_kustomization_source_subtree("rio", "cart");
28005 assert_ne!(
28006 a, b,
28007 "distinct clusters (`rio` vs `paris`) hosting the same per-caixa Servico \
28008 must resolve to distinct `spec.path` scalars — coalesce would silently route \
28009 two per-cluster reconcile loops through the same manifest sub-tree"
28010 );
28011 assert_ne!(
28012 a, c,
28013 "distinct per-caixa Servicos (`hello-rio` vs `cart`) co-resident under the \
28014 same cluster must resolve to distinct `spec.path` scalars — coalesce would \
28015 silently route two per-caixa reconcile loops through the same manifest sub-tree"
28016 );
28017 }
28018
28019 #[test]
28020 fn pleme_program_selector_carries_only_program() {
28021 let sel = pleme_program_selector("cart");
28022 assert_eq!(sel.len(), 1);
28023 assert_eq!(sel.get(LABEL_PROGRAM).map(String::as_str), Some("cart"));
28024 assert!(sel.get(LABEL_APLICACAO).is_none());
28025 }
28026
28027 #[test]
28028 fn pleme_program_in_aplicacao_selector_carries_both_axes() {
28029 let sel = pleme_program_in_aplicacao_selector("cart", "checkout");
28030 assert_eq!(sel.len(), 2);
28031 assert_eq!(sel.get(LABEL_PROGRAM).map(String::as_str), Some("cart"));
28032 assert_eq!(
28033 sel.get(LABEL_APLICACAO).map(String::as_str),
28034 Some("checkout")
28035 );
28036 }
28037
28038 #[test]
28039 fn pleme_program_in_aplicacao_selector_iterates_alphabetically() {
28040 // BTreeMap iteration is sorted by key — pin that the renderer
28041 // (which translates the selector into a serde_yaml::Mapping
28042 // by iteration) gets a deterministic key order. `aplicacao`
28043 // sorts before `program`, so the rendered YAML's
28044 // `matchLabels:` block appears in that order regardless of
28045 // call-site arg order. Mirrors the M2 overlay helper's
28046 // alphabetical-iteration determinism property
28047 // (THEORY.md §V.2.7 render determinism).
28048 let sel = pleme_program_in_aplicacao_selector("cart", "checkout");
28049 let keys: Vec<_> = sel.keys().copied().collect();
28050 assert_eq!(keys, vec![LABEL_APLICACAO, LABEL_PROGRAM]);
28051 }
28052
28053 #[test]
28054 fn pleme_program_in_aplicacao_selector_arg_order_independent() {
28055 // Renaming the program vs. the aplicacao must each only affect
28056 // its own axis — pin that the helper doesn't transpose its
28057 // args silently (a footgun the prior inline-string approach
28058 // had: `program: <de>` and `aplicacao: <name>` were two
28059 // adjacent insert() calls with structurally identical arms,
28060 // trivially swappable in a refactor).
28061 let sel = pleme_program_in_aplicacao_selector("cart", "checkout");
28062 assert_eq!(sel.get(LABEL_PROGRAM).map(String::as_str), Some("cart"));
28063 assert_eq!(
28064 sel.get(LABEL_APLICACAO).map(String::as_str),
28065 Some("checkout")
28066 );
28067 let swapped = pleme_program_in_aplicacao_selector("checkout", "cart");
28068 assert_eq!(
28069 swapped.get(LABEL_PROGRAM).map(String::as_str),
28070 Some("checkout")
28071 );
28072 assert_eq!(
28073 swapped.get(LABEL_APLICACAO).map(String::as_str),
28074 Some("cart")
28075 );
28076 }
28077
28078 #[test]
28079 fn yaml_string_mapping_empty_input_returns_empty_mapping() {
28080 // Empty input → empty Mapping. Pinned because the caller's
28081 // emptiness contract (e.g. caixa-mesh's CNP labels block: the
28082 // policy's metadata.labels exists iff there are pleme-prefixed
28083 // labels to carry) depends on this being faithful.
28084 let v: serde_yaml::Value = yaml_string_mapping(BTreeMap::<&'static str, String>::new());
28085 let m = v.as_mapping().expect("mapping shape");
28086 assert!(m.is_empty());
28087 }
28088
28089 #[test]
28090 fn yaml_string_mapping_round_trips_string_values() {
28091 let mut input = BTreeMap::new();
28092 input.insert("foo", "1".to_string());
28093 input.insert("bar", "2".to_string());
28094 let v = yaml_string_mapping(input);
28095 let m = v.as_mapping().expect("mapping shape");
28096 assert_eq!(m.len(), 2);
28097 assert_eq!(m.get("foo").and_then(|x| x.as_str()), Some("1"));
28098 assert_eq!(m.get("bar").and_then(|x| x.as_str()), Some("2"));
28099 }
28100
28101 #[test]
28102 fn yaml_string_mapping_iterates_alphabetically_on_btreemap() {
28103 // Pin that BTreeMap input → alphabetical iteration → alphabetical
28104 // YAML key order. THEORY.md §V.2.7 render determinism.
28105 let mut input = BTreeMap::new();
28106 input.insert("zebra", "z".to_string());
28107 input.insert("apple", "a".to_string());
28108 input.insert("mango", "m".to_string());
28109 let v = yaml_string_mapping(input);
28110 let m = v.as_mapping().expect("mapping shape");
28111 let keys: Vec<&str> = m.iter().filter_map(|(k, _)| k.as_str()).collect();
28112 assert_eq!(keys, vec!["apple", "mango", "zebra"]);
28113 }
28114
28115 #[test]
28116 fn yaml_string_mapping_accepts_pleme_selector_helpers() {
28117 // The lift's load-bearing use case: passing the typed pleme-io
28118 // selectors directly into yaml_string_mapping yields the K8s
28119 // matchLabels surface every Cilium / Gateway selector field
28120 // expects, with the alphabetical key order the pleme helpers'
28121 // own determinism contract guarantees. Pinning end-to-end
28122 // composition so a future refactor of either helper can't
28123 // silently break the integration.
28124 let v = yaml_string_mapping(pleme_program_in_aplicacao_selector("cart", "checkout"));
28125 let m = v.as_mapping().expect("mapping shape");
28126 assert_eq!(m.len(), 2);
28127 assert_eq!(m.get(LABEL_PROGRAM).and_then(|x| x.as_str()), Some("cart"));
28128 assert_eq!(
28129 m.get(LABEL_APLICACAO).and_then(|x| x.as_str()),
28130 Some("checkout")
28131 );
28132 }
28133
28134 #[test]
28135 fn kube_key_consts_have_expected_values() {
28136 // Pin the actual string values — these are part of the K8s API
28137 // surface that every emitted artifact's apiserver-side parser
28138 // (Cilium, Gateway API, wasm-operator) depends on. Changing any
28139 // of them is a coordinated multi-renderer migration, not an
28140 // incidental edit.
28141 assert_eq!(KUBE_KEY_API_VERSION, "apiVersion");
28142 assert_eq!(KUBE_KEY_KIND, "kind");
28143 assert_eq!(KUBE_KEY_METADATA, "metadata");
28144 assert_eq!(KUBE_KEY_NAME, "name");
28145 assert_eq!(KUBE_KEY_NAMESPACE, "namespace");
28146 assert_eq!(KUBE_KEY_LABELS, "labels");
28147 assert_eq!(KUBE_KEY_MATCH_LABELS, "matchLabels");
28148 assert_eq!(KUBE_KEY_PORT, "port");
28149 assert_eq!(KUBE_KEY_PROTOCOL, "protocol");
28150 assert_eq!(KUBE_KEY_RULES, "rules");
28151 assert_eq!(KUBE_KEY_SPEC, "spec");
28152 }
28153
28154 #[test]
28155 fn fleet_programs_key_programs_pins_canonical_value() {
28156 // Bridge-arm pin: [`FLEET_PROGRAMS_KEY_PROGRAMS`] resolves to
28157 // the canonical `"programs"` byte today — the exact YAML key
28158 // the `lareira-fleet-programs` library chart's `values.yaml`
28159 // reads under `.Values.programs[]` to iterate one `ComputeUnit`
28160 // CR per entry, and the exact key both writer-side upsert paths
28161 // in [`caixa_flux`] (`upsert_into_helmrelease_programs` on the
28162 // aggregator-HelmRelease shape, `upsert_into_programs_yaml` on
28163 // the bare-values.yaml shape) navigate to walk the entry
28164 // sequence. Pin the literal here (peer with the
28165 // [`M3_KEY_PLACEMENT`] / [`M2_KEY_LIMITS`] /
28166 // [`M2_KEY_BEHAVIOR`] / [`M2_KEY_UPGRADE_FROM`] canonical-
28167 // literal pins on the sibling fleet-programs / M2 overlay
28168 // schema-key surfaces) so a future fleet-programs schema-key
28169 // rebrand surfaces here as a coordinated edit-point: the
28170 // sibling caixa-flux `fleet_programs_key_programs_re_export_
28171 // points_at_caixa_core_canonical` pinning test already pins
28172 // the equality at the re-export axis; this pin closes the
28173 // second coordinate of the triangle by anchoring the lifted
28174 // constant's current byte to the canonical fleet-programs
28175 // library chart's documented shape.
28176 assert_eq!(FLEET_PROGRAMS_KEY_PROGRAMS, "programs");
28177 }
28178
28179 #[test]
28180 fn fleet_programs_key_name_pins_canonical_value() {
28181 // Bridge-arm pin: [`FLEET_PROGRAMS_KEY_NAME`] resolves to the
28182 // canonical `"name"` byte today — the exact YAML key the
28183 // `lareira-fleet-programs` library chart's `range .Values.programs`
28184 // step reads per-entry to key each rendered `ComputeUnit` CR's
28185 // `metadata.name` off, and the exact key both writer-side upsert
28186 // paths in [`caixa_flux`] (`upsert_into_helmrelease_programs` on
28187 // the aggregator-HelmRelease shape, `upsert_into_programs_yaml`
28188 // on the bare-values.yaml shape) navigate to
28189 // match-by-name-and-replace-or-append, and the exact key both
28190 // emit-side entry builders ([`caixa_flux::programs_yaml_entry`]
28191 // per-Servico, [`caixa_mesh::programs_for_aplicacao`] per-
28192 // `:membros`) write the per-entry name-axis at. Pin the literal
28193 // here (peer with the [`fleet_programs_key_programs_pins_canonical_value`]
28194 // top-level array-key canonical-literal pin on the sibling
28195 // fleet-programs schema surface, and with the
28196 // [`M3_KEY_PLACEMENT`] / [`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`]
28197 // / [`M2_KEY_UPGRADE_FROM`] canonical-literal pins on the peer
28198 // per-entry overlay-key surfaces) so a future fleet-programs
28199 // schema-key rebrand on the per-entry name-discriminator axis
28200 // surfaces here as a coordinated edit-point at the definition
28201 // site rather than a silent apply-time split between the two
28202 // emitters and the two upsert readers.
28203 assert_eq!(FLEET_PROGRAMS_KEY_NAME, "name");
28204 }
28205
28206 #[test]
28207 fn fleet_programs_key_aplicacao_pins_canonical_value() {
28208 // Bridge-arm pin: [`FLEET_PROGRAMS_KEY_APLICACAO`] resolves
28209 // to the canonical `"aplicacao"` byte today — the exact YAML
28210 // key the substrate operator's fleet-aggregator reads to
28211 // group each rendered `programs[]` entry back onto its parent
28212 // Aplicacao graph, and the exact key the
28213 // [`caixa_mesh::programs_for_aplicacao`] per-`:membros`
28214 // entry-builder writes the parent-Aplicacao-nome annotation
28215 // at. Pin the literal here (peer with the sibling
28216 // [`fleet_programs_key_name_pins_canonical_value`] and
28217 // [`fleet_programs_key_programs_pins_canonical_value`]
28218 // canonical-literal pins on the peer fleet-programs schema
28219 // key surfaces, and with the [`M3_KEY_PLACEMENT`] /
28220 // [`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] /
28221 // [`M2_KEY_UPGRADE_FROM`] pins on the per-entry overlay-key
28222 // surfaces) so a future fleet-programs schema-key rebrand
28223 // on the per-entry parent-graph-annotation axis surfaces
28224 // here as a coordinated edit-point at the definition site
28225 // rather than a silent apply-time split between the
28226 // caixa-mesh Aplicacao-side emitter and the substrate
28227 // operator's per-graph aggregator reduce step.
28228 assert_eq!(FLEET_PROGRAMS_KEY_APLICACAO, "aplicacao");
28229 }
28230
28231 #[test]
28232 fn fleet_programs_key_versao_pins_canonical_value() {
28233 // Bridge-arm pin: [`FLEET_PROGRAMS_KEY_VERSAO`] resolves to
28234 // the canonical `"versao"` byte today — the exact YAML key
28235 // the substrate operator's per-`:membros` resolver reads to
28236 // fetch each `programs[]` entry's caixa.lisp release against
28237 // the M3 Aplicacao's declared per-member semver / range
28238 // constraint, and the exact key the
28239 // [`caixa_mesh::programs_for_aplicacao`] per-`:membros`
28240 // entry-builder writes the version-constraint at. Pin the
28241 // literal here (peer with the sibling
28242 // [`fleet_programs_key_name_pins_canonical_value`],
28243 // [`fleet_programs_key_aplicacao_pins_canonical_value`], and
28244 // [`fleet_programs_key_programs_pins_canonical_value`]
28245 // canonical-literal pins on the peer fleet-programs schema
28246 // key surfaces, and with the [`M3_KEY_PLACEMENT`] /
28247 // [`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] /
28248 // [`M2_KEY_UPGRADE_FROM`] pins on the per-entry overlay-key
28249 // surfaces) so a future fleet-programs schema-key rebrand
28250 // on the per-entry version-constraint axis surfaces here as
28251 // a coordinated edit-point at the definition site rather
28252 // than a silent apply-time split between the caixa-mesh
28253 // Aplicacao-side emitter and the substrate operator's
28254 // per-`:membros` resolver step.
28255 assert_eq!(FLEET_PROGRAMS_KEY_VERSAO, "versao");
28256 }
28257
28258 // ── label_selector — typed K8s LabelSelector wrapper ─────────────────
28259
28260 #[test]
28261 fn label_selector_wraps_in_match_labels_envelope() {
28262 // The lift's contract: input labels appear under the canonical
28263 // `matchLabels` key, and the outer Value is a Mapping with
28264 // exactly that one key. Pinning the shape so a future
28265 // refactor can't silently drop the wrapper (which would emit
28266 // bare `aplicacao: …, program: …` directly under the K8s
28267 // selector field — a structurally invalid LabelSelector that
28268 // some apiserver-side parsers tolerate by matching the empty
28269 // set, a sharp footgun).
28270 let mut labels = BTreeMap::new();
28271 labels.insert(LABEL_APLICACAO, "checkout".to_string());
28272 labels.insert(LABEL_PROGRAM, "cart".to_string());
28273 let sel = label_selector(labels);
28274 let m = sel.as_mapping().expect("mapping shape");
28275 assert_eq!(m.len(), 1);
28276 let inner = m
28277 .get(KUBE_KEY_MATCH_LABELS)
28278 .and_then(|v| v.as_mapping())
28279 .expect("matchLabels inner mapping");
28280 assert_eq!(inner.len(), 2);
28281 assert_eq!(
28282 inner.get(LABEL_APLICACAO).and_then(|x| x.as_str()),
28283 Some("checkout")
28284 );
28285 assert_eq!(
28286 inner.get(LABEL_PROGRAM).and_then(|x| x.as_str()),
28287 Some("cart")
28288 );
28289 }
28290
28291 #[test]
28292 fn label_selector_empty_input_yields_empty_match_labels() {
28293 // Empty input → `{matchLabels: {}}`. The outer wrapper is
28294 // present (the K8s LabelSelector schema requires it as a
28295 // structural anchor, and apiserver-side parsers that see a
28296 // bare `{}` selector match-everything; pinning the wrapper
28297 // means an empty pleme-io selector at the call site renders
28298 // as the canonical "no labels declared, match nothing
28299 // specific" shape rather than an outright missing key).
28300 let v: serde_yaml::Value = label_selector(BTreeMap::<&'static str, String>::new());
28301 let m = v.as_mapping().expect("mapping shape");
28302 assert_eq!(m.len(), 1);
28303 let inner = m
28304 .get(KUBE_KEY_MATCH_LABELS)
28305 .and_then(|v| v.as_mapping())
28306 .expect("matchLabels inner mapping");
28307 assert!(inner.is_empty());
28308 }
28309
28310 #[test]
28311 fn label_selector_accepts_pleme_selector_helpers() {
28312 // The lift's load-bearing use case: passing the typed pleme-io
28313 // selectors directly into `label_selector` yields the K8s
28314 // LabelSelector shape every Cilium / Gateway / future
28315 // app-operator selector field expects. Pinning end-to-end
28316 // composition so a future refactor of either helper can't
28317 // silently break the integration.
28318 let v = label_selector(pleme_program_in_aplicacao_selector("cart", "checkout"));
28319 let inner = v
28320 .as_mapping()
28321 .and_then(|m| m.get(KUBE_KEY_MATCH_LABELS))
28322 .and_then(|v| v.as_mapping())
28323 .expect("matchLabels inner mapping");
28324 assert_eq!(inner.len(), 2);
28325 assert_eq!(
28326 inner.get(LABEL_PROGRAM).and_then(|x| x.as_str()),
28327 Some("cart")
28328 );
28329 assert_eq!(
28330 inner.get(LABEL_APLICACAO).and_then(|x| x.as_str()),
28331 Some("checkout")
28332 );
28333
28334 // Single-axis variant — only LABEL_PROGRAM under matchLabels.
28335 let v = label_selector(pleme_program_selector("cart"));
28336 let inner = v
28337 .as_mapping()
28338 .and_then(|m| m.get(KUBE_KEY_MATCH_LABELS))
28339 .and_then(|v| v.as_mapping())
28340 .unwrap();
28341 assert_eq!(inner.len(), 1);
28342 assert_eq!(
28343 inner.get(LABEL_PROGRAM).and_then(|x| x.as_str()),
28344 Some("cart")
28345 );
28346 }
28347
28348 #[test]
28349 fn label_selector_inner_iterates_alphabetically_on_btreemap() {
28350 // BTreeMap input → alphabetical iteration → alphabetical YAML
28351 // key order under `matchLabels`. THEORY.md §V.2.7 render
28352 // determinism: the rendered YAML's matchLabels: block appears
28353 // in a deterministic order independent of source-code
28354 // declaration order.
28355 let mut input = BTreeMap::new();
28356 input.insert("zebra", "z".to_string());
28357 input.insert("apple", "a".to_string());
28358 input.insert("mango", "m".to_string());
28359 let v = label_selector(input);
28360 let inner = v
28361 .as_mapping()
28362 .and_then(|m| m.get(KUBE_KEY_MATCH_LABELS))
28363 .and_then(|v| v.as_mapping())
28364 .unwrap();
28365 let keys: Vec<&str> = inner.iter().filter_map(|(k, _)| k.as_str()).collect();
28366 assert_eq!(keys, vec!["apple", "mango", "zebra"]);
28367 }
28368
28369 #[test]
28370 fn label_selector_does_not_introduce_match_expressions_axis() {
28371 // V0 emits matchLabels only — pinning that the helper doesn't
28372 // pre-insert an empty `matchExpressions: []` block (which some
28373 // apiserver-side parsers tolerate but renders noisily and
28374 // shifts the per-rule diff). A future set-based selector
28375 // extension is a deliberate API change to this helper, not an
28376 // incidental shape leak.
28377 let v = label_selector(pleme_program_selector("cart"));
28378 let m = v.as_mapping().unwrap();
28379 assert!(
28380 m.get("matchExpressions").is_none(),
28381 "label_selector must not pre-insert a matchExpressions key (V0 is matchLabels-only)"
28382 );
28383 }
28384
28385 #[test]
28386 fn kube_resource_skeleton_carries_three_top_level_keys_no_spec() {
28387 // The skeleton emits exactly apiVersion + kind + metadata; the
28388 // caller adds spec (and any other top-level keys) themselves.
28389 // Pin that contract so a future caller doesn't accidentally
28390 // double-insert apiVersion / kind / metadata after the
28391 // skeleton call. Namespace fixture arg reads through the
28392 // canonical `DEFAULT_NAMESPACE` const so a future rebrand of
28393 // the substrate's default namespace reaches every fixture by
28394 // construction rather than through a per-fixture stray
28395 // "tatara-system" byte-sequence.
28396 let skel = kube_resource_skeleton(
28397 "cilium.io/v2",
28398 "CiliumNetworkPolicy",
28399 "p-1",
28400 DEFAULT_NAMESPACE,
28401 BTreeMap::new(),
28402 );
28403 assert_eq!(skel.len(), 3);
28404 assert_eq!(
28405 skel.get(KUBE_KEY_API_VERSION).and_then(|v| v.as_str()),
28406 Some("cilium.io/v2")
28407 );
28408 assert_eq!(
28409 skel.get(KUBE_KEY_KIND).and_then(|v| v.as_str()),
28410 Some("CiliumNetworkPolicy")
28411 );
28412 assert!(skel.get(KUBE_KEY_METADATA).is_some());
28413 }
28414
28415 #[test]
28416 fn kube_resource_skeleton_metadata_carries_name_and_namespace() {
28417 let skel = kube_resource_skeleton(
28418 "gateway.networking.k8s.io/v1",
28419 "Gateway",
28420 "checkout",
28421 DEFAULT_NAMESPACE,
28422 BTreeMap::new(),
28423 );
28424 let metadata = skel
28425 .get(KUBE_KEY_METADATA)
28426 .and_then(|v| v.as_mapping())
28427 .expect("metadata mapping");
28428 assert_eq!(
28429 metadata.get(KUBE_KEY_NAME).and_then(|v| v.as_str()),
28430 Some("checkout")
28431 );
28432 // Read-back probe reads through `DEFAULT_NAMESPACE` so a
28433 // future substrate-namespace rebrand routes through the
28434 // canonical const on both the emit-side fixture arg and the
28435 // probe-side readback in one edit — a drift on either side
28436 // would otherwise silently mask the round-trip pin.
28437 assert_eq!(
28438 metadata.get(KUBE_KEY_NAMESPACE).and_then(|v| v.as_str()),
28439 Some(DEFAULT_NAMESPACE)
28440 );
28441 }
28442
28443 #[test]
28444 fn kube_resource_skeleton_omits_labels_when_empty() {
28445 // Empty labels → metadata.labels key absent (NOT present-as-empty).
28446 // K8s API server treats a missing labels key as "no labels
28447 // declared"; an empty-mapping `labels: {}` serializes
28448 // differently in some YAML libraries and is a sharp tool for
28449 // label-based selectors that match the empty set silently.
28450 let skel = kube_resource_skeleton(
28451 "gateway.networking.k8s.io/v1",
28452 "HTTPRoute",
28453 "r-1",
28454 DEFAULT_NAMESPACE,
28455 BTreeMap::new(),
28456 );
28457 let metadata = skel
28458 .get(KUBE_KEY_METADATA)
28459 .and_then(|v| v.as_mapping())
28460 .unwrap();
28461 assert!(
28462 metadata.get(KUBE_KEY_LABELS).is_none(),
28463 "metadata.labels must be absent when no labels passed"
28464 );
28465 // metadata then has exactly 2 keys: name, namespace.
28466 assert_eq!(metadata.len(), 2);
28467 }
28468
28469 #[test]
28470 fn kube_resource_skeleton_includes_labels_when_present() {
28471 let mut labels = BTreeMap::new();
28472 labels.insert(LABEL_APLICACAO, "checkout".to_string());
28473 labels.insert(LABEL_CONTRATO, "cart-to-catalog".to_string());
28474 let skel = kube_resource_skeleton(
28475 "cilium.io/v2",
28476 "CiliumNetworkPolicy",
28477 "p-1",
28478 DEFAULT_NAMESPACE,
28479 labels,
28480 );
28481 let metadata = skel
28482 .get(KUBE_KEY_METADATA)
28483 .and_then(|v| v.as_mapping())
28484 .unwrap();
28485 let labels_block = metadata
28486 .get(KUBE_KEY_LABELS)
28487 .and_then(|v| v.as_mapping())
28488 .expect("metadata.labels mapping present");
28489 assert_eq!(
28490 labels_block.get(LABEL_APLICACAO).and_then(|v| v.as_str()),
28491 Some("checkout")
28492 );
28493 assert_eq!(
28494 labels_block.get(LABEL_CONTRATO).and_then(|v| v.as_str()),
28495 Some("cart-to-catalog")
28496 );
28497 }
28498
28499 #[test]
28500 fn kube_resource_skeleton_metadata_iterates_alphabetically() {
28501 // Pin that the inner BTreeMap projection makes the rendered
28502 // YAML's metadata: block alphabetical (labels, name, namespace),
28503 // regardless of insert order. THEORY.md §V.2.7 render determinism.
28504 let mut labels = BTreeMap::new();
28505 labels.insert(LABEL_APLICACAO, "checkout".to_string());
28506 let skel = kube_resource_skeleton(
28507 "cilium.io/v2",
28508 "CiliumNetworkPolicy",
28509 "p-1",
28510 DEFAULT_NAMESPACE,
28511 labels,
28512 );
28513 let metadata = skel
28514 .get(KUBE_KEY_METADATA)
28515 .and_then(|v| v.as_mapping())
28516 .unwrap();
28517 let keys: Vec<&str> = metadata.iter().filter_map(|(k, _)| k.as_str()).collect();
28518 assert_eq!(
28519 keys,
28520 vec![KUBE_KEY_LABELS, KUBE_KEY_NAME, KUBE_KEY_NAMESPACE]
28521 );
28522 }
28523
28524 #[test]
28525 fn kube_resource_skeleton_top_level_iterates_in_insert_order() {
28526 // The top-level Mapping is a plain serde_yaml::Mapping (insert-
28527 // ordered), and the skeleton inserts apiVersion → kind →
28528 // metadata in that order. Pin so a future refactor doesn't
28529 // silently shift the rendered YAML's top-level key order
28530 // (which K8s tooling tolerates but humans + diff readability
28531 // care about — apiVersion-first is the K8s convention).
28532 let skel = kube_resource_skeleton(
28533 "cilium.io/v2",
28534 "CiliumNetworkPolicy",
28535 "p-1",
28536 DEFAULT_NAMESPACE,
28537 BTreeMap::new(),
28538 );
28539 let keys: Vec<&str> = skel.iter().filter_map(|(k, _)| k.as_str()).collect();
28540 assert_eq!(
28541 keys,
28542 vec![KUBE_KEY_API_VERSION, KUBE_KEY_KIND, KUBE_KEY_METADATA]
28543 );
28544 }
28545
28546 #[test]
28547 fn kube_resource_skeleton_does_not_introduce_spec_key() {
28548 // Sanity: the skeleton is metadata-only — `spec` is the caller's
28549 // responsibility. Pinning so a future "be helpful" refactor
28550 // doesn't auto-insert an empty `spec: {}` (which would silently
28551 // shadow caller-side spec construction).
28552 let skel = kube_resource_skeleton(
28553 "cilium.io/v2",
28554 "CiliumNetworkPolicy",
28555 "p-1",
28556 DEFAULT_NAMESPACE,
28557 BTreeMap::new(),
28558 );
28559 assert!(
28560 skel.get("spec").is_none(),
28561 "skeleton must not pre-insert a spec key"
28562 );
28563 }
28564
28565 // ── require_kind / KindMismatch — typed kind-check predicate ─────
28566
28567 #[test]
28568 fn require_kind_accepts_matching_kind() {
28569 // A Servico-kind caixa passes a `require_kind(_, Servico)`
28570 // check — the happy path every renderer sees on a correctly-
28571 // authored caixa.lisp, surfaced as `Ok(())` so the renderer's
28572 // call site reads as a one-liner gate rather than a typed
28573 // pattern match.
28574 let c = bare_servico();
28575 require_kind(&c, CaixaKind::Servico).unwrap();
28576 }
28577
28578 #[test]
28579 fn require_kind_rejects_with_typed_mismatch() {
28580 // A Biblioteca-kind caixa fails a `require_kind(_, Servico)`
28581 // check with a typed [`KindMismatch`] view that names the
28582 // offending caixa's `:nome` plus both the expected and actual
28583 // kinds. Pinning the typed shape so a future Display-format
28584 // tweak can't silently drop any of the three load-bearing
28585 // fields (which would regress the "feira verb whose error
28586 // path doesn't name the offending caixa" punch-list item the
28587 // protocol calls out).
28588 let mut c = bare_servico();
28589 c.kind = CaixaKind::Biblioteca;
28590 c.servicos = vec![];
28591 let err = require_kind(&c, CaixaKind::Servico).unwrap_err();
28592 assert_eq!(err.nome, "hello-rio");
28593 assert_eq!(err.expected, CaixaKind::Servico);
28594 assert_eq!(err.actual, CaixaKind::Biblioteca);
28595 }
28596
28597 #[test]
28598 fn require_kind_routes_offending_nome_via_caixa_nome_accessor() {
28599 // Pin: the [`KindMismatch::nome`] `String` the constructor
28600 // writes must be a byte-identical copy of what the lifted
28601 // [`crate::Caixa::nome`] accessor returns for the same
28602 // [`Caixa`] input — the same discipline the sibling
28603 // [`crate::LayoutInvariants::verify`] wrap-envelope emitters
28604 // pin at 9842a4b's `expected_nome_via_accessor` line (the
28605 // routing pin the 31-site converge introduced on the substrate's
28606 // own layout-invariant verifier's per-axis diagnostic emitters).
28607 //
28608 // Guardrails a future regression that re-inlines the raw
28609 // `caixa.nome.clone()` `String::clone()` of the underlying
28610 // field at the constructor site — the accessor's borrow
28611 // return + typed `.to_string()` `String` promotion is the
28612 // one canonical shape the substrate's own [`KindMismatch`]
28613 // typed-view constructor carries onto every downstream
28614 // renderer's `Error::From<KindMismatch>` `#[from]` arm, so
28615 // any drift (a byte-non-identical shape, e.g. a future
28616 // `CaixaNome` newtype the [`crate::Caixa::nome`] accessor
28617 // upgrades to project the display byte-string of, that
28618 // `.nome.clone()` would silently ignore) surfaces here
28619 // before the drift lands on a per-renderer `#[from]` arm.
28620 let mut c = bare_servico();
28621 c.kind = CaixaKind::Biblioteca;
28622 c.servicos = vec![];
28623 c.nome = "kind-mismatch-pin".into();
28624 let expected_nome_via_accessor = c.nome().to_string();
28625 assert_eq!(
28626 expected_nome_via_accessor, "kind-mismatch-pin",
28627 "the mutated fixture's `:nome` must be observable through \
28628 the accessor before the kind-mismatch gate fires",
28629 );
28630 let err = require_kind(&c, CaixaKind::Servico).unwrap_err();
28631 assert_eq!(
28632 err.nome, expected_nome_via_accessor,
28633 "the KindMismatch's `nome` field must equal \
28634 `caixa.nome().to_string()` — the typed-view constructor \
28635 must route through the lifted [`Caixa::nome`] accessor's \
28636 `.to_string()` extension, not the raw `caixa.nome.clone()` \
28637 `String::clone()` of the underlying field",
28638 );
28639 }
28640
28641 #[test]
28642 fn kind_mismatch_display_names_offending_caixa_nome() {
28643 // The Display impl is the load-bearing surface every renderer's
28644 // `#[error("{0}")] NotAXKind(#[from] KindMismatch)` arm prints
28645 // through. Pinning the exact rendered form so a future format
28646 // change is a one-line edit + a one-line test update, not a
28647 // silent regression of the diagnostic clarity.
28648 let err = KindMismatch {
28649 nome: "checkout".into(),
28650 expected: CaixaKind::Aplicacao,
28651 actual: CaixaKind::Servico,
28652 };
28653 let msg = format!("{err}");
28654 assert!(
28655 msg.contains("checkout"),
28656 "Display must name the offending caixa nome (got: {msg:?})"
28657 );
28658 assert!(
28659 msg.contains("Aplicacao"),
28660 "Display must name the expected kind (got: {msg:?})"
28661 );
28662 assert!(
28663 msg.contains("Servico"),
28664 "Display must name the actual kind (got: {msg:?})"
28665 );
28666 }
28667
28668 #[test]
28669 fn require_kind_distinguishes_every_pair_of_kinds() {
28670 // Sanity: the predicate is kind-axis-agnostic — it works for
28671 // every kind / expected pair, not just Servico/Biblioteca.
28672 // Pinning that the caller can use `require_kind` for any of
28673 // the five typed kinds (Biblioteca, Binario, Servico,
28674 // Supervisor, Aplicacao) without a special-cased helper per
28675 // kind. Same idiom every per-target renderer key off.
28676 let mut c = bare_servico();
28677 c.kind = CaixaKind::Aplicacao;
28678 c.servicos = vec![];
28679 let err = require_kind(&c, CaixaKind::Supervisor).unwrap_err();
28680 assert_eq!(err.expected, CaixaKind::Supervisor);
28681 assert_eq!(err.actual, CaixaKind::Aplicacao);
28682 require_kind(&c, CaixaKind::Aplicacao).unwrap();
28683 }
28684
28685 // ── require_ci / MissingCiSlot — Acao `:ci`-slot-presence gate ────
28686
28687 fn bare_acao_without_ci() -> Caixa {
28688 let mut c = bare_servico();
28689 c.kind = CaixaKind::Acao;
28690 c.servicos = vec![];
28691 c.ci = None;
28692 c
28693 }
28694
28695 fn sample_ci_run() -> canteiro_types::CiRun {
28696 canteiro_types::CiRun {
28697 workspace: "pleme-io".into(),
28698 repo: "caixa".into(),
28699 nodes: vec![],
28700 }
28701 }
28702
28703 #[test]
28704 fn require_ci_accepts_present_slot_and_returns_borrowed_ci_run() {
28705 // The happy path: an Acao-kind caixa that declares its `:ci`
28706 // slot passes `require_ci`, and the borrowed
28707 // [`canteiro_types::CiRun`] projected through the successful
28708 // return is the same author-declared value the caller was about
28709 // to bind — folding the check and the bind onto one call site,
28710 // matching how every present + roadmapped per-`Acao` consumer
28711 // uses the slot.
28712 let mut c = bare_acao_without_ci();
28713 c.ci = Some(sample_ci_run());
28714 let ci = require_ci(&c).expect("Acao with declared :ci passes");
28715 assert_eq!(ci.workspace, "pleme-io");
28716 assert_eq!(ci.repo, "caixa");
28717 }
28718
28719 #[test]
28720 fn require_ci_rejects_absent_slot_with_typed_view() {
28721 // The fail-before-pass-after pin: pre-lift `caixa-actions`'
28722 // inline `.ok_or_else(|| Error::MissingCi { nome:
28723 // caixa.nome().to_string() })` gate constructed an
28724 // `Error::MissingCi { nome: String }` at exactly one crate's
28725 // call site with no compile-time link to any typed named-caixa
28726 // view the sibling per-renderer entry-gate axes carry. A future
28727 // per-`Acao` consumer (the deferred `sui-supercacheci::canteiro
28728 // ::emit_gha` workflow renderer named in the `caixa-actions`
28729 // crate docs, the future per-`Acao` CR materializer) would
28730 // re-inline the same `.ok_or_else(...)` construction on its own
28731 // call site and open a second untracked `nome: String`-carry
28732 // path — exactly the "feira verb whose error path doesn't name
28733 // the offending caixa" punch-list item the compounding-mandate
28734 // protocol calls out. Lifting the gate onto the typed
28735 // [`MissingCiSlot`] view + [`require_ci`] predicate closes the
28736 // drift potential structurally: every future per-`Acao`
28737 // consumer reaches for the same one-liner + `#[from]` and gets
28738 // the diagnostic-naming-the-offending-caixa contract for free.
28739 let c = bare_acao_without_ci();
28740 let err = require_ci(&c).unwrap_err();
28741 assert_eq!(err.nome, "hello-rio");
28742 }
28743
28744 #[test]
28745 fn require_ci_routes_offending_nome_via_caixa_nome_accessor() {
28746 // Pin: the [`MissingCiSlot::nome`] `String` the constructor
28747 // writes must be a byte-identical copy of what the lifted
28748 // [`crate::Caixa::nome`] accessor returns for the same
28749 // [`Caixa`] input — the same routing pin discipline the peer
28750 // [`require_kind`] / [`require_single_servico`] typed views
28751 // already carry, so a future regression that re-inlines a raw
28752 // `caixa.nome.clone()` `String::clone()` of the underlying
28753 // field at the constructor site (which would silently ignore
28754 // any future `CaixaNome` newtype the [`crate::Caixa::nome`]
28755 // accessor upgrades to project the display byte-string of)
28756 // trips here before the drift lands on a per-consumer `#[from]`
28757 // arm.
28758 let mut c = bare_acao_without_ci();
28759 c.nome = "missing-ci-pin".into();
28760 let expected_nome_via_accessor = c.nome().to_string();
28761 assert_eq!(
28762 expected_nome_via_accessor, "missing-ci-pin",
28763 "the mutated fixture's `:nome` must be observable through \
28764 the accessor before the `:ci` gate fires",
28765 );
28766 let err = require_ci(&c).unwrap_err();
28767 assert_eq!(
28768 err.nome, expected_nome_via_accessor,
28769 "the MissingCiSlot's `nome` field must equal \
28770 `caixa.nome().to_string()` — the typed-view constructor \
28771 must route through the lifted [`Caixa::nome`] accessor's \
28772 `.to_string()` extension, not the raw `caixa.nome.clone()` \
28773 `String::clone()` of the underlying field",
28774 );
28775 }
28776
28777 #[test]
28778 fn missing_ci_slot_display_names_offending_caixa_nome() {
28779 // The Display impl is the load-bearing surface every per-
28780 // `Acao` consumer's `#[error("{0}")] MissingCi(#[from]
28781 // MissingCiSlot)` arm prints through. Pinning the exact rendered
28782 // form so a future format change is a one-line edit + a one-line
28783 // test update, not a silent regression of the diagnostic
28784 // clarity. Same shape every peer per-axis lift carries.
28785 let err = MissingCiSlot {
28786 nome: "hello-acao".into(),
28787 };
28788 let msg = format!("{err}");
28789 assert!(
28790 msg.contains("hello-acao"),
28791 "Display must name the offending caixa nome (got: {msg:?})"
28792 );
28793 assert!(
28794 msg.contains(":ci"),
28795 "Display must name the missing `:ci` slot (got: {msg:?})"
28796 );
28797 }
28798
28799 // ── CiDecomposeFailure — per-`Acao` decompose-failure diagnostic axis ─
28800
28801 #[test]
28802 fn ci_decompose_failure_carries_offending_nome_and_source_verbatim() {
28803 // Fail-before-pass-after pin on the [`CiDecomposeFailure`] typed
28804 // view: the constructor writes the offending caixa's `:nome`
28805 // (routed through the lifted [`crate::Caixa::nome`] accessor's
28806 // `.to_string()` extension by every consumer) alongside the
28807 // borrowed [`canteiro_types::DecomposeError`] source verbatim,
28808 // so a per-`Acao` consumer that fans on the specific
28809 // decompose-failure arm reaches for `err.source` directly
28810 // rather than re-parsing the Display bytes. Peer of the sibling
28811 // [`MissingCiSlot`] typed view's `nome`-carrying pin — extends
28812 // the same "one typed view per axis, carrying the offending
28813 // caixa's `:nome` + axis-specific detail" discipline onto the
28814 // second per-`Acao` diagnostic axis after the presence-gate
28815 // axis.
28816 let err = CiDecomposeFailure {
28817 nome: "hello-acao".into(),
28818 source: canteiro_types::DecomposeError::Cycle,
28819 };
28820 assert_eq!(err.nome, "hello-acao");
28821 assert_eq!(err.source, canteiro_types::DecomposeError::Cycle);
28822 }
28823
28824 #[test]
28825 fn ci_decompose_failure_display_names_offending_caixa_nome_and_source() {
28826 // The Display impl is the load-bearing surface every per-`Acao`
28827 // consumer's `#[error("{0}")] Decompose(#[from]
28828 // CiDecomposeFailure)` arm prints through. Pinning the exact
28829 // rendered form so a future format change is a one-line edit +
28830 // a one-line test update, not a silent regression of the
28831 // diagnostic clarity — same shape every peer per-axis lift
28832 // carries.
28833 let err = CiDecomposeFailure {
28834 nome: "hello-acao".into(),
28835 source: canteiro_types::DecomposeError::Cycle,
28836 };
28837 let msg = format!("{err}");
28838 assert!(
28839 msg.contains("hello-acao"),
28840 "Display must name the offending caixa nome (got: {msg:?})"
28841 );
28842 assert!(
28843 msg.contains(":ci"),
28844 "Display must name the `:ci` slot the decompose failed on \
28845 (got: {msg:?})"
28846 );
28847 assert!(
28848 msg.contains("decompose"),
28849 "Display must name the decompose axis (got: {msg:?})"
28850 );
28851 }
28852
28853 #[test]
28854 fn ci_decompose_failure_exposes_source_via_error_trait() {
28855 // Pin: the [`CiDecomposeFailure`] type routes its
28856 // [`canteiro_types::DecomposeError`] carrier through the
28857 // `#[source]` [`thiserror::Error`] derive so downstream
28858 // `std::error::Error::source()`-consuming diagnostic frameworks
28859 // (`anyhow`'s chain formatter, `tracing`'s `error!` event
28860 // capture, the future `feira lint` sub-diagnostic emitter) see
28861 // the underlying `DecomposeError` arm through the standard
28862 // trait rather than only through the flattened Display bytes.
28863 // Peer of the sibling per-slot `#[source]` wiring the caixa-*
28864 // renderers already carry on their own typed-view error
28865 // wrappers.
28866 let err = CiDecomposeFailure {
28867 nome: "hello-acao".into(),
28868 source: canteiro_types::DecomposeError::Cycle,
28869 };
28870 let src = std::error::Error::source(&err)
28871 .expect("CiDecomposeFailure must expose its DecomposeError via Error::source()");
28872 // The `Error::source()` trait method returns a `&dyn Error`
28873 // borrow of the underlying `DecomposeError`, so its Display
28874 // bytes must equal the source arm's own Display bytes — a
28875 // future accidental collapse of the `#[source]` wiring (which
28876 // would erase the source chain and force downstream
28877 // `anyhow::Chain` consumers back onto Display re-parsing) trips
28878 // here at caixa-core build time.
28879 let src_msg = format!("{src}");
28880 let expected_msg = format!("{}", canteiro_types::DecomposeError::Cycle);
28881 assert_eq!(src_msg, expected_msg);
28882 }
28883
28884 // ── decompose_ci — per-`Acao` decompose-axis predicate ────────────
28885
28886 fn cyclic_ci_run() -> canteiro_types::CiRun {
28887 // A minimal two-node cycle: `a` depends on `b`, `b` depends on
28888 // `a`. Every failure mode `canteiro_types::decompose` refuses
28889 // (duplicate node name, missing dependency, cycle) would work as
28890 // a fixture; the cycle arm is the same one the `caixa-actions`
28891 // per-`Acao` renderer's own `validate_rejects_a_cyclic_ci_run`
28892 // test already reads for, so both the substrate primitive's own
28893 // pin and the consumer's byte-parity pin share one canonical
28894 // fixture shape.
28895 canteiro_types::CiRun {
28896 workspace: "pleme-io".into(),
28897 repo: "caixa".into(),
28898 nodes: vec![
28899 canteiro_types::CiNode::new(
28900 "a",
28901 canteiro_types::EnvClass::None,
28902 canteiro_types::ActionRef {
28903 name: "a".into(),
28904 command: "true".into(),
28905 args: vec![],
28906 },
28907 vec!["b".into()],
28908 ),
28909 canteiro_types::CiNode::new(
28910 "b",
28911 canteiro_types::EnvClass::None,
28912 canteiro_types::ActionRef {
28913 name: "b".into(),
28914 command: "true".into(),
28915 args: vec![],
28916 },
28917 vec!["a".into()],
28918 ),
28919 ],
28920 }
28921 }
28922
28923 fn linear_ci_run() -> canteiro_types::CiRun {
28924 // A minimal two-node acyclic run: `test` depends on `build`.
28925 // Same shape as the `caixa-actions` `validate_decomposes_a_two_
28926 // node_build_then_test_run` happy-path test — one shared
28927 // canonical fixture for every downstream substrate consumer.
28928 canteiro_types::CiRun {
28929 workspace: "pleme-io".into(),
28930 repo: "caixa".into(),
28931 nodes: vec![
28932 canteiro_types::CiNode::new(
28933 "build",
28934 canteiro_types::EnvClass::None,
28935 canteiro_types::ActionRef {
28936 name: "build".into(),
28937 command: "true".into(),
28938 args: vec![],
28939 },
28940 vec![],
28941 ),
28942 canteiro_types::CiNode::new(
28943 "test",
28944 canteiro_types::EnvClass::None,
28945 canteiro_types::ActionRef {
28946 name: "test".into(),
28947 command: "true".into(),
28948 args: vec![],
28949 },
28950 vec!["build".into()],
28951 ),
28952 ],
28953 }
28954 }
28955
28956 #[test]
28957 fn decompose_ci_accepts_valid_ci_run_and_returns_canteiro_dag() {
28958 // The happy path: a valid two-node acyclic run decomposes
28959 // cleanly through `decompose_ci`, returning the owned
28960 // `canteiro_types::CanteiroDag` the sibling `canteiro_types::
28961 // decompose` returns — the substrate primitive is a
28962 // pass-through on success, only wrapping the error arm in a
28963 // typed named-caixa view. Matches the peer `require_ci`
28964 // presence-axis happy path (accept-with-borrowed-CiRun) —
28965 // extends the "one primitive per axis, pass-through on success"
28966 // discipline onto the decompose axis.
28967 let c = bare_acao_without_ci();
28968 let ci = linear_ci_run();
28969 let cd = decompose_ci(&c, &ci).expect("valid acyclic CiRun decomposes cleanly");
28970 // The topo_order() call on a successful decompose is infallible
28971 // by construction (no cycles present), so a downstream consumer
28972 // reaches for the DAG's own algebra directly rather than a
28973 // second gate. Iterating the returned order (rather than
28974 // asserting on a concrete container shape) keeps the pin
28975 // agnostic to whether topo_order returns Vec<NodeId>,
28976 // SmallVec<NodeId>, or any future returned collection.
28977 let topo = cd
28978 .topo_order()
28979 .expect("acyclic CanteiroDag returns a valid topo_order");
28980 assert_eq!(
28981 topo.iter().count(),
28982 2,
28983 "topo_order on a two-node acyclic run must yield two node ids"
28984 );
28985 }
28986
28987 #[test]
28988 fn decompose_ci_rejects_cyclic_ci_run_with_typed_view() {
28989 // The fail-before-pass-after pin: pre-lift `caixa-actions`'
28990 // inline `.map_err(|source| CiDecomposeFailure { nome: nome
28991 // .clone(), source })` gate constructed a `CiDecomposeFailure`
28992 // at exactly one crate's call site with no compile-time link to
28993 // any typed named-caixa predicate the sibling per-`Acao` /
28994 // per-renderer entry-gate axes carry. A future per-`Acao`
28995 // consumer (the deferred `sui-supercacheci::canteiro::emit_gha`
28996 // workflow renderer named in the `caixa-actions` crate docs, a
28997 // future per-`Acao` CR materializer's admission webhook) would
28998 // re-inline the same `.map_err(...)` construction on its own
28999 // call site and open a second untracked
29000 // `caixa.nome().to_string()` re-projection path — exactly the
29001 // "feira verb whose error path doesn't name the offending
29002 // caixa" punch-list item the compounding-mandate protocol calls
29003 // out. Lifting the gate onto the typed `decompose_ci` predicate
29004 // closes the drift potential structurally: every future
29005 // per-`Acao` consumer reaches for the same one-liner + `#[from]`
29006 // and gets the diagnostic-naming-the-offending-caixa contract
29007 // for free.
29008 let c = bare_acao_without_ci();
29009 let ci = cyclic_ci_run();
29010 // `unwrap_err()` needs `T: Debug`, and the Ok half here carries
29011 // `canteiro_types::CanteiroDag`, which does not derive it at the
29012 // pinned sui rev — so the whole caixa-core test target failed to
29013 // COMPILE. A let-else says the same thing without borrowing a
29014 // bound from a foreign type we do not own.
29015 let Err(err) = decompose_ci(&c, &ci) else {
29016 panic!("a cyclic CiRun must fail decompose_ci");
29017 };
29018 assert_eq!(err.nome, "hello-rio");
29019 assert_eq!(err.source, canteiro_types::DecomposeError::Cycle);
29020 }
29021
29022 #[test]
29023 fn decompose_ci_routes_offending_nome_via_caixa_nome_accessor() {
29024 // Pin: the `CiDecomposeFailure::nome` `String` the constructor
29025 // writes must be a byte-identical copy of what the lifted
29026 // `crate::Caixa::nome` accessor returns for the same `Caixa`
29027 // input — the same routing pin discipline the peer
29028 // `require_kind` / `require_single_servico` / `require_ci`
29029 // typed views already carry, so a future regression that
29030 // re-inlines a raw `caixa.nome.clone()` `String::clone()` of
29031 // the underlying field at the constructor site (which would
29032 // silently ignore any future `CaixaNome` newtype the
29033 // `crate::Caixa::nome` accessor upgrades to project the display
29034 // byte-string of) trips here before the drift lands on a
29035 // per-consumer `#[from]` arm.
29036 let mut c = bare_acao_without_ci();
29037 c.nome = "decompose-ci-pin".into();
29038 let expected_nome_via_accessor = c.nome().to_string();
29039 assert_eq!(
29040 expected_nome_via_accessor, "decompose-ci-pin",
29041 "the mutated fixture's `:nome` must be observable through \
29042 the accessor before the decompose gate fires",
29043 );
29044 let ci = cyclic_ci_run();
29045 // `unwrap_err()` needs `T: Debug`, and the Ok half here carries
29046 // `canteiro_types::CanteiroDag`, which does not derive it at the
29047 // pinned sui rev — so the whole caixa-core test target failed to
29048 // COMPILE. A let-else says the same thing without borrowing a
29049 // bound from a foreign type we do not own.
29050 let Err(err) = decompose_ci(&c, &ci) else {
29051 panic!("a cyclic CiRun must fail decompose_ci");
29052 };
29053 assert_eq!(
29054 err.nome, expected_nome_via_accessor,
29055 "the CiDecomposeFailure's `nome` field must equal \
29056 `caixa.nome().to_string()` — the `decompose_ci` predicate \
29057 must route through the lifted `Caixa::nome` accessor's \
29058 `.to_string()` extension, not a raw `caixa.nome.clone()` \
29059 `String::clone()` of the underlying field",
29060 );
29061 }
29062
29063 // ── ci_declared_edge_count — per-`Acao` declared-edge-count axis ─
29064
29065 #[test]
29066 fn ci_declared_edge_count_returns_zero_for_leaf_only_run() {
29067 // The empty-edges arm: a `CiRun` whose every node carries an
29068 // empty `deps` list has zero declared edges. Pins the
29069 // `usize::sum()` accumulator's starting value on the
29070 // no-fan-out shape a `caixa-init`-scaffolded `:kind Acao` a
29071 // caixa's stub `:ci` slot lands as before the author wires
29072 // any `deps`. Fail-before-pass-after guard: pre-lift there was
29073 // no substrate primitive, so an author-scaffolded no-deps run
29074 // would have had its `edge_count = 0` re-derived at every
29075 // consumer site through the same open-coded arithmetic. This
29076 // test now anchors the projection to `ci_declared_edge_count`.
29077 let ci = canteiro_types::CiRun {
29078 workspace: "pleme-io".into(),
29079 repo: "caixa".into(),
29080 nodes: vec![
29081 canteiro_types::CiNode::new(
29082 "build",
29083 canteiro_types::EnvClass::None,
29084 canteiro_types::ActionRef {
29085 name: "build".into(),
29086 command: "true".into(),
29087 args: vec![],
29088 },
29089 vec![],
29090 ),
29091 canteiro_types::CiNode::new(
29092 "lint",
29093 canteiro_types::EnvClass::None,
29094 canteiro_types::ActionRef {
29095 name: "lint".into(),
29096 command: "true".into(),
29097 args: vec![],
29098 },
29099 vec![],
29100 ),
29101 ],
29102 };
29103 assert_eq!(
29104 ci_declared_edge_count(&ci),
29105 0,
29106 "a two-leaf-node `:ci` run with empty `deps` lists carries \
29107 zero declared edges — the substrate primitive's `usize` \
29108 accumulator must start at zero and pass through untouched",
29109 );
29110 }
29111
29112 #[test]
29113 fn ci_declared_edge_count_returns_deps_sum_across_nodes() {
29114 // The multi-arity arm: a `CiRun` whose nodes carry `deps`
29115 // lists of arities 0/1/2 has declared-edge-count 3 (0+1+2).
29116 // Pins that the substrate primitive routes the sum through
29117 // *every* node's `deps.len()` rather than only the first
29118 // node's (a future regression that collapsed the `map(...)`
29119 // + `sum()` fold onto a `first()` / `next()` shape would
29120 // silently under-count the declared edges — the arity-3
29121 // fixture surfaces it here before the drift lands on the
29122 // `caixa-actions::validate` production `edge_count` artifact).
29123 let ci = canteiro_types::CiRun {
29124 workspace: "pleme-io".into(),
29125 repo: "caixa".into(),
29126 nodes: vec![
29127 canteiro_types::CiNode::new(
29128 "build",
29129 canteiro_types::EnvClass::None,
29130 canteiro_types::ActionRef {
29131 name: "build".into(),
29132 command: "true".into(),
29133 args: vec![],
29134 },
29135 vec![],
29136 ),
29137 canteiro_types::CiNode::new(
29138 "test",
29139 canteiro_types::EnvClass::None,
29140 canteiro_types::ActionRef {
29141 name: "test".into(),
29142 command: "true".into(),
29143 args: vec![],
29144 },
29145 vec!["build".into()],
29146 ),
29147 canteiro_types::CiNode::new(
29148 "publish",
29149 canteiro_types::EnvClass::None,
29150 canteiro_types::ActionRef {
29151 name: "publish".into(),
29152 command: "true".into(),
29153 args: vec![],
29154 },
29155 vec!["build".into(), "test".into()],
29156 ),
29157 ],
29158 };
29159 assert_eq!(
29160 ci_declared_edge_count(&ci),
29161 3,
29162 "declared-edge-count on a 0/1/2-arity node list is the sum \
29163 (0 + 1 + 2 = 3) — the primitive must fold over every node, \
29164 not just the first / last / any-single-index shape",
29165 );
29166 }
29167
29168 #[test]
29169 fn ci_declared_edge_count_counts_edges_before_decompose_gate() {
29170 // The count-is-shape-only arm: an author-declared *cyclic*
29171 // `:ci` run — the exact fixture `decompose_ci` refuses at the
29172 // sibling axis — still carries its declared edge count as a
29173 // property of the *borrowed run's shape*, not of the owned
29174 // `CanteiroDag` `decompose_ci` (would have) returned. Pins
29175 // that a future consumer that wants the declared-edge summary
29176 // *before* running `decompose_ci` (a `feira lint --acao`
29177 // per-caixa pre-flight report that names the declared edge
29178 // count on both accept + reject arms of the sibling
29179 // `decompose_ci` gate) reads a stable count on both arms.
29180 // The two-node cycle `a → b → a` from `cyclic_ci_run()`
29181 // carries exactly 2 declared edges (one per node's singleton
29182 // `deps`), so the primitive returns 2 without ever routing
29183 // through `canteiro_types::decompose`.
29184 let ci = cyclic_ci_run();
29185 assert_eq!(
29186 ci_declared_edge_count(&ci),
29187 2,
29188 "the two-node cycle carries 2 declared `deps` edges (one \
29189 per node's singleton `deps`) — the primitive must read the \
29190 count off the borrowed run's node-list shape, not off the \
29191 `decompose_ci`-produced `CanteiroDag`'s edge algebra",
29192 );
29193 }
29194
29195 #[test]
29196 fn ci_declared_edge_count_matches_open_coded_sum_across_shapes() {
29197 // Byte-parity pin — the three-path convergence discipline
29198 // every peer per-`Acao` substrate primitive carries: the
29199 // primitive's return must equal the open-coded
29200 // `ci.nodes.iter().map(|n| n.deps.len()).sum::<usize>()`
29201 // expression at each of the three canonical `:ci` run shapes
29202 // this test module already carries (`linear_ci_run` — the
29203 // canonical happy-path with one edge, `cyclic_ci_run` — the
29204 // canonical rejected-by-`decompose_ci` shape with two edges,
29205 // and the empty-edges no-fan-out shape the peer
29206 // `ci_declared_edge_count_returns_zero_for_leaf_only_run`
29207 // fixture reads). Any future refactor of the primitive's fold
29208 // shape trips here before landing on the consumer's
29209 // `RenderedAcao::edge_count` artifact.
29210 for (label, ci) in [
29211 ("linear-two-node", linear_ci_run()),
29212 ("cyclic-two-node", cyclic_ci_run()),
29213 ] {
29214 let via_primitive = ci_declared_edge_count(&ci);
29215 let via_open_coded: usize = ci.nodes.iter().map(|n| n.deps.len()).sum();
29216 assert_eq!(
29217 via_primitive, via_open_coded,
29218 "{label}: `ci_declared_edge_count` must equal the \
29219 open-coded `.nodes.iter().map(|n| n.deps.len()).sum()` \
29220 the two prior `caixa-actions` open-coded sites carried \
29221 — pre-lift regression check",
29222 );
29223 }
29224 }
29225
29226 // ── require_single_servico / ServicoCountMismatch — V0 Servico-shape ─
29227
29228 #[test]
29229 fn require_single_servico_accepts_singleton_list() {
29230 // The happy path: the canonical V0 Servico carries exactly one
29231 // `:servicos` entry (the ComputeUnit YAML pointer), the same
29232 // shape every in-tree fixture + canonical example uses. Surfaced
29233 // as `Ok(())` so the renderer's call site reads as a one-liner
29234 // gate beside the peer [`require_kind`] check rather than a
29235 // typed pattern match.
29236 let c = bare_servico();
29237 assert_eq!(
29238 c.servicos.len(),
29239 1,
29240 "fixture pin: bare_servico() is singleton"
29241 );
29242 require_single_servico(&c).unwrap();
29243 }
29244
29245 #[test]
29246 fn require_single_servico_rejects_empty_list_with_typed_mismatch() {
29247 // A Servico-kind caixa with zero `:servicos` entries fails
29248 // `require_single_servico` with a typed [`ServicoCountMismatch`]
29249 // view that names the offending caixa's `:nome` + the actual
29250 // count (0). Pinning the typed shape so a future Display-format
29251 // tweak can't silently drop either of the two load-bearing
29252 // fields (which would regress the "feira verb whose error path
29253 // doesn't name the offending caixa" punch-list item the protocol
29254 // calls out — same shape every peer per-axis lift carries).
29255 let mut c = bare_servico();
29256 c.servicos = vec![];
29257 let err = require_single_servico(&c).unwrap_err();
29258 assert_eq!(err.nome, "hello-rio");
29259 assert_eq!(err.count, 0);
29260 }
29261
29262 #[test]
29263 fn require_single_servico_rejects_multi_entry_list_with_typed_mismatch() {
29264 // The peer arm on the upper-bound axis: a Servico-kind caixa
29265 // with ≥ 2 `:servicos` entries fails the same gate, with the
29266 // typed view carrying the actual count (2). Both empty and
29267 // multi-entry lists land on the same [`ServicoCountMismatch`]
29268 // arm — the V0 contract requires *exactly* one entry, not
29269 // *at-least* one — so the single helper closes both directions
29270 // of the V0 invariant in one call site.
29271 let mut c = bare_servico();
29272 c.servicos = vec![
29273 "servicos/hello-rio.computeunit.yaml".into(),
29274 "servicos/extra.computeunit.yaml".into(),
29275 ];
29276 let err = require_single_servico(&c).unwrap_err();
29277 assert_eq!(err.nome, "hello-rio");
29278 assert_eq!(err.count, 2);
29279 }
29280
29281 #[test]
29282 fn require_single_servico_routes_offending_nome_via_caixa_nome_accessor() {
29283 // Peer to the sibling
29284 // [`require_kind_routes_offending_nome_via_caixa_nome_accessor`]
29285 // pin on the V0 Servico-shape gate's `:nome`-carry axis:
29286 // the [`ServicoCountMismatch::nome`] `String` the constructor
29287 // writes must be a byte-identical copy of what the lifted
29288 // [`crate::Caixa::nome`] accessor returns. Same 9842a4b-shaped
29289 // routing pin the substrate's own [`crate::LayoutInvariants::verify`]
29290 // wrap-envelope emitters carry, extended here to the second of
29291 // the two [`crate::render`]-module typed-view constructor sites
29292 // that carried a raw `caixa.nome.clone()` `String::clone()`
29293 // field access at the pre-converge state.
29294 let mut c = bare_servico();
29295 c.servicos = vec![];
29296 c.nome = "servico-count-pin".into();
29297 let expected_nome_via_accessor = c.nome().to_string();
29298 assert_eq!(
29299 expected_nome_via_accessor, "servico-count-pin",
29300 "the mutated fixture's `:nome` must be observable through \
29301 the accessor before the servico-count gate fires",
29302 );
29303 let err = require_single_servico(&c).unwrap_err();
29304 assert_eq!(
29305 err.nome, expected_nome_via_accessor,
29306 "the ServicoCountMismatch's `nome` field must equal \
29307 `caixa.nome().to_string()` — the typed-view constructor \
29308 must route through the lifted [`Caixa::nome`] accessor's \
29309 `.to_string()` extension, not the raw `caixa.nome.clone()` \
29310 `String::clone()` of the underlying field",
29311 );
29312 }
29313
29314 #[test]
29315 fn servico_count_mismatch_display_names_offending_caixa_nome() {
29316 // The Display impl is the load-bearing surface every renderer's
29317 // `#[error("{0}")] UnsupportedServicoCount(#[from]
29318 // ServicoCountMismatch)` arm prints through. Pinning the exact
29319 // rendered form so a future format change is a one-line edit +
29320 // a one-line test update, not a silent regression of the
29321 // diagnostic clarity that motivated the lift (the prior
29322 // per-renderer `UnsupportedServicoCount(usize)` arm named only
29323 // the count). Same shape every peer [`KindMismatch`] / typed-
29324 // view Display tests pin.
29325 let err = ServicoCountMismatch {
29326 nome: "checkout".into(),
29327 count: 3,
29328 };
29329 let msg = format!("{err}");
29330 assert!(
29331 msg.contains("checkout"),
29332 "Display must name the offending caixa nome (got: {msg:?})"
29333 );
29334 assert!(
29335 msg.contains('3'),
29336 "Display must name the actual count (got: {msg:?})"
29337 );
29338 assert!(
29339 msg.contains(":servicos"),
29340 "Display must name the offending field axis (got: {msg:?})"
29341 );
29342 assert!(
29343 msg.contains("exactly one"),
29344 "Display must name the V0 invariant (got: {msg:?})"
29345 );
29346 }
29347
29348 #[test]
29349 fn overlay_kind_agnostic_for_field_projection() {
29350 // The helper projects fields, not kind — every Caixa carries
29351 // the M2 slot fields by construction. Renderer-level kind
29352 // gates (NotAServico in caixa-helm / caixa-flux) are the
29353 // shape filter; this helper is the field projector. Keeping
29354 // them separate means the same overlay can apply to any
29355 // future per-kind renderer (e.g. when M2.4 supervisor
29356 // rendering acquires its own M2-shaped overlay path).
29357 let mut c = bare_servico();
29358 c.kind = CaixaKind::Biblioteca;
29359 c.servicos = vec![];
29360 c.limits = Some(LimitsSpec {
29361 memory: Some(crate::LIMITS_MEMORY_WASM32_PAGE_BYTES),
29362 ..Default::default()
29363 });
29364 let overlay = servico_m2_overlay(&c).unwrap();
29365 assert!(overlay.contains_key(M2_KEY_LIMITS));
29366 }
29367
29368 // ── require_v0_servico_shape — compound V0-shape entry gate ──────
29369
29370 /// Local `thiserror`-shaped renderer-error stand-in that mirrors the
29371 /// three production callers' shape (`caixa-flux::Error`,
29372 /// `caixa-helm::Error`) at the two `#[from]` variants the compound
29373 /// helper's `E: From<KindMismatch> + From<ServicoCountMismatch>`
29374 /// bound targets. Pinning the shape here so the compound helper's
29375 /// type-inference contract is unit-testable inside caixa-core
29376 /// without a workspace-crate dependency (which would bloat the
29377 /// build graph).
29378 #[derive(Debug, thiserror::Error)]
29379 enum RendererStandIn {
29380 #[error("{0}")]
29381 NotAServico(#[from] KindMismatch),
29382 #[error("{0}")]
29383 UnsupportedServicoCount(#[from] ServicoCountMismatch),
29384 }
29385
29386 #[test]
29387 fn require_v0_servico_shape_accepts_v0_servico() {
29388 // Happy path: a `:kind Servico` caixa with exactly one
29389 // `:servicos` entry — the canonical V0 shape every per-Servico
29390 // renderer's entry-point sees — passes the compound gate. Same
29391 // outcome as the two-line pair the compound helper replaces:
29392 // both predicates surface `Ok(())`, and the compound helper's
29393 // return type carries the caller's `E` inferred from the `?`
29394 // context (unit test uses [`RendererStandIn`] as the stand-in
29395 // for `caixa-flux::Error` / `caixa-helm::Error`).
29396 let c = bare_servico();
29397 let r: Result<(), RendererStandIn> = require_v0_servico_shape(&c);
29398 r.expect("v0 servico shape accepted");
29399 }
29400
29401 #[test]
29402 fn require_v0_servico_shape_forwards_kind_mismatch_first() {
29403 // Order pin: the kind gate fires before the count gate, so a
29404 // `:kind Biblioteca` caixa with zero `:servicos` entries
29405 // surfaces the [`KindMismatch`] arm (the more actionable
29406 // diagnostic — the author has the wrong `:kind`), not the
29407 // [`ServicoCountMismatch`] arm (a downstream consequence of
29408 // the mis-kinded input). Both invariants are violated on this
29409 // input, so the ordering matters — reversing it would flip
29410 // every current caller's diagnostic on a mis-kinded input.
29411 let mut c = bare_servico();
29412 c.kind = CaixaKind::Biblioteca;
29413 c.servicos = vec![];
29414 let err: RendererStandIn = require_v0_servico_shape(&c).unwrap_err();
29415 match err {
29416 RendererStandIn::NotAServico(k) => {
29417 assert_eq!(k.nome, "hello-rio");
29418 assert_eq!(k.expected, CaixaKind::Servico);
29419 assert_eq!(k.actual, CaixaKind::Biblioteca);
29420 }
29421 RendererStandIn::UnsupportedServicoCount(_) => {
29422 panic!("kind gate must fire before count gate on mis-kinded input")
29423 }
29424 }
29425 }
29426
29427 #[test]
29428 fn require_v0_servico_shape_forwards_count_mismatch_on_kind_match() {
29429 // A `:kind Servico` caixa with the wrong `:servicos` count
29430 // (empty or multi-entry) passes the kind gate and lands on the
29431 // [`ServicoCountMismatch`] arm — the same typed view every
29432 // per-renderer `#[from] ServicoCountMismatch` arm already
29433 // surfaces at the two-line pair this helper replaces. Both
29434 // directions of the V0 count invariant (empty AND ≥ 2) land on
29435 // the same arm — pinning the multi-entry direction here; the
29436 // empty direction is covered by the peer
29437 // `require_single_servico_rejects_empty_list_with_typed_mismatch`
29438 // test on the single-axis primitive.
29439 let mut c = bare_servico();
29440 c.servicos = vec![
29441 "servicos/hello-rio.computeunit.yaml".into(),
29442 "servicos/extra.computeunit.yaml".into(),
29443 ];
29444 let err: RendererStandIn = require_v0_servico_shape(&c).unwrap_err();
29445 match err {
29446 RendererStandIn::UnsupportedServicoCount(c) => {
29447 assert_eq!(c.nome, "hello-rio");
29448 assert_eq!(c.count, 2);
29449 }
29450 RendererStandIn::NotAServico(_) => {
29451 panic!("count gate must fire when kind gate passes")
29452 }
29453 }
29454 }
29455
29456 #[test]
29457 fn require_v0_servico_shape_matches_two_line_pair_semantic() {
29458 // Equivalence pin: on every input, the compound helper's
29459 // Ok/Err discrimination matches the two-line pair verbatim —
29460 // the lift is a behavioral no-op at the caller boundary. Peer
29461 // to the sibling `entry_or_default_<variant>` equivalence
29462 // tests that pin the lifted primitive against the inline
29463 // block it replaces.
29464 //
29465 // Three axes covered: V0 shape (Ok/Ok), kind gate fires
29466 // (Err/Ok on the two-line pair — pair short-circuits at the
29467 // kind gate), count gate fires (Ok/Err on the two-line pair —
29468 // pair reaches the count gate).
29469 let cases: Vec<(CaixaKind, Vec<String>)> = vec![
29470 (CaixaKind::Servico, vec!["servicos/x.yaml".into()]),
29471 (CaixaKind::Biblioteca, vec![]),
29472 (CaixaKind::Servico, vec![]),
29473 (CaixaKind::Aplicacao, vec!["servicos/x.yaml".into()]),
29474 (
29475 CaixaKind::Servico,
29476 vec!["servicos/a.yaml".into(), "servicos/b.yaml".into()],
29477 ),
29478 ];
29479 for (kind, servicos) in cases {
29480 let mut c = bare_servico();
29481 c.kind = kind;
29482 c.servicos = servicos;
29483 let pair: Result<(), RendererStandIn> = (|| {
29484 require_kind(&c, CaixaKind::Servico)?;
29485 require_single_servico(&c)?;
29486 Ok(())
29487 })();
29488 let compound: Result<(), RendererStandIn> = require_v0_servico_shape(&c);
29489 assert_eq!(
29490 pair.is_ok(),
29491 compound.is_ok(),
29492 "compound helper must match two-line pair on kind={kind:?} servicos.len()={}",
29493 c.servicos.len(),
29494 );
29495 }
29496 }
29497
29498 // ── require_aplicacao_view — compound per-Aplicacao entry gate ───
29499
29500 /// Local `thiserror`-shaped renderer-error stand-in that mirrors
29501 /// `caixa-mesh::Error`'s two `#[from]` arms at the compound
29502 /// helper's `E: From<KindMismatch> + From<AplicacaoError>` bound.
29503 /// Same discipline as the sibling [`RendererStandIn`] stand-in on
29504 /// the peer per-Servico [`require_v0_servico_shape`] gate: pins
29505 /// the compound helper's type-inference contract inside caixa-core
29506 /// without a workspace-crate dependency (which would bloat the
29507 /// build graph).
29508 #[derive(Debug, thiserror::Error)]
29509 enum AplicacaoRendererStandIn {
29510 #[error("{0}")]
29511 NotAnAplicacao(#[from] KindMismatch),
29512 #[error("{0}")]
29513 InvalidAplicacao(#[from] crate::aplicacao::AplicacaoError),
29514 }
29515
29516 fn bare_aplicacao() -> Caixa {
29517 let mut c = bare_servico();
29518 c.nome = "checkout".into();
29519 c.kind = CaixaKind::Aplicacao;
29520 c.servicos = vec![];
29521 c.membros = vec![
29522 crate::aplicacao::Membro {
29523 caixa: "cart".into(),
29524 versao: "^0.1".into(),
29525 },
29526 crate::aplicacao::Membro {
29527 caixa: "catalog".into(),
29528 versao: "^0.1".into(),
29529 },
29530 ];
29531 // `:placement` needs at least one named cluster (every strategy
29532 // uses the list as a hosting/takeover/shard pool per
29533 // MESH-COMPOSITION §II.1/§II.4); the fold-through
29534 // [`Caixa::aplicacao_view`] uses `Placement::default()` which
29535 // carries an empty `:clusters` and would trip
29536 // `AplicacaoError::PlacementWithoutClusters` at
29537 // `AplicacaoSpec::validate` — the peer per-Aplicacao
29538 // renderer fixtures (`caixa-mesh::aplicacao_caixa`) pin the
29539 // same non-empty `:clusters` shape.
29540 c.placement = Some(crate::aplicacao::Placement {
29541 estrategia: crate::aplicacao::PlacementStrategy::SingleNode,
29542 clusters: vec!["default".into()],
29543 affinity: None,
29544 shard_key: None,
29545 });
29546 c
29547 }
29548
29549 #[test]
29550 fn require_aplicacao_view_accepts_valid_aplicacao() {
29551 // Happy path: a `:kind Aplicacao` caixa with a well-formed
29552 // `:membros` stanza — the canonical V0 shape every
29553 // per-Aplicacao renderer's entry-point sees — passes the
29554 // compound three-arm gate and returns a validated
29555 // [`AplicacaoSpec`]. Same outcome as the three-line cascade
29556 // the compound helper replaces: [`require_kind`] passes,
29557 // [`Caixa::aplicacao_view`] returns `Some(spec)`, and
29558 // [`AplicacaoSpec::validate`] passes. Peer to
29559 // `require_v0_servico_shape_accepts_v0_servico` on the
29560 // sibling per-Servico compound gate.
29561 let c = bare_aplicacao();
29562 let spec: crate::aplicacao::AplicacaoSpec =
29563 require_aplicacao_view::<AplicacaoRendererStandIn>(&c)
29564 .expect("valid aplicacao shape accepted");
29565 // Route the per-Aplicacao `:membros` slice-projection through
29566 // the substrate-canonical [`AplicacaoSpec::membros`] `&[Membro]`-
29567 // return accessor rather than the raw `spec.membros` `Vec<Membro>`
29568 // field access, and the per-member `:caixa` scalar-projection
29569 // through the sibling [`crate::aplicacao::Membro::nome`] `&str`-
29570 // return accessor rather than the raw `.caixa` `String`-field
29571 // borrow, so a future rebrand of either storage (a per-cluster
29572 // `:membros`-overlay the caixa-operator reconciles ahead of
29573 // dispatch, an M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
29574 // materializer's per-member alias table, a promotion of the
29575 // per-`Membro` `caixa: String` slot to a typed `ServicoName`
29576 // newtype the accessor materializes behind the same `&str`
29577 // return contract) reaches this per-fixture happy-path
29578 // acceptance-shape probe through the one accessor edit at the
29579 // canonical caixa-core declaration rather than a coordinated
29580 // rewrite that would include this render-side test-fixture
29581 // navigation too. Peer to the sibling caixa-flux
29582 // [`sample_caixa_nome_accessor_byte_equals_raw_field`] (2ffdb44)
29583 // / caixa-crd `round_trip_preserves_core_fields` (1a160cd) /
29584 // caixa-feira load.rs (e853d45) test-side accessor
29585 // convergences on the peer per-`Caixa` scalar-axis field —
29586 // extended here onto the render-side per-`AplicacaoSpec`
29587 // `:membros` slice + per-`Membro` `:caixa` scalar axes.
29588 let membros = spec.membros();
29589 assert_eq!(membros.len(), 2);
29590 assert_eq!(membros[0].nome(), "cart");
29591 assert_eq!(membros[1].nome(), "catalog");
29592 }
29593
29594 #[test]
29595 fn require_aplicacao_view_accepts_valid_aplicacao_membros_accessor_byte_equals_raw_field() {
29596 // Byte-parity pin: [`AplicacaoSpec::membros`]'s `&[Membro]`-
29597 // return accessor must project the same slice-length and
29598 // per-entry `:caixa` bytes as the raw `spec.membros`
29599 // `Vec<Membro>` + per-`Membro` `caixa: String` field access
29600 // on the shared per-test [`bare_aplicacao`] fixture the sibling
29601 // [`require_aplicacao_view_accepts_valid_aplicacao`] happy-
29602 // path acceptance pin navigates through. Guards the paired
29603 // per-fixture convergence that just routed the three raw
29604 // `spec.membros.len()` / `spec.membros[0].caixa` /
29605 // `spec.membros[1].caixa` sites through the accessor pair: a
29606 // future implementation of [`AplicacaoSpec::membros`] that
29607 // returned a differently-shaped view (a filter over
29608 // storage-dropping optional members, a cached
29609 // `Cow<[Membro]>` materialization, an operator-side per-CR
29610 // alias-rewritten membership overlay), or a future
29611 // [`crate::aplicacao::Membro::nome`] projection that read a
29612 // canonicalized rewrite (a per-tenant namespace prefix, an
29613 // ASCII-lowered normalization) rather than the raw storage-
29614 // side `.caixa` bytes, would silently split every render-
29615 // side test-fixture navigation that routes through the
29616 // accessors from the storage-side field the peer
29617 // [`AplicacaoSpec::validate`] production membership-lookup
29618 // path still reads through the same accessor pair — this
29619 // pin surfaces the drift at caixa-core build time rather
29620 // than at a downstream per-Aplicacao renderer's
29621 // membership-lookup diagnostic on the fleet.
29622 //
29623 // Same byte-parity-pin discipline the sibling caixa-flux
29624 // `sample_caixa_nome_accessor_byte_equals_raw_field` (2ffdb44)
29625 // + caixa-crd `round_trip_preserves_core_fields` accessor
29626 // convergence (1a160cd) + caixa-feira load.rs (e853d45)
29627 // per-`Caixa` scalar-axis byte-parity pins added to lock the
29628 // peer per-`Caixa` scalar-accessor family against the raw
29629 // field-access at each crate's fixture — extended here onto
29630 // the render-side per-`AplicacaoSpec` `:membros` slice + per-
29631 // `Membro` `:caixa` scalar axes' shared test fixture.
29632 let c = bare_aplicacao();
29633 let spec: crate::aplicacao::AplicacaoSpec =
29634 require_aplicacao_view::<AplicacaoRendererStandIn>(&c)
29635 .expect("valid aplicacao shape accepted");
29636 assert_eq!(
29637 spec.membros().len(),
29638 spec.membros.len(),
29639 "AplicacaoSpec::membros() slice-length must byte-equal \
29640 the raw `membros: Vec<Membro>` field storage's `.len()`; \
29641 any implementation drift here silently splits every \
29642 render-side test-fixture navigation that routes through \
29643 the accessor from the storage-side field the peer \
29644 AplicacaoSpec::validate production membership-lookup \
29645 path still reads through the same accessor"
29646 );
29647 for (i, m) in spec.membros().iter().enumerate() {
29648 assert_eq!(
29649 m.nome(),
29650 spec.membros[i].caixa.as_str(),
29651 "Membro::nome() must borrow the same bytes as the raw \
29652 `caixa: String` field storage at member index {i}; \
29653 any implementation drift here silently splits every \
29654 render-side test-fixture navigation that routes \
29655 through the accessor from the storage-side field the \
29656 peer AplicacaoSpec::validate production membership-\
29657 lookup path still reads through the same accessor"
29658 );
29659 }
29660 }
29661
29662 #[test]
29663 fn require_aplicacao_view_forwards_kind_mismatch_first() {
29664 // Order pin: the kind gate fires before the aplicacao_view
29665 // fold-in + [`AplicacaoSpec::validate`], so a `:kind Servico`
29666 // caixa carrying a well-formed `:membros` stanza (the manifest
29667 // field's documented "silently ignored" case on a non-Aplicacao
29668 // kind) surfaces the [`KindMismatch`] arm — the more actionable
29669 // diagnostic — rather than any spec-side arm the manifest
29670 // author never intended to hit. Reversing the order would flip
29671 // every current caller's diagnostic on a mis-kinded input.
29672 // Peer to `require_v0_servico_shape_forwards_kind_mismatch_first`
29673 // on the sibling per-Servico compound gate.
29674 let mut c = bare_aplicacao();
29675 c.kind = CaixaKind::Servico;
29676 c.servicos = vec!["servicos/hello.computeunit.yaml".into()];
29677 let err: AplicacaoRendererStandIn = require_aplicacao_view(&c).unwrap_err();
29678 match err {
29679 AplicacaoRendererStandIn::NotAnAplicacao(k) => {
29680 assert_eq!(k.nome, "checkout");
29681 assert_eq!(k.expected, CaixaKind::Aplicacao);
29682 assert_eq!(k.actual, CaixaKind::Servico);
29683 }
29684 AplicacaoRendererStandIn::InvalidAplicacao(_) => {
29685 panic!("kind gate must fire before aplicacao-view fold-in on mis-kinded input")
29686 }
29687 }
29688 }
29689
29690 #[test]
29691 fn require_aplicacao_view_forwards_aplicacao_error_on_kind_match() {
29692 // A `:kind Aplicacao` caixa that passes the kind gate but
29693 // fails [`AplicacaoSpec::validate`] (empty `:membros` here —
29694 // the [`AplicacaoError::NoMembros`] arm every Aplicacao must
29695 // satisfy per MESH-COMPOSITION §III.1) lands on the
29696 // [`AplicacaoError`] arm through the compound helper's
29697 // `E: From<AplicacaoError>` bound. Same diagnostic the
29698 // three-line cascade the compound helper replaces surfaces at
29699 // `spec.validate()?`. Peer to
29700 // `require_v0_servico_shape_forwards_count_mismatch_on_kind_match`
29701 // on the sibling per-Servico compound gate.
29702 let mut c = bare_aplicacao();
29703 c.membros = vec![]; // trips AplicacaoError::NoMembros
29704 let err: AplicacaoRendererStandIn = require_aplicacao_view(&c).unwrap_err();
29705 match err {
29706 AplicacaoRendererStandIn::InvalidAplicacao(
29707 crate::aplicacao::AplicacaoError::NoMembros,
29708 ) => {}
29709 AplicacaoRendererStandIn::InvalidAplicacao(other) => {
29710 panic!("expected NoMembros arm, got {other:?}")
29711 }
29712 AplicacaoRendererStandIn::NotAnAplicacao(_) => {
29713 panic!("spec-validate arm must fire when kind gate passes")
29714 }
29715 }
29716 }
29717
29718 #[test]
29719 fn require_aplicacao_view_matches_three_line_cascade_semantic() {
29720 // Equivalence pin: on every input, the compound helper's
29721 // Ok/Err discrimination matches the three-line cascade
29722 // verbatim — the lift is a behavioral no-op at the caller
29723 // boundary. Peer to the sibling
29724 // `require_v0_servico_shape_matches_two_line_pair_semantic`
29725 // equivalence pin on the per-Servico compound gate.
29726 //
29727 // Four axes covered: Aplicacao shape (Ok/Ok), kind gate fires
29728 // (Err/Ok on the cascade — cascade short-circuits at the kind
29729 // gate), spec-validate arm fires (Ok/Err on the cascade —
29730 // cascade reaches [`AplicacaoSpec::validate`]), and a
29731 // mis-kinded caixa with a spec-invalid `:membros` stanza (both
29732 // invariants violated — the kind gate must still fire first).
29733 let cases: Vec<(CaixaKind, Vec<crate::aplicacao::Membro>)> = vec![
29734 (
29735 CaixaKind::Aplicacao,
29736 vec![
29737 crate::aplicacao::Membro {
29738 caixa: "cart".into(),
29739 versao: "^0.1".into(),
29740 },
29741 crate::aplicacao::Membro {
29742 caixa: "catalog".into(),
29743 versao: "^0.1".into(),
29744 },
29745 ],
29746 ),
29747 (CaixaKind::Servico, vec![]),
29748 (CaixaKind::Aplicacao, vec![]),
29749 (
29750 CaixaKind::Biblioteca,
29751 vec![crate::aplicacao::Membro {
29752 caixa: "cart".into(),
29753 versao: "^0.1".into(),
29754 }],
29755 ),
29756 ];
29757 for (kind, membros) in cases {
29758 let mut c = bare_aplicacao();
29759 c.kind = kind;
29760 c.membros = membros.clone();
29761 if kind == CaixaKind::Servico {
29762 c.servicos = vec!["servicos/hello.computeunit.yaml".into()];
29763 } else {
29764 c.servicos = vec![];
29765 }
29766 let cascade: Result<crate::aplicacao::AplicacaoSpec, AplicacaoRendererStandIn> =
29767 (|| {
29768 require_kind(&c, CaixaKind::Aplicacao)?;
29769 let spec = c.aplicacao_view().expect(
29770 "require_kind(Aplicacao) guarantees Caixa::aplicacao_view returns Some",
29771 );
29772 spec.validate()?;
29773 Ok(spec)
29774 })();
29775 let compound: Result<crate::aplicacao::AplicacaoSpec, AplicacaoRendererStandIn> =
29776 require_aplicacao_view(&c);
29777 assert_eq!(
29778 cascade.is_ok(),
29779 compound.is_ok(),
29780 "compound helper must match three-line cascade on kind={kind:?} membros.len()={}",
29781 membros.len(),
29782 );
29783 // Compound helper's Ok-arm return matches cascade's
29784 // Ok-arm return byte-for-byte (via serde YAML round-trip
29785 // — the `AplicacaoSpec` derives `Serialize`, so equal-
29786 // rendering values are the substrate-canonical equality
29787 // signal the peer downstream renderers key off).
29788 if let (Ok(cascade_spec), Ok(compound_spec)) = (cascade, compound) {
29789 assert_eq!(
29790 serde_yaml::to_string(&cascade_spec).expect("cascade AplicacaoSpec serializes"),
29791 serde_yaml::to_string(&compound_spec)
29792 .expect("compound AplicacaoSpec serializes"),
29793 "compound helper's Ok arm must return byte-equal AplicacaoSpec to cascade"
29794 );
29795 }
29796 }
29797 }
29798
29799 // ── require_acao_view — compound per-`Acao` entry gate ───────────
29800
29801 /// Local `thiserror`-shaped renderer-error stand-in that mirrors
29802 /// `caixa-actions::Error`'s three `#[from]` arms at the compound
29803 /// helper's `E: From<KindMismatch> + From<MissingCiSlot> +
29804 /// From<CiDecomposeFailure>` bound. Same discipline as the sibling
29805 /// [`RendererStandIn`] / [`AplicacaoRendererStandIn`] stand-ins on
29806 /// the peer per-Servico [`require_v0_servico_shape`] and
29807 /// per-Aplicacao [`require_aplicacao_view`] compound gates: pins
29808 /// the compound helper's type-inference contract inside caixa-core
29809 /// without a workspace-crate dependency (which would bloat the
29810 /// build graph).
29811 #[derive(Debug, thiserror::Error)]
29812 enum AcaoRendererStandIn {
29813 #[error("{0}")]
29814 NotAnAcao(#[from] KindMismatch),
29815 #[error("{0}")]
29816 MissingCi(#[from] MissingCiSlot),
29817 #[error("{0}")]
29818 Decompose(#[from] CiDecomposeFailure),
29819 }
29820
29821 #[test]
29822 fn require_acao_view_accepts_valid_acao() {
29823 // Happy path: a `:kind Acao` caixa with a well-formed `:ci`
29824 // stanza — the canonical V0 shape every per-`Acao` consumer's
29825 // entry-point sees — passes the compound three-arm gate and
29826 // returns the borrowed [`canteiro_types::CiRun`] paired with
29827 // the owned [`canteiro_types::CanteiroDag`] the substrate
29828 // primitive produced. Same outcome as the three-line prelude
29829 // the compound helper replaces: [`require_kind`] passes,
29830 // [`require_ci`] returns the borrowed slot, [`decompose_ci`]
29831 // accepts the run. Peer to
29832 // `require_aplicacao_view_accepts_valid_aplicacao` and
29833 // `require_v0_servico_shape_accepts_v0_servico` on the sibling
29834 // per-Aplicacao / per-Servico compound gates.
29835 let mut c = bare_acao_without_ci();
29836 c.ci = Some(linear_ci_run());
29837 let (ci, cd) = require_acao_view::<AcaoRendererStandIn>(&c)
29838 .expect("valid Acao shape accepted by compound helper");
29839 assert_eq!(ci.workspace, "pleme-io");
29840 assert_eq!(ci.nodes.len(), 2);
29841 // `topo_order()` is infallible on the DAG the compound helper
29842 // returns, mirroring the substrate-side pass-through pin at
29843 // [`decompose_ci_accepts_valid_ci_run_and_returns_canteiro_dag`].
29844 let topo = cd
29845 .topo_order()
29846 .expect("acyclic CanteiroDag returns a valid topo_order");
29847 assert_eq!(
29848 topo.iter().count(),
29849 2,
29850 "topo_order on the compound helper's returned DAG must yield \
29851 two node ids on a two-node acyclic run"
29852 );
29853 }
29854
29855 #[test]
29856 fn require_acao_view_forwards_kind_mismatch_first() {
29857 // Order pin: the kind gate fires before the presence gate + the
29858 // decompose gate, so a `:kind Servico` caixa carrying a
29859 // well-formed `:ci` stanza (the manifest field's documented
29860 // "silently ignored" case on a non-`Acao` kind) surfaces the
29861 // [`KindMismatch`] arm — the more actionable diagnostic —
29862 // rather than either downstream arm the manifest author never
29863 // intended to hit. Reversing the order would flip every
29864 // current caller's diagnostic on a mis-kinded input. Peer to
29865 // `require_aplicacao_view_forwards_kind_mismatch_first` and
29866 // `require_v0_servico_shape_forwards_kind_mismatch_first` on
29867 // the sibling per-Aplicacao / per-Servico compound gates.
29868 let mut c = bare_acao_without_ci();
29869 c.kind = CaixaKind::Servico;
29870 c.servicos = vec!["servicos/hello.computeunit.yaml".into()];
29871 c.ci = Some(linear_ci_run());
29872 // `unwrap_err()` needs `T: Debug`, and the Ok half here carries
29873 // `canteiro_types::CanteiroDag`, which does not derive it at the
29874 // pinned sui rev — so the whole caixa-core test target failed to
29875 // COMPILE. A let-else says the same thing without borrowing a
29876 // bound from a foreign type we do not own.
29877 let Err(err): Result<_, AcaoRendererStandIn> = require_acao_view(&c) else {
29878 panic!("this fixture must not produce an Acao view");
29879 };
29880 match err {
29881 AcaoRendererStandIn::NotAnAcao(k) => {
29882 assert_eq!(k.nome, "hello-rio");
29883 assert_eq!(k.expected, CaixaKind::Acao);
29884 assert_eq!(k.actual, CaixaKind::Servico);
29885 }
29886 AcaoRendererStandIn::MissingCi(_) => {
29887 panic!("kind gate must fire before presence gate on mis-kinded input")
29888 }
29889 AcaoRendererStandIn::Decompose(_) => {
29890 panic!("kind gate must fire before decompose gate on mis-kinded input")
29891 }
29892 }
29893 }
29894
29895 #[test]
29896 fn require_acao_view_forwards_missing_ci_slot_on_kind_match() {
29897 // A `:kind Acao` caixa that passes the kind gate but declares
29898 // no `:ci` slot lands on the [`MissingCiSlot`] arm through the
29899 // compound helper's `E: From<MissingCiSlot>` bound — the same
29900 // typed view the peer [`require_ci`] presence gate produces at
29901 // the single-axis primitive, propagated through the compound
29902 // gate's second arm.
29903 let c = bare_acao_without_ci();
29904 // `unwrap_err()` needs `T: Debug`, and the Ok half here carries
29905 // `canteiro_types::CanteiroDag`, which does not derive it at the
29906 // pinned sui rev — so the whole caixa-core test target failed to
29907 // COMPILE. A let-else says the same thing without borrowing a
29908 // bound from a foreign type we do not own.
29909 let Err(err): Result<_, AcaoRendererStandIn> = require_acao_view(&c) else {
29910 panic!("this fixture must not produce an Acao view");
29911 };
29912 match err {
29913 AcaoRendererStandIn::MissingCi(m) => {
29914 assert_eq!(m.nome, "hello-rio");
29915 }
29916 AcaoRendererStandIn::NotAnAcao(_) => {
29917 panic!("presence gate must fire when kind gate passes")
29918 }
29919 AcaoRendererStandIn::Decompose(_) => {
29920 panic!("presence gate must fire before decompose gate on missing `:ci` input")
29921 }
29922 }
29923 }
29924
29925 #[test]
29926 fn require_acao_view_forwards_decompose_failure_on_ci_present() {
29927 // A `:kind Acao` caixa that passes the kind + presence gates
29928 // but carries a cyclic `:ci` run lands on the
29929 // [`CiDecomposeFailure`] arm through the compound helper's
29930 // `E: From<CiDecomposeFailure>` bound — the same typed view
29931 // the peer [`decompose_ci`] gate produces at the single-axis
29932 // primitive, propagated through the compound gate's third
29933 // arm.
29934 let mut c = bare_acao_without_ci();
29935 c.ci = Some(cyclic_ci_run());
29936 // `unwrap_err()` needs `T: Debug`, and the Ok half here carries
29937 // `canteiro_types::CanteiroDag`, which does not derive it at the
29938 // pinned sui rev — so the whole caixa-core test target failed to
29939 // COMPILE. A let-else says the same thing without borrowing a
29940 // bound from a foreign type we do not own.
29941 let Err(err): Result<_, AcaoRendererStandIn> = require_acao_view(&c) else {
29942 panic!("this fixture must not produce an Acao view");
29943 };
29944 match err {
29945 AcaoRendererStandIn::Decompose(f) => {
29946 assert_eq!(f.nome, "hello-rio");
29947 assert_eq!(f.source, canteiro_types::DecomposeError::Cycle);
29948 }
29949 AcaoRendererStandIn::NotAnAcao(_) => {
29950 panic!("decompose gate must fire when kind + presence gates pass")
29951 }
29952 AcaoRendererStandIn::MissingCi(_) => {
29953 panic!("decompose gate must fire when presence gate passes")
29954 }
29955 }
29956 }
29957
29958 #[test]
29959 fn require_acao_view_matches_three_line_prelude_semantic() {
29960 // Equivalence pin: on every input, the compound helper's
29961 // Ok/Err discrimination matches the three-line prelude
29962 // verbatim — the lift is a behavioral no-op at the caller
29963 // boundary. Peer to the sibling
29964 // `require_aplicacao_view_matches_three_line_cascade_semantic`
29965 // and `require_v0_servico_shape_matches_two_line_pair_semantic`
29966 // equivalence pins on the per-Aplicacao / per-Servico compound
29967 // gates.
29968 //
29969 // Five axes covered: valid Acao (Ok/Ok), kind gate fires
29970 // (Err/Err on the prelude — prelude short-circuits at the kind
29971 // gate), presence gate fires (Ok/Err on the prelude — prelude
29972 // reaches [`require_ci`]), decompose gate fires (Ok/Err on the
29973 // prelude — prelude reaches [`decompose_ci`]), and a
29974 // mis-kinded caixa with a well-formed `:ci` (both invariants
29975 // relevant — the kind gate must still fire first).
29976 let cases: Vec<(CaixaKind, Option<canteiro_types::CiRun>)> = vec![
29977 (CaixaKind::Acao, Some(linear_ci_run())),
29978 (CaixaKind::Servico, Some(linear_ci_run())),
29979 (CaixaKind::Acao, None),
29980 (CaixaKind::Acao, Some(cyclic_ci_run())),
29981 (CaixaKind::Biblioteca, None),
29982 ];
29983 for (kind, ci) in cases {
29984 let mut c = bare_acao_without_ci();
29985 c.kind = kind;
29986 c.ci = ci.clone();
29987 if kind == CaixaKind::Servico {
29988 c.servicos = vec!["servicos/hello.computeunit.yaml".into()];
29989 } else {
29990 c.servicos = vec![];
29991 }
29992 let prelude: Result<
29993 (&canteiro_types::CiRun, canteiro_types::CanteiroDag),
29994 AcaoRendererStandIn,
29995 > = (|| {
29996 require_kind(&c, CaixaKind::Acao)?;
29997 let ci_borrowed = require_ci(&c)?;
29998 let cd = decompose_ci(&c, ci_borrowed)?;
29999 Ok((ci_borrowed, cd))
30000 })();
30001 let compound: Result<
30002 (&canteiro_types::CiRun, canteiro_types::CanteiroDag),
30003 AcaoRendererStandIn,
30004 > = require_acao_view(&c);
30005 assert_eq!(
30006 prelude.is_ok(),
30007 compound.is_ok(),
30008 "compound helper must match three-line prelude on kind={kind:?} ci.is_some()={}",
30009 ci.is_some(),
30010 );
30011 // Compound helper's Ok-arm return matches prelude's
30012 // Ok-arm return byte-for-byte on both projections: the
30013 // borrowed `&CiRun`'s node count + workspace / repo
30014 // identity, and the owned `CanteiroDag`'s
30015 // topological-order node-name projection (the substrate-
30016 // canonical equality signal every downstream per-`Acao`
30017 // consumer keys off).
30018 if let (Ok((prelude_ci, prelude_cd)), Ok((compound_ci, compound_cd))) =
30019 (prelude, compound)
30020 {
30021 assert_eq!(
30022 prelude_ci.workspace, compound_ci.workspace,
30023 "compound helper's borrowed CiRun's workspace must \
30024 equal prelude's byte-for-byte"
30025 );
30026 assert_eq!(
30027 prelude_ci.repo, compound_ci.repo,
30028 "compound helper's borrowed CiRun's repo must equal \
30029 prelude's byte-for-byte"
30030 );
30031 assert_eq!(
30032 prelude_ci.nodes.len(),
30033 compound_ci.nodes.len(),
30034 "compound helper's borrowed CiRun's node count must \
30035 equal prelude's"
30036 );
30037 let prelude_topo = prelude_cd
30038 .topo_order()
30039 .expect("prelude's DAG produces a valid topo_order");
30040 let compound_topo = compound_cd
30041 .topo_order()
30042 .expect("compound's DAG produces a valid topo_order");
30043 let prelude_names: Vec<String> = prelude_topo
30044 .iter()
30045 .filter_map(|id| prelude_cd.nodes.get(id).map(|n| n.name.clone()))
30046 .collect();
30047 let compound_names: Vec<String> = compound_topo
30048 .iter()
30049 .filter_map(|id| compound_cd.nodes.get(id).map(|n| n.name.clone()))
30050 .collect();
30051 assert_eq!(
30052 prelude_names, compound_names,
30053 "compound helper's DAG must produce byte-equal \
30054 topological-order node-name projection to prelude's"
30055 );
30056 }
30057 }
30058 }
30059
30060 // ── single_field_overlay — typed per-axis overlay primitive ──────────
30061
30062 #[test]
30063 fn single_field_overlay_none_yields_none() {
30064 // Empty-axis-skip semantic at the typed-primitive layer: a
30065 // `None` slot returns `None`, not `Some(empty Mapping)`. The
30066 // caller's `if let Some(overlay) = …` guard then becomes the
30067 // single emission gate, and a malformed `outer: {}` (the
30068 // empty-mapping form some K8s parsers reject) is structurally
30069 // impossible by construction.
30070 let v: Option<serde_yaml::Value> = single_field_overlay::<u32, _>(None, "attempts", |n| {
30071 serde_yaml::Value::Number(n.into())
30072 });
30073 assert!(v.is_none());
30074 }
30075
30076 #[test]
30077 fn single_field_overlay_some_yields_single_field_mapping() {
30078 // The Some arm builds exactly one inner key/value pair, no
30079 // more, no less. Pinning the shape so a future refactor can't
30080 // accidentally introduce a second field (which would render
30081 // as a malformed `timeouts: { request: "30s", <leak>: ... }`
30082 // overlay block).
30083 let v = single_field_overlay(Some(30u32), "attempts", |n| {
30084 serde_yaml::Value::Number(n.into())
30085 })
30086 .expect("Some arm yields Some(...)");
30087 let m = v.as_mapping().expect("mapping shape");
30088 assert_eq!(m.len(), 1);
30089 assert_eq!(m.get("attempts").and_then(|x| x.as_u64()), Some(30));
30090 }
30091
30092 #[test]
30093 fn single_field_overlay_threads_typed_value_through_closure() {
30094 // The closure receives the unwrapped typed `T` (not the
30095 // wrapping `Option<T>`), so the per-overlay value-shaping
30096 // logic stays at the call site. Three different Value shapes
30097 // pin the closure's type-flow: a `String` (for canonical
30098 // duration / enum scalars), a `Number` (for typed integer
30099 // attempt counts), and a derived `Bool` (for tristate enums).
30100 // Mirrors the three landed overlays' shapes letter-for-letter.
30101 let dur = single_field_overlay(Some("30s".to_string()), "request", |s| {
30102 serde_yaml::Value::String(s)
30103 })
30104 .unwrap();
30105 assert_eq!(dur.get("request").and_then(|v| v.as_str()), Some("30s"));
30106
30107 let num = single_field_overlay(Some(3u32), "attempts", |n| {
30108 serde_yaml::Value::Number(n.into())
30109 })
30110 .unwrap();
30111 assert_eq!(num.get("attempts").and_then(|v| v.as_u64()), Some(3));
30112
30113 // The mtls tristate's two non-None arms map to enum strings,
30114 // not raw bools (the Cilium CRD's `mode: required|disabled`
30115 // shape — pinned end-to-end at every emit site by the
30116 // `cnp_authentication_mode_serialized_as_yaml_string` test).
30117 // Both scalar-values thread through the lifted canonical
30118 // [`cilium_auth_mode`] bijection — the same `bool → &'static
30119 // str` projection the production `cilium_network_policies`
30120 // per-`(:de, :para)` overlay closure reaches for, so a future
30121 // Cilium CNP `MutualAuthenticationMode` OpenAPI schema enum
30122 // rebrand (either arm's scalar-value string, or the per-arm
30123 // dispatch) lands at the two consts + one projection body
30124 // rather than duplicated across the production emitter site
30125 // and this generic-helper pin.
30126 let mode = single_field_overlay(Some(true), CILIUM_KEY_MODE, |b| {
30127 serde_yaml::Value::String(cilium_auth_mode(b).into())
30128 })
30129 .unwrap();
30130 assert_eq!(
30131 mode.get(CILIUM_KEY_MODE).and_then(|v| v.as_str()),
30132 Some(CILIUM_AUTH_MODE_REQUIRED)
30133 );
30134 }
30135
30136 #[test]
30137 fn single_field_overlay_outer_key_is_callers_concern() {
30138 // The helper builds the *inner* (single-field) Mapping; the
30139 // *outer* key (`timeouts` / `retry` / `authentication`) is
30140 // the caller's `if let Some(overlay) = … { rule.insert(<outer>,
30141 // overlay.clone()) }` insertion. Pinning that the helper's
30142 // returned Value carries no outer-key wrapping — emitting the
30143 // outer-key-wrapped form here would silently double-wrap
30144 // every overlay (`timeouts: { timeouts: { request: "30s" } }`
30145 // post-insertion).
30146 let v = single_field_overlay(Some(30u32), "attempts", |n| {
30147 serde_yaml::Value::Number(n.into())
30148 })
30149 .unwrap();
30150 let m = v.as_mapping().unwrap();
30151 // Only the inner key — no `timeouts:` / `retry:` /
30152 // `authentication:` wrapper at this layer.
30153 for k in ["timeouts", "retry", "authentication"] {
30154 assert!(
30155 m.get(k).is_none(),
30156 "single_field_overlay must not pre-insert the outer key {k:?} \
30157 (the caller's per-rule insert is the canonical insertion site)"
30158 );
30159 }
30160 }
30161
30162 #[test]
30163 fn single_field_overlay_value_is_clonable_for_per_rule_dispatch() {
30164 // The build-once-clone-many idiom every emit-site uses: the
30165 // overlay is computed once per renderer call (so the closure
30166 // runs exactly once) and `.clone()`d into each rule of the
30167 // emitted sequence. Pin that the returned Value is in fact
30168 // cloneable (a `serde_yaml::Value` always is, but the test
30169 // pins the contract end-to-end so a future refactor that
30170 // returns a non-Cloneable wrapper surfaces here).
30171 let v = single_field_overlay(Some(30u32), "attempts", |n| {
30172 serde_yaml::Value::Number(n.into())
30173 })
30174 .unwrap();
30175 let v_clone = v.clone();
30176 assert_eq!(v, v_clone);
30177 }
30178
30179 // ── upsert_named_entry — typed sequence-upsert primitive ─────────────
30180
30181 #[test]
30182 fn upsert_named_entry_appends_when_empty() {
30183 // Empty-sequence-first arm: an initially-empty aggregator
30184 // programs.yaml carries no matching entry, so the upsert falls
30185 // through to the append-new tail and returns
30186 // `Ok(true)` (newly inserted). Pins the append-new contract
30187 // both writer-side [`caixa_flux`] upsert paths lean on when
30188 // the aggregator's `programs:` sequence is empty
30189 // (`upsert_inserts_new_entry` at the values.yaml layer,
30190 // `upsert_helmrelease_inserts_under_spec_values_programs` at
30191 // the HelmRelease layer) — the same shape at the typed-
30192 // primitive layer as the two production sites.
30193 let mut arr: Vec<serde_yaml::Value> = Vec::new();
30194 let entry: serde_yaml::Value =
30195 serde_yaml::from_str("{ name: hello-rio, module: { source: oci://x } }").unwrap();
30196 let inserted =
30197 upsert_named_entry::<()>(&mut arr, entry, FLEET_PROGRAMS_KEY_NAME, || ()).unwrap();
30198 assert!(inserted, "empty sequence + new entry must append");
30199 assert_eq!(arr.len(), 1);
30200 assert_eq!(
30201 arr[0].get(FLEET_PROGRAMS_KEY_NAME).and_then(|n| n.as_str()),
30202 Some("hello-rio")
30203 );
30204 }
30205
30206 #[test]
30207 fn upsert_named_entry_appends_when_no_match() {
30208 // Non-matching-name append arm: an aggregator sequence with a
30209 // differently-named entry carries no matching name-key value,
30210 // so the upsert falls through to the append-new tail (never
30211 // replacing) and returns `Ok(true)`. Pins the append-only
30212 // semantic that keeps every unrelated entry untouched.
30213 let mut arr: Vec<serde_yaml::Value> = vec![
30214 serde_yaml::from_str("{ name: other, module: { source: github:foo/bar } }").unwrap(),
30215 ];
30216 let entry: serde_yaml::Value =
30217 serde_yaml::from_str("{ name: hello-rio, module: { source: oci://x } }").unwrap();
30218 let inserted =
30219 upsert_named_entry::<()>(&mut arr, entry, FLEET_PROGRAMS_KEY_NAME, || ()).unwrap();
30220 assert!(inserted);
30221 assert_eq!(arr.len(), 2);
30222 assert_eq!(
30223 arr[0].get(FLEET_PROGRAMS_KEY_NAME).and_then(|n| n.as_str()),
30224 Some("other")
30225 );
30226 assert_eq!(
30227 arr[1].get(FLEET_PROGRAMS_KEY_NAME).and_then(|n| n.as_str()),
30228 Some("hello-rio")
30229 );
30230 }
30231
30232 #[test]
30233 fn upsert_named_entry_replaces_when_match() {
30234 // Match-and-replace arm: an aggregator sequence carrying an
30235 // entry whose `<name_key>` matches the new entry's name-scalar
30236 // gets its slot rewritten in place and the helper returns
30237 // `Ok(false)` (replaced-not-appended). Pins the idempotency
30238 // contract every writer-side upsert path lands on — the same
30239 // caixa.lisp deployed twice must upsert to the same
30240 // aggregator entry, never grow a duplicated `programs[]`
30241 // entry. Peer at the substrate layer with the two production
30242 // `upsert_replaces_existing_entry` /
30243 // `upsert_helmrelease_replaces_existing` tests
30244 // ([`caixa_flux`]).
30245 let mut arr: Vec<serde_yaml::Value> = vec![
30246 serde_yaml::from_str("{ name: hello-rio, module: { source: oci://old } }").unwrap(),
30247 ];
30248 let entry: serde_yaml::Value =
30249 serde_yaml::from_str("{ name: hello-rio, module: { source: oci://new } }").unwrap();
30250 let inserted =
30251 upsert_named_entry::<()>(&mut arr, entry, FLEET_PROGRAMS_KEY_NAME, || ()).unwrap();
30252 assert!(!inserted, "matching name must replace, not append");
30253 assert_eq!(arr.len(), 1);
30254 assert_eq!(
30255 arr[0]
30256 .get(COMPUTEUNIT_SPEC_KEY_MODULE)
30257 .and_then(|m| m.get(COMPUTEUNIT_MODULE_KEY_SOURCE))
30258 .and_then(|s| s.as_str()),
30259 Some("oci://new")
30260 );
30261 }
30262
30263 #[test]
30264 fn upsert_named_entry_preserves_position_on_replace() {
30265 // Position-preserving-replace pin: when an interior entry
30266 // matches, its slot is rewritten in place and the surrounding
30267 // entries stay put (first / last / any middle position). The
30268 // aggregator's fanout consumers filter `programs[]` in
30269 // declaration order (the `lareira-fleet-programs` chart's
30270 // `.Values.programs` iteration + the future
30271 // `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
30272 // per-entry admission bind); a replace-then-move-to-tail shift
30273 // (silently promoting the just-upserted entry to end-of-list)
30274 // would silently reorder every downstream consumer's iteration
30275 // window. Same declaration-order-preservation contract the
30276 // aggregator side relies on.
30277 let mut arr: Vec<serde_yaml::Value> = vec![
30278 serde_yaml::from_str("{ name: alpha, module: { source: github:a/a } }").unwrap(),
30279 serde_yaml::from_str("{ name: beta, module: { source: github:b/old } }").unwrap(),
30280 serde_yaml::from_str("{ name: gamma, module: { source: github:g/g } }").unwrap(),
30281 ];
30282 let entry: serde_yaml::Value =
30283 serde_yaml::from_str("{ name: beta, module: { source: github:b/new } }").unwrap();
30284 let inserted =
30285 upsert_named_entry::<()>(&mut arr, entry, FLEET_PROGRAMS_KEY_NAME, || ()).unwrap();
30286 assert!(!inserted);
30287 assert_eq!(arr.len(), 3);
30288 // Order pin: alpha stays at 0, beta stays at 1 (rewritten),
30289 // gamma stays at 2 — replace must preserve position.
30290 let names: Vec<&str> = arr
30291 .iter()
30292 .filter_map(|v| v.get(FLEET_PROGRAMS_KEY_NAME).and_then(|n| n.as_str()))
30293 .collect();
30294 assert_eq!(names, ["alpha", "beta", "gamma"]);
30295 assert_eq!(
30296 arr[1]
30297 .get(COMPUTEUNIT_SPEC_KEY_MODULE)
30298 .and_then(|m| m.get(COMPUTEUNIT_MODULE_KEY_SOURCE))
30299 .and_then(|s| s.as_str()),
30300 Some("github:b/new")
30301 );
30302 }
30303
30304 #[test]
30305 fn upsert_named_entry_calls_error_closure_on_missing_name_key() {
30306 // Missing-name-scalar arm: when the new entry doesn't carry
30307 // `<name_key>` as a string scalar, the helper calls the
30308 // caller's `on_missing_name` closure — the caller's own typed
30309 // [`crate::RenderError`]-shaped error surface remains
30310 // authoritative. Threaded through a closure so this crate
30311 // stays agnostic to the caller's error enum shape (the two
30312 // production sites in [`caixa_flux`] surface
30313 // `Error::MissingField(FLEET_PROGRAMS_KEY_NAME)` verbatim,
30314 // and any future upsert path — the M4
30315 // `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
30316 // per-entry upsert, the `caixa-otel` per-scrape upsert —
30317 // surfaces its own typed variant).
30318 let mut arr: Vec<serde_yaml::Value> = Vec::new();
30319 let entry: serde_yaml::Value =
30320 serde_yaml::from_str("{ module: { source: oci://x } }").unwrap();
30321 let err = upsert_named_entry(&mut arr, entry, FLEET_PROGRAMS_KEY_NAME, || {
30322 "missing-name".to_string()
30323 })
30324 .unwrap_err();
30325 assert_eq!(err, "missing-name");
30326 assert!(arr.is_empty(), "missing-name entry must not land in arr");
30327 }
30328
30329 #[test]
30330 fn upsert_named_entry_calls_error_closure_on_non_string_name_scalar() {
30331 // Non-string-name-scalar arm: when the new entry's
30332 // `<name_key>` is present but not a string (a number, a
30333 // mapping, a sequence — the paste-from-binary footgun where
30334 // an author or a schema-migration script accidentally lands a
30335 // JSON-Number in the name slot), the helper takes the same
30336 // path as the missing-name arm and calls the caller's
30337 // `on_missing_name` closure. Peer arm to the
30338 // upsert_named_entry_calls_error_closure_on_missing_name_key
30339 // pin — both non-string-scalar paths route through the same
30340 // caller-owned diagnostic.
30341 let mut arr: Vec<serde_yaml::Value> = Vec::new();
30342 let entry: serde_yaml::Value =
30343 serde_yaml::from_str("{ name: 42, module: { source: oci://x } }").unwrap();
30344 let err =
30345 upsert_named_entry(&mut arr, entry, FLEET_PROGRAMS_KEY_NAME, || 7u32).unwrap_err();
30346 assert_eq!(err, 7u32);
30347 assert!(arr.is_empty());
30348 }
30349
30350 #[test]
30351 fn upsert_named_entry_uses_parametric_name_key() {
30352 // Name-key-axis-parametric pin: the helper matches on the
30353 // `name_key` parameter, not the pinned
30354 // [`FLEET_PROGRAMS_KEY_NAME`] const — a future writer-side
30355 // upsert path keying on a different discriminator scalar
30356 // (the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
30357 // per-entry `spec.selector` axis, an in-progress rebrand
30358 // promoting `id:` alongside `name:`) reaches for the same
30359 // helper with a different key rather than re-inlining the
30360 // upsert loop.
30361 let mut arr: Vec<serde_yaml::Value> =
30362 vec![serde_yaml::from_str("{ id: alpha, payload: original }").unwrap()];
30363 let entry: serde_yaml::Value =
30364 serde_yaml::from_str("{ id: alpha, payload: replaced }").unwrap();
30365 let inserted = upsert_named_entry::<()>(&mut arr, entry, "id", || ()).unwrap();
30366 assert!(!inserted, "matching `id:` must replace, not append");
30367 assert_eq!(arr.len(), 1);
30368 assert_eq!(
30369 arr[0].get("payload").and_then(|p| p.as_str()),
30370 Some("replaced")
30371 );
30372 }
30373
30374 // ── is_dns_1123_label — shared DNS-1123 label predicate ──────────────
30375
30376 #[test]
30377 fn dns_1123_label_accepts_canonical_forms() {
30378 // Substrate-side pin: the predicate accepts the same canonical
30379 // shapes its three caller axes (`:membros :caixa`,
30380 // `:placement :clusters`, `:children :caixa`) accept at their own
30381 // gates. Drift between this list and the per-axis positive-set
30382 // sweeps surfaces here — one source of truth for the rule.
30383 for s in [
30384 "worker",
30385 "a",
30386 "0",
30387 "cache-v2",
30388 "payment-retry",
30389 "2-pool",
30390 "mar-east",
30391 ] {
30392 is_dns_1123_label(s)
30393 .unwrap_or_else(|e| panic!("canonical DNS-1123 label {s:?} must pass: {e:?}"));
30394 }
30395 }
30396
30397 #[test]
30398 fn dns_1123_label_rejects_uppercase_with_lower_suggestion() {
30399 // The diagnostic carries the lower-cased fix verbatim so every
30400 // caller's per-axis `*Invalid { reason }` wrapping the predicate's
30401 // output reads back as a one-edit-fix suggestion. Pinned at the
30402 // substrate layer so the suggestion shape lives in one place.
30403 let err = is_dns_1123_label("Rio").unwrap_err();
30404 assert!(err.contains("uppercase"), "got: {err:?}");
30405 assert!(err.contains("\"rio\""), "got: {err:?}");
30406 }
30407
30408 #[test]
30409 fn dns_1123_label_rejects_at_64_byte_boundary() {
30410 // The 63-byte cap pin — both the boundary-exceeding case and
30411 // the boundary-accepting case in one place, so a future cap
30412 // shift surfaces both arms simultaneously.
30413 let max_ok = "a".repeat(63);
30414 is_dns_1123_label(&max_ok).unwrap();
30415 let too_long = "a".repeat(64);
30416 let err = is_dns_1123_label(&too_long).unwrap_err();
30417 assert!(err.contains("63"), "got: {err:?}");
30418 assert!(err.contains("64"), "got: {err:?}");
30419 }
30420
30421 #[test]
30422 fn dns_1123_label_rejects_empty_defensively() {
30423 // Defensive re-check pin — every peer value-shape predicate in
30424 // this module (`is_gateway_api_http_path`, `is_wit_world_ref`,
30425 // `is_nats_subject`, `is_wasi_keyvalue_slot`, `is_git_ref_name`)
30426 // carries the same empty-first arm, so `is_dns_1123_label("")`
30427 // returns a clean parser-shaped `must not be empty` reason
30428 // instead of panicking at the boundary arm's `bytes[0]` access
30429 // (`bytes[0].is_ascii_alphanumeric()` on an empty slice would
30430 // index out of bounds). The per-axis narrower `*Empty` variant
30431 // (`MembroCaixaEmpty`, `PlacementClusterEmpty`, `EmptyChildName`,
30432 // `ModuleEmpty`) still fires at every current call site — this
30433 // arm exists so any future call site missing the pre-check gets
30434 // a self-locating diagnostic rather than a `panic!` far from the
30435 // source caixa.lisp, matching the "usable from any future call
30436 // site without a shape-mismatch footgun" discipline every peer
30437 // predicate's doc-comment already promises.
30438 let err = is_dns_1123_label("").unwrap_err();
30439 assert!(err.contains("empty"), "got: {err:?}");
30440 assert_eq!(err, "must not be empty");
30441 }
30442
30443 // ── is_gateway_api_http_path — shared HTTP-path predicate ────────────
30444
30445 #[test]
30446 fn gateway_api_http_path_accepts_canonical_forms() {
30447 // Substrate-side pin: the predicate accepts the same canonical
30448 // shapes both caller axes (`:entrada :paths` and `:contratos
30449 // :endpoint`) accept at their own gates. Drift between this
30450 // list and the per-axis positive-set sweeps surfaces here —
30451 // one source of truth for the rule. Includes the bare-root
30452 // `/` (the catch-all both renderers fall back to), the
30453 // `/foo..bar` interior-`..`-substring (not a `..` segment),
30454 // the `/...` and `/foo.` `.`-bearing names (not `.` segments),
30455 // and the percent-encoded form.
30456 for p in [
30457 "/",
30458 "/api/cart",
30459 "/healthz",
30460 "/api/.config",
30461 "/v1/products",
30462 "/products/:id",
30463 "/api/cart/",
30464 "/api/caf%C3%A9",
30465 "/foo..bar",
30466 "/...",
30467 "/charge",
30468 ] {
30469 is_gateway_api_http_path(p)
30470 .unwrap_or_else(|e| panic!("canonical HTTP path {p:?} must pass: {e:?}"));
30471 }
30472 }
30473
30474 #[test]
30475 fn gateway_api_http_path_rejects_each_arm_with_substring_pinned_reason() {
30476 // Substrate-side diagnostic-shape pin: each grammar arm
30477 // surfaces its own distinct reason substring. Pinned here so
30478 // a future reason-wording rephrase that drops any of these
30479 // substrings surfaces at this one place, not piecemeal across
30480 // every per-axis test sweep.
30481 for (path, needle) in [
30482 ("/api?q=1", "must not contain `?`"),
30483 ("/api#frag", "must not contain `#`"),
30484 ("/api my", "whitespace"),
30485 ("/api\x01x", "control character"),
30486 ("/api/café", "non-ASCII"),
30487 ("/api//x", "consecutive `/`"),
30488 ("/api/./x", "`.` segment"),
30489 ("/api/../x", "`..` parent-segment"),
30490 ] {
30491 let err = is_gateway_api_http_path(path)
30492 .err()
30493 .unwrap_or_else(|| panic!("path {path:?} must be rejected"));
30494 assert!(
30495 err.contains(needle),
30496 "path {path:?} reason must contain {needle:?}; got {err:?}"
30497 );
30498 }
30499 }
30500
30501 #[test]
30502 fn gateway_api_http_path_rejects_at_1025_byte_boundary() {
30503 // The 1024-byte cap pin — both the boundary-exceeding case and
30504 // the boundary-accepting case in one place, so a future cap
30505 // shift surfaces both arms simultaneously, mirroring
30506 // `dns_1123_label_rejects_at_64_byte_boundary` on the peer
30507 // predicate.
30508 let max_ok = format!("/{}", "a".repeat(1023));
30509 assert_eq!(max_ok.len(), 1024);
30510 is_gateway_api_http_path(&max_ok).unwrap();
30511 let too_long = format!("/{}", "a".repeat(1024));
30512 assert_eq!(too_long.len(), 1025);
30513 let err = is_gateway_api_http_path(&too_long).unwrap_err();
30514 assert!(err.contains("1024"), "got: {err:?}");
30515 assert!(err.contains("1025"), "got: {err:?}");
30516 }
30517
30518 #[test]
30519 fn gateway_api_http_path_rejects_empty_defensively() {
30520 // The predicate is called only after each caller's narrower
30521 // `*Empty` arm has fired; re-checking here keeps the predicate
30522 // usable from any future call site without an empty-precondition
30523 // footgun, and avoids a panic on `bytes[0]`-style indexing if
30524 // a future arm is added. Same defensive empty-check
30525 // `validate_entrada_path` carries at its call site (55410e4).
30526 let err = is_gateway_api_http_path("").unwrap_err();
30527 assert!(err.contains("empty"), "got: {err:?}");
30528 }
30529
30530 #[test]
30531 fn gateway_api_http_path_rejects_not_absolute_defensively() {
30532 // Defensive re-check of the leading-`/` invariant the per-axis
30533 // call site enforces with its own narrower `*NotAbsolute` arm;
30534 // ensures the predicate is callable from any future call site
30535 // without a shape-mismatch footgun.
30536 let err = is_gateway_api_http_path("api/cart").unwrap_err();
30537 assert!(err.contains('/'), "got: {err:?}");
30538 }
30539
30540 #[test]
30541 fn gateway_api_http_path_rejects_every_reserved_printable_ascii_byte() {
30542 // Substrate-side sweep: every one of the eleven printable-ASCII
30543 // bytes outside the K8s Gateway API HTTPPathMatch.value
30544 // apiserver-side OpenAPI regex
30545 // `^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$`
30546 // accepted set surfaces a self-locating reason naming the
30547 // offending byte verbatim plus the canonical `%XX` percent-
30548 // encoding remediation. RFC 3986 §3.3's `pchar = unreserved /
30549 // pct-encoded / sub-delims / ":" / "@"` grammar excludes these
30550 // bytes from every path segment, so the apiserver rejects them
30551 // at admission time on every
30552 // `HTTPRoute.spec.rules[].matches[].path.value` landing site —
30553 // peer with the `?` / `#` / whitespace / control / non-ASCII
30554 // arms `gateway_api_http_path_rejects_each_arm_with_substring_
30555 // pinned_reason` covers.
30556 //
30557 // Each char surfaces in a path-shape that pins the canonical
30558 // authoring footgun the K8s apiserver would otherwise catch
30559 // far from the caixa.lisp: `{id}` / `[0]` / `<placeholder>`
30560 // template forms, the Windows path-separator typo, the
30561 // shell-regex character footgun, the SQL-string-literal /
30562 // YAML-flow-mapping accidents.
30563 for (path, ch) in [
30564 ("/api/cart\"path", '"'),
30565 ("/api/cart<id>", '<'),
30566 ("/api/cart/<id>", '<'),
30567 ("/api/cart[0]", '['),
30568 ("/api/cart\\path", '\\'),
30569 ("/api/cart]", ']'),
30570 ("/api/cart/^foo", '^'),
30571 ("/api/cart/`foo", '`'),
30572 ("/api/cart/{id}", '{'),
30573 ("/api/cart|alt", '|'),
30574 ("/api/cart}", '}'),
30575 ] {
30576 let err = is_gateway_api_http_path(path)
30577 .err()
30578 .unwrap_or_else(|| panic!("path {path:?} must be rejected"));
30579 assert!(
30580 err.contains("reserved character"),
30581 "path {path:?} reason must name the reserved-character axis; got {err:?}"
30582 );
30583 assert!(
30584 err.contains(&format!("{ch:?}")),
30585 "path {path:?} reason must name the offending byte {ch:?} verbatim; got {err:?}"
30586 );
30587 let hex = format!("%{:02X}", ch as u8);
30588 assert!(
30589 err.contains(&hex),
30590 "path {path:?} reason must surface the canonical {hex:?} percent-encoding \
30591 remediation; got {err:?}"
30592 );
30593 }
30594 }
30595
30596 #[test]
30597 fn gateway_api_http_path_reserved_char_arm_fires_before_consecutive_slash() {
30598 // Precedence pin: the per-byte loop runs before the post-loop
30599 // structural arms (`//`, `/./`, `/../`), so a path that is
30600 // *both* reserved-char-bearing and consecutive-`/`-bearing
30601 // surfaces the more self-locating reserved-character diagnostic
30602 // first, naming the offending byte verbatim. Mirrors the
30603 // existing `?` / `#` / whitespace / control / non-ASCII arms'
30604 // implicit precedence the
30605 // `gateway_api_http_path_rejects_each_arm_with_substring_
30606 // pinned_reason` pin already establishes for the peer per-byte
30607 // shapes.
30608 let err = is_gateway_api_http_path("/api/{id}//x").unwrap_err();
30609 assert!(
30610 err.contains("reserved character") && err.contains("'{'"),
30611 "got: {err:?}"
30612 );
30613 assert!(
30614 !err.contains("consecutive"),
30615 "the reserved-char arm must fire before the consecutive-`/` arm; got: {err:?}"
30616 );
30617 }
30618
30619 #[test]
30620 fn gateway_api_http_path_accepts_percent_encoded_reserved_chars() {
30621 // Positive-control complement to the reserved-byte rejection
30622 // sweep: every one of the eleven reserved printable-ASCII bytes
30623 // is admissible *when* properly percent-encoded, matching the
30624 // canonical Gateway API HTTPPathMatch.value apiserver-side
30625 // OpenAPI regex's `[%][0-9a-fA-F]{2}` alternative. Pins the
30626 // canonical remediation pathway the reserved-byte arm's reason
30627 // wording names — author who carries a literal `{` percent-
30628 // encodes as `%7B` and the typed slot accepts.
30629 for path in [
30630 "/api/cart%22path",
30631 "/api/cart%3Cid%3E",
30632 "/api/cart%5B0%5D",
30633 "/api/cart%5Cpath",
30634 "/api/cart/%5Efoo",
30635 "/api/cart/%60foo",
30636 "/api/cart/%7Bid%7D",
30637 "/api/cart%7Calt",
30638 ] {
30639 is_gateway_api_http_path(path)
30640 .unwrap_or_else(|e| panic!("percent-encoded path {path:?} must pass: {e:?}"));
30641 }
30642 }
30643
30644 // ── is_wit_world_ref — shared WIT world-reference predicate ──────────
30645
30646 #[test]
30647 fn wit_world_ref_accepts_canonical_forms() {
30648 // Substrate-side pin: the predicate accepts every canonical
30649 // WIT identifier the `:contratos :wit` axis already carries in
30650 // the test fixtures + the example checkout-aplicacao (each
30651 // hand-curated to match real WIT registry references). Drift
30652 // between this list and the per-axis positive-set sweep
30653 // surfaces here — one source of truth for the rule. Includes
30654 // every shape variant: HTTP-prefixed (`wasi:http/proxy`),
30655 // KV-prefixed (`wasi:keyvalue/store`), pubsub-prefixed
30656 // (`nats:pub-sub`, `kafka:topic`), capability-only
30657 // (`custom:exchange`, `pleme:cap/audit`), the optional
30658 // `@<version>` suffix (`wasi:http/proxy@0.2.0`), and the
30659 // multi-segment `/iface/iface` form the WIT IDL grammar allows.
30660 for s in [
30661 "wasi:http/proxy",
30662 "wasi:keyvalue/store",
30663 "nats:pub-sub",
30664 "kafka:topic",
30665 "custom:exchange",
30666 "pleme:cap/audit",
30667 "http:server",
30668 "kv:store",
30669 "wasi:http/proxy@0.2.0",
30670 "wasi:keyvalue/store@0.2.0-rc.1",
30671 "pleme:cap/audit/v2",
30672 // Every legal shape SemVer 2.0.0 admits in the `@<version>`
30673 // body — bare numeric core, pre-release suffix (single +
30674 // dot-separated identifiers), build-metadata suffix (single
30675 // + dot-separated identifiers), combined pre-release +
30676 // build-metadata, and leading-zero-avoiding pre-release
30677 // identifiers — pinned here so a future tightening of the
30678 // per-byte accepted set that rejects a canonical semver
30679 // shape surfaces here rather than at the M4 CR materializer's
30680 // WIT-parse boundary.
30681 "wasi:http/proxy@1.0.0",
30682 "wasi:http/proxy@0.2.0-alpha",
30683 "wasi:http/proxy@1.0.0-alpha.1",
30684 "wasi:http/proxy@2.0.0+build.42",
30685 "wasi:http/proxy@0.0.0-rc.1+abc.def",
30686 ] {
30687 is_wit_world_ref(s)
30688 .unwrap_or_else(|e| panic!("canonical WIT reference {s:?} must pass: {e:?}"));
30689 }
30690 }
30691
30692 #[test]
30693 fn wit_world_ref_rejects_each_arm_with_substring_pinned_reason() {
30694 // Substrate-side diagnostic-shape pin: each grammar arm
30695 // surfaces its own distinct reason substring. Pinned here so a
30696 // future reason-wording rephrase that drops any of these
30697 // substrings surfaces at this one place, not piecemeal across
30698 // every per-axis test sweep. Mirrors
30699 // `gateway_api_http_path_rejects_each_arm_with_substring_pinned_reason`
30700 // on the peer predicate.
30701 for (s, needle) in [
30702 // Missing `:` separator → silent capability demotion.
30703 ("wasi-http/proxy", "must contain a `:`"),
30704 // Multiple `:` → can't split into ns + pkg.
30705 ("wasi:http:proxy", "exactly one `:`"),
30706 // Uppercase → silently bypasses the lowercase dispatch.
30707 ("WASI:http/proxy", "lowercase"),
30708 ("wasi:HTTP/proxy", "lowercase"),
30709 // Empty package half → can't resolve via WIT registry.
30710 ("wasi:", "must not be empty"),
30711 // Empty namespace half.
30712 (":http/proxy", "must not be empty"),
30713 // Underscore → DNS-1123 / WIT kebab-case footgun.
30714 ("wasi:http_proxy", "_"),
30715 // Leading digit → WIT identifiers begin with a letter.
30716 ("wasi:1http/proxy", "digit"),
30717 // Consecutive hyphens → invalid kebab-case.
30718 ("wasi:pub--sub", "consecutive `-`"),
30719 // Trailing hyphen → invalid kebab-case.
30720 ("wasi:proxy-", "must not end with `-`"),
30721 // Whitespace inside the token.
30722 ("wasi:http proxy", "whitespace"),
30723 // Control characters.
30724 ("wasi:http\x01proxy", "control character"),
30725 // Non-ASCII byte (café-style un-percent-encoded literal).
30726 ("wasi:caf\u{e9}/proxy", "non-ASCII"),
30727 // Trailing `@` with no version body.
30728 ("wasi:http/proxy@", "trailing `@`"),
30729 // Version body carrying `:` or `/`.
30730 ("wasi:http/proxy@0.2:rc1", "must not contain `:` or `/`"),
30731 // Doubled `@`.
30732 ("wasi:http/proxy@0.2@beta", "at most one `@`"),
30733 // Version body carrying a byte outside the SemVer 2.0.0
30734 // accepted set `[0-9A-Za-z.\-+]` — the canonical
30735 // author-side paste footguns (`?` from URL-query-separator
30736 // paste, `#` from URL-fragment paste, `!` from
30737 // history-expansion, `(` from parenthetical doc annotation,
30738 // `~` from tilde-range npm/Cargo semver-req paste that
30739 // strayed into the version body itself). Each surfaces the
30740 // `invalid character` reason substring so the diagnostic
30741 // wording is pinned alongside every peer per-byte rejection.
30742 ("wasi:http/proxy@0.2.0?rc1", "invalid character"),
30743 ("wasi:http/proxy@0.2.0#build", "invalid character"),
30744 ("wasi:http/proxy@0.2.0!alpha", "invalid character"),
30745 ("wasi:http/proxy@0.2.0(rc1)", "invalid character"),
30746 ("wasi:http/proxy@~0.2.0", "invalid character"),
30747 // Version body byte-set-valid but *structurally* invalid
30748 // SemVer 2.0.0 — the canonical author-side paste footguns
30749 // the byte-set gate above cannot catch. Every entry passes
30750 // the accepted-set arm `[0-9A-Za-z.\-+]` verbatim and
30751 // fails only at [`semver::Version::parse`]: two-part
30752 // numeric core (`@1.0` — Node.js `"engines"` field paste),
30753 // one-part numeric core (`@1` — Docker `:v1` tag paste),
30754 // four-part numeric core (`@1.0.0.0` — Microsoft / Java
30755 // build-number convention), `v`-prefixed version body
30756 // (`@v0.2.0` — git-tag-shape paste), leading-zero major
30757 // (`@01.0.0` — mistaken zero-padded date-based version),
30758 // trailing hyphen with empty pre-release (`@1.0.0-` —
30759 // half-typed pre-release), trailing plus with empty
30760 // build-metadata (`@1.0.0+` — peer for build-metadata),
30761 // empty pre-release identifier between dots
30762 // (`@1.0.0-.rc1` — accidental leading `.`), empty build-
30763 // metadata identifier between dots (`@1.0.0+.abc` — peer
30764 // for build-metadata), numeric pre-release identifier
30765 // with leading zero (`@1.0.0-01` — SemVer 2.0.0 rule 9),
30766 // consecutive dots inside pre-release (`@1.0.0-alpha..beta`).
30767 // Each surfaces the `structurally valid SemVer 2.0.0`
30768 // reason substring so the diagnostic wording is pinned
30769 // alongside every peer structural rejection.
30770 ("wasi:http/proxy@1.0", "structurally valid SemVer 2.0.0"),
30771 ("wasi:http/proxy@1", "structurally valid SemVer 2.0.0"),
30772 ("wasi:http/proxy@1.0.0.0", "structurally valid SemVer 2.0.0"),
30773 ("wasi:http/proxy@v0.2.0", "structurally valid SemVer 2.0.0"),
30774 ("wasi:http/proxy@01.0.0", "structurally valid SemVer 2.0.0"),
30775 ("wasi:http/proxy@1.0.0-", "structurally valid SemVer 2.0.0"),
30776 ("wasi:http/proxy@1.0.0+", "structurally valid SemVer 2.0.0"),
30777 (
30778 "wasi:http/proxy@1.0.0-.rc1",
30779 "structurally valid SemVer 2.0.0",
30780 ),
30781 (
30782 "wasi:http/proxy@1.0.0+.abc",
30783 "structurally valid SemVer 2.0.0",
30784 ),
30785 (
30786 "wasi:http/proxy@1.0.0-01",
30787 "structurally valid SemVer 2.0.0",
30788 ),
30789 (
30790 "wasi:http/proxy@1.0.0-alpha..beta",
30791 "structurally valid SemVer 2.0.0",
30792 ),
30793 // Digit-immediately-after-`-` word-start rule — the WIT IDL
30794 // `word ::= [a-z][a-z0-9]*` per-word first-byte gate the
30795 // predicate's doc-comment already documented, closed at the
30796 // implementation layer. Each identifier passes the outer
30797 // `[a-z0-9-]` byte set, the leading-`-` rejection, the
30798 // consecutive-`-` rejection, and the trailing-`-` rejection,
30799 // and was silently accepted before the arm landed — surfaces
30800 // the `word after `-`` reason substring so a future
30801 // diagnostic-wording rephrase surfaces here alongside every
30802 // peer per-arm substring pin. Canonical author-side
30803 // footguns: `"pub-1sub"` (version-shape digit paste),
30804 // `"proxy-2beta"` (v2 tag paste), `"cap-9"` (numeric
30805 // suffix). Namespace-side and interface-side variants pin
30806 // the arm fires uniformly on every WIT segment (`ns:pkg`,
30807 // `ns:pkg/iface`, not just the first).
30808 ("wasi:pub-1sub", "word after `-`"),
30809 ("wasi:proxy-2beta", "word after `-`"),
30810 ("wasi:cap-9", "word after `-`"),
30811 ("pleme-1cap:audit", "word after `-`"),
30812 ("wasi:http/proxy-3rc", "word after `-`"),
30813 ] {
30814 let err = is_wit_world_ref(s)
30815 .err()
30816 .unwrap_or_else(|| panic!("WIT reference {s:?} must be rejected"));
30817 assert!(
30818 err.contains(needle),
30819 "WIT reference {s:?} reason must contain {needle:?}; got {err:?}"
30820 );
30821 }
30822 }
30823
30824 #[test]
30825 fn wit_world_ref_word_after_hyphen_digit_arm_names_offending_byte_and_word_rule() {
30826 // Pin the per-word first-byte arm's diagnostic quality: the
30827 // offending byte appears verbatim in the reason, the WIT
30828 // grammar production is named (`[a-z][a-z0-9]*`), and the
30829 // remediation suggests a lowercase-letter prefix on the
30830 // offending word. Mirrors the `wit_world_ref_leading_digit`
30831 // sibling pin on the *first-word* first-byte arm — the two
30832 // arms enforce the same rule at complementary positions
30833 // (whole-id first byte vs. per-hyphen-word first byte), so
30834 // their diagnostic shapes stay peer.
30835 let err = is_wit_world_ref("wasi:pub-1sub").unwrap_err();
30836 assert!(err.contains("'1'"), "must name offending byte: {err:?}");
30837 assert!(
30838 err.contains("[a-z][a-z0-9]*"),
30839 "must name WIT word grammar: {err:?}"
30840 );
30841 assert!(
30842 err.contains("pub-v1sub"),
30843 "must suggest the letter-prefix remediation: {err:?}"
30844 );
30845 }
30846
30847 #[test]
30848 fn wit_world_ref_word_after_hyphen_lowercase_letter_still_accepted() {
30849 // Complement-side pin: the per-word first-byte arm strictly
30850 // targets *digits* after `-`; every canonical multi-word
30851 // lowercase identifier (`pub-sub`, `pub-sub-async`,
30852 // `wasi:http/incoming-handler`, `wasi:keyvalue/atomic-batch`)
30853 // remains in the accepted set with no new false-positive.
30854 // Pinned here so a future tightening that spills the digit-
30855 // rejection arm onto the letter-after-hyphen class surfaces
30856 // as a test failure at this positive-set pin, not at the M4
30857 // CR materializer's WIT-parse boundary. Mirrors the
30858 // `wit_world_ref_accepts_canonical_forms` positive-set
30859 // sweep, extended here to the multi-word-lowercase axis.
30860 for s in [
30861 "nats:pub-sub",
30862 "wasi:http/incoming-handler",
30863 "wasi:keyvalue/atomic-batch",
30864 "pleme:cap/audit-log",
30865 "http:server-side",
30866 ] {
30867 is_wit_world_ref(s).unwrap_or_else(|e| {
30868 panic!("canonical multi-word WIT identifier {s:?} must pass: {e:?}")
30869 });
30870 }
30871 }
30872
30873 #[test]
30874 fn wit_world_ref_word_after_hyphen_digit_arm_fires_before_byte_set_arm() {
30875 // Diagnostic-precedence pin: an identifier that is *both*
30876 // digit-after-`-` and byte-set-invalid (`"pub-1$"`) surfaces
30877 // the more self-locating word-start diagnostic, not the
30878 // generic invalid-character diagnostic. The arm order in the
30879 // loop is deliberate — the per-word first-byte gate fires on
30880 // the first offending byte (position 4 = the `1`) before the
30881 // byte-set gate can reach the `$` at position 5. Pinned here
30882 // so a future arm-reordering that moves the byte-set gate
30883 // earlier surfaces the drift at this test rather than
30884 // silently value-laundering the diagnostic.
30885 let err = is_wit_world_ref("wasi:pub-1$").unwrap_err();
30886 assert!(
30887 err.contains("word after `-`"),
30888 "must surface the per-word first-byte diagnostic, not the invalid-character one: {err:?}"
30889 );
30890 // And the `$` case *without* the digit-after-`-` still lands
30891 // on the invalid-character arm — the two diagnostics don't
30892 // collide when only one applies.
30893 let err = is_wit_world_ref("wasi:pub-x$").unwrap_err();
30894 assert!(
30895 err.contains("invalid character"),
30896 "byte-set-only rejection must still name invalid character: {err:?}"
30897 );
30898 }
30899
30900 #[test]
30901 fn wit_world_ref_rejects_empty_defensively() {
30902 // The predicate is called from `WitContract::target()` only
30903 // after the per-axis `EmptyWit` arm has fired at validate
30904 // time; re-checking here keeps the predicate usable from any
30905 // future call site without an empty-precondition footgun.
30906 // Same defensive empty-check `is_dns_1123_label` /
30907 // `is_gateway_api_http_path` carry at their call sites.
30908 let err = is_wit_world_ref("").unwrap_err();
30909 assert!(err.contains("empty"), "got: {err:?}");
30910 }
30911
30912 #[test]
30913 fn wit_world_ref_rejects_at_129_byte_boundary() {
30914 // The 128-byte cap pin — both the boundary-exceeding case and
30915 // the boundary-accepting case in one place, so a future cap
30916 // shift surfaces both arms simultaneously, mirroring
30917 // `dns_1123_label_rejects_at_64_byte_boundary` and
30918 // `gateway_api_http_path_rejects_at_1025_byte_boundary` on the
30919 // peer predicates. Constructed as `wasi:<long-pkg>` so the
30920 // kebab-shape arms don't fire first and obscure the cap arm.
30921 let pad = "a".repeat(123); // 5 + 123 = 128 (`wasi:` + pad)
30922 let max_ok = format!("wasi:{pad}");
30923 assert_eq!(max_ok.len(), 128);
30924 is_wit_world_ref(&max_ok).unwrap();
30925 let pad_over = "a".repeat(124);
30926 let too_long = format!("wasi:{pad_over}");
30927 assert_eq!(too_long.len(), 129);
30928 let err = is_wit_world_ref(&too_long).unwrap_err();
30929 assert!(err.contains("128"), "got: {err:?}");
30930 assert!(err.contains("129"), "got: {err:?}");
30931 }
30932
30933 // ── is_nats_subject — shared NATS subject predicate ──────────────────
30934
30935 #[test]
30936 fn nats_subject_accepts_canonical_forms() {
30937 // Substrate-side pin: the predicate accepts every canonical
30938 // NATS subject the `:contratos :subject` axis carries in the
30939 // caixa-mesh test fixtures + the example checkout-aplicacao
30940 // (each hand-curated to match real NATS server-side admission
30941 // shapes). Drift between this list and the per-axis positive-
30942 // set sweep surfaces here — one source of truth for the rule.
30943 // Includes single-token subjects, multi-dot subjects, snake-
30944 // case + kebab-case tokens (NATS accepts both), digit-bearing
30945 // tokens, the `*` single-token wildcard at every segment
30946 // position, and the `>` multi-token wildcard at the final
30947 // position (the two NATS subscription patterns the protocol
30948 // defines). Mirrors the canonical-forms sweeps on the peer
30949 // value-shape predicates (`gateway_api_http_path_accepts_…`,
30950 // `wit_world_ref_accepts_…`).
30951 for s in [
30952 "checkout.events.charge.failed",
30953 "rio.events.order.charged",
30954 "orders",
30955 "orders.123",
30956 "snake_case.token",
30957 "kebab-case.token",
30958 "MixedCase.Token",
30959 "alpha.beta.gamma.delta.epsilon",
30960 "orders.*.charged",
30961 "*.events.*",
30962 "orders.>",
30963 "*",
30964 ">",
30965 ] {
30966 is_nats_subject(s)
30967 .unwrap_or_else(|e| panic!("canonical NATS subject {s:?} must pass: {e:?}"));
30968 }
30969 }
30970
30971 #[test]
30972 fn nats_subject_rejects_each_arm_with_substring_pinned_reason() {
30973 // Substrate-side diagnostic-shape pin: each grammar arm
30974 // surfaces its own distinct reason substring. Pinned here so
30975 // a future reason-wording rephrase that drops any of these
30976 // substrings surfaces at this one place, not piecemeal across
30977 // every per-axis test sweep. Mirrors
30978 // `gateway_api_http_path_rejects_each_arm_with_substring_pinned_reason`
30979 // and `wit_world_ref_rejects_each_arm_with_substring_pinned_reason`
30980 // on the peer predicates.
30981 for (s, needle) in [
30982 // Whitespace inside the token.
30983 ("foo bar", "whitespace"),
30984 ("foo\tbar", "whitespace"),
30985 // Control characters.
30986 ("foo\x01bar", "control character"),
30987 // Non-ASCII byte (un-percent-encoded café-style literal).
30988 ("foo.caf\u{e9}", "non-ASCII"),
30989 // Leading `.` — empty leading token.
30990 (".foo", "must not start with `.`"),
30991 // Trailing `.` — empty trailing token.
30992 ("foo.", "must not end with `.`"),
30993 // Consecutive `.` — empty token between separators.
30994 ("foo..bar", "consecutive `.`"),
30995 // Non-trailing `>` multi-token wildcard.
30996 ("foo.>.bar", "only allowed as the final segment"),
30997 // Mid-segment `*` (not a standalone wildcard token).
30998 ("foo*.bar", "`*` mid-segment"),
30999 // Mid-segment `>` (not a standalone wildcard token).
31000 ("foo>", "`>` mid-segment"),
31001 // `.` is the separator, so `,` (or any other punctuation)
31002 // surfaces as an invalid-character arm.
31003 ("foo,bar", "invalid character"),
31004 // `:` reserved-looking — distinct invalid-character arm
31005 // (pinned separately so a future relaxation that accepts
31006 // `:` mid-segment surfaces here, not in some downstream
31007 // renderer's "this passed validate but the NATS server
31008 // rejected at publish" footgun).
31009 ("foo:bar", "invalid character"),
31010 ] {
31011 let err = is_nats_subject(s)
31012 .err()
31013 .unwrap_or_else(|| panic!("NATS subject {s:?} must be rejected"));
31014 assert!(
31015 err.contains(needle),
31016 "NATS subject {s:?} reason must contain {needle:?}; got {err:?}"
31017 );
31018 }
31019 }
31020
31021 #[test]
31022 fn nats_subject_rejects_empty_defensively() {
31023 // The predicate is called from `WitContract::target()` only
31024 // after the per-axis `ContratoSubjectEmpty` arm has fired at
31025 // validate time; re-checking here keeps the predicate usable
31026 // from any future call site without an empty-precondition
31027 // footgun. Same defensive empty-check `is_dns_1123_label`,
31028 // `is_gateway_api_http_path`, and `is_wit_world_ref` carry at
31029 // their call sites.
31030 let err = is_nats_subject("").unwrap_err();
31031 assert!(err.contains("empty"), "got: {err:?}");
31032 }
31033
31034 #[test]
31035 fn nats_subject_rejects_at_257_byte_boundary() {
31036 // The 256-byte cap pin — both the boundary-exceeding case and
31037 // the boundary-accepting case in one place, so a future cap
31038 // shift surfaces both arms simultaneously, mirroring
31039 // `dns_1123_label_rejects_at_64_byte_boundary`,
31040 // `gateway_api_http_path_rejects_at_1025_byte_boundary`, and
31041 // `wit_world_ref_rejects_at_129_byte_boundary` on the peer
31042 // predicates. Constructed as a single all-`a` token (no `.`)
31043 // so the segment / wildcard arms don't fire first and obscure
31044 // the cap arm.
31045 let max_ok = "a".repeat(256);
31046 assert_eq!(max_ok.len(), 256);
31047 is_nats_subject(&max_ok).unwrap();
31048 let too_long = "a".repeat(257);
31049 assert_eq!(too_long.len(), 257);
31050 let err = is_nats_subject(&too_long).unwrap_err();
31051 assert!(err.contains("256"), "got: {err:?}");
31052 assert!(err.contains("257"), "got: {err:?}");
31053 }
31054
31055 #[test]
31056 fn nats_subject_lone_wildcard_tokens_validate() {
31057 // The two NATS wildcards stand alone as the entire subject —
31058 // a `subscribe("*")` matches any single-token publish, a
31059 // `subscribe(">")` matches every NATS message on the connection.
31060 // Both are protocol-legal; the typed substrate accepts them
31061 // structurally and leaves the "should the typed `:contratos`
31062 // edge subscribe to literally everything?" question to a
31063 // future semantic-level gate. Pinned alongside the canonical-
31064 // forms sweep so a future tighten that disallows lone wildcards
31065 // surfaces both arms simultaneously.
31066 is_nats_subject("*").unwrap();
31067 is_nats_subject(">").unwrap();
31068 }
31069
31070 #[test]
31071 fn nats_subject_trailing_multi_wildcard_validates() {
31072 // `>` at the final segment is the canonical "match all trailing
31073 // tokens" subscription pattern. Pinned alongside the non-
31074 // trailing-`>` rejection arm so the boundary between the two
31075 // is in one place — a future relaxation that allows `>` at
31076 // non-trailing positions or a tighten that disallows trailing
31077 // `>` surfaces both arms simultaneously.
31078 is_nats_subject("orders.>").unwrap();
31079 is_nats_subject("orders.events.>").unwrap();
31080 // And the `*` single-token wildcard combines freely with the
31081 // trailing `>` — the canonical "match one middle token, then
31082 // anything trailing" subscription pattern.
31083 is_nats_subject("orders.*.>").unwrap();
31084 }
31085
31086 // ── is_wasi_keyvalue_slot — shared kv slot-template predicate ────────
31087
31088 #[test]
31089 fn wasi_kv_slot_accepts_canonical_forms() {
31090 // Substrate-side pin: the predicate accepts every canonical kv
31091 // slot template the `:contratos :slot` axis carries in the
31092 // caixa-mesh test fixtures + plausible authoring patterns
31093 // (each maps to a realistic wasi:keyvalue/store key the runtime
31094 // resolves on dispatch). Drift between this list and the
31095 // per-axis positive-set sweep surfaces here — one source of
31096 // truth for the rule. Includes:
31097 // - single-token identifiers (`"checkout"`, `"events"`);
31098 // - dot-namespaced templates (`"session.tokens.<sid>"`);
31099 // - path-namespaced templates with `$`-prefixed variables
31100 // (`"checkout/$orderId"`, the canonical Akka-cluster-
31101 // sharding-style template);
31102 // - colon-namespaced templates with brace placeholders
31103 // (`"users:{tenant}/{id}"`, the canonical multi-tenant
31104 // Redis-key shape);
31105 // - angle-bracket placeholders (`"session.<sid>"`);
31106 // - underscore identifiers (`"snake_case_key"`);
31107 // - kebab identifiers (`"kebab-case-key"`);
31108 // - mixed-case (`"MixedCase"` — kv slot templates are case-
31109 // sensitive; the predicate doesn't lowercase-fold);
31110 // - digit-bearing tokens (`"shard0"`, `"v2/key"`);
31111 // - percent-encoded fragments (`"users/caf%C3%A9"`); the
31112 // encoded form is the *valid* shape, the raw `café` is
31113 // rejected on the non-ASCII arm.
31114 // Mirrors the canonical-forms sweeps on the peer value-shape
31115 // predicates (`gateway_api_http_path_accepts_…`,
31116 // `nats_subject_accepts_canonical_forms`).
31117 for s in [
31118 "checkout",
31119 "events",
31120 "checkout/$orderId",
31121 "users:{tenant}/{id}",
31122 "session.<sid>",
31123 "session.tokens.<sid>",
31124 "snake_case_key",
31125 "kebab-case-key",
31126 "MixedCase",
31127 "shard0",
31128 "v2/key",
31129 "users/caf%C3%A9",
31130 ] {
31131 is_wasi_keyvalue_slot(s)
31132 .unwrap_or_else(|e| panic!("canonical kv slot {s:?} must pass: {e:?}"));
31133 }
31134 }
31135
31136 #[test]
31137 fn wasi_kv_slot_rejects_each_arm_with_substring_pinned_reason() {
31138 // Substrate-side diagnostic-shape pin: each grammar arm
31139 // surfaces its own distinct reason substring. Pinned here so
31140 // a future reason-wording rephrase that drops any of these
31141 // substrings surfaces at this one place, not piecemeal across
31142 // every per-axis test sweep. Mirrors
31143 // `nats_subject_rejects_each_arm_with_substring_pinned_reason`
31144 // and `gateway_api_http_path_rejects_each_arm_with_substring_pinned_reason`
31145 // on the peer predicates.
31146 for (s, needle) in [
31147 // Raw space inside the template — the canonical paste-from-
31148 // doc footgun.
31149 ("check out/$order", "whitespace"),
31150 // Tab byte — distinct arm-pinned reason from the space arm.
31151 ("check\tout", "whitespace"),
31152 // Control character (SOH = 0x01) — pinned separately from
31153 // the whitespace arm so a future relaxation that admits
31154 // raw whitespace but still rejects controls surfaces here.
31155 ("checkout/\x01order", "control character"),
31156 // Newline — the canonical "the paste-from-binary slug
31157 // spans multiple lines" footgun. Distinct from the
31158 // whitespace arm because `\n` is a control character.
31159 ("checkout\norder", "control character"),
31160 // DEL byte (0x7F) — the upper boundary of the control-
31161 // character range, pinned so a future relaxation that
31162 // only checks `< 0x20` surfaces here.
31163 ("checkout\x7forder", "control character"),
31164 // Un-percent-encoded non-ASCII byte — the canonical
31165 // "I copied the key from a doc with smart quotes /
31166 // accented characters" footgun. Author must percent-
31167 // encode (the canonical-forms sweep covers
31168 // `"users/caf%C3%A9"`).
31169 ("ch\u{e9}ckout/$order", "non-ASCII"),
31170 ] {
31171 let err = is_wasi_keyvalue_slot(s)
31172 .err()
31173 .unwrap_or_else(|| panic!("kv slot {s:?} must be rejected"));
31174 assert!(
31175 err.contains(needle),
31176 "kv slot {s:?} reason must contain {needle:?}; got {err:?}"
31177 );
31178 }
31179 }
31180
31181 #[test]
31182 fn wasi_kv_slot_rejects_empty_defensively() {
31183 // The predicate is called from `WitContract::target()` only
31184 // after the per-axis `ContratoSlotEmpty` arm has fired at
31185 // validate time; re-checking here keeps the predicate usable
31186 // from any future call site without an empty-precondition
31187 // footgun. Same defensive empty-check `is_dns_1123_label`,
31188 // `is_gateway_api_http_path`, `is_wit_world_ref`, and
31189 // `is_nats_subject` carry at their call sites.
31190 let err = is_wasi_keyvalue_slot("").unwrap_err();
31191 assert!(err.contains("empty"), "got: {err:?}");
31192 }
31193
31194 #[test]
31195 fn wasi_kv_slot_rejects_at_513_byte_boundary() {
31196 // The 512-byte cap pin — both the boundary-exceeding case and
31197 // the boundary-accepting case in one place, so a future cap
31198 // shift surfaces both arms simultaneously, mirroring
31199 // `dns_1123_label_rejects_at_64_byte_boundary`,
31200 // `gateway_api_http_path_rejects_at_1025_byte_boundary`,
31201 // `wit_world_ref_rejects_at_129_byte_boundary`, and
31202 // `nats_subject_rejects_at_257_byte_boundary` on the peer
31203 // predicates. Constructed as a single all-`a` token (no
31204 // separator / template syntax) so only the cap arm fires.
31205 let max_ok = "a".repeat(512);
31206 assert_eq!(max_ok.len(), 512);
31207 is_wasi_keyvalue_slot(&max_ok).unwrap();
31208 let too_long = "a".repeat(513);
31209 assert_eq!(too_long.len(), 513);
31210 let err = is_wasi_keyvalue_slot(&too_long).unwrap_err();
31211 assert!(err.contains("512"), "got: {err:?}");
31212 assert!(err.contains("513"), "got: {err:?}");
31213 }
31214
31215 #[test]
31216 fn wasi_kv_slot_admits_full_printable_ascii_range() {
31217 // Structural pin: the predicate admits every printable ASCII
31218 // byte from `0x21` (`!`) to `0x7E` (`~`) inclusive, including
31219 // every template-variable bracket the documented authoring
31220 // patterns use (`$`, `{`, `}`, `<`, `>`) and every namespace
31221 // separator (`/`, `:`, `.`, `-`, `_`). Drift here = a future
31222 // tighten that removes any byte from the admitted set surfaces
31223 // a name-the-byte test failure, not piecemeal across per-axis
31224 // sweeps. Constructed as a single all-bytes template (`b!`,
31225 // `b"`, …, `b~`) — the predicate doesn't impose structure,
31226 // only character-class.
31227 for b in 0x21u8..=0x7E {
31228 let s = std::str::from_utf8(&[b]).unwrap().to_string();
31229 is_wasi_keyvalue_slot(&s)
31230 .unwrap_or_else(|e| panic!("printable ASCII byte 0x{b:02x} must pass: {e:?}"));
31231 }
31232 }
31233
31234 #[test]
31235 fn git_ref_name_accepts_canonical_forms() {
31236 // Substrate-side pin: the predicate accepts every canonical
31237 // refname the `:fonte :tag` / `:fonte :branch` axes carry in
31238 // realistic authoring patterns (each maps to a refname `git
31239 // fetch <remote> tag '<value>'` and `git checkout '<value>'`
31240 // resolve cleanly at clone time). Drift between this list and
31241 // any per-axis positive-set sweep surfaces here — one source
31242 // of truth for the rule. Includes:
31243 // - semver tag with `v` prefix (`"v0.1.0"`, the canonical
31244 // pleme-io release shape);
31245 // - bare semver tag (`"0.1.0"`, the npm / Cargo idiom);
31246 // - pre-release tag (`"v0.1.0-alpha.1"`);
31247 // - release-line tag with hyphens (`"release-1.0"`);
31248 // - leaf branch (`"main"` / `"master"`);
31249 // - hierarchical feature branch (`"feature/checkout"`);
31250 // - multi-component branch with hyphens and digits
31251 // (`"user-1/feat-x-v2"`);
31252 // - dot-bearing tag (`"v0.1.0.rc1"`, mid-component dot
31253 // allowed — only consecutive `..` and trailing `.` are
31254 // rejected).
31255 // Mirrors the canonical-forms sweeps on the peer value-shape
31256 // predicates (`wasi_kv_slot_accepts_canonical_forms`,
31257 // `nats_subject_accepts_canonical_forms`).
31258 for s in [
31259 "v0.1.0",
31260 "0.1.0",
31261 "v0.1.0-alpha.1",
31262 "release-1.0",
31263 "main",
31264 "master",
31265 "feature/checkout",
31266 "user-1/feat-x-v2",
31267 "v0.1.0.rc1",
31268 "stable",
31269 ] {
31270 is_git_ref_name(s)
31271 .unwrap_or_else(|e| panic!("canonical git ref {s:?} must pass: {e:?}"));
31272 }
31273 }
31274
31275 #[test]
31276 fn git_ref_name_rejects_each_arm_with_substring_pinned_reason() {
31277 // Substrate-side diagnostic-shape pin: each grammar arm
31278 // surfaces its own distinct reason substring. Pinned here so
31279 // a future reason-wording rephrase that drops any of these
31280 // substrings surfaces at this one place, not piecemeal across
31281 // every per-axis test sweep. Mirrors
31282 // `wasi_kv_slot_rejects_each_arm_with_substring_pinned_reason`
31283 // and `nats_subject_rejects_each_arm_with_substring_pinned_reason`
31284 // on the peer predicates.
31285 for (s, needle) in [
31286 // Trailing space — the canonical paste-from-doc footgun.
31287 ("v0.1.0 ", "whitespace"),
31288 // Embedded space (branch with spaces).
31289 ("feature/foo bar", "whitespace"),
31290 // Tab byte.
31291 ("v0.1.0\t", "whitespace"),
31292 // Newline — the canonical "paste-from-multiline-doc"
31293 // footgun. Distinct from the whitespace arm because `\n`
31294 // is a control character.
31295 ("v0.1.0\n", "control character"),
31296 // DEL byte (0x7F) — upper boundary of the control range.
31297 ("v0.1.0\x7f", "control character"),
31298 // Non-ASCII byte (the canonical "I copied the tag from a
31299 // doc with smart quotes" footgun).
31300 ("v0.1.0\u{e9}", "non-ASCII"),
31301 // Tilde — git's revision grammar (`HEAD~3`).
31302 ("v0.1.0~1", "`~`"),
31303 // Caret — git's revision grammar (`HEAD^`).
31304 ("v0.1.0^", "`^`"),
31305 // Colon — git's refspec separator.
31306 ("v0.1.0:rebase", "`:`"),
31307 // Question mark — git's refspec glob.
31308 ("v0.1.0?", "`?`"),
31309 // Asterisk — git's refspec glob.
31310 ("v0.1.*", "`*`"),
31311 // Open bracket — git's refspec glob.
31312 ("v0.1.0[1]", "`[`"),
31313 // Backslash — the canonical Windows-path-leak footgun.
31314 ("feature\\foo", "`\\`"),
31315 // Consecutive dots — git's `<rev1>..<rev2>` range grammar.
31316 ("v0.1..0", "`..`"),
31317 // Reflog grammar.
31318 ("main@{upstream}", "`@{`"),
31319 // The bare `@` — git aliases to `HEAD`.
31320 ("@", "bare `@`"),
31321 // Leading slash.
31322 ("/main", "begin with `/`"),
31323 // Trailing slash.
31324 ("feature/", "end with `/`"),
31325 // Consecutive slashes.
31326 ("feature//foo", "consecutive `/`"),
31327 // Trailing dot.
31328 ("v0.1.0.", "end with `.`"),
31329 // Fully-qualified branch ref — the canonical
31330 // `git show-ref`-output-leak footgun.
31331 ("refs/heads/main", "fully-qualified"),
31332 // Fully-qualified tag ref.
31333 ("refs/tags/v0.1.0", "fully-qualified"),
31334 // Component beginning with `.` (per-component rule).
31335 ("feature/.hidden", "begin with `.`"),
31336 // Component ending with `.lock` (per-component rule).
31337 ("feature/main.lock", "`.lock`"),
31338 // Leaf ref named `<x>.lock` — same per-component rule on
31339 // the single-component refname.
31340 ("main.lock", "`.lock`"),
31341 // Case-insensitive `.LOCK` — APFS / NTFS / HFS+ admit
31342 // both spellings as the same on-disk file, so a
31343 // `:tag "v1.LOCK"` collides with git's atomic-rename
31344 // guard on case-insensitive filesystems. Pinned
31345 // separately from the canonical lowercase arm so a
31346 // future relaxation that only catches lowercase
31347 // surfaces here.
31348 ("v1.LOCK", "`.lock`"),
31349 ("feature/Main.Lock", "`.lock`"),
31350 ] {
31351 let err = is_git_ref_name(s)
31352 .err()
31353 .unwrap_or_else(|| panic!("git ref {s:?} must be rejected"));
31354 assert!(
31355 err.contains(needle),
31356 "git ref {s:?} reason must contain {needle:?}; got {err:?}"
31357 );
31358 }
31359 }
31360
31361 #[test]
31362 fn git_ref_name_rejects_empty_defensively() {
31363 // The predicate is called from `DepSource::validate` only
31364 // after the per-axis `FontePinEmpty` arm has fired at
31365 // validate time; re-checking here keeps the predicate usable
31366 // from any future call site without an empty-precondition
31367 // footgun. Same defensive empty-check `is_dns_1123_label`,
31368 // `is_gateway_api_http_path`, `is_wit_world_ref`,
31369 // `is_nats_subject`, and `is_wasi_keyvalue_slot` carry at
31370 // their call sites.
31371 let err = is_git_ref_name("").unwrap_err();
31372 assert!(err.contains("empty"), "got: {err:?}");
31373 }
31374
31375 #[test]
31376 fn git_ref_name_rejects_at_256_byte_boundary() {
31377 // The 255-byte cap pin — both the boundary-exceeding case and
31378 // the boundary-accepting case in one place, so a future cap
31379 // shift surfaces both arms simultaneously, mirroring
31380 // `dns_1123_label_rejects_at_64_byte_boundary`,
31381 // `gateway_api_http_path_rejects_at_1025_byte_boundary`,
31382 // `wit_world_ref_rejects_at_129_byte_boundary`,
31383 // `nats_subject_rejects_at_257_byte_boundary`, and
31384 // `wasi_kv_slot_rejects_at_513_byte_boundary` on the peer
31385 // predicates. Constructed as a single all-`a` leaf so only
31386 // the cap arm fires.
31387 let max_ok = "a".repeat(255);
31388 assert_eq!(max_ok.len(), 255);
31389 is_git_ref_name(&max_ok).unwrap();
31390 let too_long = "a".repeat(256);
31391 assert_eq!(too_long.len(), 256);
31392 let err = is_git_ref_name(&too_long).unwrap_err();
31393 assert!(err.contains("255"), "got: {err:?}");
31394 assert!(err.contains("256"), "got: {err:?}");
31395 }
31396
31397 #[test]
31398 fn git_ref_name_qualified_prefix_diagnostic_quotes_leaf() {
31399 // Diagnostic-shape pin: the `refs/heads/` / `refs/tags/`
31400 // rejection arm enumerates the leaf the author probably
31401 // meant, so the author's grep target is the *intended*
31402 // refname literal rather than the (rejected) qualified form.
31403 // Pinned across both prefixes so a future relaxation that
31404 // drops the leaf-suggestion surfaces here.
31405 for (qualified, leaf) in [
31406 ("refs/heads/main", "main"),
31407 ("refs/tags/v0.1.0", "v0.1.0"),
31408 ("refs/heads/feature/checkout", "feature/checkout"),
31409 ] {
31410 let err = is_git_ref_name(qualified).unwrap_err();
31411 assert!(
31412 err.contains(&format!("{leaf:?}")),
31413 "qualified ref {qualified:?} diagnostic must quote the leaf \
31414 {leaf:?}; got {err:?}"
31415 );
31416 }
31417 }
31418
31419 // ── is_git_ref_name canonical-OID-shape partition arm ────────────────
31420
31421 #[test]
31422 fn git_ref_name_rejects_canonical_sha1_oid() {
31423 // The fail-before-pass-after pin on the canonical SHA-1 OID
31424 // partition arm: a 40-char lowercase-hex string is the shape
31425 // `is_git_oid` accepts, so `is_git_ref_name` must reject it.
31426 // Until this arm landed `is_git_ref_name` accepted every
31427 // 40-char lowercase-hex string (pure hex carries none of the
31428 // forbidden refname characters, no `..`/`@{`/`/`-prefix/
31429 // `/`-suffix/`.lock`-suffix/`refs/heads/`-prefix), silently
31430 // breaking the cross-axis partition the
31431 // [`DepSource::validate`] gate routes the `:fonte` axes
31432 // through and admitting `:tag "deadbeef…"` /
31433 // `:branch "deadbeef…"` as legitimate refnames — the
31434 // canonical paste-from-`git show --format=%H` mis-slot
31435 // footgun. The diagnostic names the `:rev` axis so the author
31436 // grep-fixes in one edit.
31437 for oid in [
31438 "0123456789abcdef0123456789abcdef01234567",
31439 "deadbeefcafebabe0123456789abcdef01234567",
31440 "ffffffffffffffffffffffffffffffffffffffff",
31441 "0000000000000000000000000000000000000000",
31442 ] {
31443 assert_eq!(oid.len(), GIT_OID_SHA1_LEN);
31444 let err = is_git_ref_name(oid).unwrap_err();
31445 assert!(
31446 err.contains("OID") && err.contains(":rev"),
31447 "canonical SHA-1 OID {oid:?} must surface a diagnostic \
31448 naming OID + `:rev`; got {err:?}"
31449 );
31450 assert!(
31451 err.contains("SHA-1"),
31452 "canonical SHA-1 OID {oid:?} diagnostic must name the \
31453 hash algorithm; got {err:?}"
31454 );
31455 }
31456 }
31457
31458 #[test]
31459 fn git_ref_name_rejects_canonical_sha256_oid() {
31460 // The fail-before-pass-after pin on the canonical SHA-256 OID
31461 // partition arm — Git 2.42+ `extensions.objectFormat = sha256`
31462 // mode. 64-char lowercase-hex strings are equally OID-shaped
31463 // and must surface the same `:rev`-axis diagnostic. Pinned
31464 // separately from SHA-1 so a future relaxation that only
31465 // catches one width surfaces here.
31466 let sha256_zeros = "0".repeat(GIT_OID_SHA256_LEN);
31467 let sha256_ones = "f".repeat(GIT_OID_SHA256_LEN);
31468 let sha256_mixed = format!("deadbeefcafebabe{}", "0123456789abcdef".repeat(3));
31469 for oid in [&sha256_zeros, &sha256_ones, &sha256_mixed] {
31470 assert_eq!(oid.len(), GIT_OID_SHA256_LEN);
31471 let err = is_git_ref_name(oid).unwrap_err();
31472 assert!(
31473 err.contains("OID") && err.contains(":rev"),
31474 "canonical SHA-256 OID {oid:?} must surface a \
31475 diagnostic naming OID + `:rev`; got {err:?}"
31476 );
31477 assert!(
31478 err.contains("SHA-256"),
31479 "canonical SHA-256 OID {oid:?} diagnostic must name \
31480 the hash algorithm; got {err:?}"
31481 );
31482 }
31483 }
31484
31485 #[test]
31486 fn git_ref_name_partition_excludes_off_by_one_lengths() {
31487 // Boundary pin: lengths that *aren't* exactly 40 or 64 hex
31488 // characters are NOT canonical OIDs, so the partition arm
31489 // must not fire — they remain accepted as refnames (consistent
31490 // with `is_git_oid` rejecting them on its exact-width check).
31491 // Abbreviated OIDs (`"c0ffee0"`, 7-char prefix) are ambiguous
31492 // across repository history and `is_git_oid` rejects them
31493 // separately, but they're legitimate refname shapes per `git
31494 // check-ref-format`, so `is_git_ref_name` accepts them here.
31495 // Pinned across the 39/41/63/65-char and abbreviated arms so
31496 // a future widening of the partition arm to "any hex-shaped
31497 // value" surfaces here as a regression rather than silently
31498 // rejecting valid refnames.
31499 for accept in [
31500 // 39 hex chars — one short of SHA-1 width.
31501 "0123456789abcdef0123456789abcdef0123456",
31502 // 41 hex chars — one over SHA-1 width.
31503 "0123456789abcdef0123456789abcdef012345670",
31504 // 63 hex chars — one short of SHA-256 width.
31505 &"a".repeat(63),
31506 // 65 hex chars — one over SHA-256 width.
31507 &"a".repeat(65),
31508 // Abbreviated 7-char SHA — the `git log --short` width.
31509 "c0ffee0",
31510 // Pure-numeric 8-char (looks vaguely SHA-shaped but
31511 // isn't canonical-width).
31512 "00000000",
31513 ] {
31514 is_git_ref_name(accept).unwrap_or_else(|e| {
31515 panic!(
31516 "off-canonical-width hex-shaped value {accept:?} \
31517 (len {len}) must still pass is_git_ref_name — \
31518 the partition arm is exact-width 40/64, not a \
31519 prefix or pattern: {e:?}",
31520 len = accept.len()
31521 )
31522 });
31523 }
31524 }
31525
31526 #[test]
31527 fn git_ref_name_partition_excludes_uppercase_canonical_widths() {
31528 // Boundary pin: the partition arm targets the canonical
31529 // *lowercase-hex* OID shape `git rev-parse HEAD` /
31530 // `git show --format=%H` emit. Uppercase or mixed-case
31531 // 40/64-char hex strings are legitimate refnames per
31532 // `git check-ref-format` (uppercase letters are admitted in
31533 // refnames), so `is_git_ref_name` accepts them here; the
31534 // `:rev` axis separately rejects uppercase OIDs via
31535 // [`is_git_oid`]'s lowercase-only contract — so neither
31536 // axis silently admits an uppercase-hex value cross-slot.
31537 // Pinned across both widths + both uppercase variants so a
31538 // future relaxation of either predicate surfaces here.
31539 for accept in [
31540 // Uppercase 40-char hex — passes is_git_ref_name (valid
31541 // refname), rejected by is_git_oid on lowercase contract.
31542 "DEADBEEFCAFEBABE0123456789ABCDEF01234567",
31543 // Mixed case 40-char hex.
31544 "DeadBeefCafeBabe0123456789abcdef01234567",
31545 // Uppercase 64-char hex.
31546 &"A".repeat(64),
31547 ] {
31548 is_git_ref_name(accept).unwrap_or_else(|e| {
31549 panic!(
31550 "uppercase canonical-width hex value {accept:?} \
31551 must still pass is_git_ref_name — the partition \
31552 arm targets lowercase-canonical only (uppercase \
31553 is a legitimate refname character per \
31554 git-check-ref-format); the `:rev` axis catches \
31555 uppercase via is_git_oid's lowercase contract: \
31556 {e:?}"
31557 )
31558 });
31559 // And confirm is_git_oid rejects it on the lowercase arm
31560 // (so neither axis silently admits the value).
31561 let oid_err = is_git_oid(accept).unwrap_err();
31562 assert!(
31563 oid_err.contains("lowercase") || oid_err.contains("uppercase"),
31564 "uppercase hex value {accept:?} must be rejected by \
31565 is_git_oid on its lowercase contract; got {oid_err:?}"
31566 );
31567 }
31568 }
31569
31570 #[test]
31571 fn git_ref_name_partition_arm_fires_before_per_byte_scan() {
31572 // Order pin: the partition arm runs after the length check
31573 // but before the per-byte refname-character scan, so a
31574 // canonical-OID-shaped value surfaces the `:rev`-axis
31575 // diagnostic rather than (e.g.) falling through to a generic
31576 // per-component arm. Pinned via a canonical OID — pure hex
31577 // can't violate any of the per-byte / `..` / `@{` / `/` /
31578 // `.lock` / `refs/heads/` arms (which is precisely why the
31579 // partition arm is needed), so position-wise this pin
31580 // forecloses a future refactor that splits the partition arm
31581 // across the scan (where uppercase / mixed-case canonical-
31582 // width values would silently route through one branch).
31583 let oid = "0123456789abcdef0123456789abcdef01234567";
31584 let err = is_git_ref_name(oid).unwrap_err();
31585 // The diagnostic mentions OID + `:rev`; it does NOT contain
31586 // any of the per-byte-arm needle substrings the
31587 // `git_ref_name_rejects_each_arm_with_substring_pinned_reason`
31588 // sweep pins, structurally — canonical OIDs can't violate
31589 // those arms.
31590 assert!(err.contains("OID"), "got: {err:?}");
31591 assert!(err.contains(":rev"), "got: {err:?}");
31592 }
31593
31594 #[test]
31595 fn git_ref_name_rejects_leading_hyphen_cli_arg_injection() {
31596 // The CLI-arg-injection arm pin on the `:tag` / `:branch` axis.
31597 // Git's `check-ref-format` grammar admits a leading `-` (the
31598 // byte is a legitimate kebab continuation), so every prior
31599 // shape arm passes the value through; the diagnostic moves
31600 // the gate to the subprocess-argument boundary the resolver
31601 // consumes. Pinned across the canonical CLI-arg-injection
31602 // shapes — short-flag-shaped `"-X"`, long-option-shaped
31603 // `"-stable"`, git-config-injection-shaped
31604 // `"-c=core.merge=ours"`, the canonical
31605 // `"--upload-pack=…"` long-flag form, and the
31606 // `"--config"`-shape repeat-arg form — every shape would
31607 // silently escape `git checkout --quiet --detach <ref>` (the
31608 // resolver's invocation in `caixa-resolver/src/git.rs:41`,
31609 // no `--` argument-list terminator) and get reinterpreted by
31610 // `git checkout`'s argument parser. Peer with the
31611 // `is_git_repo_url` leading-`-` arm (same vector on the
31612 // sibling `:repo` axis), `is_cargo_feature_name` leading-`-`
31613 // arm, and `is_dns_1123_label` leading-`-` arm — the
31614 // substrate-wide "no leading `-` anywhere in a typed
31615 // single-token string slot routed through a subprocess
31616 // argument" invariant is now structurally consistent across
31617 // every value-shape-gated typed surface.
31618 for s in [
31619 "-X", // short-flag-shape
31620 "-stable", // long-option-shape
31621 "-c=core.merge=ours", // git-config-injection-shape
31622 "--upload-pack=cat /etc", // long-flag with-value
31623 "--config", // repeat-arg shape
31624 "-", // degenerate single-byte
31625 ] {
31626 let err = is_git_ref_name(s)
31627 .err()
31628 .unwrap_or_else(|| panic!("git ref {s:?} must be rejected"));
31629 assert!(
31630 err.contains("`-`"),
31631 "git ref {s:?} reason must surface the leading-`-` arm: {err:?}"
31632 );
31633 assert!(
31634 err.contains("CLI-argument-injection"),
31635 "git ref {s:?} reason must name the CLI-argument-injection \
31636 vector: {err:?}"
31637 );
31638 }
31639 // Positive control: a mid-name `-` (the canonical kebab
31640 // separator) passes — `"v0-1-0"`, `"feature-x"`, `"main-2"`
31641 // — pinning that the arm only fires at the leading position,
31642 // not anywhere else.
31643 for s in ["v0-1-0", "feature-x", "main-2"] {
31644 is_git_ref_name(s).unwrap_or_else(|e| {
31645 panic!("mid-name `-` ref {s:?} must pass the leading-`-` arm: {e:?}")
31646 });
31647 }
31648 }
31649
31650 #[test]
31651 fn git_ref_name_leading_hyphen_fires_before_per_byte_scan() {
31652 // Cascade-precedence pin: a `"-flag\n"` value carries both a
31653 // leading `-` and an embedded `\n` control byte; the leading-`-`
31654 // arm fires first (the byte sits at the leading position the
31655 // arm probes, before the per-byte cascade loop's control-byte
31656 // arm). Mirrors the order pin
31657 // `git_ref_name_partition_arm_fires_before_per_byte_scan`
31658 // establishes on the canonical-OID partition arm — both
31659 // pre-loop arms structurally precede the per-byte scan.
31660 let err = is_git_ref_name("-flag\n").unwrap_err();
31661 assert!(err.contains("`-`"), "got: {err:?}");
31662 assert!(
31663 !err.contains("control character"),
31664 "leading-`-` arm must fire before the control-byte per-byte arm: {err:?}"
31665 );
31666 }
31667
31668 #[test]
31669 fn git_ref_name_leading_hyphen_fires_after_canonical_oid_partition() {
31670 // Cascade-precedence pin: the partition arm structurally
31671 // precedes the leading-`-` arm because a canonical OID shape
31672 // (40 / 64 lowercase hex bytes) cannot start with `-` — the
31673 // byte sets are disjoint, so the precedence pin is a no-op at
31674 // value level. The pin matters only at the diagnostic-shape
31675 // level — it ensures a future codec round-trip that
31676 // synthesizes a probe-as-both value (impossible today;
31677 // possible if the OID partition arm ever relaxes its byte
31678 // set) surfaces the more self-locating `:rev`-mis-slot
31679 // diagnostic rather than the broader CLI-arg-injection one.
31680 let oid = "0123456789abcdef0123456789abcdef01234567";
31681 let err = is_git_ref_name(oid).unwrap_err();
31682 assert!(err.contains("OID"), "got: {err:?}");
31683 assert!(
31684 !err.contains("CLI-argument-injection"),
31685 "OID partition arm must precede leading-`-` arm: {err:?}"
31686 );
31687 }
31688
31689 // ── is_git_oid — `:fonte :rev` value-shape predicate ────────────────
31690
31691 #[test]
31692 fn git_oid_canonical_widths_match_sha1_and_sha256() {
31693 // The single-source-of-truth pin on the two canonical widths.
31694 // Drift between the predicate's accepted widths and the const
31695 // values would surface here as a build error, not as a silent
31696 // round-trip break at the renderer layer. Mirrors
31697 // `wasm32_memory_cap_matches_parsed_4_gib` (9d49a3a) — the
31698 // constant equality pin keeps the contract one place.
31699 assert_eq!(GIT_OID_SHA1_LEN, 40);
31700 assert_eq!(GIT_OID_SHA256_LEN, 64);
31701 // Doubled width: SHA-256 is exactly twice SHA-1 in hex char
31702 // count (256 / 4 = 64; 160 / 4 = 40). Pinned so a future
31703 // hash-algorithm widening reads the relationship here.
31704 assert_eq!(GIT_OID_SHA256_LEN, GIT_OID_SHA1_LEN * 2 - 16);
31705 }
31706
31707 #[test]
31708 fn git_oid_accepts_canonical_sha1() {
31709 // Positive control on the SHA-1 OID width: 40 lowercase hex
31710 // characters — the canonical `git rev-parse HEAD` emission
31711 // shape every realistic pleme-io upstream uses today. The all-
31712 // `f` boundary is the lexicographically-largest OID (a real
31713 // commit's hash could land here, and the predicate accepts it
31714 // because it's structurally a valid OID — the null-OID
31715 // sentinel arm partitions the all-`0` boundary only, not the
31716 // all-`f` one).
31717 is_git_oid("0123456789abcdef0123456789abcdef01234567").unwrap();
31718 is_git_oid("deadbeefcafebabe0123456789abcdef01234567").unwrap();
31719 is_git_oid("ffffffffffffffffffffffffffffffffffffffff").unwrap();
31720 }
31721
31722 #[test]
31723 fn git_oid_accepts_canonical_sha256() {
31724 // Positive control on the SHA-256 OID width: 64 lowercase hex
31725 // characters — `git`'s `extensions.objectFormat = sha256`
31726 // emission (GA since Git 2.42 / Oct 2023). Doubled SHA-1 width.
31727 let sha256_one = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
31728 assert_eq!(sha256_one.len(), 64);
31729 is_git_oid(sha256_one).unwrap();
31730 let sha256_fs = "f".repeat(64);
31731 is_git_oid(&sha256_fs).unwrap();
31732 }
31733
31734 #[test]
31735 fn git_oid_rejects_null_oid_sentinel_sha1() {
31736 // Canonical "I copy-pasted the no-such-commit sentinel out of
31737 // `git update-ref --stdin` docs / pre-receive hook example"
31738 // footgun on the SHA-1 width — the all-zero 40-char hex
31739 // string is git's `null OID` sentinel (used to indicate ref
31740 // create / delete in update-ref flows) and never names a real
31741 // commit in any repo's object database. Until the null-OID
31742 // arm landed it passed every other shape arm (canonical
31743 // length, lowercase hex) and surfaced at `git fetch <remote>
31744 // 0000…0000` time with a quoting-confused "couldn't find
31745 // remote ref" error far from the source caixa.lisp, with the
31746 // lacre's content-address locked to a `git:0000…0000` closure
31747 // that never equals any upstream's actual `HEAD`. The
31748 // diagnostic carries the `40` width verbatim so a future
31749 // SHA-256 fixture surfaces the same arm at the doubled width
31750 // boundary.
31751 let null_sha1 = "0".repeat(40);
31752 let err = is_git_oid(&null_sha1).unwrap_err();
31753 assert!(
31754 err.contains("null-OID sentinel"),
31755 "reason must name the sentinel: {err}",
31756 );
31757 assert!(err.contains("40"), "reason must name the width: {err}",);
31758 assert!(
31759 err.contains("no-such-commit") || err.contains("update-ref"),
31760 "reason must reference git's null-OID semantics: {err}",
31761 );
31762 }
31763
31764 #[test]
31765 fn git_oid_rejects_null_oid_sentinel_sha256() {
31766 // Same sentinel on the SHA-256 width — `git`'s
31767 // `extensions.objectFormat = sha256` mode (GA Git 2.42 / Oct
31768 // 2023) carries the same null-OID semantics on the doubled
31769 // 64-char width. Pinned separately so a future relaxation that
31770 // only catches the SHA-1 width surfaces here, peer with the
31771 // SHA-1 / SHA-256 pair-pinning posture
31772 // `git_oid_accepts_canonical_sha1` /
31773 // `git_oid_accepts_canonical_sha256` already establishes for
31774 // the positive controls.
31775 let null_sha256 = "0".repeat(64);
31776 let err = is_git_oid(&null_sha256).unwrap_err();
31777 assert!(
31778 err.contains("null-OID sentinel"),
31779 "reason must name the sentinel: {err}",
31780 );
31781 assert!(err.contains("64"), "reason must name the width: {err}",);
31782 }
31783
31784 #[test]
31785 fn git_oid_null_oid_fires_after_length_and_hex_arms() {
31786 // Cascade-precedence pin: the null-OID arm runs *after* the
31787 // length + character-class arms, so an off-by-one-length all-
31788 // zeros value surfaces the narrower `abbreviated` diagnostic
31789 // (the length arm's own reason wording) before the structural
31790 // null-OID diagnostic, and an uppercase all-zeros value (which
31791 // can't actually exist — `0` has no case — but pinned via the
31792 // mixed-case-but-non-null fixture) routes the same way. The
31793 // null-OID arm is the *fourth* arm, structurally the
31794 // lexicographic-content-arm after length and per-byte
31795 // character-class.
31796 let off_by_one_zeros = "0".repeat(41);
31797 let err = is_git_oid(&off_by_one_zeros).unwrap_err();
31798 assert!(
31799 err.contains("abbreviated"),
31800 "off-by-one-length all-zeros surfaces length arm first: {err}",
31801 );
31802 // The all-`f` 40-char value — same boundary class as null-OID
31803 // but at the opposite hex extreme — passes the predicate,
31804 // confirming the null-OID arm doesn't over-fire on lexicographic
31805 // boundaries.
31806 is_git_oid("ffffffffffffffffffffffffffffffffffffffff").unwrap();
31807 }
31808
31809 #[test]
31810 fn git_oid_rejects_empty_defensively() {
31811 // The predicate is called from `crate::dep::DepSource::validate`
31812 // only after the per-axis `FontePinEmpty` arm has fired at
31813 // validate time; re-checking here keeps the predicate usable
31814 // from any future call site without an empty-precondition
31815 // footgun. Same defensive empty-check `is_dns_1123_label`,
31816 // `is_gateway_api_http_path`, `is_wit_world_ref`,
31817 // `is_nats_subject`, `is_wasi_keyvalue_slot`, and
31818 // `is_git_ref_name` carry at their call sites.
31819 let err = is_git_oid("").unwrap_err();
31820 assert!(err.contains("empty"), "got: {err:?}");
31821 }
31822
31823 #[test]
31824 fn git_oid_rejects_each_arm_with_substring_pinned_reason() {
31825 // Substrate-side diagnostic-shape pin: each grammar arm
31826 // surfaces its own distinct reason substring. Pinned here so a
31827 // future reason-wording rephrase that drops any of these
31828 // substrings surfaces at this one place, not piecemeal across
31829 // every per-axis test sweep. Mirrors
31830 // `git_ref_name_rejects_each_arm_with_substring_pinned_reason`,
31831 // `wasi_kv_slot_rejects_each_arm_with_substring_pinned_reason`,
31832 // and `nats_subject_rejects_each_arm_with_substring_pinned_reason`
31833 // on the peer predicates.
31834 for (s, needle) in [
31835 // Abbreviated 7-char prefix — the canonical `git log
31836 // --short` paste-from-release-notes footgun.
31837 ("c0ffee0", "abbreviated"),
31838 // Abbreviated 12-char prefix — `git log --short=12`.
31839 ("c0ffee001234", "abbreviated"),
31840 // Off-by-one above SHA-1 width.
31841 ("0123456789abcdef0123456789abcdef012345670", "abbreviated"),
31842 // Off-by-one below SHA-256 width.
31843 (
31844 "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde",
31845 "abbreviated",
31846 ),
31847 // Off-by-one above SHA-256 width.
31848 (
31849 "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0",
31850 "abbreviated",
31851 ),
31852 // Uppercase SHA-1 — `git porcelain` lowercases on output.
31853 ("DEADBEEFCAFEBABE0123456789ABCDEF01234567", "uppercase"),
31854 // Mixed-case SHA-1 — same path as pure-uppercase; the first
31855 // uppercase byte fires the arm.
31856 ("deadbeefCAFEbabe0123456789abcdef01234567", "uppercase"),
31857 // Non-hex character at exact SHA-1 length — the cross-axis
31858 // mis-slot footgun (a refname-style char landing in `:rev`).
31859 // `g` is the first non-hex byte; the non-hex arm fires
31860 // ahead of any other rule. The hyphen / colon / slash arms
31861 // are the same path on the same predicate.
31862 ("g123456789abcdef0123456789abcdef01234567", "non-hex"),
31863 ("0123456789abcdef-123456789abcdef01234567", "non-hex"),
31864 ("0123456789abcdef/123456789abcdef01234567", "non-hex"),
31865 ("0123456789abcdef:123456789abcdef01234567", "non-hex"),
31866 // Whitespace inside an otherwise-SHA-shaped value (length
31867 // 41 — fails the length arm first; pinned to ensure the
31868 // diagnostic surfaces *some* parser wording).
31869 ("0123456789abcdef0123456789abcdef01234567 ", "abbreviated"),
31870 ] {
31871 let err = is_git_oid(s)
31872 .err()
31873 .unwrap_or_else(|| panic!("git OID {s:?} must be rejected"));
31874 assert!(
31875 err.contains(needle),
31876 "git OID {s:?} reason must contain {needle:?}; got {err:?}"
31877 );
31878 }
31879 }
31880
31881 #[test]
31882 fn git_oid_rejects_at_canonical_width_boundaries() {
31883 // Boundary pin on the two canonical widths simultaneously: 39
31884 // (below SHA-1), 40 (SHA-1 exactly), 41 (just above), 63 (just
31885 // below SHA-256), 64 (SHA-256 exactly), 65 (just above). Pinned
31886 // so a future relaxation that admits "close enough" widths
31887 // surfaces here. The failing-length fixtures use all-zero hex
31888 // so only the length arm fires (the null-OID sentinel arm is
31889 // structurally downstream of the length arm — a non-canonical
31890 // length fires the abbreviated diagnostic before the null
31891 // diagnostic). The passing-length fixtures use a non-null hex
31892 // value so the null-OID arm doesn't fire (the all-zero
31893 // canonical-width value is the sentinel and is rejected by its
31894 // own arm, pinned in `git_oid_rejects_null_oid_sentinel_*`).
31895 let nonzero_sha1 = "0123456789abcdef0123456789abcdef01234567";
31896 assert_eq!(nonzero_sha1.len(), 40);
31897 let nonzero_sha256 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
31898 assert_eq!(nonzero_sha256.len(), 64);
31899 for (len, ok) in [
31900 (1usize, false),
31901 (7, false),
31902 (39, false),
31903 (40, true),
31904 (41, false),
31905 (63, false),
31906 (64, true),
31907 (65, false),
31908 (128, false),
31909 ] {
31910 let s = if ok && len == 40 {
31911 nonzero_sha1.to_string()
31912 } else if ok && len == 64 {
31913 nonzero_sha256.to_string()
31914 } else {
31915 "0".repeat(len)
31916 };
31917 let result = is_git_oid(&s);
31918 if ok {
31919 result.unwrap_or_else(|e| panic!("len {len} must pass: {e:?}"));
31920 } else {
31921 let err = result.expect_err(&format!("len {len} must fail"));
31922 assert!(
31923 err.contains("abbreviated") || err.contains(&len.to_string()),
31924 "len {len} reason must name the offending length or surface \
31925 the abbreviation arm, got {err:?}"
31926 );
31927 }
31928 }
31929 }
31930
31931 #[test]
31932 fn git_oid_rejection_is_disjoint_from_ref_name_acceptance() {
31933 // Structural pin: the two predicates partition the `:fonte`
31934 // pin axes — every canonical refname is rejected by
31935 // `is_git_oid`, and every canonical OID is rejected by
31936 // `is_git_ref_name`. The intersection of the two valid sets
31937 // is exactly the empty set. Drift here = a value that passes
31938 // both predicates would land at *both* axes silently, defeating
31939 // the structural "cross-axis mis-slot is a build error"
31940 // contract. Pinned with a representative cross-set so a future
31941 // predicate weakening surfaces here.
31942 let canonical_refnames = [
31943 "v0.1.0",
31944 "main",
31945 "feature/checkout",
31946 "release-1.0",
31947 "user-1/feat-x-v2",
31948 ];
31949 for refname in canonical_refnames {
31950 is_git_ref_name(refname).unwrap_or_else(|e| {
31951 panic!("setup: canonical refname {refname:?} must pass is_git_ref_name: {e:?}")
31952 });
31953 assert!(
31954 is_git_oid(refname).is_err(),
31955 "canonical refname {refname:?} must NOT pass is_git_oid \
31956 (predicate-partition pin)"
31957 );
31958 }
31959 let canonical_oids = [
31960 "0123456789abcdef0123456789abcdef01234567",
31961 "deadbeefcafebabe0123456789abcdef01234567",
31962 "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
31963 ];
31964 for oid in canonical_oids {
31965 is_git_oid(oid).unwrap_or_else(|e| {
31966 panic!("setup: canonical OID {oid:?} must pass is_git_oid: {e:?}")
31967 });
31968 assert!(
31969 is_git_ref_name(oid).is_err(),
31970 "canonical OID {oid:?} must NOT pass is_git_ref_name \
31971 (predicate-partition pin)"
31972 );
31973 }
31974 }
31975
31976 // ── is_sandboxed_relative_path — `:behavior :on-*` + `:upgrade-from ─
31977 // ── :state-change :script` value-shape predicate ────────────────────
31978
31979 #[test]
31980 fn sandboxed_relative_path_accepts_canonical_relative_paths() {
31981 // Positive controls: every documented authoring shape across
31982 // the two existing call sites (`:behavior :on-init` / `:on-call`
31983 // / `:on-cast` / `:on-info` / `:on-state-change` / `:on-terminate`
31984 // and `:upgrade-from :state-change :script`) — bare filename,
31985 // standard `lib/` subdirectory, deeply-nested migrations
31986 // subdirectory, sibling-folder-shaped path, and explicit
31987 // current-dir-relative-prefixed path. Pin every leg so a
31988 // future tightening that rejects any of these (e.g. demanding
31989 // a `lib/` prefix specifically, or forbidding the explicit
31990 // `./` segment) surfaces here as a test-failure at the predicate
31991 // boundary, not piecemeal across per-axis call sites.
31992 for relpath in [
31993 "init.lisp",
31994 "lib/init.lisp",
31995 "lib/handlers.lisp",
31996 "lib/migrations/v01-to-v02.lisp",
31997 "callbacks/on_call.lisp",
31998 "./lib/init.lisp",
31999 "a",
32000 ] {
32001 is_sandboxed_relative_path(Path::new(relpath)).unwrap_or_else(|v| {
32002 panic!("canonical relative path {relpath:?} must pass, got {v:?}")
32003 });
32004 }
32005 }
32006
32007 #[test]
32008 fn sandboxed_relative_path_rejects_empty() {
32009 // The fail-before-pass-after pin on the empty arm. Both
32010 // `PathBuf::new()` (no bytes) and `PathBuf::from("")` (empty
32011 // string) hit the `as_os_str().is_empty()` precondition; both
32012 // resolve to `root` under `root.join(p)` and silently point the
32013 // `LisleLoader` at the project directory rather than a file.
32014 assert_eq!(
32015 is_sandboxed_relative_path(Path::new("")),
32016 Err(PathShapeViolation::Empty)
32017 );
32018 let blank = PathBuf::new();
32019 assert_eq!(
32020 is_sandboxed_relative_path(&blank),
32021 Err(PathShapeViolation::Empty)
32022 );
32023 }
32024
32025 #[test]
32026 fn sandboxed_relative_path_rejects_absolute() {
32027 // The fail-before-pass-after pin on the absolute arm. Sweep
32028 // the canonical sandbox-escape paste-from-shell-prompt
32029 // footguns: an `/etc/...` Lunatic-style sandbox bypass, a
32030 // user-home leak that the renderer's `root.join(p)` would
32031 // silently replace, the project-relative-shaped `/lib/...`
32032 // typo where the author meant `lib/...` without a leading
32033 // slash, and the bare root `/`. `Path::join` replaces the
32034 // base with an absolute right-hand side, so every one of
32035 // these resolves verbatim to outside the caixa root regardless
32036 // of where the layout checker rooted itself.
32037 for abs in [
32038 "/etc/passwd",
32039 "/home/user/escape.lisp",
32040 "/lib/init.lisp",
32041 "/",
32042 ] {
32043 assert_eq!(
32044 is_sandboxed_relative_path(Path::new(abs)),
32045 Err(PathShapeViolation::Absolute),
32046 "absolute path {abs:?} must surface as PathShapeViolation::Absolute"
32047 );
32048 }
32049 }
32050
32051 #[test]
32052 fn sandboxed_relative_path_rejects_parent_escape_at_every_position() {
32053 // The fail-before-pass-after pin on the parent-escape arm.
32054 // Position sweep — `..` as a leading component (the canonical
32055 // "I meant the sibling caixa" mis-author), as a mid-path
32056 // component (the canonical "lib/../../escape" path-traversal
32057 // that's structurally identical regardless of how many `..`
32058 // segments stack), as a trailing component (lib/.., resolving
32059 // to the project root via a delayed escape), and the bare `..`
32060 // (project parent directory). Each must surface as
32061 // `PathShapeViolation::ParentEscape` regardless of position —
32062 // pinned per-position so a future relaxation that only
32063 // checks one position surfaces at this one place, not
32064 // piecemeal across per-axis call sites.
32065 for escape in [
32066 "../sibling/init.lisp",
32067 "lib/../../escaped.lisp",
32068 "lib/..",
32069 "..",
32070 "lib/handlers/../../escape.lisp",
32071 ] {
32072 assert_eq!(
32073 is_sandboxed_relative_path(Path::new(escape)),
32074 Err(PathShapeViolation::ParentEscape),
32075 "parent-escape path {escape:?} must surface as \
32076 PathShapeViolation::ParentEscape"
32077 );
32078 }
32079 }
32080
32081 #[test]
32082 fn sandboxed_relative_path_arm_ordering_is_empty_absolute_parent_escape() {
32083 // Order pin: the predicate evaluates Empty → Absolute →
32084 // ParentEscape — the same arm-ordering both inlined call sites
32085 // followed verbatim (b0c8389 `BehaviorSpec::validate`'s
32086 // `validate_callback_path`, 26da2c7
32087 // `UpgradeInstruction::StateChange::validate`). A future
32088 // reordering would silently flip which diagnostic the per-axis
32089 // wrapper surfaces (e.g. an absolute-and-empty hybrid value
32090 // would suddenly raise `Absolute` instead of `Empty`). Pinned
32091 // here so a future reorder surfaces at the predicate boundary.
32092 //
32093 // The empty case can't *also* be absolute (empty paths are
32094 // relative-by-construction) or parent-escaping, so the
32095 // empty-first ordering only matters relative to the OS-string
32096 // emptiness check vs. the absolute-prefix check. Pin the two
32097 // legs that *can* compose: an absolute path with `..` segments
32098 // must raise `Absolute` (not `ParentEscape`); an absolute-but-
32099 // not-parent-escaping path must also raise `Absolute`. The
32100 // arm-ordering pin is structural — every parent-escape case
32101 // tested above is relative, so the ParentEscape arm is reached
32102 // only when both Empty and Absolute arms have been cleared.
32103 assert_eq!(
32104 is_sandboxed_relative_path(Path::new("/etc/../passwd")),
32105 Err(PathShapeViolation::Absolute),
32106 "absolute path with `..` segments must surface as Absolute (not \
32107 ParentEscape) — Empty → Absolute → ParentEscape arm-ordering pin"
32108 );
32109 }
32110
32111 #[test]
32112 fn sandboxed_relative_path_distinguishes_curdir_from_parent_escape() {
32113 // Boundary pin: `Component::CurDir` (`.`) is NOT a sandbox
32114 // escape — `root.join("./lib/x.lisp")` resolves to
32115 // `root/lib/x.lisp`, identical to `root.join("lib/x.lisp")`,
32116 // so `./` segments must pass the predicate. The arm-ordering
32117 // check above pins that `Component::ParentDir` is the only
32118 // escape vector caught here. Pinned separately so a future
32119 // tightening that *does* reject `.` segments (e.g. requiring
32120 // canonical normalized form) lands at this one predicate.
32121 is_sandboxed_relative_path(Path::new("./lib/init.lisp")).unwrap();
32122 is_sandboxed_relative_path(Path::new("lib/./handlers.lisp")).unwrap();
32123 }
32124
32125 #[test]
32126 fn sandboxed_relative_path_violations_are_distinct_variants() {
32127 // Diagnostic-shape pin: the three `PathShapeViolation` variants
32128 // are distinct enum tags so each per-axis caller can match-and-
32129 // wrap into its own typed `*Path` / `*Script` variant without
32130 // a string-parse step (the trap [`is_dns_1123_label`] etc.
32131 // avoid by returning `Result<(), String>` — but the path-shape
32132 // callers were already split three ways across `BehaviorError`
32133 // / `UpgradeError`, so a `String` return would *regress* the
32134 // diagnostic shape rather than preserve it). The PartialEq /
32135 // Copy / Hash derives on `PathShapeViolation` are pinned here
32136 // so a future API rework reads the requirement off this test.
32137 let v1 = PathShapeViolation::Empty;
32138 let v2 = PathShapeViolation::Absolute;
32139 let v3 = PathShapeViolation::ParentEscape;
32140 assert_ne!(v1, v2);
32141 assert_ne!(v2, v3);
32142 assert_ne!(v1, v3);
32143 // Copy + Eq round-trip: predicate consumers like
32144 // `BehaviorSpec::validate` and `UpgradeInstruction::validate`
32145 // pattern-match on the variant without consuming it.
32146 let v_copy = v1;
32147 assert_eq!(v1, v_copy);
32148 }
32149
32150 #[test]
32151 fn sandboxed_relative_path_matches_inlined_call_site_semantics() {
32152 // End-to-end pin: every value the two pre-lift inline gates
32153 // (`BehaviorSpec::validate_callback_path` and
32154 // `UpgradeInstruction::StateChange::validate`'s inline arms)
32155 // accepted-or-rejected must surface from the lifted predicate
32156 // with identically-classified violation tags. Drift here would
32157 // mean a previously-accepted authoring shape would suddenly
32158 // fail (or vice versa) silently across the lift commit. Pinned
32159 // by sweeping the canonical authoring shapes both pre-lift call
32160 // sites' tests cover.
32161 // Pre-lift accepts (must still pass):
32162 for accept in [
32163 "lib/init.lisp",
32164 "lib/handlers.lisp",
32165 "lib/migrations.lisp",
32166 "lib/cleanup.lisp",
32167 "lib/migrations/v01-to-v02.lisp",
32168 "callbacks/handle_call.lisp",
32169 ] {
32170 is_sandboxed_relative_path(Path::new(accept))
32171 .unwrap_or_else(|v| panic!("pre-lift accept {accept:?} regressed, got {v:?}"));
32172 }
32173 // Pre-lift rejects (must still reject, with the same tag):
32174 let cases: &[(&str, PathShapeViolation)] = &[
32175 ("", PathShapeViolation::Empty),
32176 ("/etc/passwd", PathShapeViolation::Absolute),
32177 ("/etc/migrations.lisp", PathShapeViolation::Absolute),
32178 (
32179 "../sibling/migrations.lisp",
32180 PathShapeViolation::ParentEscape,
32181 ),
32182 ("lib/../../escaped.lisp", PathShapeViolation::ParentEscape),
32183 ];
32184 for (reject, expected) in cases {
32185 assert_eq!(
32186 is_sandboxed_relative_path(Path::new(reject)).unwrap_err(),
32187 *expected,
32188 "pre-lift reject {reject:?} must classify as {expected:?}"
32189 );
32190 }
32191 }
32192
32193 #[test]
32194 fn path_shape_violation_all_lists_every_variant_in_declaration_order() {
32195 // Fail-before-pass-after pin on the paired
32196 // [`PathShapeViolation::ALL`] exhaustive-iteration surface.
32197 // Two axes in one assertion, both must hold:
32198 //
32199 // (1) The slice enumerates every arm in the closed
32200 // three-arm discriminator set exactly once, in
32201 // declaration order (`Empty` → `Absolute` →
32202 // `ParentEscape`) — the arm-ordering the
32203 // [`is_sandboxed_relative_path`] gate + every per-axis
32204 // caller in [`crate::manifest::ManifestError`] preserve
32205 // for diagnostic-precedence continuity. A future variant
32206 // addition (a `Symlink` arm the future symlink-escape
32207 // gate would raise, a `TrailingSpace` arm a future
32208 // whitespace-hygiene gate would surface) that lands on
32209 // the enum without extending `ALL` trips this test at
32210 // build time rather than surfacing as a silent
32211 // under-coverage across every downstream sweep.
32212 //
32213 // (2) For every arm in the slice, exactly one of the
32214 // [`gen_platform::IsVariant`]-derive-generated `is_*`
32215 // predicates returns `true` and the other two return
32216 // `false` — the partition property every peer closed-set
32217 // enum's `IsVariant` derive carries
32218 // ([`crate::CaixaKind`] at kind.rs,
32219 // [`crate::supervisor::RestartStrategy`] +
32220 // [`crate::supervisor::RestartPolicy`] at supervisor.rs,
32221 // [`crate::upgrade::UpgradeInstruction`] at upgrade.rs,
32222 // [`crate::aplicacao::PlacementStrategy`] +
32223 // [`crate::aplicacao::RateLimitUnit`] at aplicacao.rs,
32224 // [`crate::dep::DepList`] at dep.rs). A future variant
32225 // addition that lands on the enum without threading a
32226 // new column into the per-arm-partition assertion table
32227 // trips here at build time.
32228 assert_eq!(
32229 PathShapeViolation::ALL,
32230 &[
32231 PathShapeViolation::Empty,
32232 PathShapeViolation::Absolute,
32233 PathShapeViolation::ParentEscape,
32234 ],
32235 "PathShapeViolation::ALL must list every arm in \
32236 declaration order (Empty → Absolute → ParentEscape) — \
32237 the arm-ordering is_sandboxed_relative_path and every \
32238 per-axis ManifestError caller preserve for \
32239 diagnostic-precedence continuity"
32240 );
32241 let rows: [(PathShapeViolation, [bool; 3]); 3] = [
32242 (PathShapeViolation::Empty, [true, false, false]),
32243 (PathShapeViolation::Absolute, [false, true, false]),
32244 (PathShapeViolation::ParentEscape, [false, false, true]),
32245 ];
32246 for (variant, expected) in rows {
32247 let observed = [
32248 variant.is_empty(),
32249 variant.is_absolute(),
32250 variant.is_parent_escape(),
32251 ];
32252 assert_eq!(
32253 observed, expected,
32254 "PathShapeViolation::{variant:?} is_* predicates must \
32255 partition the arm set (empty, absolute, parent_escape); \
32256 got {observed:?}"
32257 );
32258 }
32259 }
32260
32261 #[test]
32262 fn path_shape_violation_predicates_are_byte_equal_to_matches_family() {
32263 // Byte-equal pin on the [`gen_platform::IsVariant`]-derive-
32264 // generated per-arm predicate family. For every arm on the
32265 // closed three-arm [`PathShapeViolation`] discriminator, each
32266 // per-arm `is_*` predicate must agree byte-for-byte with the
32267 // hand-rolled `matches!(_, PathShapeViolation::…)` shape a
32268 // future consumer (a `feira lint --explain-path-shape=<axis>`
32269 // per-arm listing, a future symlink-escape / whitespace-hygiene
32270 // gate that keys off "is this a sandbox-escape arm" boolean, a
32271 // future single-arm `matches!` in a downstream renderer that
32272 // treats `Empty` distinctly from the other two) would
32273 // otherwise open-code at each caller. A future rebrand (a
32274 // `#[is_variant(name = "…")]` attribute drift on the derive,
32275 // an accidental peer predicate that shadows the derive-generated
32276 // one, a hand-rolled `impl PathShapeViolation` block that
32277 // shadows one of the derive-generated methods) trips this test
32278 // the moment the two paths' bytes diverge. Peer of the sibling
32279 // `caixa_kind_is_variant_predicates_partition_the_arm_set`
32280 // (kind.rs) and every peer closed-set-enum byte-equal pin.
32281 for &variant in PathShapeViolation::ALL {
32282 assert_eq!(
32283 variant.is_empty(),
32284 matches!(variant, PathShapeViolation::Empty),
32285 "PathShapeViolation::{variant:?}.is_empty() must agree \
32286 with matches!(_, PathShapeViolation::Empty)"
32287 );
32288 assert_eq!(
32289 variant.is_absolute(),
32290 matches!(variant, PathShapeViolation::Absolute),
32291 "PathShapeViolation::{variant:?}.is_absolute() must agree \
32292 with matches!(_, PathShapeViolation::Absolute)"
32293 );
32294 assert_eq!(
32295 variant.is_parent_escape(),
32296 matches!(variant, PathShapeViolation::ParentEscape),
32297 "PathShapeViolation::{variant:?}.is_parent_escape() must agree \
32298 with matches!(_, PathShapeViolation::ParentEscape)"
32299 );
32300 }
32301 }
32302
32303 // ── is_lisp_extension — `:behavior :on-*` + `:upgrade-from ───────────
32304 // ── :state-change :script` file-type predicate ───────────────────────
32305
32306 #[test]
32307 fn lisp_extension_accepts_canonical_shapes() {
32308 // Positive controls: every documented authoring shape across
32309 // both existing call sites — bare filename, standard `lib/`
32310 // subdirectory, deeply-nested migrations subdirectory,
32311 // explicit current-dir-relative prefix, mid-path `./`
32312 // segment, single-letter stem, and the multi-dot stem
32313 // (`lib/migrations/v.0.1.lisp`) an author might use to
32314 // encode the migration's `:from` version into the filename.
32315 // The predicate only inspects the terminating extension —
32316 // `Path::extension()` returns the substring after the final
32317 // `.` — so the multi-dot stem is structurally accepted
32318 // because the final extension is still `lisp`. Drift here =
32319 // a future tightening that rejects any of these surfaces as
32320 // a test-failure at the predicate boundary, not piecemeal
32321 // across per-axis call sites (`BehaviorSpec::validate`,
32322 // `UpgradeInstruction::StateChange::validate`).
32323 for relpath in [
32324 "init.lisp",
32325 "lib/init.lisp",
32326 "lib/handlers.lisp",
32327 "lib/migrations.lisp",
32328 "lib/migrations/v01-to-v02.lisp",
32329 "./lib/init.lisp",
32330 "lib/./handlers.lisp",
32331 "lib/migrations/v.0.1.lisp",
32332 "a.lisp",
32333 ] {
32334 assert!(
32335 is_lisp_extension(Path::new(relpath)),
32336 "canonical `.lisp` shape {relpath:?} must pass is_lisp_extension"
32337 );
32338 }
32339 }
32340
32341 #[test]
32342 fn lisp_extension_rejects_no_extension() {
32343 // The fail-before-pass-after pin on the no-extension shape.
32344 // A path with no `.` component (`Path::extension()` returns
32345 // `None`) is the canonical "I declared the slot but forgot
32346 // the `.lisp` extension" authoring footgun. The wasm-engine's
32347 // `tatara_lisp::read` consumer can't infer the file type from
32348 // the path alone, so the gate refuses the value at validate
32349 // time.
32350 for relpath in [
32351 "lib/init",
32352 "init",
32353 "lib/handlers",
32354 "lib/migrations/v01-to-v02",
32355 "a",
32356 ] {
32357 assert!(
32358 !is_lisp_extension(Path::new(relpath)),
32359 "no-extension shape {relpath:?} must fail is_lisp_extension"
32360 );
32361 }
32362 }
32363
32364 #[test]
32365 fn lisp_extension_rejects_wrong_extension() {
32366 // Wrong-extension sweep: the canonical authoring footguns
32367 // an author might drag in from the workspace tree (`.txt`,
32368 // `.md`, `.json`, `.yaml`, `.toml`), the `.rs` shape that
32369 // an IDE auto-complete might propose, the `.lisp.bak` shape
32370 // an editor might leave behind (the predicate only inspects
32371 // the *terminating* extension — `Path::extension()` returns
32372 // `bak` here, not `lisp.bak` — so the gate refuses it as a
32373 // no-`.lisp` final extension), and the `.lispx` / `.lis`
32374 // near-miss shapes that a typo would produce. Each must
32375 // fail the predicate — the wasm-engine's `tatara_lisp::read`
32376 // consumer rejects all of these at hot-upgrade migration /
32377 // instance-start time.
32378 for relpath in [
32379 "lib/init.rs",
32380 "lib/init.txt",
32381 "lib/init.md",
32382 "lib/init.json",
32383 "lib/init.yaml",
32384 "lib/init.toml",
32385 "lib/init.lisp.bak",
32386 "lib/init.lispx",
32387 "lib/init.lis",
32388 ] {
32389 assert!(
32390 !is_lisp_extension(Path::new(relpath)),
32391 "wrong-extension shape {relpath:?} must fail is_lisp_extension"
32392 );
32393 }
32394 }
32395
32396 #[test]
32397 fn lisp_extension_is_case_sensitive() {
32398 // Strict lowercase pin: every case-folded shape a
32399 // case-insensitive volume's existence check would match the
32400 // on-disk file must still fail the predicate — the
32401 // canonical-form codec emits lowercase `.lisp` verbatim, so
32402 // a case-folded shape mismatches the round-trip-stable
32403 // canonical form (THEORY.md §V.2.7 render-determinism).
32404 // Same case-sensitive discipline the byte-size / duration
32405 // codecs and every other shape-gate predicate in `render.rs`
32406 // (label / scheme / unit boundaries) carry. Pinned at the
32407 // predicate boundary so any future case-folding regression
32408 // surfaces here rather than piecemeal across per-axis call
32409 // sites.
32410 for relpath in [
32411 "lib/init.LISP",
32412 "lib/init.Lisp",
32413 "lib/init.LiSp",
32414 "lib/init.lISP",
32415 "lib/init.LISp",
32416 ] {
32417 assert!(
32418 !is_lisp_extension(Path::new(relpath)),
32419 "case-folded `.lisp` shape {relpath:?} must fail is_lisp_extension \
32420 (strict lowercase, render-determinism pin)"
32421 );
32422 }
32423 }
32424
32425 #[test]
32426 fn lisp_extension_constant_matches_predicate() {
32427 // Cross-pin: the [`LISP_SOURCE_EXTENSION`] const and the
32428 // predicate's accepted set are the same single source of
32429 // truth. Drift would let a future renderer / per-axis
32430 // wrapper emit `.<const>` while the predicate accepts only
32431 // `.lisp` (or vice versa), silently breaking the
32432 // round-trip-stable canonical form. Pinned by constructing
32433 // a path from the const and round-tripping through the
32434 // predicate.
32435 assert_eq!(LISP_SOURCE_EXTENSION, "lisp");
32436 let p = PathBuf::from(format!("lib/init.{LISP_SOURCE_EXTENSION}"));
32437 assert!(
32438 is_lisp_extension(&p),
32439 "path constructed from LISP_SOURCE_EXTENSION must pass is_lisp_extension"
32440 );
32441 }
32442
32443 #[test]
32444 fn lisp_extension_matches_inlined_call_site_semantics() {
32445 // End-to-end pin: every value the pre-lift inline gate
32446 // (`BehaviorSpec::validate_callback_path`, c97815a) accepted-
32447 // or-rejected must surface from the lifted predicate
32448 // identically. Drift here would mean a previously-accepted
32449 // authoring shape would suddenly fail (or vice versa)
32450 // silently across the lift commit. Sweeps the canonical
32451 // authoring shapes the pre-lift call site's tests covered
32452 // verbatim.
32453 // Pre-lift accepts (must still pass):
32454 for accept in [
32455 "lib/init.lisp",
32456 "lib/handlers.lisp",
32457 "lib/migrations/v01-to-v02.lisp",
32458 "init.lisp",
32459 "a.lisp",
32460 "./lib/init.lisp",
32461 "lib/./handlers.lisp",
32462 "lib/migrations/v.0.1.lisp",
32463 ] {
32464 assert!(
32465 is_lisp_extension(Path::new(accept)),
32466 "pre-lift accept {accept:?} regressed"
32467 );
32468 }
32469 // Pre-lift rejects (must still reject):
32470 for reject in [
32471 "lib/init",
32472 "init",
32473 "lib/init.rs",
32474 "lib/init.txt",
32475 "lib/init.lisp.bak",
32476 "lib/init.lispx",
32477 "lib/init.LISP",
32478 "lib/init.Lisp",
32479 ] {
32480 assert!(
32481 !is_lisp_extension(Path::new(reject)),
32482 "pre-lift reject {reject:?} regressed"
32483 );
32484 }
32485 }
32486
32487 // ── is_computeunit_yaml_extension — `:servicos` compound-suffix predicate ───
32488
32489 #[test]
32490 fn computeunit_yaml_extension_accepts_canonical_shapes() {
32491 // Positive controls: every canonical authoring shape every
32492 // in-tree fixture and the `Caixa::template` scaffold use. The
32493 // predicate inspects the final file-name component and checks
32494 // for the compound `.computeunit.yaml` suffix with at least
32495 // one byte of stem preceding it.
32496 for relpath in [
32497 "servicos/demo.computeunit.yaml",
32498 "servicos/hello-rio.computeunit.yaml",
32499 "servicos/my-service.computeunit.yaml",
32500 "servicos/a.computeunit.yaml",
32501 "./servicos/demo.computeunit.yaml",
32502 "servicos/./demo.computeunit.yaml",
32503 "servicos/sub/nested.computeunit.yaml",
32504 "servicos/v0.1.computeunit.yaml",
32505 ] {
32506 assert!(
32507 is_computeunit_yaml_extension(Path::new(relpath)),
32508 "canonical `.computeunit.yaml` shape {relpath:?} must pass \
32509 is_computeunit_yaml_extension"
32510 );
32511 }
32512 }
32513
32514 #[test]
32515 fn computeunit_yaml_extension_rejects_no_extension() {
32516 // No-extension shape — the canonical "I declared the slot
32517 // but forgot the `.computeunit.yaml` suffix" footgun. The
32518 // peer caixa-helm / caixa-flux `serde_yaml::from_str`
32519 // consumer can't infer the file type from the path alone, so
32520 // the gate refuses the value at validate time.
32521 for relpath in ["servicos/demo", "demo", "servicos/sub/nested"] {
32522 assert!(
32523 !is_computeunit_yaml_extension(Path::new(relpath)),
32524 "no-extension shape {relpath:?} must fail \
32525 is_computeunit_yaml_extension"
32526 );
32527 }
32528 }
32529
32530 #[test]
32531 fn computeunit_yaml_extension_rejects_wrong_extension() {
32532 // Wrong-extension sweep across the canonical authoring footguns
32533 // an author might drag in from the workspace tree — bare
32534 // `.yaml` (the canonical "I forgot the `.computeunit` segment"
32535 // typo), `.yml` (Helm-shorthand leak), `.json` (FluxCD
32536 // bundle leak), `.toml` (Cargo workspace leak), `.txt`
32537 // / `.md` (paste-from-doc footguns), `.yaml.bak` (editor
32538 // backup), the near-miss `.computeunit.yam` / `.computeunit.yamls`
32539 // typo, and the off-by-one-segment `computeunit-yaml`
32540 // / `computeunit_yaml` shapes. Each must fail the predicate.
32541 for relpath in [
32542 "servicos/demo.yaml",
32543 "servicos/demo.yml",
32544 "servicos/demo.json",
32545 "servicos/demo.toml",
32546 "servicos/demo.txt",
32547 "servicos/demo.md",
32548 "servicos/demo.computeunit.yaml.bak",
32549 "servicos/demo.computeunit.yam",
32550 "servicos/demo.computeunit.yamls",
32551 "servicos/demo.computeunit",
32552 "servicos/demo-computeunit.yaml",
32553 "servicos/demo_computeunit.yaml",
32554 ] {
32555 assert!(
32556 !is_computeunit_yaml_extension(Path::new(relpath)),
32557 "wrong-extension shape {relpath:?} must fail \
32558 is_computeunit_yaml_extension"
32559 );
32560 }
32561 }
32562
32563 #[test]
32564 fn computeunit_yaml_extension_is_case_sensitive() {
32565 // Strict lowercase pin: every case-folded shape a
32566 // case-insensitive volume's existence check would match the
32567 // on-disk file must still fail the predicate — the canonical-
32568 // form codec emits lowercase `.computeunit.yaml` verbatim, so
32569 // a case-folded shape mismatches the round-trip-stable
32570 // canonical form (THEORY.md §V.2.7 render-determinism). Same
32571 // case-sensitive discipline the byte-size / duration codecs
32572 // and the peer `is_lisp_extension` predicate carry.
32573 for relpath in [
32574 "servicos/demo.ComputeUnit.yaml",
32575 "servicos/demo.COMPUTEUNIT.yaml",
32576 "servicos/demo.computeunit.YAML",
32577 "servicos/demo.computeunit.Yaml",
32578 "servicos/demo.COMPUTEUNIT.YAML",
32579 ] {
32580 assert!(
32581 !is_computeunit_yaml_extension(Path::new(relpath)),
32582 "case-folded `.computeunit.yaml` shape {relpath:?} must fail \
32583 is_computeunit_yaml_extension (strict lowercase, \
32584 render-determinism pin)"
32585 );
32586 }
32587 }
32588
32589 #[test]
32590 fn computeunit_yaml_extension_rejects_empty_stem() {
32591 // Degenerate hidden-file shape: a file name exactly equal to
32592 // the suffix (`.computeunit.yaml` — no stem preceding the
32593 // suffix) is the structural "Servico declared with no
32594 // identity" footgun. The substrate identifies each ComputeUnit
32595 // by the file-stem segment that precedes `.computeunit.yaml`
32596 // (the rendered `lareira-<stem>` Helm chart, the per-Servico
32597 // `metadata.name`, the M3 `:contratos` membership lookup), so
32598 // an empty stem leaves the Servico unidentifiable. Predicate
32599 // pin: the `name.len() > SUFFIX.len()` bound rejects the
32600 // hidden-file shape at the predicate boundary.
32601 for relpath in [".computeunit.yaml", "servicos/.computeunit.yaml"] {
32602 assert!(
32603 !is_computeunit_yaml_extension(Path::new(relpath)),
32604 "empty-stem shape {relpath:?} must fail \
32605 is_computeunit_yaml_extension"
32606 );
32607 }
32608 }
32609
32610 #[test]
32611 fn computeunit_yaml_extension_constant_matches_predicate() {
32612 // Cross-pin: the [`COMPUTEUNIT_YAML_SUFFIX`] const and the
32613 // predicate's accepted set are the same single source of
32614 // truth. Drift would let a future renderer / per-axis wrapper
32615 // emit `<stem><const>` while the predicate accepts only
32616 // `.computeunit.yaml` (or vice versa), silently breaking the
32617 // round-trip-stable canonical form. Pinned by constructing a
32618 // path from the const and round-tripping through the
32619 // predicate. Mirrors the peer
32620 // `lisp_extension_constant_matches_predicate` pin.
32621 assert_eq!(COMPUTEUNIT_YAML_SUFFIX, ".computeunit.yaml");
32622 let p = PathBuf::from(format!("servicos/demo{COMPUTEUNIT_YAML_SUFFIX}"));
32623 assert!(
32624 is_computeunit_yaml_extension(&p),
32625 "path constructed from COMPUTEUNIT_YAML_SUFFIX must pass \
32626 is_computeunit_yaml_extension"
32627 );
32628 }
32629
32630 // ── is_cargo_feature_name — shared `:caracteristicas` feature-name predicate ──
32631
32632 #[test]
32633 fn cargo_feature_name_accepts_canonical_forms() {
32634 // Substrate-side pin: the predicate accepts every canonical Cargo
32635 // feature name shape `:caracteristicas` entries carry. Drift between
32636 // this list and the per-axis `dep::tests::validate_accepts_canonical_caracteristicas`
32637 // positive-set sweep surfaces here — one source of truth for the
32638 // rule. Includes single-token (`http`), kebab-case (`runtime-tokio`),
32639 // snake-case (`derive_macros`), namespaced-dot (`tokio.full`),
32640 // version-suffix (`v0.1`), `+`-separated (`http+json`), leading
32641 // underscore (`_internal`), doubled-underscore (`__private`),
32642 // and digit-starting (`v0_1`) — the canonical authoring shapes
32643 // every realistic Cargo feature in the pleme-io ecosystem uses.
32644 for s in [
32645 "http",
32646 "json",
32647 "derive",
32648 "serde",
32649 "serde_json",
32650 "runtime-tokio",
32651 "tokio.full",
32652 "v0.1",
32653 "v1",
32654 "http+json",
32655 "_internal",
32656 "__private",
32657 "default",
32658 "rt-multi-thread",
32659 "12factor",
32660 "feat.v2",
32661 "client+server",
32662 ] {
32663 is_cargo_feature_name(s)
32664 .unwrap_or_else(|e| panic!("canonical Cargo feature name {s:?} must pass: {e:?}"));
32665 }
32666 }
32667
32668 #[test]
32669 fn cargo_feature_name_rejects_each_arm_with_substring_pinned_reason() {
32670 // Substrate-side diagnostic-shape pin: each grammar arm
32671 // surfaces its own distinct reason substring. Pinned here so a
32672 // future reason-wording rephrase that drops any of these
32673 // substrings surfaces at this one place, not piecemeal across
32674 // every per-axis test sweep. Mirrors
32675 // `git_repo_url`'s and `git_ref_name`'s arm-substring sweeps
32676 // on the peer predicates.
32677 for (s, needle) in [
32678 // Leading `+` — the canonical paste-from-`+optional-feature`
32679 // activation-form-in-feature-name-slot footgun.
32680 ("+http", "`+`"),
32681 // Leading `-` — kebab-leak / CLI-arg-injection adjacent.
32682 ("-json", "`-`"),
32683 // Leading `.` — dotted-version-suffix-as-feature-name typo.
32684 (".feat", "`.`"),
32685 // Whitespace inside — multi-token blob.
32686 ("http feature", "whitespace"),
32687 // Tab inside.
32688 ("http\tjson", "whitespace"),
32689 // Leading whitespace — paste-from-aligned-doc.
32690 (" http", "whitespace"),
32691 // Comma — list-separator-belongs-to-list-grammar.
32692 ("http,json", "`,`"),
32693 // Forward slash — Cargo's `dep/feat` namespaced-dep syntax.
32694 ("http/json", "`/`"),
32695 // Question mark — URL-reserved.
32696 ("http?", "`?`"),
32697 // Hash — URL-reserved.
32698 ("http#frag", "`#`"),
32699 // Embedded control character.
32700 ("http\x01json", "control character"),
32701 // Newline — paste-from-multiline-doc.
32702 ("http\njson", "control character"),
32703 // DEL byte (0x7F).
32704 ("http\x7fjson", "control character"),
32705 // Non-ASCII byte — un-percent-encoded character.
32706 ("caf\u{e9}", "non-ASCII"),
32707 // Non-ASCII at first byte.
32708 ("\u{e9}feat", "non-ASCII"),
32709 // Forbidden punctuation in the continuation set.
32710 ("http@1", "invalid character"),
32711 ("http&json", "invalid character"),
32712 ("http=v1", "invalid character"),
32713 ] {
32714 let err = is_cargo_feature_name(s)
32715 .err()
32716 .unwrap_or_else(|| panic!("Cargo feature name {s:?} must be rejected"));
32717 assert!(
32718 err.contains(needle),
32719 "Cargo feature name {s:?} reason must contain {needle:?}; got {err:?}"
32720 );
32721 }
32722 }
32723
32724 #[test]
32725 fn cargo_feature_name_rejects_empty_defensively() {
32726 // The predicate is called from `crate::dep::Dep::validate_caracteristicas`
32727 // only after the per-axis `CaracteristicaEmpty` arm has fired
32728 // at validate time; re-checking here keeps the predicate usable
32729 // from any future call site without an empty-precondition
32730 // footgun. Same defensive empty-check `is_dns_1123_label`,
32731 // `is_gateway_api_http_path`, `is_wit_world_ref`,
32732 // `is_nats_subject`, `is_wasi_keyvalue_slot`, `is_git_ref_name`,
32733 // `is_git_oid`, and `is_git_repo_url` carry at their call sites.
32734 let err = is_cargo_feature_name("").unwrap_err();
32735 assert!(err.contains("empty"), "got: {err:?}");
32736 }
32737
32738 #[test]
32739 fn cargo_feature_name_rejects_at_65_byte_boundary() {
32740 // The 64-byte cap pin — both the boundary-exceeding case and
32741 // the boundary-accepting case in one place, so a future cap
32742 // shift surfaces both arms simultaneously, mirroring
32743 // `dns_1123_label_rejects_at_64_byte_boundary`,
32744 // `gateway_api_http_path_rejects_at_1025_byte_boundary`,
32745 // `wit_world_ref_rejects_at_129_byte_boundary`,
32746 // `nats_subject_rejects_at_257_byte_boundary`,
32747 // `wasi_kv_slot_rejects_at_513_byte_boundary`, and
32748 // `git_ref_name_rejects_at_256_byte_boundary` on the peer
32749 // predicates. Constructed as a single all-`a` token so only
32750 // the cap arm fires.
32751 let max_ok = "a".repeat(CARGO_FEATURE_NAME_MAX_LEN);
32752 assert_eq!(max_ok.len(), 64);
32753 is_cargo_feature_name(&max_ok).unwrap();
32754 let too_long = "a".repeat(CARGO_FEATURE_NAME_MAX_LEN + 1);
32755 assert_eq!(too_long.len(), 65);
32756 let err = is_cargo_feature_name(&too_long).unwrap_err();
32757 assert!(err.contains("64"), "got: {err:?}");
32758 assert!(err.contains("65"), "got: {err:?}");
32759 }
32760
32761 #[test]
32762 fn cargo_feature_name_first_byte_diagnostics_name_the_leading_char() {
32763 // Diagnostic-shape pin: the leading-character rejection arms
32764 // name the specific punctuation (`+`, `-`, `.`) verbatim so the
32765 // author's grep target is unambiguous. Pinned across the three
32766 // canonical leading-char footguns so a future relaxation that
32767 // drops any of the three surfaces here. The `+`-arm's wording
32768 // additionally points the author at the canonical Cargo
32769 // `+<feature>` activation-form-vs-feature-name discipline so
32770 // the paste-from-doc footgun lands its remediation in the
32771 // diagnostic itself.
32772 let err_plus = is_cargo_feature_name("+http").unwrap_err();
32773 assert!(err_plus.contains("`+`"), "got: {err_plus:?}");
32774 assert!(
32775 err_plus.contains("activation"),
32776 "got: {err_plus:?} (must name the Cargo +<feature> activation-form)"
32777 );
32778 let err_hyphen = is_cargo_feature_name("-json").unwrap_err();
32779 assert!(err_hyphen.contains("`-`"), "got: {err_hyphen:?}");
32780 let err_dot = is_cargo_feature_name(".feat").unwrap_err();
32781 assert!(err_dot.contains("`.`"), "got: {err_dot:?}");
32782 }
32783
32784 // ── is_spdx_expression_shape — shared `:licenca` SPDX-expression predicate ──
32785
32786 #[test]
32787 fn spdx_expression_shape_accepts_canonical_forms() {
32788 // Substrate-side pin: the predicate accepts every canonical
32789 // SPDX expression shape the `:licenca` axis carries. Drift
32790 // between this list and the per-axis
32791 // `manifest::tests::validate_licenca_accepts_canonical_expressions`
32792 // positive-set sweep surfaces here — one source of truth for
32793 // the rule. Covers single-license, `OR`/`AND`-compound,
32794 // `WITH`-exception, parenthesis-grouped, `+`-suffix, and
32795 // `LicenseRef-` / `DocumentRef-:LicenseRef-` shapes.
32796 for s in [
32797 "MIT",
32798 "Apache-2.0",
32799 "BSD-3-Clause",
32800 "MPL-2.0",
32801 "GPL-3.0-or-later",
32802 "GPL-2.0+",
32803 "Apache-2.0 OR MIT",
32804 "Apache-2.0 AND MIT",
32805 "Apache-2.0 WITH LLVM-exception",
32806 "(MIT OR Apache-2.0) AND BSD-3-Clause",
32807 "(MIT OR Apache-2.0) AND BSD-3-Clause AND ISC",
32808 "LicenseRef-MyLicense",
32809 "DocumentRef-spdx-tool:LicenseRef-MIT-Style",
32810 "x",
32811 ] {
32812 is_spdx_expression_shape(s)
32813 .unwrap_or_else(|e| panic!("canonical SPDX expression {s:?} must pass: {e:?}"));
32814 }
32815 }
32816
32817 #[test]
32818 fn spdx_expression_shape_rejects_each_arm_with_substring_pinned_reason() {
32819 // Substrate-side diagnostic-shape pin: each alphabet arm
32820 // surfaces its own distinct reason substring. Pinned here so a
32821 // future reason-wording rephrase that drops any of these
32822 // substrings surfaces at this one place, not piecemeal across
32823 // every per-axis test sweep. Mirrors
32824 // `cargo_feature_name_rejects_each_arm_with_substring_pinned_reason`
32825 // on the peer predicate.
32826 for (s, needle) in [
32827 // Leading whitespace — paste-from-aligned-doc.
32828 (" MIT", "whitespace"),
32829 // Trailing whitespace — paste-from-doc.
32830 ("MIT ", "whitespace"),
32831 // Tab inside — tab-from-aligned-doc.
32832 ("MIT\tOR Apache-2.0", "tab"),
32833 // Embedded control character.
32834 ("MIT\x01OR Apache-2.0", "control character"),
32835 // Newline — paste-from-multiline-doc.
32836 ("MIT\nOR Apache-2.0", "control character"),
32837 // CRLF — paste-from-multiline-doc.
32838 ("MIT\rApache-2.0", "control character"),
32839 // DEL byte (0x7F).
32840 ("MIT\x7fApache-2.0", "control character"),
32841 // Non-ASCII byte — smart-quote paste.
32842 ("MIT\u{a0}OR Apache-2.0", "non-ASCII"),
32843 // Non-ASCII at first byte — fullwidth letter.
32844 ("\u{ff2d}IT", "non-ASCII"),
32845 // Underscore — snake-case-instead-of-kebab-case typo.
32846 ("Apache_2.0", "`_`"),
32847 // Comma — list-separator-belongs-to-list-grammar.
32848 ("MIT, Apache-2.0", "`,`"),
32849 // Forward slash — colloquial dual-license idiom.
32850 ("MIT/Apache-2.0", "`/`"),
32851 // Semicolon — list-separator confusion.
32852 ("MIT; Apache-2.0", "`;`"),
32853 // Forbidden punctuation in the alphabet.
32854 ("MIT@1.0", "invalid character"),
32855 ("MIT&Apache-2.0", "invalid character"),
32856 ("MIT=Apache-2.0", "invalid character"),
32857 ("MIT*1.0", "invalid character"),
32858 ] {
32859 let err = is_spdx_expression_shape(s)
32860 .err()
32861 .unwrap_or_else(|| panic!("SPDX expression {s:?} must be rejected"));
32862 assert!(
32863 err.contains(needle),
32864 "SPDX expression {s:?} reason must contain {needle:?}; got {err:?}"
32865 );
32866 }
32867 }
32868
32869 #[test]
32870 fn spdx_expression_shape_rejects_empty_defensively() {
32871 // The predicate is called from `crate::Caixa::validate_licenca`
32872 // only after the per-axis `LicencaEmpty` arm has fired at
32873 // validate time; re-checking here keeps the predicate usable
32874 // from any future call site without an empty-precondition
32875 // footgun. Same defensive empty-check `is_dns_1123_label`,
32876 // `is_gateway_api_http_path`, `is_wit_world_ref`,
32877 // `is_nats_subject`, `is_wasi_keyvalue_slot`,
32878 // `is_git_ref_name`, `is_git_oid`, `is_git_repo_url`, and
32879 // `is_cargo_feature_name` carry at their call sites.
32880 let err = is_spdx_expression_shape("").unwrap_err();
32881 assert!(err.contains("empty"), "got: {err:?}");
32882 }
32883
32884 #[test]
32885 fn spdx_expression_shape_rejects_at_257_byte_boundary() {
32886 // The 256-byte cap pin — both the boundary-exceeding case and
32887 // the boundary-accepting case in one place, so a future cap
32888 // shift surfaces both arms simultaneously, mirroring the peer
32889 // cap-boundary pins. Constructed as a single all-`a` token so
32890 // only the cap arm fires (256 `a` bytes is alphabet-valid).
32891 let max_ok = "a".repeat(SPDX_EXPRESSION_MAX_LEN);
32892 assert_eq!(max_ok.len(), 256);
32893 is_spdx_expression_shape(&max_ok).unwrap();
32894 let too_long = "a".repeat(SPDX_EXPRESSION_MAX_LEN + 1);
32895 assert_eq!(too_long.len(), 257);
32896 let err = is_spdx_expression_shape(&too_long).unwrap_err();
32897 assert!(err.contains("256"), "got: {err:?}");
32898 assert!(err.contains("257"), "got: {err:?}");
32899 }
32900
32901 // ── is_chart_description_shape — shared `:descricao` chart-description predicate ──
32902
32903 #[test]
32904 fn chart_description_shape_accepts_canonical_forms() {
32905 // Substrate-side pin: the predicate accepts every canonical
32906 // chart-description shape the `:descricao` axis carries.
32907 // Drift between this list and the per-axis
32908 // `manifest::tests::validate_descricao_accepts_canonical_summary`
32909 // positive-set sweep surfaces here — one source of truth for
32910 // the rule. Covers ASCII summaries, the Unicode `→` from the
32911 // canonical Rust→wasm fixture, and the Unicode `—` em-dash
32912 // from the `Caixa::template` scaffold every `feira init`
32913 // emits.
32914 for s in [
32915 "Canonical Rust→wasm32-wasip2 caixa Servico.",
32916 "Checkout flow.",
32917 "AWS provider caixa for tatara-lisp",
32918 "FIXME — describe this caixa",
32919 "x",
32920 ] {
32921 is_chart_description_shape(s)
32922 .unwrap_or_else(|e| panic!("canonical chart description {s:?} must pass: {e:?}"));
32923 }
32924 }
32925
32926 #[test]
32927 fn chart_description_shape_rejects_each_arm_with_substring_pinned_reason() {
32928 // Substrate-side diagnostic-shape pin: each arm surfaces its
32929 // own distinct reason substring. Pinned here so a future
32930 // reason-wording rephrase that drops any of these substrings
32931 // surfaces at this one place, not piecemeal across every
32932 // per-axis test sweep. Mirrors
32933 // `spdx_expression_shape_rejects_each_arm_with_substring_pinned_reason`
32934 // on the peer predicate.
32935 for (s, needle) in [
32936 // Leading whitespace — paste-from-aligned-doc.
32937 (" Checkout flow.", "whitespace"),
32938 // Trailing whitespace — paste-from-doc.
32939 ("Checkout flow. ", "whitespace"),
32940 // Tab inside — tab-from-aligned-doc.
32941 ("Checkout\tflow.", "tab"),
32942 // Newline — paste-from-multiline-doc.
32943 ("Checkout\nflow.", "newline"),
32944 // Carriage return — paste-from-Windows-CRLF-doc.
32945 ("Checkout\rflow.", "carriage return"),
32946 // NUL byte — paste-from-binary-blob.
32947 ("Checkout\x00flow.", "control character"),
32948 // BEL byte — paste-from-binary-blob.
32949 ("Checkout\x07flow.", "control character"),
32950 // ESC byte — paste-from-binary-blob.
32951 ("Checkout\x1bflow.", "control character"),
32952 // DEL byte (0x7F).
32953 ("Checkout\x7fflow.", "control character"),
32954 ] {
32955 let err = is_chart_description_shape(s)
32956 .err()
32957 .unwrap_or_else(|| panic!("chart description {s:?} must be rejected"));
32958 assert!(
32959 err.contains(needle),
32960 "chart description {s:?} reason must contain {needle:?}; got {err:?}"
32961 );
32962 }
32963 }
32964
32965 #[test]
32966 fn chart_description_shape_accepts_unicode() {
32967 // Positive control on the non-ASCII arm: the predicate must
32968 // accept Unicode beyond the ASCII alphabet — the canonical
32969 // pleme-io descricao fixtures carry `→` (U+2192) and `—`
32970 // (U+2014), and every downstream consumer (YAML 1.2, Helm v3,
32971 // every chart-aware UI) round-trips Unicode losslessly.
32972 // Mirrors the spdx-rejects-non-ASCII arm by inverting it — a
32973 // future tightening that bans non-ASCII bytes would regress
32974 // every canonical fixture and surface here as a regression.
32975 for s in [
32976 "Canonical Rust→wasm32-wasip2",
32977 "FIXME — describe this caixa",
32978 "Caixa pour le projet tâche",
32979 "日本語の説明",
32980 "naïve",
32981 ] {
32982 is_chart_description_shape(s)
32983 .unwrap_or_else(|e| panic!("Unicode chart description {s:?} must pass: {e:?}"));
32984 }
32985 }
32986
32987 #[test]
32988 fn chart_description_shape_rejects_empty_defensively() {
32989 // The predicate is called from `crate::Caixa::validate_descricao`
32990 // only after the per-axis `DescricaoEmpty` arm has fired at
32991 // validate time; re-checking here keeps the predicate usable
32992 // from any future call site without an empty-precondition
32993 // footgun. Same defensive empty-check `is_dns_1123_label`,
32994 // `is_gateway_api_http_path`, `is_wit_world_ref`,
32995 // `is_nats_subject`, `is_wasi_keyvalue_slot`,
32996 // `is_git_ref_name`, `is_git_oid`, `is_git_repo_url`,
32997 // `is_cargo_feature_name`, and `is_spdx_expression_shape`
32998 // carry at their call sites.
32999 let err = is_chart_description_shape("").unwrap_err();
33000 assert!(err.contains("empty"), "got: {err:?}");
33001 }
33002
33003 #[test]
33004 fn chart_description_shape_rejects_at_513_byte_boundary() {
33005 // The 512-byte cap pin — both the boundary-exceeding case and
33006 // the boundary-accepting case in one place, so a future cap
33007 // shift surfaces both arms simultaneously, mirroring the peer
33008 // cap-boundary pins. Constructed as a single all-`a` token so
33009 // only the cap arm fires (512 `a` bytes is alphabet-valid).
33010 let max_ok = "a".repeat(CHART_DESCRIPTION_MAX_LEN);
33011 assert_eq!(max_ok.len(), 512);
33012 is_chart_description_shape(&max_ok).unwrap();
33013 let too_long = "a".repeat(CHART_DESCRIPTION_MAX_LEN + 1);
33014 assert_eq!(too_long.len(), 513);
33015 let err = is_chart_description_shape(&too_long).unwrap_err();
33016 assert!(err.contains("512"), "got: {err:?}");
33017 assert!(err.contains("513"), "got: {err:?}");
33018 }
33019
33020 #[test]
33021 fn chart_description_shape_rejects_each_unicode_bidi_override_codepoint() {
33022 // The Trojan Source (CVE-2021-42574) arm — pins every UAX #9
33023 // bidirectional-override / isolate format codepoint as a
33024 // structural rejection on the typed `:descricao` axis. The
33025 // per-byte non-ASCII pass deliberately admits Unicode letters
33026 // / em-dash / arrows because the canonical fixtures carry them
33027 // (`Canonical Rust→wasm32-wasip2`, `FIXME — describe this
33028 // caixa`); only the typed codepoint scan catches the nine
33029 // bidi-override codepoints that flip the rendered visual order
33030 // of every following character, so a future drop of any one
33031 // arm here surfaces as a `must be rejected` panic at this one
33032 // place rather than as a silent regression downstream. Each
33033 // case carries an alphabet-valid prefix + suffix so only the
33034 // bidi-override arm fires.
33035 for (cp, name) in [
33036 ('\u{202A}', "U+202A"),
33037 ('\u{202B}', "U+202B"),
33038 ('\u{202C}', "U+202C"),
33039 ('\u{202D}', "U+202D"),
33040 ('\u{202E}', "U+202E"),
33041 ('\u{2066}', "U+2066"),
33042 ('\u{2067}', "U+2067"),
33043 ('\u{2068}', "U+2068"),
33044 ('\u{2069}', "U+2069"),
33045 ] {
33046 let s = format!("alice{cp}bob");
33047 let err = is_chart_description_shape(&s)
33048 .err()
33049 .unwrap_or_else(|| panic!("chart description with {name} must be rejected"));
33050 assert!(
33051 err.contains(name),
33052 "chart description reason for {name} must name the codepoint verbatim; got {err:?}"
33053 );
33054 assert!(
33055 err.contains("bidirectional-override")
33056 || err.contains("Unicode bidi")
33057 || err.contains("Trojan Source"),
33058 "chart description reason for {name} must name the Trojan-Source banner; \
33059 got {err:?}"
33060 );
33061 }
33062 }
33063
33064 #[test]
33065 fn chart_description_shape_accepts_pure_rtl_text_without_bidi_override() {
33066 // Positive control on the bidi-override arm: pure visual
33067 // right-to-left scripts (Hebrew, Arabic) decode to non-bidi-
33068 // override codepoints and the predicate must accept them
33069 // natively — banning all RTL would regress every Hebrew /
33070 // Arabic-authored caixa, which the substrate explicitly
33071 // supports via the non-ASCII byte arm. The structural axis the
33072 // bidi-override arm closes is the explicit direction-mark
33073 // codepoint, not the RTL script itself.
33074 for s in [
33075 // Hebrew word (RTL script, no bidi-override codepoint).
33076 "שלום",
33077 // Arabic word (RTL script, no bidi-override codepoint).
33078 "مرحبا",
33079 // Mixed LTR / RTL caixa — the canonical multilingual
33080 // description shape every YAML 1.2 + Helm v3 + Artifact
33081 // Hub consumer round-trips losslessly.
33082 "Caixa para שלום",
33083 ] {
33084 is_chart_description_shape(s).unwrap_or_else(|e| {
33085 panic!("pure-RTL chart description {s:?} must pass without bidi override: {e:?}")
33086 });
33087 }
33088 }
33089
33090 #[test]
33091 fn chart_description_shape_rejects_each_unicode_line_break_codepoint() {
33092 // The non-ASCII Unicode line-break arm — pins each of the three
33093 // UAX #14 / YAML 1.1 §4.1 line-break codepoints outside the
33094 // ASCII `\n` / `\r` bytes already caught at the per-byte pass.
33095 // Each case carries an alphabet-valid prefix + suffix so only
33096 // the line-break arm fires; the per-byte `\n` / `\r` arms
33097 // would shadow the codepoint scan if the line-break helper
33098 // accepted single-byte ASCII line terminators. A future drop
33099 // of any one arm here surfaces as a `must be rejected` panic
33100 // at this one place rather than as a silent regression
33101 // through YAML 1.1-compat downstream consumers (go-yaml v2 /
33102 // Helm v3 / kubectl). Mirrors the peer
33103 // `chart_maintainer_name_shape_rejects_each_unicode_line_break_codepoint`
33104 // on the sibling predicate — both predicates route through the
33105 // same lifted `find_unicode_line_break` helper.
33106 for (cp, name) in [
33107 ('\u{0085}', "U+0085"),
33108 ('\u{2028}', "U+2028"),
33109 ('\u{2029}', "U+2029"),
33110 ] {
33111 let s = format!("first line{cp}second line");
33112 let err = is_chart_description_shape(&s)
33113 .err()
33114 .unwrap_or_else(|| panic!("chart description with {name} must be rejected"));
33115 assert!(
33116 err.contains(name),
33117 "chart description reason for {name} must name the codepoint verbatim; got {err:?}"
33118 );
33119 assert!(
33120 err.contains("line-break") || err.contains("UAX #14") || err.contains("YAML 1.1"),
33121 "chart description reason for {name} must name the Unicode-line-break banner; \
33122 got {err:?}"
33123 );
33124 }
33125 }
33126
33127 #[test]
33128 fn chart_description_shape_accepts_non_line_break_unicode() {
33129 // Positive control on the line-break arm: the predicate must
33130 // accept every non-line-break Unicode shape the canonical
33131 // fixtures carry. Pinned alongside the per-codepoint rejection
33132 // sweep so a future helper widening that accidentally rejects
33133 // a non-line-break codepoint (the structural-floor regression
33134 // class) surfaces here as a single-source-of-truth pin. The
33135 // canonical multilingual descriptions, RTL text, em-dash and
33136 // arrows must all pass.
33137 for s in [
33138 "Canonical Rust→wasm32-wasip2 caixa Servico.",
33139 "FIXME — describe this caixa",
33140 "Caixa para שלום",
33141 "日本語の説明テスト",
33142 // U+00A0 NO-BREAK SPACE is NOT a line-break codepoint
33143 // (UAX #14 class GL — Glue, non-breaking) — must pass.
33144 "Caixa\u{00A0}for tests",
33145 ] {
33146 is_chart_description_shape(s).unwrap_or_else(|e| {
33147 panic!(
33148 "non-line-break Unicode chart description {s:?} must pass without rejection: \
33149 {e:?}"
33150 )
33151 });
33152 }
33153 }
33154
33155 #[test]
33156 fn chart_description_shape_rejects_each_unicode_invisible_format_codepoint() {
33157 // The Unicode invisible-format arm — pins each of the eight
33158 // BMP Cf-category zero-width codepoints with no visible glyph
33159 // in any conforming font. The per-byte non-ASCII pass
33160 // deliberately admits multi-byte UTF-8 sequences (Unicode
33161 // letters / arrows / em-dash are canonical fixtures); only the
33162 // typed codepoint scan catches these eight. Each case carries
33163 // an alphabet-valid prefix + suffix so only the invisible-
33164 // format arm fires. A future drop of any one arm here surfaces
33165 // as a `must be rejected` panic at this one place rather than
33166 // as a silent regression through invisible-codepoint-homograph
33167 // downstream consumers (Artifact Hub description-search
33168 // misses, byte-level diff / grep / equality disagreement with
33169 // the visible-glyph match). Peer of
33170 // `chart_maintainer_name_shape_rejects_each_unicode_invisible_format_codepoint`
33171 // on the sibling predicate — both predicates route through the
33172 // same lifted `find_unicode_invisible_format` helper. Covers
33173 // the four paste-from-Word / paste-from-BOM-editor / paste-
33174 // from-typesetting shapes (U+00AD / U+200B / U+2060 / U+FEFF)
33175 // and the four math-formula invisible operators (U+2061
33176 // FUNCTION APPLICATION / U+2062 INVISIBLE TIMES / U+2063
33177 // INVISIBLE SEPARATOR / U+2064 INVISIBLE PLUS — the canonical
33178 // paste-from-MathJax / paste-from-LaTeX-rendered-formula
33179 // footgun where the renderer emits an invisible operator
33180 // between adjacent symbols for screen-reader operator
33181 // semantics).
33182 for (cp, name) in [
33183 ('\u{00AD}', "U+00AD"),
33184 ('\u{200B}', "U+200B"),
33185 ('\u{2060}', "U+2060"),
33186 ('\u{2061}', "U+2061"),
33187 ('\u{2062}', "U+2062"),
33188 ('\u{2063}', "U+2063"),
33189 ('\u{2064}', "U+2064"),
33190 ('\u{FEFF}', "U+FEFF"),
33191 ] {
33192 let s = format!("Canonical{cp}Servico");
33193 let err = is_chart_description_shape(&s)
33194 .err()
33195 .unwrap_or_else(|| panic!("chart description with {name} must be rejected"));
33196 assert!(
33197 err.contains(name),
33198 "chart description reason for {name} must name the codepoint verbatim; got {err:?}"
33199 );
33200 assert!(
33201 err.contains("invisible-format")
33202 || err.contains("Cf-category")
33203 || err.contains("zero-width"),
33204 "chart description reason for {name} must name the invisible-format banner; \
33205 got {err:?}"
33206 );
33207 }
33208 }
33209
33210 #[test]
33211 fn chart_description_shape_accepts_non_invisible_format_unicode() {
33212 // Positive control on the invisible-format arm: the predicate
33213 // must accept every non-invisible-format Unicode shape canonical
33214 // fixtures carry — including U+200C ZWNJ / U+200D ZWJ
33215 // (legitimate compositional load in Indic / Persian scripts and
33216 // emoji ZWJ sequences) and U+200E LRM / U+200F RLM (legitimate
33217 // single-character direction hints in mixed-script prose). A
33218 // future helper widening that accidentally rejects any of these
33219 // would regress legitimate fixture shapes and surfaces here as
33220 // a single-source-of-truth pin. Mirrors
33221 // `chart_maintainer_name_shape_accepts_non_invisible_format_unicode`
33222 // on the sibling predicate.
33223 for s in [
33224 "Canonical Rust→wasm32-wasip2 caixa Servico.",
33225 "FIXME — describe this caixa",
33226 // Emoji ZWJ sequence (U+200D) — must NOT be rejected: the
33227 // canonical multi-codepoint emoji authoring shape every
33228 // chart-aware UI renders as a single glyph.
33229 "Caixa for the 👨\u{200D}💻 family",
33230 // ZWNJ (U+200C) — legitimate Persian / Indic script
33231 // composition; the helper must NOT claim it.
33232 "Caixa for می\u{200C}باشد",
33233 // Bidi marks LRM (U+200E) and RLM (U+200F) — legitimate
33234 // single-character direction hints, separate class from
33235 // the bidi *overrides* the prior helper rejects.
33236 "Caixa for ASCII\u{200E}embedded in RTL",
33237 "Caixa for \u{200F}RTL hint",
33238 ] {
33239 is_chart_description_shape(s).unwrap_or_else(|e| {
33240 panic!(
33241 "non-invisible-format Unicode chart description {s:?} must pass without \
33242 rejection: {e:?}"
33243 )
33244 });
33245 }
33246 }
33247
33248 // ── is_chart_maintainer_name_shape — shared `:autores` chart-maintainer predicate ──
33249
33250 #[test]
33251 fn chart_maintainer_name_shape_accepts_canonical_forms() {
33252 // Substrate-side pin: the predicate accepts every canonical
33253 // chart-maintainer-name shape the `:autores` axis carries.
33254 // Drift between this list and the per-axis
33255 // `manifest::tests::validate_autores_accepts_canonical_forms`
33256 // positive-set sweep surfaces here — one source of truth for
33257 // the rule. Covers the hello-rio / checkout-aplicacao
33258 // `:autores ("pleme-io")` fixture, the multi-author
33259 // `"Pleme Contributors"` shape, and the canonical Helm
33260 // `"name <email>"` shape downstream packaging surfaces emit.
33261 for s in [
33262 "pleme-io",
33263 "Pleme Contributors",
33264 "alice <alice@example.com>",
33265 "bob <bob@example.com>",
33266 "Acme Corporation",
33267 "x",
33268 ] {
33269 is_chart_maintainer_name_shape(s).unwrap_or_else(|e| {
33270 panic!("canonical chart maintainer name {s:?} must pass: {e:?}")
33271 });
33272 }
33273 }
33274
33275 #[test]
33276 fn chart_maintainer_name_shape_rejects_each_arm_with_substring_pinned_reason() {
33277 // Substrate-side diagnostic-shape pin: each arm surfaces its
33278 // own distinct reason substring. Pinned here so a future
33279 // reason-wording rephrase that drops any of these substrings
33280 // surfaces at this one place, not piecemeal across every
33281 // per-axis test sweep. Mirrors
33282 // `chart_description_shape_rejects_each_arm_with_substring_pinned_reason`
33283 // on the peer predicate.
33284 for (s, needle) in [
33285 // Leading whitespace — paste-from-aligned-doc.
33286 (" pleme-io", "whitespace"),
33287 // Trailing whitespace — paste-from-doc.
33288 ("pleme-io ", "whitespace"),
33289 // Tab inside — tab-from-aligned-doc.
33290 ("Pleme\tContributors", "tab"),
33291 // Newline — paste-from-multiline-doc (author pasted
33292 // multi-line author block into one entry).
33293 ("alice\nbob", "newline"),
33294 // Carriage return — paste-from-Windows-CRLF-doc.
33295 ("alice\rbob", "carriage return"),
33296 // NUL byte — paste-from-binary-blob.
33297 ("alice\x00bob", "control character"),
33298 // BEL byte — paste-from-binary-blob.
33299 ("alice\x07bob", "control character"),
33300 // ESC byte — paste-from-binary-blob.
33301 ("alice\x1bbob", "control character"),
33302 // DEL byte (0x7F).
33303 ("alice\x7fbob", "control character"),
33304 ] {
33305 let err = is_chart_maintainer_name_shape(s)
33306 .err()
33307 .unwrap_or_else(|| panic!("chart maintainer name {s:?} must be rejected"));
33308 assert!(
33309 err.contains(needle),
33310 "chart maintainer name {s:?} reason must contain {needle:?}; got {err:?}"
33311 );
33312 }
33313 }
33314
33315 #[test]
33316 fn chart_maintainer_name_shape_accepts_unicode() {
33317 // Positive control on the non-ASCII arm: the predicate must
33318 // accept Unicode beyond the ASCII alphabet — realistic
33319 // maintainer names carry Unicode (`François`, `日本語`,
33320 // `naïve`), and every downstream consumer (YAML 1.2, Helm v3,
33321 // every chart-aware UI) round-trips Unicode losslessly. A
33322 // future tightening that bans non-ASCII bytes would regress
33323 // every Unicode-named maintainer and surface here as a
33324 // regression. Mirrors the peer
33325 // `chart_description_shape_accepts_unicode`.
33326 for s in [
33327 "François Dupont",
33328 "日本語の名前",
33329 "naïve <naive@example.com>",
33330 "André",
33331 ] {
33332 is_chart_maintainer_name_shape(s)
33333 .unwrap_or_else(|e| panic!("Unicode chart maintainer name {s:?} must pass: {e:?}"));
33334 }
33335 }
33336
33337 #[test]
33338 fn chart_maintainer_name_shape_rejects_empty_defensively() {
33339 // The predicate is called from `crate::Caixa::validate_autores`
33340 // only after the per-axis `AutorEmpty` arm has fired at
33341 // validate time; re-checking here keeps the predicate usable
33342 // from any future call site without an empty-precondition
33343 // footgun. Same defensive empty-check `is_dns_1123_label`,
33344 // `is_gateway_api_http_path`, `is_wit_world_ref`,
33345 // `is_nats_subject`, `is_wasi_keyvalue_slot`,
33346 // `is_git_ref_name`, `is_git_oid`, `is_git_repo_url`,
33347 // `is_cargo_feature_name`, `is_spdx_expression_shape`, and
33348 // `is_chart_description_shape` carry at their call sites.
33349 let err = is_chart_maintainer_name_shape("").unwrap_err();
33350 assert!(err.contains("empty"), "got: {err:?}");
33351 }
33352
33353 #[test]
33354 fn chart_maintainer_name_shape_rejects_at_129_byte_boundary() {
33355 // The 128-byte cap pin — both the boundary-exceeding case and
33356 // the boundary-accepting case in one place, so a future cap
33357 // shift surfaces both arms simultaneously, mirroring the peer
33358 // cap-boundary pins (`chart_description_shape_rejects_at_513_byte_boundary`
33359 // on the 512-byte sibling, `spdx_expression_shape_rejects_at_257_byte_boundary`
33360 // on the 256-byte sibling). Constructed as a single all-`a`
33361 // token so only the cap arm fires (128 `a` bytes is
33362 // alphabet-valid).
33363 let max_ok = "a".repeat(CHART_MAINTAINER_NAME_MAX_LEN);
33364 assert_eq!(max_ok.len(), 128);
33365 is_chart_maintainer_name_shape(&max_ok).unwrap();
33366 let too_long = "a".repeat(CHART_MAINTAINER_NAME_MAX_LEN + 1);
33367 assert_eq!(too_long.len(), 129);
33368 let err = is_chart_maintainer_name_shape(&too_long).unwrap_err();
33369 assert!(err.contains("128"), "got: {err:?}");
33370 assert!(err.contains("129"), "got: {err:?}");
33371 }
33372
33373 #[test]
33374 fn chart_maintainer_name_shape_rejects_each_unicode_bidi_override_codepoint() {
33375 // The Trojan Source (CVE-2021-42574) arm — pins every UAX #9
33376 // bidirectional-override / isolate format codepoint as a
33377 // structural rejection on the typed `:autores` axis. Mirrors
33378 // `chart_description_shape_rejects_each_unicode_bidi_override_codepoint`
33379 // on the peer predicate — both predicates route through the
33380 // same lifted `find_unicode_bidi_override` helper, so dropping
33381 // any one of the nine arms from the helper's match would
33382 // regress both peer test sweeps simultaneously at this one
33383 // structural floor rather than at piecemeal per-axis call
33384 // sites. The canonical attacker shape: an `:autores
33385 // "alice\u{202E}example.com<bob@"` entry renders in `helm
33386 // list`'s maintainer column / Artifact Hub as the visually-
33387 // reversed `alice<@bob>moc.elpmaxe` while riding verbatim
33388 // into the Chart.yaml `maintainers:` array — exactly the
33389 // class this arm closes.
33390 for (cp, name) in [
33391 ('\u{202A}', "U+202A"),
33392 ('\u{202B}', "U+202B"),
33393 ('\u{202C}', "U+202C"),
33394 ('\u{202D}', "U+202D"),
33395 ('\u{202E}', "U+202E"),
33396 ('\u{2066}', "U+2066"),
33397 ('\u{2067}', "U+2067"),
33398 ('\u{2068}', "U+2068"),
33399 ('\u{2069}', "U+2069"),
33400 ] {
33401 let s = format!("alice{cp}bob");
33402 let err = is_chart_maintainer_name_shape(&s)
33403 .err()
33404 .unwrap_or_else(|| panic!("chart maintainer name with {name} must be rejected"));
33405 assert!(
33406 err.contains(name),
33407 "chart maintainer name reason for {name} must name the codepoint verbatim; \
33408 got {err:?}"
33409 );
33410 assert!(
33411 err.contains("bidirectional-override")
33412 || err.contains("Unicode bidi")
33413 || err.contains("Trojan Source"),
33414 "chart maintainer name reason for {name} must name the Trojan-Source banner; \
33415 got {err:?}"
33416 );
33417 }
33418 }
33419
33420 #[test]
33421 fn chart_maintainer_name_shape_accepts_pure_rtl_text_without_bidi_override() {
33422 // Positive control on the bidi-override arm: pure visual
33423 // right-to-left scripts (Hebrew, Arabic) decode to non-bidi-
33424 // override codepoints and the predicate must accept them
33425 // natively — banning all RTL would regress every Hebrew /
33426 // Arabic-authored maintainer-name entry, which the substrate
33427 // supports via the non-ASCII byte arm. Peer of
33428 // `chart_description_shape_accepts_pure_rtl_text_without_bidi_override`
33429 // on the sibling YAML-plain-style-scalar surface.
33430 for s in [
33431 // Pure Hebrew maintainer name.
33432 "שלום",
33433 // Pure Arabic maintainer name.
33434 "مرحبا",
33435 // Mixed-script — canonical multilingual maintainer
33436 // shape every YAML 1.2 + Helm v3 round-trips losslessly.
33437 "Acme שלום",
33438 ] {
33439 is_chart_maintainer_name_shape(s).unwrap_or_else(|e| {
33440 panic!(
33441 "pure-RTL chart maintainer name {s:?} must pass without bidi override: {e:?}"
33442 )
33443 });
33444 }
33445 }
33446
33447 #[test]
33448 fn chart_maintainer_name_shape_rejects_each_unicode_line_break_codepoint() {
33449 // The non-ASCII Unicode line-break arm — pins each of the three
33450 // UAX #14 / YAML 1.1 §4.1 line-break codepoints outside the
33451 // ASCII `\n` / `\r` bytes already caught at the per-byte pass.
33452 // The canonical YAML-1.1-vs-YAML-1.2 paste-from-doc footgun: an
33453 // `:autores "alice\u{2028}bob"` entry parses as one
33454 // `maintainers:` array entry through a YAML 1.2-strict parser
33455 // and as two entries through a YAML 1.1 parser (go-yaml v2 /
33456 // Helm v3). Mirrors
33457 // `chart_description_shape_rejects_each_unicode_line_break_codepoint`
33458 // on the peer predicate — both predicates route through the
33459 // same lifted `find_unicode_line_break` helper, so dropping
33460 // any one of the three arms from the helper's match would
33461 // regress both peer test sweeps simultaneously at this one
33462 // structural floor.
33463 for (cp, name) in [
33464 ('\u{0085}', "U+0085"),
33465 ('\u{2028}', "U+2028"),
33466 ('\u{2029}', "U+2029"),
33467 ] {
33468 let s = format!("alice{cp}bob");
33469 let err = is_chart_maintainer_name_shape(&s)
33470 .err()
33471 .unwrap_or_else(|| panic!("chart maintainer name with {name} must be rejected"));
33472 assert!(
33473 err.contains(name),
33474 "chart maintainer name reason for {name} must name the codepoint verbatim; \
33475 got {err:?}"
33476 );
33477 assert!(
33478 err.contains("line-break") || err.contains("UAX #14") || err.contains("YAML 1.1"),
33479 "chart maintainer name reason for {name} must name the Unicode-line-break banner; \
33480 got {err:?}"
33481 );
33482 }
33483 }
33484
33485 #[test]
33486 fn chart_maintainer_name_shape_accepts_non_line_break_unicode() {
33487 // Positive control on the line-break arm: the predicate must
33488 // accept every non-line-break Unicode shape canonical
33489 // maintainer names carry. Pinned alongside the per-codepoint
33490 // rejection sweep so a future helper widening that
33491 // accidentally rejects a non-line-break codepoint surfaces
33492 // here as a single-source-of-truth pin. Peer of
33493 // `chart_description_shape_accepts_non_line_break_unicode`
33494 // on the sibling YAML-plain-style-scalar surface.
33495 for s in [
33496 "François Dupont",
33497 "日本語の名前",
33498 "naïve <naive@example.com>",
33499 "André",
33500 // U+00A0 NO-BREAK SPACE is NOT a line-break codepoint
33501 // (UAX #14 class GL — Glue, non-breaking) and is the
33502 // canonical authoring shape for unbreakable space inside
33503 // a multi-token maintainer name — must pass.
33504 "Acme\u{00A0}Corp",
33505 ] {
33506 is_chart_maintainer_name_shape(s).unwrap_or_else(|e| {
33507 panic!(
33508 "non-line-break Unicode chart maintainer name {s:?} must pass without \
33509 rejection: {e:?}"
33510 )
33511 });
33512 }
33513 }
33514
33515 #[test]
33516 fn chart_maintainer_name_shape_rejects_each_unicode_invisible_format_codepoint() {
33517 // The Unicode invisible-format arm — pins each of the eight
33518 // BMP Cf-category zero-width codepoints with no visible glyph.
33519 // The canonical maintainer-identity homograph footgun: an
33520 // `:autores "alice\u{200B}"` entry renders identically to
33521 // `:autores "alice"` in `helm list` / Artifact Hub's
33522 // maintainer column, but the byte sequence is distinct — the
33523 // Artifact Hub maintainer-index lookup misses the authored
33524 // `"alice"` entry, a future CLA-signer lookup matches a
33525 // visually-identical-but-byte-distinct identity. Mirrors
33526 // `chart_description_shape_rejects_each_unicode_invisible_format_codepoint`
33527 // on the peer predicate — both predicates route through the
33528 // same lifted `find_unicode_invisible_format` helper, so
33529 // dropping any one of the eight arms from the helper's match
33530 // would regress both peer test sweeps simultaneously at this
33531 // one structural floor. Covers the four paste-from-Word /
33532 // paste-from-BOM-editor / paste-from-typesetting shapes
33533 // (U+00AD / U+200B / U+2060 / U+FEFF) and the four math-
33534 // formula invisible operators (U+2061 FUNCTION APPLICATION /
33535 // U+2062 INVISIBLE TIMES / U+2063 INVISIBLE SEPARATOR /
33536 // U+2064 INVISIBLE PLUS — paste-from-MathJax / paste-from-
33537 // LaTeX-rendered-formula footgun).
33538 for (cp, name) in [
33539 ('\u{00AD}', "U+00AD"),
33540 ('\u{200B}', "U+200B"),
33541 ('\u{2060}', "U+2060"),
33542 ('\u{2061}', "U+2061"),
33543 ('\u{2062}', "U+2062"),
33544 ('\u{2063}', "U+2063"),
33545 ('\u{2064}', "U+2064"),
33546 ('\u{FEFF}', "U+FEFF"),
33547 ] {
33548 let s = format!("alice{cp}bob");
33549 let err = is_chart_maintainer_name_shape(&s)
33550 .err()
33551 .unwrap_or_else(|| panic!("chart maintainer name with {name} must be rejected"));
33552 assert!(
33553 err.contains(name),
33554 "chart maintainer name reason for {name} must name the codepoint verbatim; \
33555 got {err:?}"
33556 );
33557 assert!(
33558 err.contains("invisible-format")
33559 || err.contains("Cf-category")
33560 || err.contains("zero-width"),
33561 "chart maintainer name reason for {name} must name the invisible-format banner; \
33562 got {err:?}"
33563 );
33564 }
33565 }
33566
33567 #[test]
33568 fn chart_maintainer_name_shape_accepts_non_invisible_format_unicode() {
33569 // Positive control on the invisible-format arm: the predicate
33570 // must accept the legitimate-use codepoints the helper
33571 // deliberately excludes — U+200C ZWNJ / U+200D ZWJ (emoji ZWJ
33572 // sequences are canonical for modern maintainer-display names;
33573 // Indic / Persian script composition relies on ZWNJ to break
33574 // inappropriate ligatures) and U+200E LRM / U+200F RLM
33575 // (mixed-script direction hints are canonical for "Arabic name
33576 // with embedded ASCII email" shapes). Peer of
33577 // `chart_description_shape_accepts_non_invisible_format_unicode`
33578 // on the sibling YAML-plain-style-scalar surface.
33579 for s in [
33580 "François Dupont",
33581 "naïve <naive@example.com>",
33582 // Emoji ZWJ sequence (U+200D) — canonical multi-codepoint
33583 // emoji authoring shape.
33584 "Joe 👨\u{200D}💻 Developer",
33585 // ZWNJ (U+200C) — legitimate Persian / Indic composition.
33586 "Persian می\u{200C}باشد maintainer",
33587 // Bidi marks LRM / RLM — legitimate direction hints in
33588 // mixed-script maintainer names.
33589 "Arabic\u{200F}name <maintainer@example.com>",
33590 "ASCII\u{200E}embedded in RTL context",
33591 ] {
33592 is_chart_maintainer_name_shape(s).unwrap_or_else(|e| {
33593 panic!(
33594 "non-invisible-format Unicode chart maintainer name {s:?} must pass without \
33595 rejection: {e:?}"
33596 )
33597 });
33598 }
33599 }
33600
33601 #[test]
33602 fn find_unicode_bidi_override_pins_the_nine_codepoint_accepted_set() {
33603 // The shared helper's accepted set — pinned in one place so
33604 // every per-predicate caller (`is_chart_description_shape`,
33605 // `is_chart_maintainer_name_shape`, every future free-form-
33606 // prose surface) reads from one canonical accepted set. The
33607 // nine UAX #9 bidirectional-override / isolate format
33608 // codepoints in document order, plus negative controls on
33609 // bytes the helper must NOT reject (ASCII / non-bidi Unicode
33610 // letters / arrows / em-dash / RTL letters). A future shift
33611 // in the accepted set surfaces here as a single-source-of-
33612 // truth edit at this one test rather than across every
33613 // per-predicate per-arm sweep.
33614 for cp in [
33615 '\u{202A}', '\u{202B}', '\u{202C}', '\u{202D}', '\u{202E}', '\u{2066}', '\u{2067}',
33616 '\u{2068}', '\u{2069}',
33617 ] {
33618 let s = format!("a{cp}b");
33619 assert_eq!(
33620 find_unicode_bidi_override(&s),
33621 Some(cp),
33622 "helper must flag bidi override U+{:04X} on input {s:?}",
33623 cp as u32
33624 );
33625 }
33626 for s in [
33627 "alice",
33628 "Canonical Rust→wasm32-wasip2",
33629 "FIXME — describe this caixa",
33630 "François Dupont",
33631 "日本語の説明",
33632 "naïve",
33633 "שלום",
33634 "مرحبا",
33635 ] {
33636 assert_eq!(
33637 find_unicode_bidi_override(s),
33638 None,
33639 "helper must accept {s:?} (no bidi-override codepoint)"
33640 );
33641 }
33642 // Empty input — defensive precondition for the helper's
33643 // call-site contract on any future caller that doesn't gate
33644 // emptiness ahead of the scan.
33645 assert_eq!(find_unicode_bidi_override(""), None);
33646 }
33647
33648 #[test]
33649 fn find_unicode_line_break_pins_the_three_codepoint_accepted_set() {
33650 // The shared helper's accepted set — pinned in one place so
33651 // every per-predicate caller (`is_chart_description_shape`,
33652 // `is_chart_maintainer_name_shape`, every future free-form-
33653 // prose surface) reads from one canonical accepted set. The
33654 // three UAX #14 / YAML 1.1 §4.1 non-ASCII line-break
33655 // codepoints in document order, plus negative controls on
33656 // bytes the helper must NOT reject (ASCII text, Unicode
33657 // letters / arrows / em-dash / RTL letters, the canonical
33658 // non-line-break U+00A0 NBSP shape downstream YAML 1.2 +
33659 // Helm v3 + every chart-aware UI round-trip losslessly). A
33660 // future shift in the accepted set surfaces here as a
33661 // single-source-of-truth edit at this one test rather than
33662 // across every per-predicate per-arm sweep. Peer of
33663 // `find_unicode_bidi_override_pins_the_nine_codepoint_accepted_set`
33664 // on the sibling lifted-helper one trajectory earlier.
33665 for cp in ['\u{0085}', '\u{2028}', '\u{2029}'] {
33666 let s = format!("a{cp}b");
33667 assert_eq!(
33668 find_unicode_line_break(&s),
33669 Some(cp),
33670 "helper must flag line-break codepoint U+{:04X} on input {s:?}",
33671 cp as u32
33672 );
33673 }
33674 for s in [
33675 "alice",
33676 "Canonical Rust→wasm32-wasip2",
33677 "FIXME — describe this caixa",
33678 "François Dupont",
33679 "日本語の説明",
33680 "naïve",
33681 "שלום",
33682 "مرحبا",
33683 // U+00A0 NO-BREAK SPACE — UAX #14 class GL (Glue,
33684 // non-breaking) — must NOT be rejected: the canonical
33685 // unbreakable-space shape every typed maintainer-name
33686 // axis admits.
33687 "Acme\u{00A0}Corp",
33688 // U+0009 TAB and U+000A LF and U+000D CR — ASCII
33689 // line-break / whitespace bytes the per-byte arm on the
33690 // calling predicate already closes; the helper must NOT
33691 // claim them as its own (single-source-of-truth: ASCII
33692 // arms live in the per-byte loop, the helper closes the
33693 // non-ASCII codepoints).
33694 "alice\tbob",
33695 "alice\nbob",
33696 "alice\rbob",
33697 ] {
33698 assert_eq!(
33699 find_unicode_line_break(s),
33700 None,
33701 "helper must accept {s:?} (no non-ASCII line-break codepoint)"
33702 );
33703 }
33704 // Empty input — defensive precondition for the helper's
33705 // call-site contract on any future caller that doesn't gate
33706 // emptiness ahead of the scan.
33707 assert_eq!(find_unicode_line_break(""), None);
33708 }
33709
33710 #[test]
33711 fn find_unicode_invisible_format_pins_the_eight_codepoint_accepted_set() {
33712 // The shared helper's accepted set — pinned in one place so
33713 // every per-predicate caller (`is_chart_description_shape`,
33714 // `is_chart_maintainer_name_shape`, every future free-form-
33715 // prose surface) reads from one canonical accepted set. The
33716 // eight BMP Cf-category zero-width codepoints in document
33717 // order — the four paste-from-Word / paste-from-BOM-editor /
33718 // paste-from-typesetting-doc shapes (U+00AD SHY / U+200B ZWSP /
33719 // U+2060 WJ / U+FEFF ZWNBSP-BOM) and the four math-formula
33720 // invisible operators (U+2061 FUNCTION APPLICATION / U+2062
33721 // INVISIBLE TIMES / U+2063 INVISIBLE SEPARATOR / U+2064
33722 // INVISIBLE PLUS — paste-from-MathJax / paste-from-LaTeX-
33723 // rendered-formula / paste-from-InDesign-math-equation
33724 // shapes) — plus negative controls on codepoints the helper
33725 // must NOT reject — the deliberate exclusions: U+200C ZWNJ /
33726 // U+200D ZWJ (emoji ZWJ sequences + Indic / Persian script
33727 // composition) and U+200E LRM / U+200F RLM (mixed-script
33728 // direction hints). A future shift in the accepted set
33729 // surfaces here as a single-source-of-truth edit at this one
33730 // test rather than across every per-predicate per-arm sweep.
33731 // Third pin in the UAX-driven render-determinism trio (peer of
33732 // `find_unicode_bidi_override_pins_the_nine_codepoint_accepted_set`
33733 // on the visual-order axis and
33734 // `find_unicode_line_break_pins_the_three_codepoint_accepted_set`
33735 // on the single-line/multi-line axis).
33736 for cp in [
33737 '\u{00AD}', '\u{200B}', '\u{2060}', '\u{2061}', '\u{2062}', '\u{2063}', '\u{2064}',
33738 '\u{FEFF}',
33739 ] {
33740 let s = format!("a{cp}b");
33741 assert_eq!(
33742 find_unicode_invisible_format(&s),
33743 Some(cp),
33744 "helper must flag invisible-format codepoint U+{:04X} on input {s:?}",
33745 cp as u32
33746 );
33747 }
33748 for s in [
33749 "alice",
33750 "Canonical Rust→wasm32-wasip2",
33751 "FIXME — describe this caixa",
33752 "François Dupont",
33753 "日本語の説明",
33754 "naïve",
33755 "שלום",
33756 "مرحبا",
33757 // U+00A0 NO-BREAK SPACE — class GL (Glue), visible-width
33758 // codepoint — must NOT be claimed by the invisible-format
33759 // helper (the canonical unbreakable-space shape).
33760 "Acme\u{00A0}Corp",
33761 // U+200C ZWNJ — deliberately excluded (Indic / Persian
33762 // composition + emoji ZWJ-adjacent context).
33763 "می\u{200C}باشد",
33764 // U+200D ZWJ — deliberately excluded (emoji ZWJ
33765 // sequences are canonical: 👨💻 is MAN + ZWJ + LAPTOP).
33766 "Joe 👨\u{200D}💻 Developer",
33767 // U+200E LRM — deliberately excluded (direction-hint
33768 // mark, not a direction-override; legitimate in
33769 // mixed-script prose).
33770 "ASCII\u{200E}embedded",
33771 // U+200F RLM — deliberately excluded (mirror of LRM
33772 // on the RTL axis).
33773 "Arabic\u{200F}name",
33774 // Bidi-override codepoints (U+202A..U+202E, U+2066..U+2069)
33775 // — caught by the sibling `find_unicode_bidi_override`
33776 // helper, not this one (single-source-of-truth: each
33777 // helper closes exactly its class).
33778 "alice\u{202E}bob",
33779 // Line-break codepoints (U+0085, U+2028, U+2029) — caught
33780 // by the sibling `find_unicode_line_break` helper.
33781 "alice\u{2028}bob",
33782 ] {
33783 assert_eq!(
33784 find_unicode_invisible_format(s),
33785 None,
33786 "helper must accept {s:?} (no invisible-format codepoint in the four-codepoint set)"
33787 );
33788 }
33789 // Empty input — defensive precondition for the helper's
33790 // call-site contract on any future caller that doesn't gate
33791 // emptiness ahead of the scan.
33792 assert_eq!(find_unicode_invisible_format(""), None);
33793 }
33794
33795 // ── is_chart_keyword_shape — shared `:etiquetas` chart-keyword predicate ──
33796
33797 #[test]
33798 fn chart_keyword_shape_accepts_canonical_forms() {
33799 // Substrate-side pin: the predicate accepts every canonical
33800 // chart-keyword shape the `:etiquetas` axis carries. Drift
33801 // between this list and the per-axis
33802 // `manifest::tests::validate_etiquetas_accepts_canonical_shaped_forms`
33803 // positive-set sweep surfaces here — one source of truth for
33804 // the rule. Covers the example fixtures'
33805 // `:etiquetas` lists (`"example"`, `"aplicacao"`, `"mesh"`,
33806 // `"ecommerce"`, `"demo"`, `"infrastructure"`, `"aws"`,
33807 // `"akeyless"`, `"pangea-native"`) and the substrate-fixed
33808 // tags caixa-helm unions in at chart render (`"lareira"`,
33809 // `"wasm"`, `"tatara-lisp"`, `"caixa-servico"`).
33810 let example_fixture_tags = [
33811 "example",
33812 "aplicacao",
33813 "mesh",
33814 "ecommerce",
33815 "demo",
33816 "infrastructure",
33817 "aws",
33818 "akeyless",
33819 "pangea-native",
33820 "hello-world",
33821 "rust",
33822 "Foo",
33823 "Bar123",
33824 "x",
33825 "snake_case_tag",
33826 ];
33827 for s in example_fixture_tags
33828 .iter()
33829 .copied()
33830 .chain(LAREIRA_CHART_KEYWORDS.iter().copied())
33831 {
33832 is_chart_keyword_shape(s)
33833 .unwrap_or_else(|e| panic!("canonical chart keyword {s:?} must pass: {e:?}"));
33834 }
33835 }
33836
33837 #[test]
33838 fn lareira_chart_keywords_pins_canonical_ordered_set() {
33839 // Substrate-side canonical-set pin: byte-pins the
33840 // substrate-fixed `Chart.yaml` `keywords:` union caixa-helm's
33841 // `build_chart_yaml` folds into every rendered `lareira-<nome>`
33842 // chart on top of the caixa author's own `:etiquetas`. The
33843 // ordered array shape (`BTreeSet`-canonical ascii-alphabetical)
33844 // pins the same order the emitted `Chart.yaml` `keywords:`
33845 // sequence lists them after the intermediate
33846 // `BTreeSet<String>` fold at the caixa-helm emit site. A drift
33847 // between the canonical array and either the production emit
33848 // at `caixa-helm::build_chart_yaml` (the sole consumer) or
33849 // the peer positive-set sweep tests (this crate's
33850 // `chart_keyword_shape_accepts_canonical_forms` and
33851 // `manifest::tests::validate_etiquetas_accepts_canonical_shaped_forms`)
33852 // surfaces at this one substrate-side pin.
33853 assert_eq!(
33854 LAREIRA_CHART_KEYWORDS,
33855 &["caixa-servico", "lareira", "tatara-lisp", "wasm"],
33856 );
33857 }
33858
33859 #[test]
33860 fn lareira_chart_keywords_stays_btreeset_canonical_ordered() {
33861 // Substrate-side ordering pin: the array is
33862 // `BTreeSet`-canonical ascii-alphabetical, so its declared
33863 // order matches the shape the emitted `Chart.yaml`
33864 // `keywords:` sequence carries after
33865 // `caixa-helm::build_chart_yaml`'s intermediate
33866 // `BTreeSet<String>` fold — a future substrate-fixed keyword
33867 // addition that lands out-of-order (an `"opentelemetry"` entry
33868 // dropped before `"tatara-lisp"`, an `"lunatic"` entry dropped
33869 // after `"wasm"`) trips this pin at caixa-core build time
33870 // rather than surfacing as a byte-shape drift between the
33871 // array's declared order and the emitted `keywords:` sequence
33872 // order at chart render time downstream.
33873 let mut sorted: Vec<&str> = LAREIRA_CHART_KEYWORDS.to_vec();
33874 sorted.sort_unstable();
33875 assert_eq!(LAREIRA_CHART_KEYWORDS, sorted.as_slice());
33876 }
33877
33878 #[test]
33879 fn lareira_chart_keywords_each_entry_passes_is_chart_keyword_shape() {
33880 // Substrate-side shape-invariant pin: every substrate-fixed
33881 // chart-keyword entry must satisfy the per-`Chart.yaml`
33882 // `keywords:` entry validation predicate the substrate
33883 // enforces on the author-side `:etiquetas` axis — a future
33884 // substrate-fixed keyword addition that happens to break the
33885 // shape rule (a leading digit, an uppercase letter, a byte
33886 // over the `CHART_KEYWORD_MAX_LEN` cap, an ASCII whitespace,
33887 // a Unicode-invisible-format code point) trips this pin at
33888 // caixa-core build time rather than surfacing at
33889 // `helm lint` time on the rendered chart downstream.
33890 for keyword in LAREIRA_CHART_KEYWORDS {
33891 is_chart_keyword_shape(keyword).unwrap_or_else(|e| {
33892 panic!(
33893 "substrate-fixed chart keyword {keyword:?} must pass \
33894 is_chart_keyword_shape: {e:?}"
33895 )
33896 });
33897 }
33898 }
33899
33900 #[test]
33901 fn chart_keyword_shape_rejects_each_arm_with_substring_pinned_reason() {
33902 // Substrate-side diagnostic-shape pin: each arm surfaces its
33903 // own distinct reason substring. Pinned here so a future
33904 // reason-wording rephrase that drops any of these substrings
33905 // surfaces at this one place, not piecemeal across every
33906 // per-axis test sweep. Mirrors
33907 // `chart_maintainer_name_shape_rejects_each_arm_with_substring_pinned_reason`
33908 // on the peer predicate.
33909 for (s, needle) in [
33910 // Leading whitespace — paste-from-aligned-doc.
33911 (" mesh", "whitespace"),
33912 // Leading hyphen — kebab-leak footgun.
33913 ("-foo", "`-`"),
33914 // Leading underscore — snake-leak footgun.
33915 ("_foo", "`_`"),
33916 // Leading digit — paste-from-numbered-list footgun.
33917 ("1foo", "digit"),
33918 // Embedded whitespace — multi-tag-blob footgun.
33919 ("web service", "whitespace"),
33920 // Tab inside — tab-from-aligned-doc.
33921 ("mesh\thttp", "whitespace"),
33922 // Newline — paste-from-multiline-doc.
33923 ("mesh\nhttp", "newline"),
33924 // Carriage return — paste-from-Windows-CRLF-doc.
33925 ("mesh\rhttp", "carriage return"),
33926 // Comma — CSV-list-separator confusion.
33927 ("mesh,http", "`,`"),
33928 // Slash — path-separator confusion.
33929 ("caixa/servico", "`/`"),
33930 // Semicolon — alt-list-separator confusion.
33931 ("mesh;http", "`;`"),
33932 // Period — namespace / version-suffix confusion.
33933 ("http.1", "`.`"),
33934 // NUL byte — paste-from-binary-blob.
33935 ("mesh\x00http", "control character"),
33936 // DEL byte (0x7F).
33937 ("mesh\x7fhttp", "control character"),
33938 // Non-ASCII inside.
33939 ("café", "non-ASCII"),
33940 // Non-ASCII leading.
33941 ("éclair", "non-ASCII"),
33942 ] {
33943 let err = is_chart_keyword_shape(s)
33944 .err()
33945 .unwrap_or_else(|| panic!("chart keyword {s:?} must be rejected"));
33946 assert!(
33947 err.contains(needle),
33948 "chart keyword {s:?} reason must contain {needle:?}; got {err:?}"
33949 );
33950 }
33951 }
33952
33953 #[test]
33954 fn chart_keyword_shape_rejects_empty_defensively() {
33955 // The predicate is called from `crate::Caixa::validate_etiquetas`
33956 // only after the per-axis `EtiquetaEmpty` arm has fired at
33957 // validate time; re-checking here keeps the predicate usable
33958 // from any future call site without an empty-precondition
33959 // footgun. Same defensive empty-check `is_dns_1123_label`,
33960 // `is_gateway_api_http_path`, `is_wit_world_ref`,
33961 // `is_nats_subject`, `is_wasi_keyvalue_slot`,
33962 // `is_git_ref_name`, `is_git_oid`, `is_git_repo_url`,
33963 // `is_cargo_feature_name`, `is_spdx_expression_shape`,
33964 // `is_chart_description_shape`, and
33965 // `is_chart_maintainer_name_shape` carry at their call sites.
33966 let err = is_chart_keyword_shape("").unwrap_err();
33967 assert!(err.contains("empty"), "got: {err:?}");
33968 }
33969
33970 #[test]
33971 fn chart_keyword_shape_rejects_at_21_byte_boundary() {
33972 // The 20-byte cap pin — both the boundary-exceeding case and
33973 // the boundary-accepting case in one place, so a future cap
33974 // shift surfaces both arms simultaneously, mirroring the peer
33975 // cap-boundary pins
33976 // (`chart_maintainer_name_shape_rejects_at_129_byte_boundary`
33977 // on the 128-byte sibling,
33978 // `chart_description_shape_rejects_at_513_byte_boundary` on
33979 // the 512-byte sibling). Constructed as a single all-`a`
33980 // token so only the cap arm fires (20 `a` bytes is alphabet-
33981 // valid).
33982 let max_ok = "a".repeat(CHART_KEYWORD_MAX_LEN);
33983 assert_eq!(max_ok.len(), 20);
33984 is_chart_keyword_shape(&max_ok).unwrap();
33985 let too_long = "a".repeat(CHART_KEYWORD_MAX_LEN + 1);
33986 assert_eq!(too_long.len(), 21);
33987 let err = is_chart_keyword_shape(&too_long).unwrap_err();
33988 assert!(err.contains("20"), "got: {err:?}");
33989 assert!(err.contains("21"), "got: {err:?}");
33990 }
33991
33992 // ── shared predicate: find_ascii_whitespace_byte ──────────────────
33993 //
33994 // Pins the accepted / rejected set of the lifted ASCII byte-scan
33995 // every typed-magnitude codec in caixa-core calls (`parse_byte_size`
33996 // / `parse_duration` / `parse_millicores` / shared
33997 // `duration_codec` / `rate_limit_codec`). Peer of the non-ASCII
33998 // `find_non_ascii_whitespace_char` predicate below — together they
33999 // partition the full Unicode `White_Space` axis.
34000
34001 #[test]
34002 fn find_ascii_whitespace_byte_accepts_whitespace_free_strings() {
34003 // Complement-side pin: every whitespace-free canonical form
34004 // the renderers emit returns `None`.
34005 assert!(find_ascii_whitespace_byte("64MiB").is_none());
34006 assert!(find_ascii_whitespace_byte("30s").is_none());
34007 assert!(find_ascii_whitespace_byte("500m").is_none());
34008 assert!(find_ascii_whitespace_byte("100/s").is_none());
34009 assert!(find_ascii_whitespace_byte("").is_none());
34010 assert!(find_ascii_whitespace_byte("abcdef0123-_").is_none());
34011 // Non-whitespace ASCII bytes near the whitespace range stay
34012 // accepted (the predicate must not over-fire on peer control
34013 // bytes like VT `0x0B` which POSIX admits but WhatWG excludes).
34014 assert!(find_ascii_whitespace_byte("\u{0B}64MiB").is_none());
34015 }
34016
34017 #[test]
34018 fn find_ascii_whitespace_byte_flags_space() {
34019 // Space (`0x20`) — the canonical paste-from-shell-history /
34020 // paste-from-aligned-doc drift class.
34021 assert_eq!(find_ascii_whitespace_byte(" 64MiB"), Some(0x20));
34022 assert_eq!(find_ascii_whitespace_byte("30s "), Some(0x20));
34023 assert_eq!(find_ascii_whitespace_byte("100 /s"), Some(0x20));
34024 }
34025
34026 #[test]
34027 fn find_ascii_whitespace_byte_flags_tab_lf_ff_cr() {
34028 // Tab (`0x09`), LF (`0x0A`), FF (`0x0C`), CR (`0x0D`) —
34029 // the remaining four bytes in the WhatWG ASCII whitespace
34030 // set the predicate covers, verbatim.
34031 assert_eq!(find_ascii_whitespace_byte("\t500m"), Some(0x09));
34032 assert_eq!(find_ascii_whitespace_byte("30s\n"), Some(0x0A));
34033 assert_eq!(find_ascii_whitespace_byte("\x0c64MiB"), Some(0x0C));
34034 assert_eq!(find_ascii_whitespace_byte("100/s\r"), Some(0x0D));
34035 }
34036
34037 #[test]
34038 fn find_ascii_whitespace_byte_returns_first_match_byte_order() {
34039 // The predicate returns the *first* offending byte in scan
34040 // order — pinning this so a self-locating codec diagnostic can
34041 // report "position 0" / "position N" verbatim without the
34042 // predicate ever reordering matches.
34043 assert_eq!(find_ascii_whitespace_byte(" \t30s"), Some(0x20));
34044 assert_eq!(find_ascii_whitespace_byte("\t 30s"), Some(0x09));
34045 }
34046
34047 #[test]
34048 fn find_ascii_whitespace_byte_does_not_flag_non_ascii_whitespace() {
34049 // NBSP (`\u{00A0}`), LINE SEPARATOR (`\u{2028}`), IDEOGRAPHIC
34050 // SPACE (`\u{3000}`) — none of their UTF-8 bytes match
34051 // `u8::is_ascii_whitespace` (NBSP's `0xC2 0xA0`, LINE
34052 // SEPARATOR's `0xE2 0x80 0xA8`, IDEOGRAPHIC SPACE's `0xE3
34053 // 0x80 0x80` all sit above `0x7F` or well outside the
34054 // {`0x09`, `0x0A`, `0x0C`, `0x0D`, `0x20`} set). Pinning this
34055 // exclusion so the peer `find_non_ascii_whitespace_char`
34056 // predicate remains strictly complementary — the two together
34057 // partition the full Unicode `White_Space` axis with zero
34058 // overlap.
34059 assert!(find_ascii_whitespace_byte("\u{00A0}64MiB").is_none());
34060 assert!(find_ascii_whitespace_byte("30s\u{2028}").is_none());
34061 assert!(find_ascii_whitespace_byte("64MiB\u{3000}").is_none());
34062 }
34063
34064 // ── shared predicate: find_non_ascii_whitespace_char ──────────────────
34065 //
34066 // Pins the accepted / rejected set of the lifted predicate every
34067 // typed-magnitude codec in caixa-core calls (byte-size / duration /
34068 // shared duration / rate-limit). The predicate's job is exclusively
34069 // to name the strictly-complementary drift class the peer
34070 // `u8::is_ascii_whitespace` byte-scan cannot see — the non-ASCII
34071 // Unicode `White_Space` subset that `str::trim` silently swallows.
34072
34073 #[test]
34074 fn find_non_ascii_whitespace_char_accepts_ascii_only_strings() {
34075 // Complement-side pin: every ASCII-only string (canonical form
34076 // and ASCII whitespace alike) returns `None`. The predicate is
34077 // strictly complementary to the per-codec ASCII byte-scan; it
34078 // must not shadow its coverage.
34079 assert!(find_non_ascii_whitespace_char("64MiB").is_none());
34080 assert!(find_non_ascii_whitespace_char("30s").is_none());
34081 assert!(find_non_ascii_whitespace_char("100/s").is_none());
34082 assert!(find_non_ascii_whitespace_char(" \t\n").is_none());
34083 assert!(find_non_ascii_whitespace_char("").is_none());
34084 // Non-whitespace ASCII byte peers stay accepted too.
34085 assert!(find_non_ascii_whitespace_char("abcdef0123-_").is_none());
34086 }
34087
34088 #[test]
34089 fn find_non_ascii_whitespace_char_flags_nbsp() {
34090 // `\u{00A0}` NBSP — the canonical paste-from-typography /
34091 // paste-from-word-processor drift class.
34092 assert_eq!(
34093 find_non_ascii_whitespace_char("64\u{00A0}MiB"),
34094 Some('\u{00A0}')
34095 );
34096 assert_eq!(find_non_ascii_whitespace_char("\u{00A0}"), Some('\u{00A0}'));
34097 }
34098
34099 #[test]
34100 fn find_non_ascii_whitespace_char_flags_line_and_paragraph_separators() {
34101 // LINE SEPARATOR (`\u{2028}`) / PARAGRAPH SEPARATOR
34102 // (`\u{2029}`) — the paste-from-web-doc drift class every
34103 // RTF/HTML → plain-text conversion emits at soft-wrap
34104 // boundaries.
34105 assert_eq!(
34106 find_non_ascii_whitespace_char("30s\u{2028}"),
34107 Some('\u{2028}')
34108 );
34109 assert_eq!(
34110 find_non_ascii_whitespace_char("30s\u{2029}"),
34111 Some('\u{2029}')
34112 );
34113 }
34114
34115 #[test]
34116 fn find_non_ascii_whitespace_char_flags_ideographic_space() {
34117 // IDEOGRAPHIC SPACE (`\u{3000}`) — the CJK-typography drift
34118 // class every full-width IME auto-widens ASCII space to on
34119 // Japanese / Chinese input methods.
34120 assert_eq!(
34121 find_non_ascii_whitespace_char("64MiB\u{3000}"),
34122 Some('\u{3000}')
34123 );
34124 }
34125
34126 #[test]
34127 fn find_non_ascii_whitespace_char_does_not_flag_zwsp_or_bom() {
34128 // BOM (`\u{FEFF}`, ZERO WIDTH NO-BREAK SPACE) and ZWSP
34129 // (`\u{200B}`, ZERO WIDTH SPACE) — both have
34130 // `char::is_whitespace() == false` per the Unicode
34131 // `White_Space` property, so `str::trim` does *not* strip
34132 // either. Both currently land on the downstream
34133 // `BadByteMagnitude` / `BadDurationMagnitude` arm at parse time
34134 // with the byte-shape diagnostic intact; the render-determinism
34135 // contract is unbroken on those inputs today. This test pins
34136 // the predicate's exclusion so a future widening that starts
34137 // flagging BOM / ZWSP here surfaces as a test failure rather
34138 // than a silent over-fire on a class the downstream arm
34139 // already closes.
34140 assert!(find_non_ascii_whitespace_char("\u{FEFF}64MiB").is_none());
34141 assert!(find_non_ascii_whitespace_char("\u{200B}30s").is_none());
34142 }
34143
34144 // ── shared predicate: is_leading_zero_padded_magnitude ──────────────
34145 //
34146 // Pins the accepted / rejected set of the lifted leading-zero
34147 // predicate every typed-magnitude codec in caixa-core calls
34148 // (`parse_byte_size` / `parse_duration` / `parse_millicores` /
34149 // shared `duration_codec` / `rate_limit_codec`). Same lifted-
34150 // source-of-truth discipline the peer whitespace predicates
34151 // (`find_ascii_whitespace_byte` / `find_non_ascii_whitespace_char`)
34152 // carry — drift between any two codec sites' rejection set becomes
34153 // a single-edit fix at this predicate.
34154
34155 #[test]
34156 fn is_leading_zero_padded_magnitude_accepts_canonical_forms() {
34157 // Complement-side pin: every canonical form the typed-magnitude
34158 // `render_*` canonicalizers emit — the single-byte `"0"` case
34159 // and every non-leading-zero magnitude — returns `false`.
34160 assert!(!is_leading_zero_padded_magnitude("0"));
34161 assert!(!is_leading_zero_padded_magnitude("1"));
34162 assert!(!is_leading_zero_padded_magnitude("64"));
34163 assert!(!is_leading_zero_padded_magnitude("500"));
34164 assert!(!is_leading_zero_padded_magnitude("1024"));
34165 assert!(!is_leading_zero_padded_magnitude("999999"));
34166 // Empty magnitude is not a leading-zero shape either — the
34167 // upstream `digit_only` gate at each codec site refuses empty
34168 // magnitudes on its own arm before this predicate is consulted.
34169 assert!(!is_leading_zero_padded_magnitude(""));
34170 // Non-digit-only bodies are outside the predicate's scope — the
34171 // upstream `digit_only` gate refuses them with its own
34172 // `NonInteger*` / `Bad*` diagnostic; this predicate is invoked
34173 // only after that gate accepts.
34174 assert!(!is_leading_zero_padded_magnitude("a"));
34175 assert!(!is_leading_zero_padded_magnitude("1.5"));
34176 }
34177
34178 #[test]
34179 fn is_leading_zero_padded_magnitude_flags_two_byte_leading_zero() {
34180 // The minimal leading-zero drift shape: two-byte magnitude
34181 // starting with `'0'` — `"00"` / `"01"` / `"09"`. Every one
34182 // round-trips through the peer codecs' `render_*` to the
34183 // leading-zero-stripped form (`"0"` / `"1"` / `"9"`).
34184 assert!(is_leading_zero_padded_magnitude("00"));
34185 assert!(is_leading_zero_padded_magnitude("01"));
34186 assert!(is_leading_zero_padded_magnitude("09"));
34187 }
34188
34189 #[test]
34190 fn is_leading_zero_padded_magnitude_flags_multi_byte_leading_zero() {
34191 // The canonical paste-from-fixed-width-alignment /
34192 // paste-from-columnar-report drift class each codec's
34193 // `render_*` emits the stripped form for: `"0064"` (byte-size
34194 // magnitude), `"030"` (duration magnitude), `"0500"`
34195 // (millicores magnitude), `"0100"` (rate-limit magnitude),
34196 // `"01024"` (multi-digit byte-size magnitude).
34197 assert!(is_leading_zero_padded_magnitude("0064"));
34198 assert!(is_leading_zero_padded_magnitude("030"));
34199 assert!(is_leading_zero_padded_magnitude("0500"));
34200 assert!(is_leading_zero_padded_magnitude("0100"));
34201 assert!(is_leading_zero_padded_magnitude("01024"));
34202 // All-zeros multi-byte magnitude — `"000"` / `"0000"` — every
34203 // one round-trips to `"0"`. The single-byte `"0"` case is the
34204 // canonical zero and stays accepted; the multi-byte all-zero
34205 // shape is leading-zero drift.
34206 assert!(is_leading_zero_padded_magnitude("000"));
34207 assert!(is_leading_zero_padded_magnitude("0000"));
34208 }
34209
34210 #[test]
34211 fn is_leading_zero_padded_magnitude_pins_single_zero_boundary() {
34212 // The single-byte magnitude `"0"` is the canonical zero the
34213 // peer codecs' `render_*` canonicalizers emit for the zero
34214 // value verbatim (`render_byte_size(0)` = `"0"`,
34215 // `render_duration(Duration::ZERO)` = `"0s"` with `"0"` as
34216 // the magnitude, `render_millicores(0)` = `"0m"` with `"0"`
34217 // as the magnitude, `RateLimit::render` for rate=0 = `"0/s"`
34218 // with `"0"` as the magnitude). Pinning this boundary so a
34219 // future widening that starts flagging the single-byte `"0"`
34220 // here surfaces as a test failure rather than a silent break
34221 // of the codec-layer / typed-validate-layer partition — the
34222 // semantic-zero gates at the typed-validate layer above
34223 // (`LimitsError::MemoryZero`, `LimitsError::WallClockZero`,
34224 // `LimitsError::CpuZero`, `SupervisorError::ZeroRestartWindow`,
34225 // `AplicacaoError::PolicyTimeoutZero` /
34226 // `PolicyCircuitBreakerWindowZero` / `PolicyRateLimitZero`)
34227 // are what refuse zero-magnitude authoring, not this codec-
34228 // layer predicate.
34229 assert!(!is_leading_zero_padded_magnitude("0"));
34230 }
34231
34232 // ── shared predicate: is_digit_only_magnitude ───────────────────────
34233 //
34234 // Pins the accepted / rejected set of the lifted digit-only
34235 // predicate every typed-magnitude codec in caixa-core calls
34236 // (`parse_byte_size` / `parse_duration` / `parse_millicores` /
34237 // shared `duration_codec` / `rate_limit_codec`). Same lifted-
34238 // source-of-truth discipline the peer canonical-form predicates
34239 // (`find_ascii_whitespace_byte` / `find_non_ascii_whitespace_char`
34240 // / `is_leading_zero_padded_magnitude`) carry — drift between any
34241 // two codec sites' rejection set becomes a single-edit fix at
34242 // this predicate.
34243
34244 #[test]
34245 fn is_digit_only_magnitude_accepts_canonical_forms() {
34246 // Complement-side pin: every canonical form the typed-magnitude
34247 // `render_*` canonicalizers emit — the single-byte `"0"` case
34248 // and every non-zero non-leading-zero magnitude — returns
34249 // `true`.
34250 assert!(is_digit_only_magnitude("0"));
34251 assert!(is_digit_only_magnitude("1"));
34252 assert!(is_digit_only_magnitude("64"));
34253 assert!(is_digit_only_magnitude("500"));
34254 assert!(is_digit_only_magnitude("1024"));
34255 assert!(is_digit_only_magnitude("999999"));
34256 }
34257
34258 #[test]
34259 fn is_digit_only_magnitude_flags_empty_magnitude() {
34260 // Defense-in-depth: the empty string is non-digit-only per the
34261 // predicate's contract, so a future codec reaching for this
34262 // predicate before landing its own upstream empty-magnitude
34263 // arm still routes empty input to the non-canonical branch
34264 // rather than silently accepting it via the vacuous
34265 // `bytes().all(_)` truth on the empty byte-slice.
34266 assert!(!is_digit_only_magnitude(""));
34267 }
34268
34269 #[test]
34270 fn is_digit_only_magnitude_flags_leading_sign() {
34271 // The paste-from-signed-report drift class every codec's
34272 // `render_*` emits the unsigned form for. On current Rust
34273 // `u64::from_str` / `u32::from_str` permissively accept a
34274 // leading `+` (`"+500"` → 500), so `"+30"`, `"+500"`, `"+100"`
34275 // survive the parser and round-trip through `render_*` to the
34276 // sign-stripped form (`"30"`, `"500"`, `"100"`) — a *different*
34277 // canonical string on the next emit, breaking the THEORY.md
34278 // Part V render-determinism contract. The digit-only gate is
34279 // what closes the leading-sign class at each codec site.
34280 assert!(!is_digit_only_magnitude("+30"));
34281 assert!(!is_digit_only_magnitude("+500"));
34282 assert!(!is_digit_only_magnitude("+100"));
34283 assert!(!is_digit_only_magnitude("-30"));
34284 assert!(!is_digit_only_magnitude("-1"));
34285 }
34286
34287 #[test]
34288 fn is_digit_only_magnitude_flags_fractional_and_decimal() {
34289 // The paste-from-floating-point-source drift class every
34290 // codec's `render_*` emits the integer form for. On the peer
34291 // duration codec the parser accepts `f64`-shaped magnitudes
34292 // (`"1.5s"` → 1500ms → `"1500ms"`, `"1.0s"` → 1s → `"1s"`,
34293 // `"0.5m"` → 30s → `"30s"`) — a *different* canonical string
34294 // on the next emit, breaking the THEORY.md Part V render-
34295 // determinism contract. The digit-only gate closes the
34296 // decimal-point / fractional / exponent class at each codec
34297 // site.
34298 assert!(!is_digit_only_magnitude("1.5"));
34299 assert!(!is_digit_only_magnitude("1.0"));
34300 assert!(!is_digit_only_magnitude("0.5"));
34301 assert!(!is_digit_only_magnitude("1e3"));
34302 assert!(!is_digit_only_magnitude(".5"));
34303 assert!(!is_digit_only_magnitude("5."));
34304 }
34305
34306 #[test]
34307 fn is_digit_only_magnitude_flags_alphabetic_and_symbol_bytes() {
34308 // Complement-side pin on the "garbage" branch: alphabetic
34309 // bytes / symbol bytes / whitespace bytes each land on the
34310 // non-digit-only side. At the codec site the downstream
34311 // "non-canonical-but-numeric vs garbage" partition surfaces
34312 // these with the narrower `Bad*` diagnostic; here the
34313 // predicate simply reports `false`.
34314 assert!(!is_digit_only_magnitude("a"));
34315 assert!(!is_digit_only_magnitude("64a"));
34316 assert!(!is_digit_only_magnitude("6_4"));
34317 assert!(!is_digit_only_magnitude("64 "));
34318 assert!(!is_digit_only_magnitude(" 64"));
34319 }
34320
34321 #[test]
34322 fn is_digit_only_magnitude_pins_leading_zero_boundary() {
34323 // The leading-zero-padded magnitude shape stays inside the
34324 // digit-only accepted set at this predicate — every byte is
34325 // an ASCII digit. The peer
34326 // [`is_leading_zero_padded_magnitude`] predicate closes the
34327 // leading-zero drift class on a separate, strictly-later arm
34328 // at each codec site. Pinning this partition so a future
34329 // widening that collapses the two arms surfaces as a test
34330 // failure rather than a silent break of the two-predicate
34331 // codec-layer discipline.
34332 assert!(is_digit_only_magnitude("00"));
34333 assert!(is_digit_only_magnitude("0064"));
34334 assert!(is_digit_only_magnitude("0500"));
34335 }
34336
34337 // ── require_positive_bounded_{u32,u64} ──────────────────────────────
34338
34339 #[derive(Debug, PartialEq, Eq)]
34340 enum TestErr {
34341 Zero,
34342 Cap(u64),
34343 }
34344
34345 #[test]
34346 fn require_positive_bounded_u32_accepts_in_range() {
34347 assert_eq!(
34348 require_positive_bounded_u32::<TestErr>(
34349 1,
34350 10,
34351 || TestErr::Zero,
34352 |v| TestErr::Cap(u64::from(v))
34353 ),
34354 Ok(())
34355 );
34356 assert_eq!(
34357 require_positive_bounded_u32::<TestErr>(
34358 10,
34359 10,
34360 || TestErr::Zero,
34361 |v| TestErr::Cap(u64::from(v))
34362 ),
34363 Ok(())
34364 );
34365 assert_eq!(
34366 require_positive_bounded_u32::<TestErr>(
34367 5,
34368 10,
34369 || TestErr::Zero,
34370 |v| TestErr::Cap(u64::from(v))
34371 ),
34372 Ok(())
34373 );
34374 }
34375
34376 #[test]
34377 fn require_positive_bounded_u32_rejects_zero_with_self_locating_diagnostic() {
34378 // The zero-floor arm strictly precedes the cap arm — a value of
34379 // 0 surfaces the `on_zero` callback's discriminator (which every
34380 // per-axis error variant documents an omit-axis remediation for),
34381 // never the `on_cap_exceeded` callback (which would misframe
34382 // "0 > cap == false" as an above-cap value).
34383 assert_eq!(
34384 require_positive_bounded_u32::<TestErr>(
34385 0,
34386 10,
34387 || TestErr::Zero,
34388 |v| TestErr::Cap(u64::from(v))
34389 ),
34390 Err(TestErr::Zero)
34391 );
34392 // Pin the ordering under the degenerate cap == 0 boundary: even
34393 // when the cap itself is 0 (never valid for a positive-bounded
34394 // axis in production, but pins the ordering contract), 0 routes
34395 // through the zero arm — not the cap arm.
34396 assert_eq!(
34397 require_positive_bounded_u32::<TestErr>(
34398 0,
34399 0,
34400 || TestErr::Zero,
34401 |v| TestErr::Cap(u64::from(v))
34402 ),
34403 Err(TestErr::Zero)
34404 );
34405 }
34406
34407 #[test]
34408 fn require_positive_bounded_u32_rejects_above_cap_with_value_threaded() {
34409 assert_eq!(
34410 require_positive_bounded_u32::<TestErr>(
34411 11,
34412 10,
34413 || TestErr::Zero,
34414 |v| TestErr::Cap(u64::from(v))
34415 ),
34416 Err(TestErr::Cap(11))
34417 );
34418 assert_eq!(
34419 require_positive_bounded_u32::<TestErr>(
34420 u32::MAX,
34421 10,
34422 || TestErr::Zero,
34423 |v| TestErr::Cap(u64::from(v))
34424 ),
34425 Err(TestErr::Cap(u64::from(u32::MAX)))
34426 );
34427 }
34428
34429 #[test]
34430 fn require_positive_bounded_u64_accepts_in_range() {
34431 assert_eq!(
34432 require_positive_bounded_u64::<TestErr>(1, 10, || TestErr::Zero, TestErr::Cap),
34433 Ok(())
34434 );
34435 assert_eq!(
34436 require_positive_bounded_u64::<TestErr>(10, 10, || TestErr::Zero, TestErr::Cap),
34437 Ok(())
34438 );
34439 }
34440
34441 #[test]
34442 fn require_positive_bounded_u64_rejects_zero_and_above_cap() {
34443 assert_eq!(
34444 require_positive_bounded_u64::<TestErr>(0, 10, || TestErr::Zero, TestErr::Cap),
34445 Err(TestErr::Zero)
34446 );
34447 assert_eq!(
34448 require_positive_bounded_u64::<TestErr>(11, 10, || TestErr::Zero, TestErr::Cap),
34449 Err(TestErr::Cap(11))
34450 );
34451 assert_eq!(
34452 require_positive_bounded_u64::<TestErr>(u64::MAX, 10, || TestErr::Zero, TestErr::Cap),
34453 Err(TestErr::Cap(u64::MAX))
34454 );
34455 }
34456
34457 // ── require_positive_quantum_multiple_bounded_u64 ────────────────────
34458
34459 #[derive(Debug, PartialEq, Eq)]
34460 enum QuantumTestErr {
34461 Zero,
34462 BelowQuantum(u64),
34463 Cap(u64),
34464 NotMultiple(u64),
34465 }
34466
34467 fn q_gate(value: u64, quantum: u64, cap: u64) -> Result<(), QuantumTestErr> {
34468 require_positive_quantum_multiple_bounded_u64(
34469 value,
34470 quantum,
34471 cap,
34472 || QuantumTestErr::Zero,
34473 QuantumTestErr::BelowQuantum,
34474 QuantumTestErr::Cap,
34475 QuantumTestErr::NotMultiple,
34476 )
34477 }
34478
34479 #[test]
34480 fn require_positive_quantum_multiple_bounded_u64_accepts_in_range_multiples() {
34481 // Every canonical quantum-multiple in `quantum..=cap` — the shared
34482 // accepted set every quantized-byte-cap consumer inherits — must
34483 // pass the gate. Pin the accepted set here so a future tightening
34484 // surfaces as a test failure rather than a silent narrowing at
34485 // the single consumer site (`:limits :memory`).
34486 let quantum = 64 * 1024;
34487 let cap = 4 * 1024 * 1024 * 1024;
34488 for value in [quantum, quantum * 2, quantum * 100, quantum * 1000, cap] {
34489 assert_eq!(
34490 q_gate(value, quantum, cap),
34491 Ok(()),
34492 "quantum-multiple in-range value {value} must pass the gate",
34493 );
34494 }
34495 }
34496
34497 #[test]
34498 fn require_positive_quantum_multiple_bounded_u64_rejects_zero_before_other_arms() {
34499 // The zero-floor arm strictly precedes the below-quantum, cap,
34500 // and not-multiple arms — a value of 0 surfaces the
34501 // caller's self-locating `on_zero` diagnostic (every per-axis
34502 // error variant documents an "omit the axis to express no-bound"
34503 // remediation for) rather than the misleading below-quantum arm
34504 // (which would also fire because 0 < quantum) or the not-multiple
34505 // arm (which the modulus check `0 % quantum == 0` would silently
34506 // accept).
34507 let quantum = 64 * 1024;
34508 let cap = 4 * 1024 * 1024 * 1024;
34509 assert_eq!(q_gate(0, quantum, cap), Err(QuantumTestErr::Zero));
34510 // The degenerate `cap == 0` / `quantum == 1` boundaries: 0 still
34511 // routes through the zero arm — the ordering contract holds even
34512 // when the cap or quantum themselves take the degenerate shape
34513 // (never valid production shapes for a positive-bounded quantized
34514 // axis, but pin the arm ordering).
34515 assert_eq!(q_gate(0, 1, 0), Err(QuantumTestErr::Zero));
34516 assert_eq!(q_gate(0, quantum, 0), Err(QuantumTestErr::Zero));
34517 }
34518
34519 #[test]
34520 fn require_positive_quantum_multiple_bounded_u64_rejects_below_quantum_before_cap_and_multiple()
34521 {
34522 // The below-quantum arm strictly precedes the cap and
34523 // not-multiple arms — a sub-quantum non-zero value (which is
34524 // ALSO not a quantum-multiple by construction, since the
34525 // smallest positive quantum-multiple *is* `quantum`) surfaces
34526 // the more actionable "raise to at least one quantum" diagnostic
34527 // rather than the not-multiple no-op. Pin the ordering across
34528 // the value grid — every value in `1..quantum` must fire the
34529 // below-quantum arm with the offending byte count threaded
34530 // through the callback.
34531 let quantum = 64 * 1024;
34532 let cap = 4 * 1024 * 1024 * 1024;
34533 for value in [1u64, 2, 32 * 1024, quantum - 1] {
34534 assert_eq!(
34535 q_gate(value, quantum, cap),
34536 Err(QuantumTestErr::BelowQuantum(value)),
34537 "sub-quantum {value} must surface BelowQuantum before Cap / NotMultiple",
34538 );
34539 }
34540 }
34541
34542 #[test]
34543 fn require_positive_quantum_multiple_bounded_u64_rejects_above_cap_before_not_multiple() {
34544 // The cap arm strictly precedes the not-multiple arm — a value
34545 // that is *both* above-cap and sub-quantum-residue must surface
34546 // the more aggressive cap-shape diagnostic first (the
34547 // not-multiple remediation would be misleading when the
34548 // offending value exceeds the upper bracket anyway; the
34549 // canonical fix collapses both into "pin a quantum-aligned
34550 // value ≤ cap"). Pin the ordering across the value grid,
34551 // including the boundary case `cap + 1`.
34552 let quantum = 64 * 1024;
34553 let cap = 4 * 1024 * 1024 * 1024;
34554 for value in [
34555 cap + 1, // above-cap AND sub-quantum-residue
34556 cap + quantum, // above-cap and quantum-aligned
34557 cap + quantum * 100, // well above-cap and quantum-aligned
34558 u64::MAX, // maximally above-cap
34559 ] {
34560 assert_eq!(
34561 q_gate(value, quantum, cap),
34562 Err(QuantumTestErr::Cap(value)),
34563 "above-cap {value} must surface Cap before NotMultiple",
34564 );
34565 }
34566 }
34567
34568 #[test]
34569 fn require_positive_quantum_multiple_bounded_u64_rejects_not_multiple_with_value_threaded() {
34570 // The not-multiple arm surfaces the offending value verbatim so
34571 // the caller's `on_not_quantum_multiple` variant threads it into
34572 // its discriminator field (`bytes:`). Pin the arm across the
34573 // in-range-but-not-aligned value grid — every value in
34574 // `quantum..=cap` carrying a sub-quantum residue must fire the
34575 // not-multiple arm.
34576 let quantum = 64 * 1024;
34577 let cap = 4 * 1024 * 1024 * 1024;
34578 for value in [
34579 quantum + 1, // one page plus a 1-byte residue
34580 quantum * 2 - 1, // two pages minus one byte
34581 100_000, // ≈ 97.65 KiB — one page + 34_464-byte residue
34582 quantum * 100 + 7, // 100 pages plus a 7-byte residue
34583 ] {
34584 assert_eq!(
34585 q_gate(value, quantum, cap),
34586 Err(QuantumTestErr::NotMultiple(value)),
34587 "sub-quantum-residue {value} must surface NotMultiple",
34588 );
34589 }
34590 }
34591
34592 // ── require_positive_canonical_bounded_duration ─────────────────────
34593
34594 #[derive(Debug, PartialEq, Eq)]
34595 enum DurationTestErr {
34596 Zero,
34597 NotCanonical(Duration),
34598 Cap(Duration),
34599 }
34600
34601 #[test]
34602 fn require_positive_canonical_bounded_duration_accepts_in_range_canonical_values() {
34603 // Every canonical integer-millisecond `Duration` in
34604 // `1ms..=cap` — the shared accepted set every typed-`Duration`
34605 // consumer inherits — must pass the gate. Pin the canonical
34606 // set here so a future tightening surfaces as a test failure
34607 // rather than a silent narrowing at one of the four consumer
34608 // sites (`:politicas :timeout`, `:circuit-breaker :window`,
34609 // `:limits :wall-clock`, `:supervisor :restart-window`).
34610 let cap = Duration::from_secs(3600); // matches the 1h peer caps
34611 for value in [
34612 Duration::from_millis(1),
34613 Duration::from_millis(500),
34614 Duration::from_millis(1500),
34615 Duration::from_secs(30),
34616 Duration::from_secs(60),
34617 cap,
34618 ] {
34619 assert_eq!(
34620 require_positive_canonical_bounded_duration::<DurationTestErr>(
34621 value,
34622 cap,
34623 || DurationTestErr::Zero,
34624 DurationTestErr::NotCanonical,
34625 DurationTestErr::Cap,
34626 ),
34627 Ok(()),
34628 "canonical in-range value {value:?} must pass the gate",
34629 );
34630 }
34631 }
34632
34633 #[test]
34634 fn require_positive_canonical_bounded_duration_rejects_zero_before_canonical_and_cap() {
34635 // The zero-floor arm strictly precedes the canonical-form and
34636 // cap arms — `Duration::ZERO` (which has `subsec_nanos() == 0`
34637 // and would pass the canonical-form predicate; and would pass
34638 // the cap arm since 0 ≤ cap) routes through the zero arm so
34639 // the caller's self-locating `on_zero` diagnostic (every
34640 // per-axis error variant documents an omit-axis remediation
34641 // for) is surfaced, not the misleading no-op the two later
34642 // arms would return.
34643 let cap = Duration::from_secs(3600);
34644 assert_eq!(
34645 require_positive_canonical_bounded_duration::<DurationTestErr>(
34646 Duration::ZERO,
34647 cap,
34648 || DurationTestErr::Zero,
34649 DurationTestErr::NotCanonical,
34650 DurationTestErr::Cap,
34651 ),
34652 Err(DurationTestErr::Zero),
34653 );
34654 // The degenerate `cap == Duration::ZERO` boundary: `Duration::ZERO`
34655 // still routes through the zero arm — the ordering contract holds
34656 // even when the cap itself is zero (never a valid production cap
34657 // for a positive-bounded axis, but pins the arm ordering).
34658 assert_eq!(
34659 require_positive_canonical_bounded_duration::<DurationTestErr>(
34660 Duration::ZERO,
34661 Duration::ZERO,
34662 || DurationTestErr::Zero,
34663 DurationTestErr::NotCanonical,
34664 DurationTestErr::Cap,
34665 ),
34666 Err(DurationTestErr::Zero),
34667 );
34668 }
34669
34670 #[test]
34671 fn require_positive_canonical_bounded_duration_rejects_sub_millisecond_before_cap() {
34672 // The canonical-form arm strictly precedes the cap arm — a
34673 // `Duration` that is *both* sub-millisecond and above-cap must
34674 // surface the more fundamental round-trip-shape diagnostic
34675 // first (the cap arm's `1ms..=<cap>` remediation prose would
34676 // be misleading when no integer-ms form of the offending
34677 // value exists). Pin the ordering across the value grid.
34678 let cap = Duration::from_secs(1);
34679 for value in [
34680 Duration::from_micros(1),
34681 Duration::from_micros(500),
34682 Duration::from_micros(1500),
34683 Duration::from_nanos(1),
34684 Duration::from_nanos(999_999),
34685 Duration::from_nanos(1_000_001),
34686 // Sub-millisecond *and* above-cap: canonical-form arm wins.
34687 cap + Duration::from_nanos(1),
34688 ] {
34689 let result = require_positive_canonical_bounded_duration::<DurationTestErr>(
34690 value,
34691 cap,
34692 || DurationTestErr::Zero,
34693 DurationTestErr::NotCanonical,
34694 DurationTestErr::Cap,
34695 );
34696 assert_eq!(
34697 result,
34698 Err(DurationTestErr::NotCanonical(value)),
34699 "sub-millisecond {value:?} must surface NotCanonical before Cap",
34700 );
34701 }
34702 }
34703
34704 #[test]
34705 fn require_positive_canonical_bounded_duration_rejects_above_cap_with_value_threaded() {
34706 // The cap arm surfaces the offending value verbatim so the
34707 // caller's `on_cap_exceeded` variant threads it into its
34708 // discriminator field (`timeout` / `window` / `wall_clock`).
34709 // The value grid covers the canonical `<n>ms` / `<n>s`
34710 // integer-millisecond shape past the 1h cap so the arm ordering
34711 // (canonical-form first) doesn't intercept these values.
34712 let cap = Duration::from_secs(3600);
34713 for value in [
34714 cap + Duration::from_millis(1),
34715 cap + Duration::from_secs(1),
34716 Duration::from_secs(24 * 3600), // 24h — canonical string
34717 Duration::from_secs(7 * 24 * 3600), // 7d
34718 ] {
34719 assert_eq!(
34720 require_positive_canonical_bounded_duration::<DurationTestErr>(
34721 value,
34722 cap,
34723 || DurationTestErr::Zero,
34724 DurationTestErr::NotCanonical,
34725 DurationTestErr::Cap,
34726 ),
34727 Err(DurationTestErr::Cap(value)),
34728 "above-cap canonical value {value:?} must thread through the cap arm",
34729 );
34730 }
34731 }
34732
34733 // ── require_valid_versao_requirement ────────────────────────────────
34734
34735 #[derive(Debug, PartialEq, Eq)]
34736 enum VersaoTestErr {
34737 Empty,
34738 Invalid(String),
34739 }
34740
34741 #[test]
34742 fn require_valid_versao_requirement_accepts_canonical_forms() {
34743 // Every Cargo-shaped requirement string the substrate accepts on
34744 // any `:versao` axis (`:deps`, `:membros`, `:children`) must pass
34745 // the shared gate — pin the canonical set here so a future
34746 // tightening surfaces as a test failure rather than a silent
34747 // narrowing at one of the three consumer sites. Same accepted set
34748 // as `accepts_canonical_membro_versao_forms` /
34749 // `accepts_canonical_dep_versao_forms` on the sibling per-axis
34750 // pins.
34751 for form in [
34752 "^0.1", // caret — minor-range pin (the most common shape)
34753 "~0.1.2", // tilde — patch-range pin
34754 "0.1.0", // exact — single-version pin
34755 "*", // wildcard — explicitly any-version (VersionReq::STAR)
34756 ">=0.1, <2", // multi-range — comma-separated comparators
34757 ] {
34758 assert_eq!(
34759 require_valid_versao_requirement::<VersaoTestErr>(
34760 form,
34761 || VersaoTestErr::Empty,
34762 VersaoTestErr::Invalid,
34763 ),
34764 Ok(()),
34765 "canonical form {form:?} must pass the gate",
34766 );
34767 }
34768 }
34769
34770 #[test]
34771 fn require_valid_versao_requirement_rejects_empty_before_parse() {
34772 // The empty-first arm strictly precedes the parse arm. Without
34773 // this arm the parser silently widens `""` to
34774 // `VersionReq { comparators: [] }` (semantically `*`) — a
34775 // "silent widening" footgun the three consumer sites each
34776 // documented in their `MembroVersaoEmpty` / `EmptyChildVersion` /
34777 // `VersaoEmpty` variants and now inherit by construction.
34778 assert_eq!(
34779 require_valid_versao_requirement::<VersaoTestErr>(
34780 "",
34781 || VersaoTestErr::Empty,
34782 VersaoTestErr::Invalid,
34783 ),
34784 Err(VersaoTestErr::Empty),
34785 );
34786 }
34787
34788 #[test]
34789 fn require_valid_versao_requirement_rejects_malformed_with_reason_threaded() {
34790 // The canonical malformed-shape set the three consumer sites
34791 // formerly each re-tested inline. The gate threads the
34792 // parser's `to_string()` output through as the invalid arm's
34793 // `reason:` verbatim — the field the three sibling error
34794 // variants (`{Dep,Membro,Child}VersaoInvalid.reason`) each
34795 // carry to the author's remediation prose.
34796 for bad in [
34797 "^^0.1", // doubled-caret typo
34798 "v0.1", // git-tag-shape leaking into requirement slot
34799 "abc", // gibberish
34800 "~~", // stacked-operator gibberish
34801 ] {
34802 let result = require_valid_versao_requirement::<VersaoTestErr>(
34803 bad,
34804 || VersaoTestErr::Empty,
34805 VersaoTestErr::Invalid,
34806 );
34807 match result {
34808 Err(VersaoTestErr::Invalid(reason)) => {
34809 assert!(
34810 !reason.is_empty(),
34811 "invalid arm must thread a non-empty reason for {bad:?}",
34812 );
34813 }
34814 other => panic!("expected Invalid for {bad:?}, got {other:?}"),
34815 }
34816 }
34817 }
34818
34819 // ── require_valid_dns_1123_label ────────────────────────────────────
34820
34821 #[derive(Debug, PartialEq, Eq)]
34822 enum LabelTestErr {
34823 Empty,
34824 Invalid(String),
34825 }
34826
34827 #[test]
34828 fn require_valid_dns_1123_label_accepts_canonical_forms() {
34829 // Every DNS-1123-label-shaped Servico-name reference the substrate
34830 // accepts on any name axis (`:membros :caixa`, `:placement :clusters`,
34831 // `:placement :affinity`, `:contratos :de`/`:para`, `:entrada :para`,
34832 // `:children :caixa`, `:nome`, `:upgrade-from :module`) must pass
34833 // the shared gate — pin the canonical set here so a future
34834 // tightening surfaces as a test failure rather than a silent
34835 // narrowing at one of the eight consumer sites. Same accepted set
34836 // as the sibling per-axis DNS-1123-label pins already carry.
34837 for form in [
34838 "hello-rio", // canonical dashed
34839 "cart", // single-token
34840 "rio-1", // trailing digit
34841 "1-rio", // leading digit
34842 "a", // one byte
34843 &"a".repeat(DNS_1123_LABEL_MAX_LEN), // max length exact
34844 ] {
34845 assert_eq!(
34846 require_valid_dns_1123_label::<LabelTestErr>(
34847 form,
34848 || LabelTestErr::Empty,
34849 LabelTestErr::Invalid,
34850 ),
34851 Ok(()),
34852 "canonical form {form:?} must pass the gate",
34853 );
34854 }
34855 }
34856
34857 #[test]
34858 fn require_valid_dns_1123_label_rejects_empty_before_shape() {
34859 // The empty-first arm strictly precedes the shape arm so a
34860 // literal `""` surfaces each per-axis error variant's narrower
34861 // self-locating `_Empty` diagnostic rather than the shared
34862 // predicate's generic "must not be empty" prose the shape arm
34863 // would thread through — the same "misframed generic diagnostic"
34864 // footgun the peer [`require_valid_versao_requirement`] closes
34865 // on its empty arm. The eight consumer sites each documented
34866 // this ordering in their `MembroCaixaEmpty` / `PlacementClusterEmpty`
34867 // / `PlacementAffinityEmpty` / `ContratoCaixaEmpty` /
34868 // `EntradaParaEmpty` / `NomeEmpty` / `EmptyChildName` /
34869 // `ModuleEmpty` variants and now inherit it by construction.
34870 assert_eq!(
34871 require_valid_dns_1123_label::<LabelTestErr>(
34872 "",
34873 || LabelTestErr::Empty,
34874 LabelTestErr::Invalid,
34875 ),
34876 Err(LabelTestErr::Empty),
34877 );
34878 }
34879
34880 #[test]
34881 fn require_valid_dns_1123_label_rejects_malformed_with_reason_threaded() {
34882 // The canonical malformed-shape set the eight consumer sites
34883 // formerly each re-tested inline. The gate threads the
34884 // predicate's shape-shaped reason through as the invalid arm's
34885 // `reason:` verbatim — the field every sibling error variant
34886 // (`{MembroCaixa,PlacementCluster,PlacementAffinity,ContratoCaixa,
34887 // EntradaPara,Nome,ChildCaixa,Module}Invalid.reason`) each
34888 // carry to the author's remediation prose.
34889 for bad in [
34890 "Rio", // uppercase — the canonical TitleCase-from-an-ADR typo
34891 "my_cart", // underscore — the Python-module-name leak
34892 "team.cart", // dot — the namespace-dot-on-a-label confusion
34893 "-cart", // leading hyphen — boundary violation
34894 "cart-", // trailing hyphen — boundary violation
34895 ] {
34896 let result = require_valid_dns_1123_label::<LabelTestErr>(
34897 bad,
34898 || LabelTestErr::Empty,
34899 LabelTestErr::Invalid,
34900 );
34901 match result {
34902 Err(LabelTestErr::Invalid(reason)) => {
34903 assert!(
34904 !reason.is_empty(),
34905 "invalid arm must thread a non-empty reason for {bad:?}",
34906 );
34907 }
34908 other => panic!("expected Invalid for {bad:?}, got {other:?}"),
34909 }
34910 }
34911 }
34912
34913 // ── require_sandboxed_lisp_path ─────────────────────────────────────
34914
34915 #[derive(Debug, PartialEq, Eq)]
34916 enum LispPathTestErr {
34917 Empty,
34918 Absolute,
34919 ParentEscape,
34920 NonLisp,
34921 }
34922
34923 fn call_require_sandboxed_lisp_path(path: &Path) -> Result<(), LispPathTestErr> {
34924 require_sandboxed_lisp_path(
34925 path,
34926 || LispPathTestErr::Empty,
34927 || LispPathTestErr::Absolute,
34928 || LispPathTestErr::ParentEscape,
34929 || LispPathTestErr::NonLisp,
34930 )
34931 }
34932
34933 #[test]
34934 fn require_sandboxed_lisp_path_accepts_canonical_forms() {
34935 // Every sandboxed-relative `.lisp`-terminating path the substrate
34936 // accepts on either M2 tatara-lisp source-path axis (`:behavior :on-*`
34937 // callback paths, `:upgrade-from :state-change :script`) must pass
34938 // the shared gate. Pin the canonical set here so a future tightening
34939 // surfaces as a test failure rather than a silent narrowing at one
34940 // of the two consumer sites.
34941 for form in [
34942 "lib/init.lisp", // canonical example
34943 "lib/handlers.lisp", // multi-callback shape
34944 "lib/migrations/v01-to-v02.lisp", // nested-directory shape
34945 "a.lisp", // one-byte stem
34946 "lib/deep/nested/path/to/file.lisp", // deeply nested
34947 ] {
34948 assert_eq!(
34949 call_require_sandboxed_lisp_path(Path::new(form)),
34950 Ok(()),
34951 "canonical sandboxed `.lisp` form {form:?} must pass the gate",
34952 );
34953 }
34954 }
34955
34956 #[test]
34957 fn require_sandboxed_lisp_path_rejects_empty_before_all_later_arms() {
34958 // The empty-first arm strictly precedes every downstream arm — a
34959 // literal `""` (which the is_absolute check would return false on,
34960 // which carries no ParentDir component, and whose extension is
34961 // absent) routes through the `on_empty` closure so the caller's
34962 // narrower self-locating `_Empty` / `_EmptyScript` diagnostic fires,
34963 // not a misleading `_Absolute` / `_ParentEscape` / `_NonLisp` miss
34964 // downstream. Peer of every zero-first arm ordering the sibling
34965 // require_positive_bounded_* helpers already carry.
34966 assert_eq!(
34967 call_require_sandboxed_lisp_path(Path::new("")),
34968 Err(LispPathTestErr::Empty),
34969 );
34970 }
34971
34972 #[test]
34973 fn require_sandboxed_lisp_path_rejects_absolute_before_parent_escape_and_non_lisp() {
34974 // The absolute arm strictly precedes the parent-escape and
34975 // non-`.lisp`-extension arms — an absolute path (regardless of
34976 // whether it also carries `..` components or a non-`.lisp`
34977 // extension) routes through the `on_absolute` closure so the
34978 // caller's `_Absolute` / `_AbsoluteScript` diagnostic fires with
34979 // its "must be relative to the caixa root" remediation, not the
34980 // misleading later arms. Pin the ordering across the value grid
34981 // covering "absolute + parent-escape" and "absolute + non-`.lisp`"
34982 // compound-violation shapes so a future arm-reorder silently
34983 // narrowing the accepted set would surface at build time.
34984 for absolute in [
34985 "/etc/passwd", // canonical absolute
34986 "/lib/init.lisp", // absolute + `.lisp` (extension arm never reached)
34987 "/lib/../init.lisp", // absolute + parent-escape (later arm never reached)
34988 "/etc/init.txt", // absolute + non-`.lisp`
34989 ] {
34990 assert_eq!(
34991 call_require_sandboxed_lisp_path(Path::new(absolute)),
34992 Err(LispPathTestErr::Absolute),
34993 "absolute path {absolute:?} must route through Absolute arm",
34994 );
34995 }
34996 }
34997
34998 #[test]
34999 fn require_sandboxed_lisp_path_rejects_parent_escape_before_non_lisp() {
35000 // The parent-escape arm strictly precedes the non-`.lisp`-extension
35001 // arm — a relative path carrying any `..` component routes through
35002 // the `on_parent_escape` closure so the caller's `_ParentEscape` /
35003 // `_ParentEscapeScript` diagnostic fires with its "must not
35004 // traverse above the caixa root" remediation, not the misleading
35005 // extension-shape arm. Pin the ordering across leading / mid-path
35006 // / trailing parent-escape positions plus the compound
35007 // "parent-escape + non-`.lisp`" shape.
35008 for escape in [
35009 "../sibling/x.lisp", // leading `..`
35010 "lib/../other.lisp", // mid-path `..`
35011 "lib/handlers/../..", // trailing `..`
35012 "../sibling/x.txt", // parent-escape + non-`.lisp`
35013 ] {
35014 assert_eq!(
35015 call_require_sandboxed_lisp_path(Path::new(escape)),
35016 Err(LispPathTestErr::ParentEscape),
35017 "parent-escaping path {escape:?} must route through ParentEscape arm",
35018 );
35019 }
35020 }
35021
35022 #[test]
35023 fn require_sandboxed_lisp_path_rejects_non_lisp_only_after_all_path_shape_arms_accept() {
35024 // The non-`.lisp`-extension arm fires only when every prior arm
35025 // (empty / absolute / parent-escape) accepts the path — a
35026 // sandboxed relative path whose only violation is a non-`.lisp`
35027 // terminating extension routes through the `on_non_lisp` closure
35028 // so the caller's `_NonLispExtension` / `_NonLispExtensionScript`
35029 // diagnostic fires with its `.lisp`-remediation prose. Pin the
35030 // downstream-most-arm reachability across the canonical
35031 // `.txt`/`.rs`/no-extension/double-extension-shadow shape set the
35032 // two consumer sites' error variants each document.
35033 for bad_ext in [
35034 "lib/init.txt", // wrong extension
35035 "lib/init.rs", // Rust source leaked into caixa
35036 "lib/init.lisp.bak", // double-extension shadow
35037 "lib/init", // no extension
35038 "lib/migrations", // no extension, no dot
35039 "lib/init.LISP", // uppercase — case-sensitive gate
35040 ] {
35041 assert_eq!(
35042 call_require_sandboxed_lisp_path(Path::new(bad_ext)),
35043 Err(LispPathTestErr::NonLisp),
35044 "non-`.lisp` path {bad_ext:?} must route through NonLisp arm",
35045 );
35046 }
35047 }
35048
35049 #[test]
35050 fn require_sandboxed_lisp_path_ordering_matches_inline_pre_lift_cascade() {
35051 // Byte-for-byte the same `Empty → Absolute → ParentEscape → NonLisp`
35052 // arm-ordering the two consumer sites (`validate_callback_path` in
35053 // `caixa-core::behavior`, `UpgradeInstruction::validate`'s
35054 // `StateChange` arm in `caixa-core::upgrade`) each formerly inlined
35055 // verbatim. This pin catches any future reorder that would
35056 // silently reshape the diagnostic dispatch at either site — the
35057 // helper's ordering IS the two sites' ordering, not a re-derived
35058 // convention. Pins the same
35059 // smallest-scope-arm-fires-last three-path drift-detection
35060 // posture the peer `require_positive_bounded_*` /
35061 // `require_positive_canonical_bounded_duration` helpers already
35062 // carry on their own arm sets.
35063 assert_eq!(
35064 call_require_sandboxed_lisp_path(Path::new("")),
35065 Err(LispPathTestErr::Empty),
35066 );
35067 assert_eq!(
35068 call_require_sandboxed_lisp_path(Path::new("/abs/x.lisp")),
35069 Err(LispPathTestErr::Absolute),
35070 );
35071 assert_eq!(
35072 call_require_sandboxed_lisp_path(Path::new("../x.lisp")),
35073 Err(LispPathTestErr::ParentEscape),
35074 );
35075 assert_eq!(
35076 call_require_sandboxed_lisp_path(Path::new("lib/x.txt")),
35077 Err(LispPathTestErr::NonLisp),
35078 );
35079 assert_eq!(
35080 call_require_sandboxed_lisp_path(Path::new("lib/x.lisp")),
35081 Ok(()),
35082 );
35083 }
35084
35085 #[test]
35086 fn gateway_api_hostname_max_len_pins_canonical_value() {
35087 // Pin the actual byte count so a typo in this lift can't silently
35088 // rebrand the K8s Gateway API v1 `Listener.hostname` /
35089 // `HTTPRoute.spec.hostnames[]` admission-schema `maxLength:` cap
35090 // the `AplicacaoSpec::validate` `:entrada :host` total-length arm
35091 // reads. The value is part of the cluster-side contract with
35092 // every Gateway API v1 CRD schema validator (apiserver-side +
35093 // Cilium / Envoy Gateway / Istio / NGINX per-implementation
35094 // webhooks) — the OpenAPI schema on the Hostname type binds
35095 // `maxLength: 253` verbatim (RFC 1035 / RFC 1123 DNS name limit:
35096 // 255 wire bytes minus the trailing-dot + one length prefix), so
35097 // a drifted value at either the aplicacao-side validator or a
35098 // downstream renderer's per-host validator silently emits a
35099 // Gateway / HTTPRoute the apiserver rejects at admission time
35100 // with an opaque `field is invalid` diagnostic far from the
35101 // caixa.lisp source line. Changing this value is a coordinated
35102 // Gateway API promotion alongside the upstream SIG-Network
35103 // Hostname schema evolution, not an incidental edit. Peer to
35104 // [`GATEWAY_API_HTTP_PATH_MAX_LEN`] (1024) on the sibling
35105 // per-route path-value cap axis — both are apiserver-side
35106 // `maxLength:` bounds on Gateway API v1 landing sites, both lift
35107 // to `caixa-core::render` so the M4 CR materializer's per-axis
35108 // validators (per-host, per-path) read from one place.
35109 assert_eq!(GATEWAY_API_HOSTNAME_MAX_LEN, 253);
35110 }
35111
35112 #[test]
35113 fn gateway_api_hostname_max_len_exceeds_dns_1123_label_max_len() {
35114 // Cross-axis structural invariant: every `.`-separated label in
35115 // a Gateway API v1 Hostname is a DNS-1123 label, so the total
35116 // Hostname cap must strictly exceed the per-label cap — otherwise
35117 // even a single-label host `"foo"` couldn't reach the per-label
35118 // ceiling before hitting the total-length ceiling, and the
35119 // `AplicacaoSpec::validate` `:entrada :host` per-label arm at
35120 // `validate_entrada_host` would be structurally unreachable via
35121 // the total-length arm's own ordering. Pinning the ordering here
35122 // means a future substrate-side tightening of either bound (a
35123 // K8s SIG-Network Hostname promotion narrowing the total cap, a
35124 // DNS-1123 label promotion widening the per-label cap) that
35125 // inverted the two would fail this pin at build time rather than
35126 // silently rendering the per-label arm unreachable.
35127 assert!(
35128 GATEWAY_API_HOSTNAME_MAX_LEN > DNS_1123_LABEL_MAX_LEN,
35129 "GATEWAY_API_HOSTNAME_MAX_LEN ({GATEWAY_API_HOSTNAME_MAX_LEN}) must strictly \
35130 exceed DNS_1123_LABEL_MAX_LEN ({DNS_1123_LABEL_MAX_LEN}) — every \
35131 `.`-separated label in a Gateway API v1 Hostname is itself a DNS-1123 \
35132 label under the apiserver's OpenAPI regex, so the total-length cap \
35133 must be able to accommodate at least one per-label-max label",
35134 );
35135 }
35136
35137 #[test]
35138 fn gateway_api_hostname_max_len_matches_rfc_1035_dns_name_limit() {
35139 // Cross-axis structural invariant: the Gateway API v1 Hostname
35140 // `maxLength: 253` cap is the RFC 1035 / RFC 1123 DNS name limit
35141 // — 255 wire bytes minus one length prefix minus the implicit
35142 // trailing dot — the same cap every DNS-compliant `HostName`
35143 // primitive downstream substrate consumer (the future
35144 // per-`Certificate` SAN emitter for cert-manager, the future
35145 // multi-`:entrada` host-collision gate) will inherit by
35146 // construction. Pinning the arithmetic here rather than the
35147 // literal `253` makes the RFC derivation explicit at the const's
35148 // test site so a future migration onto a different DNS-name
35149 // ceiling (an eventual RFC-successor limit, a per-cluster
35150 // override the operator pins) surfaces at this pin, not at every
35151 // downstream renderer's admission-rejection loop.
35152 assert_eq!(
35153 GATEWAY_API_HOSTNAME_MAX_LEN,
35154 255 - 1 - 1,
35155 "GATEWAY_API_HOSTNAME_MAX_LEN must equal the RFC 1035 / RFC 1123 DNS \
35156 name limit (255 wire bytes minus one length prefix minus the trailing \
35157 dot)",
35158 );
35159 }
35160
35161 #[test]
35162 fn gateway_api_default_http_listener_port_pins_canonical_80_literal() {
35163 // The canonical-constant arm — pins
35164 // [`GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT`] at the verbatim
35165 // `80` literal the sole `caixa-mesh::gateway_routes` per-
35166 // Aplicacao `Gateway` per-listener HTTP-listener-port axis
35167 // reads from. Peer with the
35168 // [`crate::DEFAULT_SERVICO_PORT`]-pins-`8080` discipline on the
35169 // sibling per-renderer canonical-K8s-port-axis typed `u16`
35170 // const: a future refactor that drifts the constant out from
35171 // under either consumer surfaces here ahead of any per-renderer
35172 // Gateway emission. The literal value is IANA's well-known
35173 // `http` service port (RFC 9110 §4.2.2), so an
35174 // `http://<entrada.host>/…` URL without a `:<port>` selector
35175 // reaches the listener by construction.
35176 assert_eq!(
35177 GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT, 80,
35178 "canonical Gateway API v1 HTTP listener port literal must remain \
35179 `80` verbatim — this is the value the caixa-mesh Gateway emitter \
35180 reads from and the IANA-registered well-known `http` service port"
35181 );
35182 }
35183
35184 #[test]
35185 fn gateway_api_default_http_listener_port_distinct_from_default_servico_port() {
35186 // Cross-axis structural invariant: the Gateway listener's
35187 // external HTTP port ([`GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT`],
35188 // 80) and the per-Servico in-cluster L4 port
35189 // ([`DEFAULT_SERVICO_PORT`], 8080) are two distinct axes — the
35190 // external-ingress port the K8s Gateway API controller opens on
35191 // the cluster boundary, and the internal-Servico port the
35192 // `pleme-computeunit` chart emits per Servico `Service`.
35193 // Collapsing the two would silently emit a Gateway whose
35194 // listener port matched the Servico's own port, so a stray
35195 // Servico exposing its Service directly to a cluster-external
35196 // LoadBalancer would shadow the Aplicacao's Gateway path — the
35197 // typed two-axis distinction guards against a rebrand on either
35198 // axis silently converging on the other's value. Peer with the
35199 // [`GATEWAY_API_HOSTNAME_MAX_LEN`]-strictly-exceeds-[`DNS_1123_LABEL_MAX_LEN`]
35200 // discipline on the sibling per-axis structural-ordering pin
35201 // set — both are cross-axis invariants between two lifted
35202 // constants that share a downstream renderer.
35203 assert_ne!(
35204 GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT,
35205 crate::DEFAULT_SERVICO_PORT,
35206 "GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT ({GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT}) \
35207 must remain distinct from DEFAULT_SERVICO_PORT ({}) — the two axes name \
35208 different scalars (external-Gateway listener port vs in-cluster Servico port), \
35209 collapsing them silently shadows the Aplicacao's Gateway path",
35210 crate::DEFAULT_SERVICO_PORT,
35211 );
35212 }
35213
35214 #[test]
35215 fn gateway_api_default_http_listener_name_pins_canonical_http_literal() {
35216 // The canonical-constant arm — pins
35217 // [`GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME`] at the verbatim
35218 // `"http"` literal the sole `caixa-mesh::gateway_routes` per-
35219 // Aplicacao `Gateway` per-listener name-discriminator axis
35220 // reads from. Peer with the
35221 // [`GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT`]-pins-`80` discipline
35222 // on the sibling per-listener HTTP-listener-port scalar-axis:
35223 // both are the Aplicacao-side substrate-canonical scalar-value
35224 // pins the sole per-Aplicacao `Gateway` emitter reaches for, so
35225 // a future refactor that drifts either constant out from under
35226 // the emitter surfaces here ahead of any per-renderer Gateway
35227 // emission. The literal value is the substrate's V0 arbitrary-
35228 // author-chosen short listener-name (K8s Gateway API v1's
35229 // `SectionName`-typed field carries no CRD-schema-pinned value
35230 // — the substrate picks `"http"` verbatim to match the
35231 // listener's carried protocol shape at the reader's eye), so
35232 // downstream `HTTPRoute` `sectionName` selectors bind to this
35233 // exact byte-string by construction.
35234 assert_eq!(
35235 GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME, "http",
35236 "canonical Gateway API v1 HTTP listener-name literal must remain \
35237 `\"http\"` verbatim — this is the value the caixa-mesh Gateway \
35238 emitter reads from and the substrate's V0 arbitrary-author-chosen \
35239 short listener-name identifier every downstream `HTTPRoute` \
35240 `parentRefs[].sectionName` selector binds to"
35241 );
35242 }
35243
35244 #[test]
35245 fn gateway_api_default_http_listener_name_carries_dns_1123_label_shape() {
35246 // Cross-axis invariant: K8s Gateway API v1 `Listener.name` is
35247 // `SectionName`-typed — a required DNS-1123 label unique within
35248 // the parent Gateway's listener list. Pinning the shape here
35249 // means a future rebrand on the canonical lift can't silently
35250 // land a malformed listener-name identifier (empty, uppercase,
35251 // whitespace, `.` / `_` / non-alphanumeric characters, an
35252 // overlong string past the DNS-1123 label ceiling) that the
35253 // apiserver-side Gateway API CRD schema validator would reject
35254 // far from the rebrand commit's source. The predicate the
35255 // `caixa-mesh::gateway_routes` per-listener-name emitter never
35256 // consults directly (the value is a const — no author input
35257 // reaches this axis today) gets consulted here so any future
35258 // rebrand routes through the same DNS-1123-label admission
35259 // grammar every K8s CRD `name`-shaped axis carries. Peer to
35260 // `default_gateway_class_name_is_a_valid_dns_1123_label` on
35261 // the sibling per-Gateway `gatewayClassName` scalar-axis pin
35262 // and `default_namespace_is_a_valid_dns_1123_label` on the
35263 // canonical-K8s-namespace lifted scalar — every substrate-side
35264 // K8s-CRD-name-shaped lift carries the same DNS-1123 label
35265 // admission-grammar cross-axis invariant.
35266 assert!(
35267 is_dns_1123_label(GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME).is_ok(),
35268 "GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME ({GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME:?}) \
35269 must be a valid DNS-1123 label — K8s Gateway API v1 `Listener.name` is \
35270 `SectionName`-typed and the apiserver-side CRD schema validator refuses \
35271 any other shape"
35272 );
35273 }
35274
35275 #[test]
35276 fn gateway_api_default_http_route_path_pins_canonical_root_literal() {
35277 // The canonical-constant arm — pins
35278 // [`GATEWAY_API_DEFAULT_HTTP_ROUTE_PATH`] at the verbatim `"/"`
35279 // literal the sole `caixa-mesh::gateway_routes` per-Aplicacao
35280 // `HTTPRoute` empty-`:entrada :paths` catch-all URL-path
35281 // resolver reads from. Peer with the
35282 // [`GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME`]-pins-`"http"` and
35283 // [`GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT`]-pins-`80`
35284 // disciplines on the sibling per-listener substrate-canonical
35285 // scalar-value axes: all three are the Aplicacao-side
35286 // substrate-canonical scalar-value pins the sole per-Aplicacao
35287 // Gateway API v1 CRD emitter reaches for, so a future refactor
35288 // that drifts any one constant out from under the emitter
35289 // surfaces here ahead of any per-renderer HTTPRoute emission.
35290 // The literal value is the K8s Gateway API v1 canonical
35291 // catch-all shape: `PathPrefix "/"` — the upstream docs at
35292 // <https://gateway-api.sigs.k8s.io/api-types/httproute/#path-based-routing>
35293 // pin the bare-root byte-string as the "match anything the
35294 // listener admits" idiom every gateway-class controller treats
35295 // as the equivalent of "no path predicate".
35296 assert_eq!(
35297 GATEWAY_API_DEFAULT_HTTP_ROUTE_PATH, "/",
35298 "canonical Gateway API v1 HTTPRoute catch-all path literal must remain \
35299 `\"/\"` verbatim — this is the value the caixa-mesh HTTPRoute emitter \
35300 renders whenever the typed `:entrada :paths` list is empty and every \
35301 gateway-class controller (Cilium's Envoy, Envoy Gateway, Istio Gateway) \
35302 treats as the canonical `PathPrefix` catch-all"
35303 );
35304 }
35305
35306 #[test]
35307 fn gateway_api_default_http_route_path_carries_valid_gateway_api_http_path_shape() {
35308 // Cross-axis invariant: K8s Gateway API v1
35309 // `HTTPPathMatch.value` is admitted by the apiserver-side CRD
35310 // schema regex the substrate mirrors in the shared
35311 // [`is_gateway_api_http_path`] predicate — the same admission
35312 // grammar every author-supplied [`crate::aplicacao::Entrada`]
35313 // `:paths` entry clears at typed-validate time. Pinning the
35314 // shape here means a future rebrand on the canonical lift can't
35315 // silently land a malformed catch-all URL-path scalar (empty,
35316 // no leading `/`, overlong past the K8s Gateway API v1
35317 // `HTTPPathMatch.value` ceiling, `..`-segment-bearing, ASCII-
35318 // control-bearing, non-ASCII-bearing) that the apiserver-side
35319 // Gateway API CRD schema validator would reject far from the
35320 // rebrand commit's source. The paired
35321 // [`caixa_mesh::gateway_routes`] emitter never consults the
35322 // predicate directly (the catch-all value is a const — no
35323 // author input reaches this axis today) so consulting it here
35324 // means any future rebrand routes through the same
35325 // admission-grammar the peer author-side
35326 // `:entrada :paths` slot's `AplicacaoSpec::validate` gate
35327 // carries. Peer to
35328 // `gateway_api_default_http_listener_name_carries_dns_1123_label_shape`
35329 // on the sibling per-listener name-scalar cross-axis invariant
35330 // — every substrate-side Gateway-API-scalar lift carries the
35331 // matching per-axis admission-grammar cross-axis pin.
35332 assert!(
35333 is_gateway_api_http_path(GATEWAY_API_DEFAULT_HTTP_ROUTE_PATH).is_ok(),
35334 "GATEWAY_API_DEFAULT_HTTP_ROUTE_PATH ({GATEWAY_API_DEFAULT_HTTP_ROUTE_PATH:?}) \
35335 must clear the shared HTTP-path admission grammar — K8s Gateway API v1 \
35336 `HTTPPathMatch.value` is CRD-schema-regex-validated and the apiserver-side \
35337 schema validator refuses any other shape at apply time"
35338 );
35339 }
35340
35341 // ── insert_first_seen ───────────────────────────────────────────────
35342
35343 #[derive(Debug, PartialEq, Eq)]
35344 enum DupTestErr {
35345 Dup(&'static str),
35346 }
35347
35348 #[test]
35349 fn insert_first_seen_accepts_distinct_keys_without_firing_closure() {
35350 // The happy path — every distinct key returns `Ok(())` and the
35351 // caller's `on_duplicate` closure is never invoked. Pins the
35352 // `HashSet::insert`-returning-`true`-on-first-insertion contract
35353 // the ten consumer sites (`:membros`, `:placement :clusters`,
35354 // `:entrada :paths`, `:contratos`, `:children`, `:deps`,
35355 // `:deps-dev`, `:etiquetas`, `:autores`, `:caracteristicas`,
35356 // code-paths) each rely on — a future refactor that flips the
35357 // sense of the delegated `insert` return would surface here
35358 // ahead of every per-consumer duplicate arm silently mis-firing
35359 // on distinct keys.
35360 let mut seen: std::collections::HashSet<&str> = std::collections::HashSet::new();
35361 for key in ["cart", "catalog", "payment"] {
35362 assert_eq!(
35363 insert_first_seen::<&str, DupTestErr, _>(&mut seen, key, || DupTestErr::Dup(
35364 "must not fire"
35365 )),
35366 Ok(()),
35367 "first insertion of {key:?} must return Ok(())",
35368 );
35369 }
35370 assert_eq!(seen.len(), 3, "every distinct key must land in the set");
35371 }
35372
35373 #[test]
35374 fn insert_first_seen_surfaces_caller_shaped_error_on_second_insertion() {
35375 // The duplicate arm — the second occurrence of any key surfaces
35376 // the caller's `on_duplicate` return verbatim. Pins the
35377 // "declaration-order-preserving first-collision" discipline every
35378 // peer `Duplicate*` variant documents: the first colliding entry
35379 // reports, not the last. Same shape the ten consumer sites'
35380 // `*_duplicate_diagnostic_names_second_collision` posture tests
35381 // pin at the caller layer; this lift makes the sequencing a
35382 // property of the helper, not a per-call-site convention.
35383 let mut seen: std::collections::HashSet<&str> = std::collections::HashSet::new();
35384 assert_eq!(
35385 insert_first_seen::<&str, DupTestErr, _>(&mut seen, "cart", || DupTestErr::Dup(
35386 "first"
35387 )),
35388 Ok(()),
35389 "first insertion must Ok",
35390 );
35391 assert_eq!(
35392 insert_first_seen::<&str, DupTestErr, _>(&mut seen, "cart", || DupTestErr::Dup(
35393 "second"
35394 )),
35395 Err(DupTestErr::Dup("second")),
35396 "second insertion must fire the caller's closure with its own tag",
35397 );
35398 }
35399
35400 #[test]
35401 fn insert_first_seen_generic_over_tuple_key_used_by_contratos_gate() {
35402 // The [`crate::AplicacaoSpec::validate`] `:contratos` gate carries
35403 // a six-tuple typed-edge identity key
35404 // (`(de, para, wit, endpoint, subject, slot)`) — the only non-
35405 // `&str` key shape in the crate's per-list uniqueness set. Pin
35406 // the generic-over-`K` contract here so a future refactor that
35407 // narrows the helper to `&str`-only keys (a hypothetical
35408 // `HashSet<&str>`-specialized rewrite) surfaces at this pin
35409 // rather than as a compile error at the sole tuple-carrying
35410 // consumer. The tuple set here mirrors the shape
35411 // `ContratoIdentity` carries.
35412 let mut seen: std::collections::HashSet<(&str, &str, &str, Option<&str>)> =
35413 std::collections::HashSet::new();
35414 let key = ("cart", "catalog", "wasi:http/proxy", Some("/products"));
35415 assert_eq!(
35416 insert_first_seen::<_, DupTestErr, _>(&mut seen, key, || DupTestErr::Dup(
35417 "must not fire"
35418 )),
35419 Ok(()),
35420 );
35421 assert_eq!(
35422 insert_first_seen::<_, DupTestErr, _>(&mut seen, key, || DupTestErr::Dup("collision")),
35423 Err(DupTestErr::Dup("collision")),
35424 "identical tuple key on second insertion must fire the duplicate arm",
35425 );
35426 }
35427
35428 // ── assert_str_reexport_identity ──────────────────────────────────
35429
35430 #[test]
35431 fn assert_str_reexport_identity_accepts_same_static_allocation() {
35432 // Positive path — passing the same `&'static str` twice (the
35433 // shape a `pub use caixa_core::X;` re-export produces at every
35434 // consumer site) must not panic. This is the ~75-caller-site
35435 // happy path that the lifted test-side pin gate collapses onto.
35436 // The compiler-interned literal `"KUBE_KEY_SPEC"` reaches this
35437 // helper twice through the same `&'static` allocation, so
35438 // `std::ptr::eq(a.as_ptr(), b.as_ptr())` returns true and the
35439 // second `assert!` arm passes without firing.
35440 const CANONICAL: &str = "canonical-value";
35441 assert_str_reexport_identity("CANONICAL_UNDER_TEST", CANONICAL, CANONICAL);
35442 }
35443
35444 #[test]
35445 #[should_panic(
35446 expected = "SIBLING_UNDER_TEST must be a re-export of caixa_core::SIBLING_UNDER_TEST"
35447 )]
35448 fn assert_str_reexport_identity_rejects_sibling_allocation_with_same_bytes() {
35449 // Negative path — passing two byte-equal `&'static str`s whose
35450 // underlying allocations differ (the shape a sibling `pub const
35451 // X: &str = "…"` at a renderer crate produces, silently carrying
35452 // the same bytes but its own `&'static` allocation) must panic
35453 // on the [`std::ptr::eq`] arm, naming the offending re-export.
35454 // Reproduces the canonical drift footgun the lift closes: byte-
35455 // equality via [`assert_eq!`] alone silently admits the drift
35456 // — the two strings are equal — but the allocation-identity
35457 // arm catches it structurally. Uses [`String::leak`] to
35458 // materialize a fresh `&'static str` allocation carrying the
35459 // same bytes as the compiler-interned canonical literal, so
35460 // the two share bytes but differ in allocation.
35461 const CANONICAL: &str = "canonical-value";
35462 let sibling: &'static str = String::from("canonical-value").leak();
35463 // Sanity — the sibling and canonical share bytes …
35464 assert_eq!(sibling, CANONICAL);
35465 // … but must live at distinct `&'static` allocations for this
35466 // negative path to fire on the identity arm rather than
35467 // silently pass on the equality arm.
35468 assert!(!std::ptr::eq(sibling.as_ptr(), CANONICAL.as_ptr()));
35469 assert_str_reexport_identity("SIBLING_UNDER_TEST", sibling, CANONICAL);
35470 }
35471
35472 #[test]
35473 #[should_panic(expected = "DRIFTED_UNDER_TEST must byte-equal caixa_core::DRIFTED_UNDER_TEST")]
35474 fn assert_str_reexport_identity_rejects_bytes_drift_before_identity_arm() {
35475 // Ordering pin — when the two byte-strings differ, the
35476 // [`assert_eq!`] arm must fire *before* the [`std::ptr::eq`]
35477 // identity arm reaches for `.as_ptr()`. Pins the arm sequencing
35478 // so a future refactor that flipped the two arms (identity
35479 // first, byte-equality second) would surface here rather than
35480 // report the wrong diagnostic against a drifted canonical
35481 // (the byte-equality diagnostic self-locates the value drift;
35482 // the identity diagnostic self-locates the allocation drift —
35483 // reporting the identity arm on a value-drifted pair points
35484 // the reader at the wrong failure class). Same discipline as
35485 // the peer `require_positive_canonical_bounded_duration`
35486 // three-arm-ordering pin above.
35487 const CANONICAL: &str = "canonical-value";
35488 const DRIFTED: &str = "drifted-value";
35489 assert_str_reexport_identity("DRIFTED_UNDER_TEST", DRIFTED, CANONICAL);
35490 }
35491
35492 #[test]
35493 fn computeunit_spec_key_module_pins_canonical_value() {
35494 // Pin the actual byte-string so a typo in this lift can't silently
35495 // rebrand the `wasm.pleme.io/v1alpha1/ComputeUnit` CRD per-CR
35496 // `spec.module` sub-block key both caixa-flux and caixa-helm
35497 // navigate to reach the per-Servico wasm-component reference the
35498 // M2.5 wasm-engine instantiator loads at Servico bring-up. The
35499 // value is part of the cluster-side contract with the
35500 // `pleme-computeunit` library chart's per-values module-source
35501 // routing + the `caixa-operator` `ComputeUnit` CR admission
35502 // webhook's per-CR module-reference resolver; changing it is a
35503 // coordinated ComputeUnit-CRD schema migration alongside the
35504 // upstream substrate release, not an incidental edit. Peer to
35505 // `default_namespace_pins_canonical_value` /
35506 // `helm_values_yaml_filename_pins_canonical_value` /
35507 // `helm_chart_yaml_filename_pins_canonical_value` on the sibling
35508 // canonical-substrate-schema-key axes.
35509 assert_eq!(COMPUTEUNIT_SPEC_KEY_MODULE, "module");
35510 }
35511
35512 #[test]
35513 fn computeunit_spec_key_trigger_pins_canonical_value() {
35514 // Peer to `computeunit_spec_key_module_pins_canonical_value` on
35515 // the same ComputeUnit-CRD per-`spec.*` sub-block axis — pins
35516 // the per-CR invocation-shape sub-block key every
35517 // `pleme-computeunit`-library-chart-driven per-Servico
35518 // `trigger.service.port` / `trigger.service.paths` /
35519 // `trigger.service.breathability` values-block route reads back.
35520 assert_eq!(COMPUTEUNIT_SPEC_KEY_TRIGGER, "trigger");
35521 }
35522
35523 #[test]
35524 fn computeunit_spec_key_capabilities_pins_canonical_value() {
35525 // Peer to `computeunit_spec_key_module_pins_canonical_value` and
35526 // `computeunit_spec_key_trigger_pins_canonical_value` on the same
35527 // ComputeUnit-CRD per-`spec.*` sub-block axis — pins the per-CR
35528 // WASI-capability-token-list sub-block key the M2.5 wasm-engine
35529 // instantiator reads to bind the per-component capability set
35530 // (WASI-preview-2 preview-interfaces per the WIT Component Model)
35531 // at Servico bring-up.
35532 assert_eq!(COMPUTEUNIT_SPEC_KEY_CAPABILITIES, "capabilities");
35533 }
35534
35535 #[test]
35536 fn computeunit_spec_keys_carry_lowercase_shape() {
35537 // Cross-axis invariant: every `wasm.pleme.io/v1alpha1/ComputeUnit`
35538 // CRD per-`spec.*` sub-block key is all-ASCII-lowercase
35539 // throughout — the ComputeUnit CRD's schema convention on the
35540 // per-`spec.*` sub-block axis. A drifted UpperCamelCase /
35541 // hyphenated variant (`"Module"` / `"module-source"` /
35542 // `"Trigger"` / `"Capabilities"` — the OpenAPI-CRD-schema
35543 // canonical-form footgun the peer `KUBE_KEY_*` axes share) would
35544 // land the emit-side key outside the CRD's admitted per-sub-
35545 // block set and the `caixa-operator` admission webhook would
35546 // silently drop the per-Servico wasm-runtime binding — the
35547 // Servico pods would come up under the library-chart defaults
35548 // (no module bound, no trigger bound, no capability set)
35549 // instead of the caixa.lisp's declared per-`:servicos` axis.
35550 // Same all-ASCII-lowercase shape gate as the peer M2 typed-slot
35551 // camelCase-key axes ([`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] —
35552 // the compound-word slot [`M2_KEY_UPGRADE_FROM`] adds a
35553 // camelHump per its `#[serde(rename_all = "camelCase")]`-derived
35554 // shape, but the leading-word gate is the same).
35555 for k in [
35556 COMPUTEUNIT_SPEC_KEY_MODULE,
35557 COMPUTEUNIT_SPEC_KEY_TRIGGER,
35558 COMPUTEUNIT_SPEC_KEY_CAPABILITIES,
35559 ] {
35560 assert!(
35561 k.bytes().all(|b| b.is_ascii_lowercase()),
35562 "ComputeUnit CRD per-`spec.*` sub-block key {k:?} must be \
35563 all-ASCII-lowercase per the CRD schema convention"
35564 );
35565 }
35566 }
35567
35568 #[test]
35569 fn computeunit_spec_keys_appear_verbatim_in_sample_computeunit_yaml() {
35570 // Round-trip pin: the exact byte-strings the three lifted
35571 // constants carry appear verbatim as the top-level `spec.*`
35572 // sub-block keys of a canonical in-tree `ComputeUnit` YAML —
35573 // the same shape [`caixa_flux::programs_yaml_entry`] and
35574 // [`caixa_helm::build_values_yaml`] consume via
35575 // `serde_yaml::from_str`. Pins the const-to-schema round-trip
35576 // so a future ComputeUnit-CRD schema rebrand (a `binary:` /
35577 // `component:` / `invoke:` / `caps:` / `spec.wasm.*` axis
35578 // rename the ABSORPTION-ROADMAP.md M4-M5 trajectory names)
35579 // surfaces here as a build error rather than as a silent
35580 // per-Servico wasm-runtime-binding drop at cluster-apply time.
35581 let cu: serde_yaml::Value = serde_yaml::from_str(
35582 r#"
35583apiVersion: wasm.pleme.io/v1alpha1
35584kind: ComputeUnit
35585metadata:
35586 name: hello-rio
35587spec:
35588 module:
35589 source: oci://ghcr.io/pleme-io/hello-rio:v0.1.0
35590 trigger:
35591 service:
35592 port: 8080
35593 paths: ["/"]
35594 capabilities:
35595 - env
35596"#,
35597 )
35598 .unwrap();
35599 let spec = cu.get(KUBE_KEY_SPEC).expect("spec key present");
35600 assert!(
35601 spec.get(COMPUTEUNIT_SPEC_KEY_MODULE).is_some(),
35602 "spec.{COMPUTEUNIT_SPEC_KEY_MODULE} sub-block must be present"
35603 );
35604 assert!(
35605 spec.get(COMPUTEUNIT_SPEC_KEY_TRIGGER).is_some(),
35606 "spec.{COMPUTEUNIT_SPEC_KEY_TRIGGER} sub-block must be present"
35607 );
35608 assert!(
35609 spec.get(COMPUTEUNIT_SPEC_KEY_CAPABILITIES).is_some(),
35610 "spec.{COMPUTEUNIT_SPEC_KEY_CAPABILITIES} sub-block must be present"
35611 );
35612 // Nested `spec.module.source` leaf-scalar sub-block: every
35613 // rendered ComputeUnit YAML declares the wasm-component
35614 // reference under this leaf, and every downstream
35615 // `programs[].module.source` readback the
35616 // [`caixa_flux::programs_yaml_entry`] round-trip pins reaches
35617 // for the same `&'static str`. Peer to the top-level
35618 // `spec.{module,trigger,capabilities}` presence assertions
35619 // above — extends the round-trip pin one level deeper onto
35620 // the module-block's leaf reference-value axis.
35621 let module = spec
35622 .get(COMPUTEUNIT_SPEC_KEY_MODULE)
35623 .expect("spec.module block present");
35624 assert!(
35625 module.get(COMPUTEUNIT_MODULE_KEY_SOURCE).is_some(),
35626 "spec.{COMPUTEUNIT_SPEC_KEY_MODULE}.{COMPUTEUNIT_MODULE_KEY_SOURCE} \
35627 leaf-scalar sub-block must be present"
35628 );
35629 assert_eq!(
35630 module
35631 .get(COMPUTEUNIT_MODULE_KEY_SOURCE)
35632 .and_then(|s| s.as_str()),
35633 Some("oci://ghcr.io/pleme-io/hello-rio:v0.1.0"),
35634 "the ComputeUnit CRD per-`module.source` axis carries the wasm-\
35635 component OCI/git reference verbatim"
35636 );
35637 }
35638
35639 #[test]
35640 fn computeunit_module_key_source_pins_canonical_value() {
35641 // Peer to `computeunit_spec_key_module_pins_canonical_value` on
35642 // the nested `spec.module.*` sub-block axis — pins the per-CR
35643 // wasm-component-reference leaf-scalar key every
35644 // [`caixa_flux::programs_yaml_entry`] round-trip navigator and
35645 // every [`caixa_flux::upsert_into_programs_yaml`] /
35646 // [`caixa_flux::upsert_into_helmrelease_programs`] cross-
35647 // upsert readback resolves under the parent
35648 // `COMPUTEUNIT_SPEC_KEY_MODULE`. Changing this value is a
35649 // coordinated ComputeUnit-CRD schema migration alongside the
35650 // `pleme-computeunit` library chart's per-values module-source
35651 // routing + the `caixa-operator` `ComputeUnit` CR admission
35652 // webhook's per-CR module-reference resolver, not an
35653 // incidental edit.
35654 assert_eq!(COMPUTEUNIT_MODULE_KEY_SOURCE, "source");
35655 }
35656
35657 #[test]
35658 fn computeunit_module_key_source_carries_lowercase_shape() {
35659 // Cross-axis invariant: the nested `spec.module.*` leaf-scalar
35660 // sub-block key is all-ASCII-lowercase throughout — the
35661 // ComputeUnit CRD's schema convention on the per-`spec.module.*`
35662 // leaf axis, same as the top-level per-`spec.*` sub-block
35663 // axis the sibling `COMPUTEUNIT_SPEC_KEY_*` peers gate.
35664 // A drifted UpperCamelCase / hyphenated variant (`"Source"` /
35665 // `"module-source"` / `"src"` — the OpenAPI-CRD-schema
35666 // canonical-form footgun the peer `KUBE_KEY_*` axes share)
35667 // would land the emit-side key outside the CRD's admitted
35668 // per-`module.*` set and the `caixa-operator` admission
35669 // webhook would silently drop the per-Servico wasm-module
35670 // reference — the Servico pods would come up under the
35671 // library-chart defaults (no module bound) instead of the
35672 // caixa.lisp's declared per-`:servicos` axis. Same all-ASCII-
35673 // lowercase shape gate as the peer `COMPUTEUNIT_SPEC_KEY_*`
35674 // top-level axes.
35675 assert!(
35676 COMPUTEUNIT_MODULE_KEY_SOURCE
35677 .bytes()
35678 .all(|b| b.is_ascii_lowercase()),
35679 "ComputeUnit CRD per-`spec.module.*` leaf-scalar sub-block key \
35680 {COMPUTEUNIT_MODULE_KEY_SOURCE:?} must be all-ASCII-lowercase \
35681 per the CRD schema convention"
35682 );
35683 }
35684
35685 #[test]
35686 fn mapping_ext_insert_str_key_promotes_key_to_yaml_string() {
35687 // The trait method promotes an arbitrary `&str` key to
35688 // `Value::String(key.to_string())` — pin the promotion so a
35689 // future refactor that reaches for a different `Value` variant
35690 // for the key (e.g. `Value::Tagged`) is a compile-visible break,
35691 // not a silent per-consumer regression at the K8s-artifact-emit
35692 // surface.
35693 let mut m = serde_yaml::Mapping::new();
35694 let prior = m.insert_str_key("spec", serde_yaml::Value::Bool(true));
35695 assert!(
35696 prior.is_none(),
35697 "insert_str_key returns None on first insertion, mirroring \
35698 serde_yaml::Mapping::insert"
35699 );
35700 // Key is exactly the `Value::String` promotion of the input.
35701 let got = m
35702 .get(serde_yaml::Value::String("spec".to_string()))
35703 .expect("inserted key is present under Value::String promotion");
35704 assert_eq!(
35705 got,
35706 &serde_yaml::Value::Bool(true),
35707 "insert_str_key routes value verbatim to the underlying \
35708 serde_yaml::Mapping::insert"
35709 );
35710 }
35711
35712 #[test]
35713 fn mapping_ext_insert_str_key_returns_prior_value_on_replace() {
35714 // The trait method mirrors [`serde_yaml::Mapping::insert`]'s
35715 // return contract: the prior value at that key, or `None` if
35716 // absent. Pin the replace-returns-prior semantic so a future
35717 // refactor that swaps to a `HashMap::entry`-style flow doesn't
35718 // silently drop the prior-value handoff downstream consumers may
35719 // reach for (the M4 per-`:politicas` overlay merger, the future
35720 // `feira app deploy` idempotent-write dry-run comparator).
35721 let mut m = serde_yaml::Mapping::new();
35722 m.insert_str_key("kind", serde_yaml::Value::String("Gateway".into()));
35723 let prior = m.insert_str_key("kind", serde_yaml::Value::String("HTTPRoute".into()));
35724 assert_eq!(
35725 prior,
35726 Some(serde_yaml::Value::String("Gateway".into())),
35727 "insert_str_key returns the prior value when replacing an existing key"
35728 );
35729 let got = m
35730 .get(serde_yaml::Value::String("kind".to_string()))
35731 .expect("key is still present after replace");
35732 assert_eq!(
35733 got,
35734 &serde_yaml::Value::String("HTTPRoute".into()),
35735 "replaced value is now the most-recently-inserted one"
35736 );
35737 }
35738
35739 #[test]
35740 fn mapping_ext_insert_str_key_matches_hand_written_promotion() {
35741 // Cross-check the trait method against the hand-written
35742 // `mapping.insert(Value::String(key.into()), value)` shape the
35743 // ~48 lifted call sites previously carried. A drift between the
35744 // trait method's promotion and the inline promotion the prior
35745 // call sites used would silently emit a different YAML mapping
35746 // (a differently-quoted key, a different `Value` variant) at
35747 // every routed consumer — pin the equivalence so the trait
35748 // remains a drop-in replacement.
35749 let mut via_trait = serde_yaml::Mapping::new();
35750 via_trait.insert_str_key(KUBE_KEY_KIND, serde_yaml::Value::String("Gateway".into()));
35751
35752 let mut via_inline = serde_yaml::Mapping::new();
35753 via_inline.insert(
35754 serde_yaml::Value::String(KUBE_KEY_KIND.into()),
35755 serde_yaml::Value::String("Gateway".into()),
35756 );
35757
35758 assert_eq!(
35759 via_trait, via_inline,
35760 "insert_str_key(KEY, V) must byte-equal \
35761 insert(Value::String(KEY.into()), V) — otherwise the \
35762 ~48 routed consumer sites drift silently at emit time"
35763 );
35764 }
35765
35766 #[test]
35767 fn mapping_get_bare_str_key_byte_equals_value_string_wrapped_form() {
35768 // The read-side twin of the `insert_str_key`-vs-hand-written pin.
35769 // `serde_yaml::Mapping::get<I: Index>` accepts any `I: Index`;
35770 // the crate ships `impl Index for str` (routing through a
35771 // no-allocation `HashLikeValue(&str)` bucket lookup) and
35772 // `impl Index for Value` (matching the `Value::String(_)`
35773 // key verbatim). The ~78 test-side probes across `caixa-mesh`,
35774 // `caixa-flux`, and `caixa-core::render` that previously spelled
35775 // out `.get(serde_yaml::Value::String(<KEY>.into()))` were
35776 // swept onto the shorter `.get(<KEY>)` form because the two
35777 // must resolve to the same bucket for the sweep to be a
35778 // drop-in. Pin the equivalence — the `HashLikeValue(&str)`
35779 // hash must byte-equal the `Value::String(String)` hash so
35780 // the two paths agree on `get`, `contains_key`, and the
35781 // absence path (`None` when the key is missing) — otherwise
35782 // a future `serde_yaml` upgrade could silently divert every
35783 // swept probe past the value the emitter inserted.
35784 let mut m = serde_yaml::Mapping::new();
35785 m.insert_str_key(KUBE_KEY_KIND, serde_yaml::Value::String("Gateway".into()));
35786 // Present-key path: both forms find the same value.
35787 assert_eq!(
35788 m.get(KUBE_KEY_KIND),
35789 m.get(serde_yaml::Value::String(KUBE_KEY_KIND.into())),
35790 "mapping.get(<KEY>) must byte-equal \
35791 mapping.get(Value::String(<KEY>.into())) — otherwise the \
35792 ~78 swept test-side probes drift silently past the value \
35793 the emitter inserted under the promoted Value::String key"
35794 );
35795 // Absent-key path: both forms return None.
35796 assert_eq!(
35797 m.get(KUBE_KEY_SPEC),
35798 m.get(serde_yaml::Value::String(KUBE_KEY_SPEC.into())),
35799 "absent-key lookup via bare-&str must byte-equal absent-key \
35800 lookup via Value::String — both must return None so the \
35801 swept `assert!(_.get(K).is_none())` shape stays load-bearing"
35802 );
35803 // contains_key parity: both forms agree on present + absent.
35804 assert_eq!(
35805 m.contains_key(KUBE_KEY_KIND),
35806 m.contains_key(serde_yaml::Value::String(KUBE_KEY_KIND.into())),
35807 "mapping.contains_key(<KEY>) must byte-equal \
35808 mapping.contains_key(Value::String(<KEY>.into())) — \
35809 otherwise the swept `assert!(_.contains_key(K))` shape \
35810 disagrees with the emitter's `insert_str_key` promotion"
35811 );
35812 assert_eq!(
35813 m.contains_key(KUBE_KEY_SPEC),
35814 m.contains_key(serde_yaml::Value::String(KUBE_KEY_SPEC.into())),
35815 "absent-key contains_key via bare-&str must byte-equal \
35816 absent-key contains_key via Value::String"
35817 );
35818 }
35819
35820 #[test]
35821 fn mapping_get_mut_bare_str_key_byte_equals_value_string_wrapped_form() {
35822 // The mutation-path twin of the read-side pin above.
35823 // `serde_yaml::Mapping::get_mut<I: Index>` accepts any
35824 // `I: Index` — the crate ships `impl Index for str` (routing
35825 // through the same no-allocation `HashLikeValue(&str)` bucket
35826 // lookup the read-side `get` / `contains_key` sweep landed on
35827 // in 0e84fb9) and `impl Index for Value` (matching the
35828 // `Value::String(_)` key verbatim). Until this pin landed the
35829 // sole production `.get_mut(serde_yaml::Value::String(<KEY>.into()))`
35830 // probe — [`caixa_flux::upsert_into_helmrelease_programs`]'s
35831 // `root.get_mut(…)` HelmRelease-side spec-mutate at
35832 // `caixa-flux/src/lib.rs:845` (which the sibling
35833 // `kube_key_spec_re_export_points_at_caixa_core_canonical`
35834 // pinning test's docstring already described in the shorter
35835 // `root.get_mut("spec")` form the 0e84fb9 read-side sweep
35836 // landed elsewhere on) — carried the verbose `Value::String`-
35837 // wrapped shape as the last stray hold-out on the `get_mut`
35838 // axis. The sweep swaps it onto the bare-`&str` form, matching
35839 // the ~78 read-side probes 0e84fb9 already swept and the
35840 // in-file `kube_key_spec_re_export_points_at_caixa_core_canonical`
35841 // docstring's canonical description. Pin the equivalence — the
35842 // `HashLikeValue(&str)` hash must byte-equal the
35843 // `Value::String(String)` hash so the two paths agree on both
35844 // the present-key path (returns `Some(&mut _)` at the same
35845 // slot) and the absent-key path (returns `None` when the key
35846 // is missing) — otherwise a future `serde_yaml` upgrade could
35847 // silently divert the writer-side upsert past the value the
35848 // emitter previously mutated. Peer to the read-side
35849 // [`mapping_get_bare_str_key_byte_equals_value_string_wrapped_form`]
35850 // pin on the sibling `get` / `contains_key` axes; together the
35851 // two pins pin every `Index`-polymorphic probe axis the
35852 // caixa-flux upsert path walks.
35853 let mut m = serde_yaml::Mapping::new();
35854 m.insert_str_key(KUBE_KEY_KIND, serde_yaml::Value::String("Gateway".into()));
35855 // Present-key path: both forms find the same slot.
35856 // Cross-check by mutating through the bare-&str path and
35857 // observing the mutation via the Value::String path (and vice
35858 // versa) — anything short of exact bucket-equality would
35859 // silently split the two probes onto different slots.
35860 {
35861 let via_bare = m
35862 .get_mut(KUBE_KEY_KIND)
35863 .expect("present key must resolve via bare-&str");
35864 *via_bare = serde_yaml::Value::String("HTTPRoute".into());
35865 }
35866 assert_eq!(
35867 m.get(serde_yaml::Value::String(KUBE_KEY_KIND.into())),
35868 Some(&serde_yaml::Value::String("HTTPRoute".into())),
35869 "mutation via mapping.get_mut(<KEY>) must be visible via \
35870 mapping.get(Value::String(<KEY>.into())) — otherwise the \
35871 swept `get_mut` writer-side probe drifts past the value \
35872 the emitter reads through the promoted Value::String key"
35873 );
35874 {
35875 let via_wrapped = m
35876 .get_mut(serde_yaml::Value::String(KUBE_KEY_KIND.into()))
35877 .expect("present key must also resolve via Value::String");
35878 *via_wrapped = serde_yaml::Value::String("Gateway".into());
35879 }
35880 assert_eq!(
35881 m.get(KUBE_KEY_KIND),
35882 Some(&serde_yaml::Value::String("Gateway".into())),
35883 "mutation via mapping.get_mut(Value::String(<KEY>.into())) \
35884 must be visible via mapping.get(<KEY>) — the two paths \
35885 address the same bucket in both directions"
35886 );
35887 // Absent-key path: both forms return None so the sole swept
35888 // `.get_mut(<KEY>).ok_or(Error::MissingField(<KEY>))` shape
35889 // stays load-bearing.
35890 assert!(
35891 m.get_mut(KUBE_KEY_SPEC).is_none(),
35892 "absent-key mapping.get_mut(<KEY>) must return None"
35893 );
35894 assert!(
35895 m.get_mut(serde_yaml::Value::String(KUBE_KEY_SPEC.into()))
35896 .is_none(),
35897 "absent-key mapping.get_mut(Value::String(<KEY>.into())) \
35898 must also return None — the two forms must agree on \
35899 absence so the swept `.ok_or(Error::MissingField(<KEY>))` \
35900 diagnostic still fires on a missing spec block"
35901 );
35902 }
35903
35904 #[test]
35905 fn mapping_ext_insert_string_promotes_value_to_yaml_string() {
35906 // The trait method promotes an arbitrary `Into<String>` value
35907 // to `Value::String(value.into())` — pin the promotion so a
35908 // future refactor that reaches for a different `Value` variant
35909 // for the string-scalar payload (e.g. `Value::Tagged` under a
35910 // K8s Server-Side-Apply typed-field-ownership axis rebrand) is
35911 // a compile-visible break, not a silent per-consumer regression
35912 // at the K8s-artifact-emit surface. Peer with
35913 // [`mapping_ext_insert_str_key_promotes_key_to_yaml_string`] on
35914 // the sibling `insert_str_key` primitive's key-promotion pin.
35915 let mut m = serde_yaml::Mapping::new();
35916 let prior = m.insert_string("kind", "Gateway");
35917 assert!(
35918 prior.is_none(),
35919 "insert_string returns None on first insertion, mirroring \
35920 serde_yaml::Mapping::insert"
35921 );
35922 let got = m
35923 .get("kind")
35924 .expect("inserted key is present under Value::String promotion");
35925 assert_eq!(
35926 got,
35927 &serde_yaml::Value::String("Gateway".into()),
35928 "insert_string routes value verbatim through Value::String \
35929 promotion"
35930 );
35931 }
35932
35933 #[test]
35934 fn mapping_ext_insert_string_returns_prior_value_on_replace() {
35935 // The trait method mirrors [`serde_yaml::Mapping::insert`]'s
35936 // return contract: the prior value at that key, or `None` if
35937 // absent. Pin the replace-returns-prior semantic so a future
35938 // refactor that swaps to a `HashMap::entry`-style flow doesn't
35939 // silently drop the prior-value handoff downstream consumers
35940 // may reach for. Peer with
35941 // [`mapping_ext_insert_str_key_returns_prior_value_on_replace`]
35942 // on the sibling `insert_str_key` primitive's replace-semantics
35943 // pin.
35944 let mut m = serde_yaml::Mapping::new();
35945 m.insert_string(KUBE_KEY_KIND, "Gateway");
35946 let prior = m.insert_string(KUBE_KEY_KIND, "HTTPRoute");
35947 assert_eq!(
35948 prior,
35949 Some(serde_yaml::Value::String("Gateway".into())),
35950 "insert_string returns the prior value when replacing an \
35951 existing key"
35952 );
35953 let got = m
35954 .get(KUBE_KEY_KIND)
35955 .expect("key is still present after replace");
35956 assert_eq!(
35957 got,
35958 &serde_yaml::Value::String("HTTPRoute".into()),
35959 "replaced value is now the most-recently-inserted one"
35960 );
35961 }
35962
35963 #[test]
35964 fn mapping_ext_insert_string_matches_hand_written_promotion() {
35965 // Cross-check the trait method against the hand-written
35966 // `mapping.insert_str_key(KEY, Value::String(V.into()))` shape
35967 // the ~17 lifted call sites previously carried. A drift between
35968 // the trait method's promotion and the inline promotion would
35969 // silently emit a different YAML mapping (a differently-quoted
35970 // scalar, a different `Value` variant) at every routed
35971 // consumer — pin the equivalence so the trait remains a drop-in
35972 // replacement. Also cross-checks that all three input shapes
35973 // (`&'static str` → `.into()`, `String` → `.clone()` /
35974 // `.to_string()`, integer → `.to_string()`) converge on the same
35975 // `Value::String` promotion, since the ~17 call sites cover all
35976 // three input flavors.
35977 let mut via_trait = serde_yaml::Mapping::new();
35978 via_trait.insert_string(KUBE_KEY_KIND, "Gateway");
35979 via_trait.insert_string(KUBE_KEY_NAME, String::from("hello"));
35980 via_trait.insert_string(KUBE_KEY_PORT, 8080u16.to_string());
35981
35982 let mut via_inline = serde_yaml::Mapping::new();
35983 via_inline.insert_str_key(KUBE_KEY_KIND, serde_yaml::Value::String("Gateway".into()));
35984 via_inline.insert_str_key(
35985 KUBE_KEY_NAME,
35986 serde_yaml::Value::String(String::from("hello")),
35987 );
35988 via_inline.insert_str_key(
35989 KUBE_KEY_PORT,
35990 serde_yaml::Value::String(8080u16.to_string()),
35991 );
35992
35993 assert_eq!(
35994 via_trait, via_inline,
35995 "insert_string(KEY, V) must byte-equal \
35996 insert_str_key(KEY, Value::String(V.into())) — otherwise \
35997 the ~17 routed consumer sites drift silently at emit time"
35998 );
35999 }
36000
36001 #[test]
36002 fn mapping_ext_insert_number_promotes_value_to_yaml_number() {
36003 // The trait method promotes an arbitrary `Into<serde_yaml::Number>`
36004 // value to `Value::Number(value.into())` — pin the promotion so a
36005 // future refactor that reaches for a different `Value` variant
36006 // for the integer-scalar payload (e.g. `Value::Tagged` under a
36007 // K8s Server-Side-Apply typed-field-ownership axis rebrand, or
36008 // the deprecated `Value::String(n.to_string())` "stringy port"
36009 // rendering some pre-Gateway-API-v1 CRDs still shipped with) is
36010 // a compile-visible break, not a silent per-consumer regression
36011 // at the K8s-artifact-emit surface. Peer with
36012 // [`mapping_ext_insert_string_promotes_value_to_yaml_string`] on
36013 // the sibling `insert_string` primitive's string-scalar
36014 // promotion pin.
36015 let mut m = serde_yaml::Mapping::new();
36016 let prior = m.insert_number(KUBE_KEY_PORT, GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT);
36017 assert!(
36018 prior.is_none(),
36019 "insert_number returns None on first insertion, mirroring \
36020 serde_yaml::Mapping::insert"
36021 );
36022 let got = m
36023 .get(KUBE_KEY_PORT)
36024 .expect("inserted key is present under Value::Number promotion");
36025 assert_eq!(
36026 got.as_u64(),
36027 Some(u64::from(GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT)),
36028 "insert_number routes value verbatim through Value::Number \
36029 promotion — the u16 payload survives round-trip as a Number \
36030 the as_u64 accessor decodes verbatim"
36031 );
36032 assert!(
36033 matches!(got, serde_yaml::Value::Number(_)),
36034 "the promoted value is Value::Number, not Value::String — a \
36035 stringy-port drift would emit `port: \"80\"` (rejected by \
36036 Gateway API v1 apiserver as a type mismatch)"
36037 );
36038 }
36039
36040 #[test]
36041 fn mapping_ext_insert_number_returns_prior_value_on_replace() {
36042 // The trait method mirrors [`serde_yaml::Mapping::insert`]'s
36043 // return contract: the prior value at that key, or `None` if
36044 // absent. Pin the replace-returns-prior semantic so a future
36045 // refactor that swaps to a `HashMap::entry`-style flow doesn't
36046 // silently drop the prior-value handoff downstream consumers
36047 // may reach for. Peer with
36048 // [`mapping_ext_insert_string_returns_prior_value_on_replace`] on
36049 // the sibling `insert_string` primitive's replace-semantics pin.
36050 let mut m = serde_yaml::Mapping::new();
36051 m.insert_number(KUBE_KEY_PORT, 80u16);
36052 let prior = m.insert_number(KUBE_KEY_PORT, 443u16);
36053 assert_eq!(
36054 prior.as_ref().and_then(serde_yaml::Value::as_u64),
36055 Some(80),
36056 "insert_number returns the prior value when replacing an \
36057 existing key — the u16 payload round-trips verbatim through \
36058 the returned Value::Number handoff"
36059 );
36060 let got = m
36061 .get(KUBE_KEY_PORT)
36062 .expect("key is still present after replace");
36063 assert_eq!(
36064 got.as_u64(),
36065 Some(443),
36066 "replaced value is now the most-recently-inserted one"
36067 );
36068 }
36069
36070 #[test]
36071 fn mapping_ext_insert_number_matches_hand_written_promotion() {
36072 // Cross-check the trait method against the hand-written
36073 // `mapping.insert_str_key(KEY, Value::Number(N.into()))` shape
36074 // the two lifted caixa-mesh call sites previously carried. A
36075 // drift between the trait method's promotion and the inline
36076 // promotion would silently emit a different YAML mapping (a
36077 // differently-typed scalar, a different `Value` variant) at
36078 // every routed consumer — pin the equivalence so the trait
36079 // remains a drop-in replacement. Two arms pin the axis end-to-
36080 // end: a `u16` typed-const arm (the lifted
36081 // `GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT` external HTTP
36082 // listener-port, cd60fde) and a `u16` typed-field arm (the
36083 // per-`entrada.port` backend-target Servico port routed through
36084 // the `AplicacaoSpec` `:entrada :port` slot).
36085 let mut via_trait = serde_yaml::Mapping::new();
36086 via_trait.insert_number(KUBE_KEY_PORT, GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT);
36087 via_trait.insert_number(GATEWAY_API_KEY_VALUE, 8443u16);
36088
36089 let mut via_inline = serde_yaml::Mapping::new();
36090 via_inline.insert_str_key(
36091 KUBE_KEY_PORT,
36092 serde_yaml::Value::Number(GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT.into()),
36093 );
36094 via_inline.insert_str_key(
36095 GATEWAY_API_KEY_VALUE,
36096 serde_yaml::Value::Number(8443u16.into()),
36097 );
36098
36099 assert_eq!(
36100 via_trait, via_inline,
36101 "insert_number(KEY, N) must byte-equal \
36102 insert_str_key(KEY, Value::Number(N.into())) — otherwise \
36103 the two routed caixa-mesh consumer sites drift silently at \
36104 emit time"
36105 );
36106 }
36107
36108 #[test]
36109 fn mapping_ext_insert_mapping_promotes_value_to_yaml_mapping() {
36110 // The trait method promotes an arbitrary `serde_yaml::Mapping`
36111 // value to `Value::Mapping(value)` — pin the promotion so a
36112 // future refactor that reaches for a different `Value` variant
36113 // for the nested-Mapping payload (e.g. `Value::Tagged` under a
36114 // K8s Server-Side-Apply typed-field-ownership axis rebrand) is
36115 // a compile-visible break, not a silent per-consumer regression
36116 // at the K8s-artifact-emit surface. Peer with
36117 // [`mapping_ext_insert_string_promotes_value_to_yaml_string`] on
36118 // the sibling `insert_string` primitive's scalar-promotion pin
36119 // and with
36120 // [`mapping_ext_insert_str_key_promotes_key_to_yaml_string`] on
36121 // the base `insert_str_key` primitive's key-promotion pin.
36122 let mut inner = serde_yaml::Mapping::new();
36123 inner.insert_string(KUBE_KEY_NAME, "hello-rio");
36124 let mut m = serde_yaml::Mapping::new();
36125 let prior = m.insert_mapping(KUBE_KEY_METADATA, inner.clone());
36126 assert!(
36127 prior.is_none(),
36128 "insert_mapping returns None on first insertion, mirroring \
36129 serde_yaml::Mapping::insert"
36130 );
36131 let got = m
36132 .get(KUBE_KEY_METADATA)
36133 .expect("inserted key is present under Value::Mapping promotion");
36134 assert_eq!(
36135 got,
36136 &serde_yaml::Value::Mapping(inner),
36137 "insert_mapping routes value verbatim through Value::Mapping \
36138 promotion"
36139 );
36140 }
36141
36142 #[test]
36143 fn mapping_ext_insert_mapping_returns_prior_value_on_replace() {
36144 // The trait method mirrors [`serde_yaml::Mapping::insert`]'s
36145 // return contract: the prior value at that key, or `None` if
36146 // absent. Pin the replace-returns-prior semantic so a future
36147 // refactor that swaps to a `HashMap::entry`-style flow doesn't
36148 // silently drop the prior-value handoff downstream consumers
36149 // may reach for. Peer with
36150 // [`mapping_ext_insert_string_returns_prior_value_on_replace`]
36151 // and
36152 // [`mapping_ext_insert_str_key_returns_prior_value_on_replace`]
36153 // on the sibling primitive-pair members' replace-semantics
36154 // pins.
36155 let mut first_inner = serde_yaml::Mapping::new();
36156 first_inner.insert_string(KUBE_KEY_NAME, "first");
36157 let mut second_inner = serde_yaml::Mapping::new();
36158 second_inner.insert_string(KUBE_KEY_NAME, "second");
36159 let mut m = serde_yaml::Mapping::new();
36160 m.insert_mapping(KUBE_KEY_METADATA, first_inner.clone());
36161 let prior = m.insert_mapping(KUBE_KEY_METADATA, second_inner.clone());
36162 assert_eq!(
36163 prior,
36164 Some(serde_yaml::Value::Mapping(first_inner)),
36165 "insert_mapping returns the prior value when replacing an \
36166 existing key"
36167 );
36168 let got = m
36169 .get(KUBE_KEY_METADATA)
36170 .expect("key is still present after replace");
36171 assert_eq!(
36172 got,
36173 &serde_yaml::Value::Mapping(second_inner),
36174 "replaced value is now the most-recently-inserted one"
36175 );
36176 }
36177
36178 #[test]
36179 fn mapping_ext_insert_mapping_matches_hand_written_promotion() {
36180 // Cross-check the trait method against the hand-written
36181 // `mapping.insert_str_key(KEY, Value::Mapping(inner))` shape the
36182 // 6 lifted call sites previously carried. A drift between the
36183 // trait method's promotion and the inline promotion would
36184 // silently emit a different YAML mapping (a differently-wrapped
36185 // outer variant, a differently-shaped inner Mapping) at every
36186 // routed consumer — pin the equivalence so the trait remains a
36187 // drop-in replacement. Two cases pin the shape end-to-end:
36188 // an empty inner Mapping (no silent is_empty short-circuit) and
36189 // a populated inner Mapping (the `metadata` / `spec` /
36190 // `spec.rules[].path` sub-block shape).
36191 let mut inner_empty = serde_yaml::Mapping::new();
36192 let _ = &mut inner_empty; // keep as mut for parity with populated arm below
36193 let mut inner_populated = serde_yaml::Mapping::new();
36194 inner_populated.insert_string(KUBE_KEY_NAME, "hello-rio");
36195 inner_populated.insert_string(KUBE_KEY_NAMESPACE, DEFAULT_NAMESPACE);
36196
36197 let mut via_trait = serde_yaml::Mapping::new();
36198 via_trait.insert_mapping(KUBE_KEY_SPEC, inner_empty.clone());
36199 via_trait.insert_mapping(KUBE_KEY_METADATA, inner_populated.clone());
36200
36201 let mut via_inline = serde_yaml::Mapping::new();
36202 via_inline.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(inner_empty));
36203 via_inline.insert_str_key(
36204 KUBE_KEY_METADATA,
36205 serde_yaml::Value::Mapping(inner_populated),
36206 );
36207
36208 assert_eq!(
36209 via_trait, via_inline,
36210 "insert_mapping(KEY, inner) must byte-equal \
36211 insert_str_key(KEY, Value::Mapping(inner)) — otherwise the \
36212 six routed consumer sites drift silently at emit time"
36213 );
36214 }
36215
36216 #[test]
36217 fn mapping_ext_insert_sequence_promotes_value_to_yaml_sequence() {
36218 // The trait method promotes an arbitrary `Vec<Value>` value to
36219 // `Value::Sequence(value)` — pin the promotion so a future
36220 // refactor that reaches for a different `Value` variant for the
36221 // list-shape payload (e.g. `Value::Tagged` under a K8s Server-
36222 // Side-Apply typed-field-ownership axis rebrand, a serde_yaml
36223 // successor's `Value::Array` / `Value::List` variant rename) is
36224 // a compile-visible break, not a silent per-consumer regression
36225 // at the K8s-artifact-emit surface. Peer with
36226 // [`mapping_ext_insert_mapping_promotes_value_to_yaml_mapping`]
36227 // on the sibling `insert_mapping` primitive's nested-Mapping-
36228 // promotion pin, and with
36229 // [`mapping_ext_insert_string_promotes_value_to_yaml_string`]
36230 // on the sibling `insert_string` primitive's scalar-promotion
36231 // pin.
36232 let inner = vec![
36233 serde_yaml::Value::String("hello".into()),
36234 serde_yaml::Value::String("world".into()),
36235 ];
36236 let mut m = serde_yaml::Mapping::new();
36237 let prior = m.insert_sequence(GATEWAY_API_KEY_HOSTNAMES, inner.clone());
36238 assert!(
36239 prior.is_none(),
36240 "insert_sequence returns None on first insertion, mirroring \
36241 serde_yaml::Mapping::insert"
36242 );
36243 let got = m
36244 .get(GATEWAY_API_KEY_HOSTNAMES)
36245 .expect("inserted key is present under Value::Sequence promotion");
36246 assert_eq!(
36247 got,
36248 &serde_yaml::Value::Sequence(inner),
36249 "insert_sequence routes value verbatim through Value::Sequence \
36250 promotion"
36251 );
36252 }
36253
36254 #[test]
36255 fn mapping_ext_insert_sequence_returns_prior_value_on_replace() {
36256 // The trait method mirrors [`serde_yaml::Mapping::insert`]'s
36257 // return contract: the prior value at that key, or `None` if
36258 // absent. Pin the replace-returns-prior semantic so a future
36259 // refactor that swaps to a `HashMap::entry`-style flow doesn't
36260 // silently drop the prior-value handoff downstream consumers
36261 // may reach for. Peer with
36262 // [`mapping_ext_insert_mapping_returns_prior_value_on_replace`],
36263 // [`mapping_ext_insert_string_returns_prior_value_on_replace`],
36264 // and
36265 // [`mapping_ext_insert_str_key_returns_prior_value_on_replace`]
36266 // on the sibling primitive-quadruple members' replace-semantics
36267 // pins.
36268 let first: Vec<serde_yaml::Value> = vec![serde_yaml::Value::String("a".into())];
36269 let second: Vec<serde_yaml::Value> = vec![
36270 serde_yaml::Value::String("b".into()),
36271 serde_yaml::Value::String("c".into()),
36272 ];
36273 let mut m = serde_yaml::Mapping::new();
36274 m.insert_sequence(KUBE_KEY_RULES, first.clone());
36275 let prior = m.insert_sequence(KUBE_KEY_RULES, second.clone());
36276 assert_eq!(
36277 prior,
36278 Some(serde_yaml::Value::Sequence(first)),
36279 "insert_sequence returns the prior value when replacing an \
36280 existing key"
36281 );
36282 let got = m
36283 .get(KUBE_KEY_RULES)
36284 .expect("key is still present after replace");
36285 assert_eq!(
36286 got,
36287 &serde_yaml::Value::Sequence(second),
36288 "replaced value is now the most-recently-inserted one"
36289 );
36290 }
36291
36292 #[test]
36293 fn mapping_ext_insert_sequence_matches_hand_written_promotion() {
36294 // Cross-check the trait method against the hand-written
36295 // `mapping.insert_str_key(KEY, Value::Sequence(v))` shape the 4
36296 // lifted call sites previously carried. A drift between the
36297 // trait method's promotion and the inline promotion would
36298 // silently emit a different YAML mapping (a differently-wrapped
36299 // outer variant, a differently-shaped inner sequence) at every
36300 // routed consumer — pin the equivalence so the trait remains a
36301 // drop-in replacement. Three cases pin the shape end-to-end:
36302 // an empty inner Vec (no silent is_empty short-circuit), a
36303 // singleton-Value inner Vec (the `fromEndpoints[<selector>]` /
36304 // `hostnames[<host>]` singleton shape), and a multi-Value inner
36305 // Vec (the `toPorts[…]` / `rules[…]` multi-entry shape).
36306 let inner_empty: Vec<serde_yaml::Value> = Vec::new();
36307 let inner_singleton: Vec<serde_yaml::Value> =
36308 vec![serde_yaml::Value::String("example.com".into())];
36309 let mut host_entry = serde_yaml::Mapping::new();
36310 host_entry.insert_string(KUBE_KEY_NAME, "svc-a");
36311 let mut port_entry = serde_yaml::Mapping::new();
36312 port_entry.insert_string(KUBE_KEY_NAME, "svc-b");
36313 let inner_multi: Vec<serde_yaml::Value> = vec![
36314 serde_yaml::Value::Mapping(host_entry.clone()),
36315 serde_yaml::Value::Mapping(port_entry.clone()),
36316 ];
36317
36318 let mut via_trait = serde_yaml::Mapping::new();
36319 via_trait.insert_sequence(CILIUM_KEY_TO_PORTS, inner_empty.clone());
36320 via_trait.insert_sequence(GATEWAY_API_KEY_HOSTNAMES, inner_singleton.clone());
36321 via_trait.insert_sequence(KUBE_KEY_RULES, inner_multi.clone());
36322
36323 let mut via_inline = serde_yaml::Mapping::new();
36324 via_inline.insert_str_key(
36325 CILIUM_KEY_TO_PORTS,
36326 serde_yaml::Value::Sequence(inner_empty),
36327 );
36328 via_inline.insert_str_key(
36329 GATEWAY_API_KEY_HOSTNAMES,
36330 serde_yaml::Value::Sequence(inner_singleton),
36331 );
36332 via_inline.insert_str_key(KUBE_KEY_RULES, serde_yaml::Value::Sequence(inner_multi));
36333
36334 assert_eq!(
36335 via_trait, via_inline,
36336 "insert_sequence(KEY, v) must byte-equal \
36337 insert_str_key(KEY, Value::Sequence(v)) — otherwise the \
36338 four routed consumer sites drift silently at emit time"
36339 );
36340 }
36341
36342 // ── insert_singleton_mapping_sequence — composed primitive ───────────
36343 //
36344 // The trait method composes [`Self::insert_str_key`] with
36345 // [`singleton_mapping_sequence`]: every hand-inline
36346 // `mapping.insert_str_key(K, singleton_mapping_sequence(m))` two-symbol
36347 // composition previously carried at 7 sites across caixa-mesh
36348 // collapses onto one method call. Three peer pins pin the trait
36349 // method's shape end-to-end.
36350
36351 #[test]
36352 fn mapping_ext_insert_singleton_mapping_sequence_promotes_value_to_singleton_mapping_seq() {
36353 // First-insertion returns None (mirroring [`Mapping::insert`])
36354 // and the inserted value is a `Value::Sequence` of exactly one
36355 // element, wrapping the caller's Mapping as `Value::Mapping`.
36356 // Peer with the sibling
36357 // `mapping_ext_insert_sequence_promotes_value_to_yaml_sequence`
36358 // / `mapping_ext_insert_mapping_promotes_value_to_yaml_mapping`
36359 // / `mapping_ext_insert_string_promotes_value_to_yaml_string`
36360 // first-insert pins on the sibling MappingExt primitive
36361 // members.
36362 let mut inner = serde_yaml::Mapping::new();
36363 inner.insert_str_key(
36364 GATEWAY_API_KEY_NAME,
36365 serde_yaml::Value::String("gw-listener".into()),
36366 );
36367 let mut m = serde_yaml::Mapping::new();
36368 let prior = m.insert_singleton_mapping_sequence(GATEWAY_API_KEY_LISTENERS, inner.clone());
36369 assert_eq!(
36370 prior, None,
36371 "insert_singleton_mapping_sequence returns None on first insertion, \
36372 mirroring serde_yaml::Mapping::insert"
36373 );
36374 let got = m
36375 .get(GATEWAY_API_KEY_LISTENERS)
36376 .expect("inserted key is present under Value::Sequence promotion");
36377 assert_eq!(
36378 got,
36379 &serde_yaml::Value::Sequence(vec![serde_yaml::Value::Mapping(inner)]),
36380 "insert_singleton_mapping_sequence routes value verbatim through \
36381 the singleton_mapping_sequence(_) helper wrap"
36382 );
36383 }
36384
36385 #[test]
36386 fn mapping_ext_insert_singleton_mapping_sequence_returns_prior_value_on_replace() {
36387 // The trait method mirrors [`serde_yaml::Mapping::insert`]'s
36388 // return contract: the prior value at that key, or `None` if
36389 // absent. Pin the replace-returns-prior semantic so a future
36390 // refactor that swaps to a `HashMap::entry`-style flow doesn't
36391 // silently drop the prior-value handoff downstream consumers
36392 // may reach for. Peer with the sibling
36393 // `mapping_ext_insert_sequence_returns_prior_value_on_replace`
36394 // and its siblings on the primitive-quintuple axis.
36395 let mut first = serde_yaml::Mapping::new();
36396 first.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("a".into()));
36397 let mut second = serde_yaml::Mapping::new();
36398 second.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("b".into()));
36399 let mut m = serde_yaml::Mapping::new();
36400 m.insert_singleton_mapping_sequence(GATEWAY_API_KEY_PARENT_REFS, first.clone());
36401 let prior =
36402 m.insert_singleton_mapping_sequence(GATEWAY_API_KEY_PARENT_REFS, second.clone());
36403 assert_eq!(
36404 prior,
36405 Some(serde_yaml::Value::Sequence(vec![
36406 serde_yaml::Value::Mapping(first)
36407 ])),
36408 "insert_singleton_mapping_sequence returns the prior value \
36409 when replacing an existing key"
36410 );
36411 let got = m
36412 .get(GATEWAY_API_KEY_PARENT_REFS)
36413 .expect("key is still present after replace");
36414 assert_eq!(
36415 got,
36416 &serde_yaml::Value::Sequence(vec![serde_yaml::Value::Mapping(second)]),
36417 "replaced value is now the most-recently-inserted singleton \
36418 mapping sequence"
36419 );
36420 }
36421
36422 #[test]
36423 fn mapping_ext_insert_singleton_mapping_sequence_matches_hand_written_composition() {
36424 // Cross-check the trait method against the hand-written
36425 // `mapping.insert_str_key(KEY, singleton_mapping_sequence(m))`
36426 // two-symbol composition the 7 lifted call sites previously
36427 // carried. A drift between the trait method's routing and the
36428 // inline composition would silently emit a different YAML
36429 // mapping (a differently-wrapped outer variant, a
36430 // differently-shaped inner singleton-Mapping list) at every
36431 // routed consumer — pin the equivalence so the trait remains a
36432 // drop-in replacement. Three cases pin the shape end-to-end:
36433 // an empty inner Mapping (no silent is_empty short-circuit,
36434 // matches the sibling `singleton_mapping_sequence_preserves_empty_inner_mapping`
36435 // pin), a single-key inner Mapping (the
36436 // `CILIUM_KEY_HTTP` / `CILIUM_KEY_INGRESS` singleton-rule
36437 // shape), and a multi-key inner Mapping (the
36438 // `GATEWAY_API_KEY_LISTENERS` per-listener shape).
36439 let inner_empty = serde_yaml::Mapping::new();
36440 let mut inner_single_key = serde_yaml::Mapping::new();
36441 inner_single_key
36442 .insert_str_key(CILIUM_KEY_PATH, serde_yaml::Value::String("/health".into()));
36443 let mut inner_multi_key = serde_yaml::Mapping::new();
36444 inner_multi_key.insert_str_key(
36445 GATEWAY_API_KEY_NAME,
36446 serde_yaml::Value::String("http".into()),
36447 );
36448 inner_multi_key.insert_str_key(
36449 KUBE_KEY_PORT,
36450 serde_yaml::Value::Number(GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT.into()),
36451 );
36452
36453 let mut via_trait = serde_yaml::Mapping::new();
36454 via_trait.insert_singleton_mapping_sequence(CILIUM_KEY_HTTP, inner_empty.clone());
36455 via_trait.insert_singleton_mapping_sequence(CILIUM_KEY_INGRESS, inner_single_key.clone());
36456 via_trait
36457 .insert_singleton_mapping_sequence(GATEWAY_API_KEY_LISTENERS, inner_multi_key.clone());
36458
36459 let mut via_inline = serde_yaml::Mapping::new();
36460 via_inline.insert_str_key(CILIUM_KEY_HTTP, singleton_mapping_sequence(inner_empty));
36461 via_inline.insert_str_key(
36462 CILIUM_KEY_INGRESS,
36463 singleton_mapping_sequence(inner_single_key),
36464 );
36465 via_inline.insert_str_key(
36466 GATEWAY_API_KEY_LISTENERS,
36467 singleton_mapping_sequence(inner_multi_key),
36468 );
36469
36470 assert_eq!(
36471 via_trait, via_inline,
36472 "insert_singleton_mapping_sequence(KEY, m) must byte-equal \
36473 insert_str_key(KEY, singleton_mapping_sequence(m)) — otherwise \
36474 the seven routed caixa-mesh consumer sites drift silently at \
36475 emit time"
36476 );
36477 }
36478
36479 // ── entry_str_key — entry-API twin of insert_str_key ─────────────────
36480
36481 #[test]
36482 fn mapping_ext_entry_str_key_or_inserts_default_under_yaml_string_promoted_key_when_absent() {
36483 // The trait method promotes an arbitrary `&str` key to
36484 // `Value::String(key.to_string())` on the entry-API axis — pin
36485 // the promotion + the entry-API contract so a future refactor
36486 // that reaches for a different `Value` variant for the entry
36487 // key (e.g. `Value::Tagged`) or breaks the entry-API
36488 // `.or_insert(...)` composition is a compile-visible break,
36489 // not a silent per-consumer regression at the 4 lifted
36490 // `caixa-flux` idempotent-upsert sites. Peer with the sibling
36491 // [`mapping_ext_insert_str_key_promotes_key_to_yaml_string`] on
36492 // the fresh-emit axis of the same key promotion.
36493 let mut m = serde_yaml::Mapping::new();
36494 let default_val = serde_yaml::Value::Sequence(Vec::new());
36495 let inserted = m.entry_str_key("programs").or_insert(default_val.clone());
36496 assert_eq!(
36497 inserted, &default_val,
36498 "entry_str_key(K).or_insert(D) returns &mut D on the absent-key \
36499 path, mirroring serde_yaml::mapping::Entry::or_insert"
36500 );
36501 // Key is exactly the `Value::String` promotion of the input.
36502 let got = m
36503 .get("programs")
36504 .expect("or_insert-defaulted key is present under Value::String promotion");
36505 assert_eq!(
36506 got, &default_val,
36507 "entry_str_key routes the default verbatim to the underlying \
36508 serde_yaml::Mapping::entry(...).or_insert(...) path"
36509 );
36510 }
36511
36512 #[test]
36513 fn mapping_ext_entry_str_key_leaves_prior_value_untouched_on_or_insert_when_present() {
36514 // The trait method mirrors [`serde_yaml::mapping::Entry::or_insert`]'s
36515 // present-key contract: the prior value is preserved, and the
36516 // returned `&mut Value` points at that prior value (NOT the
36517 // discarded default). Pin the leave-prior-untouched semantic so a
36518 // future refactor that swaps to an `.insert`-style overwrite
36519 // flow doesn't silently clobber every idempotent-upsert consumer
36520 // (the M4 per-`:politicas` overlay merger, the `feira app
36521 // deploy` idempotent-write dry-run comparator). Peer with the
36522 // sibling [`mapping_ext_insert_str_key_returns_prior_value_on_replace`]
36523 // pin on the fresh-emit axis (which mirrors the `insert`
36524 // replace-and-return-prior semantic, not the `entry.or_insert`
36525 // preserve-prior semantic — the two APIs partition the
36526 // `Mapping`-write surface exactly on this axis).
36527 let mut m = serde_yaml::Mapping::new();
36528 m.insert_str_key(
36529 FLEET_PROGRAMS_KEY_PROGRAMS,
36530 serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("existing".into())]),
36531 );
36532 let discarded_default = serde_yaml::Value::Sequence(Vec::new());
36533 let returned = m
36534 .entry_str_key(FLEET_PROGRAMS_KEY_PROGRAMS)
36535 .or_insert(discarded_default);
36536 assert_eq!(
36537 returned,
36538 &serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("existing".into())]),
36539 "entry_str_key(K).or_insert(D) returns &mut prior on the \
36540 present-key path — the discarded default must not overwrite \
36541 the emitter's prior write"
36542 );
36543 // Value at the key is still the pre-existing one, verbatim.
36544 let got = m
36545 .get(FLEET_PROGRAMS_KEY_PROGRAMS)
36546 .expect("key is still present after or_insert on the present-key path");
36547 assert_eq!(
36548 got,
36549 &serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("existing".into())]),
36550 "or_insert on the present-key path preserves the prior value \
36551 verbatim — no clobber, no reshape"
36552 );
36553 }
36554
36555 #[test]
36556 fn mapping_ext_entry_str_key_matches_hand_written_composition() {
36557 // Cross-check the trait method against the hand-written
36558 // `mapping.entry(Value::String(KEY.into()))` three-token
36559 // composition the 4 lifted `caixa-flux` call sites previously
36560 // carried. A drift between the trait method's promotion and the
36561 // inline promotion the prior call sites used would silently
36562 // route every idempotent-upsert consumer past a different bucket
36563 // (a differently-promoted key on absent-key insert, a hash-key
36564 // mismatch that always fires the `or_insert` default even when
36565 // the emitter's `insert_str_key` already wrote a value under
36566 // the same key). Two cases pin the shape end-to-end: an
36567 // absent-key path (both routes take the vacant `or_insert`
36568 // branch, both end up storing the same default under the
36569 // promoted key) and a present-key path (both routes take the
36570 // occupied `or_insert` branch, both leave the prior value
36571 // untouched — the twin of the
36572 // `mapping_ext_insert_str_key_matches_hand_written_promotion`
36573 // pin on the fresh-emit axis).
36574 //
36575 // Absent-key path — the vacant `or_insert` branch.
36576 let mut via_trait_absent = serde_yaml::Mapping::new();
36577 via_trait_absent
36578 .entry_str_key(FLEET_PROGRAMS_KEY_PROGRAMS)
36579 .or_insert(serde_yaml::Value::Sequence(Vec::new()));
36580 let mut via_inline_absent = serde_yaml::Mapping::new();
36581 via_inline_absent
36582 .entry(serde_yaml::Value::String(
36583 FLEET_PROGRAMS_KEY_PROGRAMS.into(),
36584 ))
36585 .or_insert(serde_yaml::Value::Sequence(Vec::new()));
36586 assert_eq!(
36587 via_trait_absent, via_inline_absent,
36588 "entry_str_key(K).or_insert(D) must byte-equal \
36589 entry(Value::String(K.into())).or_insert(D) on the absent-key \
36590 path — otherwise the 4 routed caixa-flux consumer sites \
36591 land the default under a different bucket than the emitter's \
36592 `insert_str_key` write and the idempotent-upsert semantic \
36593 silently doubles the entry on every call"
36594 );
36595
36596 // Present-key path — the occupied `or_insert` branch. Seed both
36597 // mappings via the fresh-emit `insert_str_key` peer (which the
36598 // `matches_hand_written_promotion` pin already gates), so the
36599 // present-key path here inherits the promotion-agreement guarantee
36600 // from that peer and tests only the entry-API branch difference.
36601 let seed = serde_yaml::Value::Mapping(serde_yaml::Mapping::new());
36602 let mut via_trait_present = serde_yaml::Mapping::new();
36603 via_trait_present.insert_str_key(FLUX_KEY_VALUES, seed.clone());
36604 via_trait_present
36605 .entry_str_key(FLUX_KEY_VALUES)
36606 .or_insert(serde_yaml::Value::Sequence(Vec::new()));
36607 let mut via_inline_present = serde_yaml::Mapping::new();
36608 via_inline_present.insert_str_key(FLUX_KEY_VALUES, seed);
36609 via_inline_present
36610 .entry(serde_yaml::Value::String(FLUX_KEY_VALUES.into()))
36611 .or_insert(serde_yaml::Value::Sequence(Vec::new()));
36612 assert_eq!(
36613 via_trait_present, via_inline_present,
36614 "entry_str_key(K).or_insert(D) must byte-equal \
36615 entry(Value::String(K.into())).or_insert(D) on the \
36616 present-key path — otherwise a promoted-key mismatch would \
36617 cause the trait routing to see the seed as absent and \
36618 overwrite the emitter's prior write while the hand-written \
36619 inline routing sees it as present and preserves it (or vice \
36620 versa)"
36621 );
36622 }
36623
36624 // ── entry_or_default_{mapping,sequence} — entry-API-with-container-check ─
36625
36626 #[test]
36627 fn mapping_ext_entry_or_default_mapping_seeds_empty_inner_when_absent() {
36628 // Absent-key path — the helper mints an empty
36629 // `Value::Mapping(Mapping::new())` under the promoted key and
36630 // returns `Some(&mut inner)` pointing at the fresh empty inner.
36631 // Pin the seed shape so a future refactor that reaches for a
36632 // different empty-container variant (e.g. `Value::Null`, or a
36633 // `Mapping::with_capacity(_)` non-empty pre-allocation) or
36634 // breaks the `Option::Some` return contract is a compile-visible
36635 // break, not a silent per-consumer regression at the caixa-flux
36636 // `upsert_into_helmrelease_programs` `spec.values` container-
36637 // upsert. Peer with the sibling
36638 // [`mapping_ext_entry_or_default_sequence_seeds_empty_inner_when_absent`]
36639 // on the sibling list-container axis.
36640 let mut m = serde_yaml::Mapping::new();
36641 {
36642 let inner = m
36643 .entry_or_default_mapping(FLUX_KEY_VALUES)
36644 .expect("absent-key path seeds an empty Mapping and returns Some(&mut _)");
36645 assert!(
36646 inner.is_empty(),
36647 "the seeded default must be an EMPTY Mapping — a \
36648 non-empty pre-allocation would land a K8s CRD schema \
36649 pre-populated block the emitter never authored"
36650 );
36651 }
36652 // Key is exactly the `Value::String` promotion of the input,
36653 // and the value is the empty-Mapping seed.
36654 let got = m
36655 .get(FLUX_KEY_VALUES)
36656 .expect("or_default seeded the key under Value::String promotion");
36657 assert_eq!(
36658 got,
36659 &serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
36660 "entry_or_default_mapping seeds Value::Mapping(Mapping::new()) \
36661 verbatim on the absent-key arm — no reshape, no wrap"
36662 );
36663 }
36664
36665 #[test]
36666 fn mapping_ext_entry_or_default_mapping_preserves_prior_mapping_on_present_arm() {
36667 // Present-key path with matching variant — the helper mirrors
36668 // [`serde_yaml::mapping::Entry::or_insert_with`]'s occupied
36669 // branch: the prior value is preserved, and the returned
36670 // `&mut Mapping` points at that prior inner Mapping (NOT a
36671 // fresh empty default). Pin the leave-prior-untouched semantic
36672 // so a future refactor that reaches for an `.insert`-style
36673 // overwrite flow doesn't silently clobber every idempotent-
36674 // container-upsert consumer (the `feira app deploy` per-cluster
36675 // write path, the M4 per-cluster HelmRelease overlay merger).
36676 let mut m = serde_yaml::Mapping::new();
36677 let mut prior_inner = serde_yaml::Mapping::new();
36678 prior_inner.insert_str_key(HELM_VALUES_KEY_ENABLED, serde_yaml::Value::Bool(true));
36679 m.insert_mapping(FLUX_KEY_VALUES, prior_inner.clone());
36680 {
36681 let inner = m
36682 .entry_or_default_mapping(FLUX_KEY_VALUES)
36683 .expect("present-Mapping-variant path returns Some(&mut prior)");
36684 assert_eq!(
36685 inner, &prior_inner,
36686 "entry_or_default_mapping returns &mut prior on the \
36687 present-key path — the default empty Mapping must not \
36688 overwrite the emitter's prior write"
36689 );
36690 }
36691 // Value at the key is still the pre-existing one, verbatim.
36692 let got = m
36693 .get(FLUX_KEY_VALUES)
36694 .expect("key is still present after or_default on the present-key path");
36695 assert_eq!(
36696 got,
36697 &serde_yaml::Value::Mapping(prior_inner),
36698 "or_default on the present-key path preserves the prior \
36699 value verbatim — no clobber, no reshape"
36700 );
36701 }
36702
36703 #[test]
36704 fn mapping_ext_entry_or_default_mapping_returns_none_on_variant_mismatch() {
36705 // Present-key path with mismatched variant — the helper returns
36706 // `None`, letting the caller surface its domain-specific
36707 // "expected Mapping at this schema key" diagnostic (rather than
36708 // silently clobbering the mismatched prior value). Pin the
36709 // structural-mismatch-is-None contract so a future refactor
36710 // that reaches for a fallback-to-empty-default flow doesn't
36711 // silently overwrite user-authored non-Mapping data at the
36712 // canonical caixa-flux `Error::MissingField("spec.values must
36713 // be a mapping")` site — the mismatched-variant arm is
36714 // load-bearing for the domain-error diagnostic path, not just
36715 // a corner case.
36716 let mut m = serde_yaml::Mapping::new();
36717 m.insert_string(FLUX_KEY_VALUES, "not-a-mapping");
36718 let result = m.entry_or_default_mapping(FLUX_KEY_VALUES);
36719 assert!(
36720 result.is_none(),
36721 "entry_or_default_mapping returns None on variant \
36722 mismatch — the caller's `.ok_or(Error::MissingField(_))?` \
36723 chain surfaces the structural type-mismatch diagnostic"
36724 );
36725 let got = m
36726 .get(FLUX_KEY_VALUES)
36727 .expect("mismatched-variant prior value stays present after variant-check");
36728 assert_eq!(
36729 got,
36730 &serde_yaml::Value::String("not-a-mapping".into()),
36731 "None arm on variant mismatch leaves the prior value \
36732 untouched — the caller's domain-error path fires without \
36733 clobbering the user-authored data"
36734 );
36735 }
36736
36737 #[test]
36738 fn mapping_ext_entry_or_default_sequence_seeds_empty_inner_when_absent() {
36739 // Absent-key path — the helper mints an empty
36740 // `Value::Sequence(Vec::new())` under the promoted key and
36741 // returns `Some(&mut inner)` pointing at the fresh empty
36742 // `Vec<Value>`. Peer with
36743 // [`mapping_ext_entry_or_default_mapping_seeds_empty_inner_when_absent`]
36744 // on the nested-Mapping-container axis.
36745 let mut m = serde_yaml::Mapping::new();
36746 {
36747 let inner = m
36748 .entry_or_default_sequence(FLEET_PROGRAMS_KEY_PROGRAMS)
36749 .expect("absent-key path seeds an empty Vec and returns Some(&mut _)");
36750 assert!(
36751 inner.is_empty(),
36752 "the seeded default must be an EMPTY Vec — a non-empty \
36753 pre-allocation would land a pre-populated fleet-programs \
36754 list the emitter never authored"
36755 );
36756 }
36757 let got = m
36758 .get(FLEET_PROGRAMS_KEY_PROGRAMS)
36759 .expect("or_default seeded the key under Value::String promotion");
36760 assert_eq!(
36761 got,
36762 &serde_yaml::Value::Sequence(Vec::new()),
36763 "entry_or_default_sequence seeds Value::Sequence(Vec::new()) \
36764 verbatim on the absent-key arm — no reshape, no wrap"
36765 );
36766 }
36767
36768 #[test]
36769 fn mapping_ext_entry_or_default_sequence_preserves_prior_sequence_on_present_arm() {
36770 // Present-key path with matching variant — the helper mirrors
36771 // [`serde_yaml::mapping::Entry::or_insert_with`]'s occupied
36772 // branch: the prior `Vec` is preserved, and the returned
36773 // `&mut Vec<Value>` points at that prior inner Vec (NOT a
36774 // fresh empty default). The exact idempotent-upsert semantic
36775 // caixa-flux's `upsert_into_programs_yaml` /
36776 // `upsert_into_helmrelease_programs` depend on to preserve
36777 // prior `programs[]` entries across per-Servico rewrites.
36778 let mut m = serde_yaml::Mapping::new();
36779 let prior_inner = vec![serde_yaml::Value::String("existing".into())];
36780 m.insert_sequence(FLEET_PROGRAMS_KEY_PROGRAMS, prior_inner.clone());
36781 {
36782 let inner = m
36783 .entry_or_default_sequence(FLEET_PROGRAMS_KEY_PROGRAMS)
36784 .expect("present-Sequence-variant path returns Some(&mut prior)");
36785 assert_eq!(
36786 inner, &prior_inner,
36787 "entry_or_default_sequence returns &mut prior on the \
36788 present-key path — the default empty Vec must not \
36789 overwrite the emitter's prior write"
36790 );
36791 }
36792 let got = m
36793 .get(FLEET_PROGRAMS_KEY_PROGRAMS)
36794 .expect("key is still present after or_default on the present-key path");
36795 assert_eq!(
36796 got,
36797 &serde_yaml::Value::Sequence(prior_inner),
36798 "or_default on the present-key path preserves the prior \
36799 value verbatim — no clobber, no reshape"
36800 );
36801 }
36802
36803 #[test]
36804 fn mapping_ext_entry_or_default_sequence_returns_none_on_variant_mismatch() {
36805 // Present-key path with mismatched variant — the helper returns
36806 // `None`, letting the caller surface its domain-specific
36807 // "programs must be a sequence" diagnostic (rather than
36808 // silently clobbering the mismatched prior value). Pin the
36809 // structural-mismatch-is-None contract so a future refactor
36810 // that reaches for a fallback-to-empty-default flow doesn't
36811 // silently overwrite user-authored non-Sequence data at the
36812 // canonical caixa-flux `Error::MissingField("programs must be
36813 // a sequence")` site.
36814 let mut m = serde_yaml::Mapping::new();
36815 m.insert_string(FLEET_PROGRAMS_KEY_PROGRAMS, "not-a-sequence");
36816 let result = m.entry_or_default_sequence(FLEET_PROGRAMS_KEY_PROGRAMS);
36817 assert!(
36818 result.is_none(),
36819 "entry_or_default_sequence returns None on variant \
36820 mismatch — the caller's `.ok_or(Error::MissingField(_))?` \
36821 chain surfaces the structural type-mismatch diagnostic"
36822 );
36823 let got = m
36824 .get(FLEET_PROGRAMS_KEY_PROGRAMS)
36825 .expect("mismatched-variant prior value stays present after variant-check");
36826 assert_eq!(
36827 got,
36828 &serde_yaml::Value::String("not-a-sequence".into()),
36829 "None arm on variant mismatch leaves the prior value \
36830 untouched — the caller's domain-error path fires without \
36831 clobbering the user-authored data"
36832 );
36833 }
36834
36835 // ── insert_str_key_if_some — arity-0-or-1 twin of insert_str_key ─────
36836
36837 #[test]
36838 fn mapping_ext_insert_str_key_if_some_none_arm_leaves_mapping_untouched() {
36839 // The None arm skips the insert entirely — no clone, no
36840 // key-promotion, no bucket touch. Pin the no-op semantic so a
36841 // future refactor that reaches for an `Option::unwrap_or_default`
36842 // shape (which would emit `Value::Null` under the key on the
36843 // None arm) or an `.into_iter().for_each` scaffold (which would
36844 // still walk the bucket-lookup path) is a compile-visible break,
36845 // not a silent per-consumer regression at the 3 lifted
36846 // `caixa-mesh` overlay-insert sites (where the `None` arm is
36847 // the author's default when no `:politicas` slot is set — a
36848 // silent `Value::Null` emission would land a K8s CRD schema
36849 // rejection at every unset-slot Aplicacao).
36850 let mut m = serde_yaml::Mapping::new();
36851 let prior = m.insert_str_key_if_some(CILIUM_KEY_AUTHENTICATION, None);
36852 assert_eq!(
36853 prior, None,
36854 "insert_str_key_if_some(K, None) returns None — no insert \
36855 fires, so no prior value can be surfaced"
36856 );
36857 assert!(
36858 m.get(CILIUM_KEY_AUTHENTICATION).is_none(),
36859 "None arm must leave the key absent — a silent `Value::Null` \
36860 insertion would land a K8s CRD schema rejection at every \
36861 `:politicas`-unset Aplicacao"
36862 );
36863 assert_eq!(
36864 m.len(),
36865 0,
36866 "None arm must not touch any bucket — the Mapping stays \
36867 empty verbatim"
36868 );
36869 }
36870
36871 #[test]
36872 fn mapping_ext_insert_str_key_if_some_some_arm_promotes_key_to_yaml_string() {
36873 // The Some arm clones the borrowed inner value and delegates to
36874 // [`Self::insert_str_key`] — pin the promotion + the first-
36875 // insert-returns-None contract so a future refactor that reaches
36876 // for a different `Value` variant for the key (e.g.
36877 // `Value::Tagged`) or breaks the underlying
36878 // [`serde_yaml::Mapping::insert`] return contract is a compile-
36879 // visible break, not a silent per-consumer regression at the 3
36880 // lifted `caixa-mesh` overlay-insert sites. Peer with the sibling
36881 // [`mapping_ext_insert_str_key_promotes_key_to_yaml_string`] on
36882 // the always-1 arity axis of the same key promotion.
36883 let mut m = serde_yaml::Mapping::new();
36884 let overlay = serde_yaml::Value::Mapping({
36885 let mut inner = serde_yaml::Mapping::new();
36886 inner.insert_str_key(
36887 CILIUM_KEY_MODE,
36888 serde_yaml::Value::String("required".into()),
36889 );
36890 inner
36891 });
36892 let prior = m.insert_str_key_if_some(CILIUM_KEY_AUTHENTICATION, Some(&overlay));
36893 assert_eq!(
36894 prior, None,
36895 "insert_str_key_if_some(K, Some(&V)) returns None on first \
36896 insertion, mirroring serde_yaml::Mapping::insert"
36897 );
36898 // Key is exactly the `Value::String` promotion of the input.
36899 let got = m
36900 .get(CILIUM_KEY_AUTHENTICATION)
36901 .expect("Some arm inserts under the Value::String-promoted key");
36902 assert_eq!(
36903 got, &overlay,
36904 "insert_str_key_if_some routes the borrowed inner value \
36905 through a `.clone()` verbatim to the underlying \
36906 `insert_str_key` path — no reshape, no wrap, no unwrap"
36907 );
36908 // The borrowed input is untouched — the caller can reuse the
36909 // outer overlay binding across the next iteration of a per-
36910 // `(:de, :para)` loop (the exact reuse the three lifted
36911 // caixa-mesh sites depend on).
36912 assert!(
36913 overlay.get(CILIUM_KEY_MODE).is_some(),
36914 "insert_str_key_if_some must not move out of the borrowed \
36915 overlay — the caller-side outer binding stays available \
36916 for the next iteration of the enclosing per-`(:de, :para)` \
36917 or per-rule loop"
36918 );
36919 }
36920
36921 #[test]
36922 fn mapping_ext_insert_str_key_if_some_some_arm_returns_prior_value_on_replace() {
36923 // The Some arm mirrors [`serde_yaml::Mapping::insert`]'s return
36924 // contract on the replace-existing path: the prior value at that
36925 // key, surfaced verbatim. Pin the replace-returns-prior semantic
36926 // so a future refactor that reaches for an `entry.or_insert`-
36927 // style preserve-prior flow doesn't silently swap the axis's
36928 // semantic under the three routed caixa-mesh overlay sites (the
36929 // `:politicas` overlay is meant to override an author-provided
36930 // sub-block if one was present, not preserve it — the
36931 // replace-and-return-prior semantic is load-bearing).
36932 let mut m = serde_yaml::Mapping::new();
36933 let existing = serde_yaml::Value::String("cluster-default".into());
36934 let overlay = serde_yaml::Value::Mapping({
36935 let mut inner = serde_yaml::Mapping::new();
36936 inner.insert_str_key(
36937 GATEWAY_API_KEY_REQUEST,
36938 serde_yaml::Value::String("30s".into()),
36939 );
36940 inner
36941 });
36942 m.insert_str_key(GATEWAY_API_KEY_TIMEOUTS, existing.clone());
36943 let prior = m.insert_str_key_if_some(GATEWAY_API_KEY_TIMEOUTS, Some(&overlay));
36944 assert_eq!(
36945 prior,
36946 Some(existing),
36947 "insert_str_key_if_some(K, Some(&V)) returns the prior value \
36948 when replacing an existing key — the overlay overrides the \
36949 author-provided sub-block; the prior value surfaces so the \
36950 caller can log/compare/roll back if needed"
36951 );
36952 // Value at the key is now the overlay, verbatim.
36953 let got = m
36954 .get(GATEWAY_API_KEY_TIMEOUTS)
36955 .expect("key is still present after replace");
36956 assert_eq!(
36957 got, &overlay,
36958 "replaced value is now the most-recently-inserted overlay — \
36959 the Some arm carries through to the underlying \
36960 `insert_str_key` replace path"
36961 );
36962 }
36963
36964 #[test]
36965 fn mapping_ext_insert_str_key_if_some_matches_hand_written_composition() {
36966 // Cross-check the trait method against the hand-written
36967 // `if let Some(x) = &overlay { m.insert_str_key(K, x.clone()); }`
36968 // three-line block the 3 lifted `caixa-mesh` overlay call sites
36969 // previously carried. A drift between the trait method's
36970 // conditional-insert routing and the inline `if let Some`
36971 // composition would silently emit a different Mapping (a
36972 // present-key `Value::Null` on the None arm, a different clone-
36973 // vs-move policy on the Some arm) at every routed consumer —
36974 // pin the equivalence so the trait remains a drop-in replacement.
36975 // Four cases pin the shape end-to-end: None arm (skip), Some
36976 // arm on absent key (fresh insert), Some arm on present key
36977 // (replace-and-return-prior), None arm on present key (no
36978 // touch — the axis's load-bearing "author's value wins when
36979 // overlay is unset" contract).
36980 let overlay = serde_yaml::Value::Mapping({
36981 let mut inner = serde_yaml::Mapping::new();
36982 inner.insert_str_key(
36983 CILIUM_KEY_MODE,
36984 serde_yaml::Value::String("required".into()),
36985 );
36986 inner
36987 });
36988
36989 // Case 1: None arm on empty mapping — both routes no-op.
36990 let mut via_trait_none = serde_yaml::Mapping::new();
36991 via_trait_none.insert_str_key_if_some(CILIUM_KEY_AUTHENTICATION, None);
36992 let via_inline_none = serde_yaml::Mapping::new();
36993 let overlay_slot_none: Option<serde_yaml::Value> = None;
36994 let mut via_inline_none_mut = via_inline_none.clone();
36995 if let Some(a) = &overlay_slot_none {
36996 via_inline_none_mut.insert_str_key(CILIUM_KEY_AUTHENTICATION, a.clone());
36997 }
36998 assert_eq!(
36999 via_trait_none, via_inline_none_mut,
37000 "insert_str_key_if_some(K, None) must byte-equal \
37001 `if let Some(_) = None {{ … }}` — the no-op arm must not \
37002 emit a stray `Value::Null` under the key"
37003 );
37004
37005 // Case 2: Some arm on empty mapping — both routes fresh-insert.
37006 let mut via_trait_some = serde_yaml::Mapping::new();
37007 via_trait_some.insert_str_key_if_some(CILIUM_KEY_AUTHENTICATION, Some(&overlay));
37008 let mut via_inline_some = serde_yaml::Mapping::new();
37009 let overlay_slot_some = Some(overlay.clone());
37010 if let Some(a) = &overlay_slot_some {
37011 via_inline_some.insert_str_key(CILIUM_KEY_AUTHENTICATION, a.clone());
37012 }
37013 assert_eq!(
37014 via_trait_some, via_inline_some,
37015 "insert_str_key_if_some(K, Some(&V)) must byte-equal \
37016 `if let Some(x) = &Some(V.clone()) {{ m.insert_str_key(K, \
37017 x.clone()); }}` on the fresh-insert path — same clone-and-\
37018 insert semantics under the same Value::String-promoted \
37019 bucket"
37020 );
37021
37022 // Case 3: Some arm on present key — both routes replace-and-
37023 // return-prior.
37024 let existing = serde_yaml::Value::String("cluster-default".into());
37025 let mut via_trait_replace = serde_yaml::Mapping::new();
37026 via_trait_replace.insert_str_key(GATEWAY_API_KEY_TIMEOUTS, existing.clone());
37027 let trait_prior =
37028 via_trait_replace.insert_str_key_if_some(GATEWAY_API_KEY_TIMEOUTS, Some(&overlay));
37029 let mut via_inline_replace = serde_yaml::Mapping::new();
37030 via_inline_replace.insert_str_key(GATEWAY_API_KEY_TIMEOUTS, existing.clone());
37031 let overlay_slot_replace = Some(overlay.clone());
37032 let inline_prior = if let Some(a) = &overlay_slot_replace {
37033 via_inline_replace.insert_str_key(GATEWAY_API_KEY_TIMEOUTS, a.clone())
37034 } else {
37035 None
37036 };
37037 assert_eq!(
37038 trait_prior, inline_prior,
37039 "insert_str_key_if_some replace-and-return-prior must byte-\
37040 equal the hand-written `if let Some {{ insert_str_key }}` \
37041 composition's return"
37042 );
37043 assert_eq!(
37044 via_trait_replace, via_inline_replace,
37045 "insert_str_key_if_some replace-post-state must byte-equal \
37046 the hand-written composition's post-state — the overlay \
37047 overrode the author's value in both routes"
37048 );
37049
37050 // Case 4: None arm on present key — both routes preserve the
37051 // author's value verbatim. The load-bearing "author's value
37052 // wins when overlay is unset" contract the three lifted sites
37053 // depend on.
37054 let mut via_trait_preserve = serde_yaml::Mapping::new();
37055 via_trait_preserve.insert_str_key(GATEWAY_API_KEY_TIMEOUTS, existing.clone());
37056 via_trait_preserve.insert_str_key_if_some(GATEWAY_API_KEY_TIMEOUTS, None);
37057 let mut via_inline_preserve = serde_yaml::Mapping::new();
37058 via_inline_preserve.insert_str_key(GATEWAY_API_KEY_TIMEOUTS, existing.clone());
37059 let overlay_slot_preserve: Option<serde_yaml::Value> = None;
37060 if let Some(a) = &overlay_slot_preserve {
37061 via_inline_preserve.insert_str_key(GATEWAY_API_KEY_TIMEOUTS, a.clone());
37062 }
37063 assert_eq!(
37064 via_trait_preserve, via_inline_preserve,
37065 "insert_str_key_if_some(K, None) on a present key must byte-\
37066 equal the hand-written `if let Some(_) = None {{ … }}` — \
37067 the None arm must preserve the author's value verbatim, \
37068 not clobber it with `Value::Null` or drop the key"
37069 );
37070 assert_eq!(
37071 via_trait_preserve
37072 .get(GATEWAY_API_KEY_TIMEOUTS)
37073 .expect("None arm preserves the pre-existing key"),
37074 &existing,
37075 "None arm on a present key surfaces the author's prior \
37076 value verbatim — the load-bearing contract the three \
37077 lifted `:politicas` overlay sites rest on"
37078 );
37079 }
37080
37081 // ── SequenceExt::push_mapping — Vec<Value>-side sibling ──────────────
37082
37083 #[test]
37084 fn sequence_ext_push_mapping_appends_promoted_mapping_value() {
37085 // The method appends the caller's `Mapping` as a fresh
37086 // `Value::Mapping(_)` element on the tail of `self`. Pin the
37087 // per-append routing (`.push(Value::Mapping(_))`) so a future
37088 // refactor that reaches for a different outer variant (a
37089 // Server-Side-Apply-typed `Value::Tagged`, a fresh singleton-list
37090 // wrap via `singleton_mapping_sequence`) or a different
37091 // Vec-mutation shape (e.g. `.insert(0, _)` shifting the axis
37092 // from append to prepend) is a compile-visible break, not a
37093 // silent per-consumer regression at the 4 lifted `caixa-mesh`
37094 // append sites — where the emission order is load-bearing (the
37095 // Cilium `spec.ingress[].toPorts[]` per-edge order, the
37096 // Gateway API `spec.rules[]` per-path order, the top-level CNP
37097 // and programs.yaml document order all depend on the append
37098 // semantics).
37099 let mut seq: Vec<serde_yaml::Value> = Vec::new();
37100 let mut m = serde_yaml::Mapping::new();
37101 m.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("first".into()));
37102 seq.push_mapping(m.clone());
37103 assert_eq!(
37104 seq.len(),
37105 1,
37106 "push_mapping must append exactly one element — the axis's \
37107 fresh-element semantic"
37108 );
37109 assert_eq!(
37110 seq[0],
37111 serde_yaml::Value::Mapping(m),
37112 "the appended element must be the caller's Mapping wrapped \
37113 verbatim as Value::Mapping — no reshape, no clone-and-drop"
37114 );
37115 }
37116
37117 #[test]
37118 fn sequence_ext_push_mapping_preserves_prior_elements_in_insertion_order() {
37119 // Successive push_mapping calls preserve the caller's per-
37120 // iteration order — the Vec grows at the tail, prior elements
37121 // stay at their prior indices. Pin the insertion-order semantic
37122 // so a future refactor that reaches for a per-append sort /
37123 // dedup / hoist-to-front reordering is a test-visible break,
37124 // not a silent behavior shift at the 4 lifted `caixa-mesh`
37125 // append sites (where THEORY.md §V.2.7 render determinism
37126 // pins the per-iteration emission order to the source
37127 // `:contratos` / `:paths` / `:membros` declaration order).
37128 let mut seq: Vec<serde_yaml::Value> = Vec::new();
37129 let mut first = serde_yaml::Mapping::new();
37130 first.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("a".into()));
37131 let mut second = serde_yaml::Mapping::new();
37132 second.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("b".into()));
37133 let mut third = serde_yaml::Mapping::new();
37134 third.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("c".into()));
37135 seq.push_mapping(first.clone());
37136 seq.push_mapping(second.clone());
37137 seq.push_mapping(third.clone());
37138 assert_eq!(
37139 seq.len(),
37140 3,
37141 "three push_mapping calls append three elements"
37142 );
37143 assert_eq!(
37144 seq,
37145 vec![
37146 serde_yaml::Value::Mapping(first),
37147 serde_yaml::Value::Mapping(second),
37148 serde_yaml::Value::Mapping(third),
37149 ],
37150 "push_mapping preserves per-iteration insertion order — the \
37151 axis's render-determinism contract at the 4 lifted \
37152 `caixa-mesh` append sites"
37153 );
37154 }
37155
37156 #[test]
37157 fn sequence_ext_push_mapping_matches_hand_written_composition() {
37158 // Cross-check the trait method against the hand-written
37159 // `<vec>.push(serde_yaml::Value::Mapping(<M>))` three-token
37160 // block the 4 lifted `caixa-mesh` append call sites previously
37161 // carried. A drift between the trait method's routing and the
37162 // inline `Value::Mapping(_)` promotion would silently emit a
37163 // different `Vec<Value>` (a different outer variant on the
37164 // appended element, a different length, a different order) at
37165 // every routed consumer — pin the equivalence so the trait
37166 // remains a drop-in replacement across the fresh-empty, prior-
37167 // populated, and empty-payload cases.
37168
37169 // Case 1: fresh-empty Vec + non-empty Mapping payload.
37170 let mut inner = serde_yaml::Mapping::new();
37171 inner.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("policy-a".into()));
37172 let mut via_trait: Vec<serde_yaml::Value> = Vec::new();
37173 via_trait.push_mapping(inner.clone());
37174 let mut via_inline: Vec<serde_yaml::Value> = Vec::new();
37175 via_inline.push(serde_yaml::Value::Mapping(inner.clone()));
37176 assert_eq!(
37177 via_trait, via_inline,
37178 "push_mapping(M) on empty Vec must byte-equal \
37179 `.push(Value::Mapping(M))` — same variant-promotion, same \
37180 append semantics"
37181 );
37182
37183 // Case 2: prior-populated Vec + non-empty Mapping payload — pin
37184 // that the append fires at the tail, not at the head or the
37185 // middle.
37186 let seed = serde_yaml::Value::String("seed".into());
37187 let mut via_trait_populated: Vec<serde_yaml::Value> = vec![seed.clone()];
37188 via_trait_populated.push_mapping(inner.clone());
37189 let mut via_inline_populated: Vec<serde_yaml::Value> = vec![seed];
37190 via_inline_populated.push(serde_yaml::Value::Mapping(inner.clone()));
37191 assert_eq!(
37192 via_trait_populated, via_inline_populated,
37193 "push_mapping(M) on populated Vec must byte-equal \
37194 `.push(Value::Mapping(M))` — the append fires at the tail, \
37195 prior elements stay at their prior indices"
37196 );
37197
37198 // Case 3: empty Mapping payload — the axis's "empty-vs-absent"
37199 // distinction the 4 lifted sites rest on. An empty inner
37200 // `Mapping` still round-trips as a `Value::Mapping(<empty>)`
37201 // element, not as a skipped no-op, because some K8s CRD schemas
37202 // (Cilium CNP `spec.ingress[].toPorts[].rules.http[]` with an
37203 // empty match set) require an empty inner object to distinguish
37204 // "explicitly-empty" from "absent".
37205 let mut via_trait_empty: Vec<serde_yaml::Value> = Vec::new();
37206 via_trait_empty.push_mapping(serde_yaml::Mapping::new());
37207 let mut via_inline_empty: Vec<serde_yaml::Value> = Vec::new();
37208 via_inline_empty.push(serde_yaml::Value::Mapping(serde_yaml::Mapping::new()));
37209 assert_eq!(
37210 via_trait_empty, via_inline_empty,
37211 "push_mapping(empty Mapping) must byte-equal \
37212 `.push(Value::Mapping(empty))` — no is_empty()-guarded \
37213 short-circuit, no skip"
37214 );
37215 assert_eq!(
37216 via_trait_empty.len(),
37217 1,
37218 "push_mapping on an empty Mapping still appends one element \
37219 — the axis carries no is_empty() short-circuit"
37220 );
37221 }
37222
37223 #[test]
37224 fn singleton_mapping_sequence_wraps_input_as_sole_element() {
37225 // The helper wraps its input `Mapping` as the single element of
37226 // a `Value::Sequence`. Pin the outer variant shape and the
37227 // exactly-one-element length so a future refactor that reaches
37228 // for a different container (e.g. `Value::Tagged`, a
37229 // 0-or-1-element `Option`-shaped emission axis) is a
37230 // compile-visible break, not a silent per-caller regression at
37231 // every K8s-CRD-list-shape-required emit site.
37232 let mut inner = serde_yaml::Mapping::new();
37233 inner.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("hello".into()));
37234 let out = singleton_mapping_sequence(inner.clone());
37235 match out {
37236 serde_yaml::Value::Sequence(seq) => {
37237 assert_eq!(
37238 seq.len(),
37239 1,
37240 "singleton_mapping_sequence emits exactly one element — \
37241 the K8s-CRD-list-shape-required singleton axis"
37242 );
37243 assert_eq!(
37244 seq[0],
37245 serde_yaml::Value::Mapping(inner),
37246 "the sole element must be the caller's Mapping wrapped \
37247 verbatim as Value::Mapping — no reshape, no clone-and-drop"
37248 );
37249 }
37250 other => panic!(
37251 "singleton_mapping_sequence must return Value::Sequence, got {other:?} — \
37252 an outer-variant drift breaks every K8s-CRD-list-shape consumer"
37253 ),
37254 }
37255 }
37256
37257 #[test]
37258 fn singleton_mapping_sequence_preserves_empty_inner_mapping() {
37259 // An empty inner `Mapping` still round-trips through the helper
37260 // as a `Value::Sequence(vec![Value::Mapping(<empty>)])` — the
37261 // helper carries no "skip-empty" short-circuit (empty-vs-absent
37262 // is the caller's decision; some K8s CRD schemas require an
37263 // empty inner object to distinguish "explicitly-empty" from
37264 // "absent"). Pin the shape so a future refactor that reaches
37265 // for an is_empty()-guarded short-circuit is a test-visible
37266 // break, not a silent behavior shift.
37267 let out = singleton_mapping_sequence(serde_yaml::Mapping::new());
37268 let seq = match out {
37269 serde_yaml::Value::Sequence(s) => s,
37270 other => panic!("expected Value::Sequence, got {other:?}"),
37271 };
37272 assert_eq!(seq.len(), 1, "empty inner still wraps as a 1-element seq");
37273 assert_eq!(
37274 seq[0],
37275 serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
37276 "the sole element is an empty Value::Mapping, verbatim"
37277 );
37278 }
37279
37280 #[test]
37281 fn singleton_mapping_sequence_byte_equals_hand_written_inline_shape() {
37282 // Cross-check the helper against the hand-written
37283 // `Value::Sequence(vec![Value::Mapping(m)])` three-token shape
37284 // the seven lifted call sites previously carried. A drift
37285 // between the helper's wrapping and the inline shape would
37286 // silently emit a different YAML sequence (a differently-shaped
37287 // outer variant, a differently-wrapped inner Mapping) at every
37288 // routed consumer — pin the byte-equivalence so the helper
37289 // remains a drop-in replacement.
37290 let mut inner = serde_yaml::Mapping::new();
37291 inner.insert_str_key(
37292 GATEWAY_API_KEY_NAME,
37293 serde_yaml::Value::String("gw-listener".into()),
37294 );
37295 inner.insert_str_key(
37296 KUBE_KEY_PORT,
37297 serde_yaml::Value::Number(GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT.into()),
37298 );
37299
37300 let via_helper = singleton_mapping_sequence(inner.clone());
37301 let via_inline = serde_yaml::Value::Sequence(vec![serde_yaml::Value::Mapping(inner)]);
37302
37303 assert_eq!(
37304 via_helper, via_inline,
37305 "singleton_mapping_sequence(m) must byte-equal \
37306 Value::Sequence(vec![Value::Mapping(m)]) — otherwise the \
37307 seven routed caixa-mesh call sites drift silently at emit time"
37308 );
37309 }
37310
37311 #[test]
37312 fn string_keyed_entries_yields_each_string_key_and_value_ref() {
37313 // The lift's load-bearing contract: given a Value::Mapping with
37314 // string keys, yield each `(&str, &Value)` pair in insertion
37315 // order. Both routed renderers (caixa-flux::programs_yaml_entry
37316 // and caixa-helm::build_values_yaml) depend on the yielded pair
37317 // shape to drive their per-destination insert — a drift in
37318 // yielded item type is a compile-visible break, not a silent
37319 // shape shift.
37320 let mut spec = serde_yaml::Mapping::new();
37321 spec.insert_str_key(
37322 COMPUTEUNIT_SPEC_KEY_MODULE,
37323 serde_yaml::Value::String("oci://…".into()),
37324 );
37325 spec.insert_str_key(
37326 COMPUTEUNIT_SPEC_KEY_TRIGGER,
37327 serde_yaml::Value::String("http".into()),
37328 );
37329 spec.insert_str_key(
37330 COMPUTEUNIT_SPEC_KEY_CAPABILITIES,
37331 serde_yaml::Value::Sequence(vec![]),
37332 );
37333 let v = serde_yaml::Value::Mapping(spec);
37334 let keys: Vec<&str> = string_keyed_entries(&v).map(|(k, _)| k).collect();
37335 assert_eq!(
37336 keys,
37337 vec![
37338 COMPUTEUNIT_SPEC_KEY_MODULE,
37339 COMPUTEUNIT_SPEC_KEY_TRIGGER,
37340 COMPUTEUNIT_SPEC_KEY_CAPABILITIES,
37341 ],
37342 "string_keyed_entries must yield every string-keyed entry in \
37343 the underlying Mapping's insertion order — both routed \
37344 renderers depend on `spec.module` reaching the destination \
37345 ahead of `spec.trigger` ahead of `spec.capabilities` so the \
37346 emitted values.yaml / programs.yaml entry's key order tracks \
37347 the upstream ComputeUnit YAML author's order"
37348 );
37349 // The paired &Value ref also reaches through — sanity-check on
37350 // the second axis of the yielded tuple.
37351 let module = string_keyed_entries(&v)
37352 .find(|(k, _)| *k == COMPUTEUNIT_SPEC_KEY_MODULE)
37353 .map(|(_, v)| v.clone())
37354 .expect("module entry present");
37355 assert_eq!(module, serde_yaml::Value::String("oci://…".into()));
37356 }
37357
37358 #[test]
37359 fn string_keyed_entries_short_circuits_on_non_mapping_shapes() {
37360 // The prior inline `if let Value::Mapping(_) = spec { … }` arm
37361 // silently no-oped on every non-Mapping shape (Null / String /
37362 // Sequence / Number / Bool). The lift's iterator surface pins
37363 // the same contract: a non-Mapping Value contributes zero
37364 // yielded entries. Pinned because both routed renderers'
37365 // "always splice `spec.*` if it's a Mapping, otherwise skip"
37366 // contract is upstream-schema-validated at the ComputeUnit CRD
37367 // parser but not at the renderer entry point — so a legally-
37368 // authored `spec: null` short-circuits without raising.
37369 for shape in [
37370 serde_yaml::Value::Null,
37371 serde_yaml::Value::String("scalar".into()),
37372 serde_yaml::Value::Sequence(vec![]),
37373 serde_yaml::Value::Number(0.into()),
37374 serde_yaml::Value::Bool(false),
37375 ] {
37376 let count = string_keyed_entries(&shape).count();
37377 assert_eq!(
37378 count, 0,
37379 "string_keyed_entries({shape:?}) must yield zero entries — \
37380 the prior `if let Value::Mapping(_)` arm silently \
37381 short-circuited on this shape, so the lift must preserve \
37382 that no-op contract or every routed renderer regresses on \
37383 the legally-authored non-Mapping `spec:` axis"
37384 );
37385 }
37386 }
37387
37388 #[test]
37389 fn string_keyed_entries_drops_non_string_keys() {
37390 // serde_yaml permits arbitrary `Value` keys — numeric, boolean,
37391 // sub-mapping — that don't round-trip through the downstream
37392 // K8s YAML-key surface (which requires string keys). Both
37393 // routed renderers previously carried an inline `if let Some(s)
37394 // = k.as_str()` filter to silently drop these; pin the lift's
37395 // filter contract so a future refactor that reaches for
37396 // `.as_str().unwrap()` (which would panic on a numeric key) is
37397 // a test-visible break, not a runtime regression at the first
37398 // ComputeUnit YAML that carries one.
37399 let mut spec = serde_yaml::Mapping::new();
37400 spec.insert(
37401 serde_yaml::Value::String(COMPUTEUNIT_SPEC_KEY_MODULE.into()),
37402 serde_yaml::Value::String("oci://…".into()),
37403 );
37404 spec.insert(
37405 serde_yaml::Value::Number(42.into()),
37406 serde_yaml::Value::String("dropped".into()),
37407 );
37408 spec.insert(
37409 serde_yaml::Value::Bool(true),
37410 serde_yaml::Value::String("also-dropped".into()),
37411 );
37412 spec.insert(
37413 serde_yaml::Value::String(COMPUTEUNIT_SPEC_KEY_TRIGGER.into()),
37414 serde_yaml::Value::String("http".into()),
37415 );
37416 let v = serde_yaml::Value::Mapping(spec);
37417 let keys: Vec<&str> = string_keyed_entries(&v).map(|(k, _)| k).collect();
37418 assert_eq!(
37419 keys,
37420 vec![COMPUTEUNIT_SPEC_KEY_MODULE, COMPUTEUNIT_SPEC_KEY_TRIGGER],
37421 "string_keyed_entries must silently drop non-string-keyed \
37422 entries (Value::Number, Value::Bool, Value::Mapping keys) \
37423 — the K8s YAML-key surface downstream requires string keys, \
37424 and every routed renderer's inline `k.as_str()` filter \
37425 expected exactly this drop-not-panic contract"
37426 );
37427 }
37428
37429 #[test]
37430 fn string_keyed_entries_matches_prior_inline_walk() {
37431 // Cross-check the helper's yielded sequence against the prior
37432 // inline `if let Value::Mapping(_) = spec { for (k, v) in _ {
37433 // if let Some(s) = k.as_str() { <collect (s, v.clone())> } } }`
37434 // walk both renderers previously carried. A drift between the
37435 // helper's yielded sequence and the inline walk would silently
37436 // emit a different destination map at every routed consumer —
37437 // pin the byte-equivalence so the helper remains a drop-in
37438 // replacement for both renderers' prior five-line block.
37439 let mut spec = serde_yaml::Mapping::new();
37440 spec.insert_str_key(
37441 COMPUTEUNIT_SPEC_KEY_MODULE,
37442 serde_yaml::Value::String("oci://ghcr.io/pleme-io/hello-rio:0.1.0".into()),
37443 );
37444 spec.insert(
37445 serde_yaml::Value::Number(1.into()),
37446 serde_yaml::Value::String("silently-dropped".into()),
37447 );
37448 spec.insert_str_key(
37449 COMPUTEUNIT_SPEC_KEY_TRIGGER,
37450 serde_yaml::Value::String("http".into()),
37451 );
37452 let v = serde_yaml::Value::Mapping(spec);
37453
37454 let via_helper: Vec<(String, serde_yaml::Value)> = string_keyed_entries(&v)
37455 .map(|(k, v)| (k.to_string(), v.clone()))
37456 .collect();
37457
37458 let mut via_inline: Vec<(String, serde_yaml::Value)> = Vec::new();
37459 if let serde_yaml::Value::Mapping(map) = &v {
37460 for (k, v) in map {
37461 if let Some(s) = k.as_str() {
37462 via_inline.push((s.to_string(), v.clone()));
37463 }
37464 }
37465 }
37466
37467 assert_eq!(
37468 via_helper, via_inline,
37469 "string_keyed_entries must yield the same (String, Value) \
37470 sequence as the prior inline `if let Value::Mapping + for + \
37471 if let Some(k.as_str())` walk — otherwise the two routed \
37472 renderers drift silently at ComputeUnit-YAML-`spec.*`-splice \
37473 time"
37474 );
37475 }
37476
37477 #[test]
37478 fn kube_metadata_str_field_reads_metadata_name_and_namespace_string_scalars() {
37479 // The lift's load-bearing contract: given a Value carrying a
37480 // top-level `metadata: { name: <str>, namespace: <str> }` block
37481 // (every K8s CR document the emit-side `kube_resource_skeleton`
37482 // renders), the helper returns Some(<str>) borrowing into the
37483 // input Value. Pinned because every routed test-side site (the
37484 // six caixa-mesh CNP filters + the caixa-flux kustomization.yaml
37485 // pin) reaches through this exact string-scalar readback, and a
37486 // drift in the borrowed-string contract would silently regress
37487 // every routed site's per-CR filter equality.
37488 let mut metadata = serde_yaml::Mapping::new();
37489 metadata.insert_str_key(
37490 KUBE_KEY_NAME,
37491 serde_yaml::Value::String("checkout-cart-to-catalog".into()),
37492 );
37493 metadata.insert_str_key(
37494 KUBE_KEY_NAMESPACE,
37495 serde_yaml::Value::String(DEFAULT_NAMESPACE.into()),
37496 );
37497 let mut cr = serde_yaml::Mapping::new();
37498 cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
37499 let value = serde_yaml::Value::Mapping(cr);
37500
37501 assert_eq!(
37502 kube_metadata_str_field(&value, KUBE_KEY_NAME),
37503 Some("checkout-cart-to-catalog"),
37504 "kube_metadata_str_field must read metadata.name as a string \
37505 scalar — the six caixa-mesh CNP per-`(:de, :para)` filter \
37506 sites reach through this axis for policy-identity equality"
37507 );
37508 assert_eq!(
37509 kube_metadata_str_field(&value, KUBE_KEY_NAMESPACE),
37510 Some(DEFAULT_NAMESPACE),
37511 "kube_metadata_str_field must read metadata.namespace as a \
37512 string scalar — the caixa-flux programs_yaml_entry \
37513 production readback + the cluster_bundle kustomization.yaml \
37514 test pin both reach through this axis"
37515 );
37516 }
37517
37518 #[test]
37519 fn kube_metadata_str_field_returns_none_when_metadata_block_absent() {
37520 // Every K8s CR document the emit-side `kube_resource_skeleton`
37521 // renders carries a `metadata:` block, but the readback surface
37522 // is called on arbitrary Value inputs (upstream ComputeUnit
37523 // YAML documents, external YAML documents parsed by tests) that
37524 // may legally omit the block. The prior inline three-hop chain
37525 // silently short-circuits on the first `.get(KUBE_KEY_METADATA)`
37526 // hop when the block is absent; pin the helper's None return so
37527 // the prior no-panic contract holds. The two production-shape
37528 // paths — caixa-flux's `programs_yaml_entry` production
37529 // readback with `.unwrap_or(DEFAULT_NAMESPACE)` fallback, the
37530 // caixa-mesh test-side `.unwrap()` after equality-filter —
37531 // both depend on this None-arm for their fallback / test-harness
37532 // semantics.
37533 let value = serde_yaml::Value::Mapping(serde_yaml::Mapping::new());
37534 assert_eq!(
37535 kube_metadata_str_field(&value, KUBE_KEY_NAME),
37536 None,
37537 "kube_metadata_str_field must short-circuit to None when the \
37538 top-level `metadata:` block is absent — the prior inline \
37539 chain's `.get(KUBE_KEY_METADATA)` outer hop returned None \
37540 here, and every routed caller (production fallback + test \
37541 expect) depends on the None-arm reaching through"
37542 );
37543
37544 // Also verify the shape on a non-Mapping outer Value — the K8s
37545 // CR readback surface accepts arbitrary Value inputs, including
37546 // the Value::Null / Value::Sequence / Value::String shapes an
37547 // external YAML document may parse into.
37548 for shape in [
37549 serde_yaml::Value::Null,
37550 serde_yaml::Value::String("scalar".into()),
37551 serde_yaml::Value::Sequence(vec![]),
37552 serde_yaml::Value::Number(0.into()),
37553 serde_yaml::Value::Bool(false),
37554 ] {
37555 assert_eq!(
37556 kube_metadata_str_field(&shape, KUBE_KEY_NAME),
37557 None,
37558 "kube_metadata_str_field({shape:?}, KUBE_KEY_NAME) must \
37559 return None on non-Mapping shapes — the prior inline \
37560 `.get(KUBE_KEY_METADATA)` hop yields None on every \
37561 non-Mapping Value, and the lift must preserve that \
37562 contract"
37563 );
37564 }
37565 }
37566
37567 #[test]
37568 fn kube_metadata_str_field_returns_none_when_requested_field_absent() {
37569 // A `metadata:` block present but missing the requested axis-key
37570 // — a well-formed K8s CR that legally omits the requested field
37571 // (a Cluster-scoped CR omits `metadata.namespace`, a
37572 // Server-Side-Apply-authored CR omits `metadata.name` in favor
37573 // of `metadata.generateName`). Every routed caller expects the
37574 // three-hop chain to short-circuit through here to None; pin
37575 // the middle-hop None-arm so a future refactor that reaches for
37576 // `.get(field).unwrap()` (which would panic on a legally-omitted
37577 // axis-key) is a test-visible break.
37578 let mut metadata = serde_yaml::Mapping::new();
37579 metadata.insert_str_key(
37580 KUBE_KEY_NAME,
37581 serde_yaml::Value::String("cluster-scoped-cr".into()),
37582 );
37583 let mut cr = serde_yaml::Mapping::new();
37584 cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
37585 let value = serde_yaml::Value::Mapping(cr);
37586 assert_eq!(
37587 kube_metadata_str_field(&value, KUBE_KEY_NAMESPACE),
37588 None,
37589 "kube_metadata_str_field must return None when the requested \
37590 `metadata.<field>` axis-key is absent — the prior inline \
37591 chain's middle `.and_then(|m| m.get(<FIELD>))` hop short- \
37592 circuited here, and the lift must preserve that None-arm \
37593 for every legally-omitted axis-key"
37594 );
37595 }
37596
37597 #[test]
37598 fn kube_metadata_str_field_returns_none_when_field_carries_non_string_type() {
37599 // A `metadata.<field>` axis-key present but carrying a non-
37600 // string YAML type — schema-invalid per the K8s apiserver's
37601 // OpenAPI schema but tolerated here as None so the readback
37602 // stays a total function. The prior inline chain's trailing
37603 // `.and_then(|n| n.as_str())` shape gate silently short-
37604 // circuits here; pin the helper's None-arm so a future refactor
37605 // that reaches for `.as_str().unwrap()` (which would panic on
37606 // a numeric axis-value) is a test-visible break, not a runtime
37607 // regression at the first schema-invalid CR the reader sees.
37608 for non_string in [
37609 serde_yaml::Value::Null,
37610 serde_yaml::Value::Number(42.into()),
37611 serde_yaml::Value::Bool(true),
37612 serde_yaml::Value::Sequence(vec![]),
37613 serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
37614 ] {
37615 let mut metadata = serde_yaml::Mapping::new();
37616 metadata.insert_str_key(KUBE_KEY_NAME, non_string.clone());
37617 let mut cr = serde_yaml::Mapping::new();
37618 cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
37619 let value = serde_yaml::Value::Mapping(cr);
37620 assert_eq!(
37621 kube_metadata_str_field(&value, KUBE_KEY_NAME),
37622 None,
37623 "kube_metadata_str_field must return None when \
37624 metadata.name carries a non-string YAML type ({non_string:?}) \
37625 — the prior inline chain's `.and_then(|n| n.as_str())` \
37626 shape gate short-circuited here, and every routed caller \
37627 depends on that None-arm to keep the readback total"
37628 );
37629 }
37630 }
37631
37632 #[test]
37633 fn kube_metadata_str_field_matches_prior_inline_chain() {
37634 // Cross-check the helper's output byte-for-byte against the
37635 // prior inline three-hop chain both routed callers previously
37636 // carried. A drift between the helper's return and the inline
37637 // chain would silently regress every routed test-side filter's
37638 // equality comparison + the caixa-flux production readback's
37639 // fallback semantics — pin the byte-equivalence so the helper
37640 // remains a drop-in replacement for every routed site's prior
37641 // three-line block.
37642 let mut metadata = serde_yaml::Mapping::new();
37643 metadata.insert_str_key(
37644 KUBE_KEY_NAME,
37645 serde_yaml::Value::String("checkout-payment-to-cart".into()),
37646 );
37647 metadata.insert_str_key(
37648 KUBE_KEY_NAMESPACE,
37649 serde_yaml::Value::String(DEFAULT_NAMESPACE.into()),
37650 );
37651 let mut cr = serde_yaml::Mapping::new();
37652 cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
37653 let value = serde_yaml::Value::Mapping(cr);
37654
37655 for field in [KUBE_KEY_NAME, KUBE_KEY_NAMESPACE] {
37656 let via_helper = kube_metadata_str_field(&value, field);
37657 let via_inline = value
37658 .get(KUBE_KEY_METADATA)
37659 .and_then(|m| m.get(field))
37660 .and_then(|n| n.as_str());
37661 assert_eq!(
37662 via_helper, via_inline,
37663 "kube_metadata_str_field(_, {field:?}) must yield the same \
37664 Option<&str> as the prior inline three-hop chain — \
37665 otherwise every routed caller's equality-filter / \
37666 production-fallback drifts silently at readback time"
37667 );
37668 }
37669 }
37670
37671 #[test]
37672 fn kube_root_str_field_reads_api_version_and_kind_string_scalars() {
37673 // The lift's load-bearing contract: given a Value carrying
37674 // top-level `apiVersion:` + `kind:` string scalars (every K8s
37675 // CR document the emit-side `kube_resource_skeleton` renders
37676 // spells the pair by construction), the helper returns
37677 // Some(<str>) borrowing into the input Value on both axes.
37678 // Pinned because every routed test-side site — the
37679 // caixa-flux `cluster_bundle_*_uses_lifted_flux_api_version`
37680 // per-document apiVersion pins + the caixa-mesh
37681 // `gateway_routes` per-`(Gateway, HTTPRoute)` kind-filter
37682 // + the sibling caixa-mesh
37683 // `cilium_authentication_mode_serialized_as_yaml_string`
37684 // CNP-kind filter — reaches through this exact top-level
37685 // string-scalar readback, and a drift in the borrowed-string
37686 // contract would silently regress every routed site's
37687 // per-CR filter / discriminator-pin equality.
37688 let mut cr = serde_yaml::Mapping::new();
37689 cr.insert_str_key(
37690 KUBE_KEY_API_VERSION,
37691 serde_yaml::Value::String(GATEWAY_API_API_VERSION.into()),
37692 );
37693 cr.insert_str_key(
37694 KUBE_KEY_KIND,
37695 serde_yaml::Value::String(GATEWAY_API_KIND_GATEWAY.into()),
37696 );
37697 let value = serde_yaml::Value::Mapping(cr);
37698
37699 assert_eq!(
37700 kube_root_str_field(&value, KUBE_KEY_API_VERSION),
37701 Some(GATEWAY_API_API_VERSION),
37702 "kube_root_str_field must read top-level apiVersion as a \
37703 string scalar — the caixa-flux `cluster_bundle_*_uses_\
37704 lifted_flux_api_version` pins + caixa-mesh per-CR \
37705 apiVersion pins reach through this axis for discriminator \
37706 equality"
37707 );
37708 assert_eq!(
37709 kube_root_str_field(&value, KUBE_KEY_KIND),
37710 Some(GATEWAY_API_KIND_GATEWAY),
37711 "kube_root_str_field must read top-level kind as a string \
37712 scalar — the 15 caixa-mesh `gateway_routes` per-CR find \
37713 sites reach through this axis to filter the multi-doc \
37714 emission sequence by kind discriminator"
37715 );
37716 }
37717
37718 #[test]
37719 fn kube_root_str_field_returns_none_when_field_absent() {
37720 // Every K8s CR document the emit-side `kube_resource_skeleton`
37721 // renders carries `apiVersion:` + `kind:` scalars, but the
37722 // readback surface is called on arbitrary Value inputs
37723 // (multi-doc sequences under iteration, upstream ComputeUnit
37724 // YAML documents) that may legally omit either axis-key. The
37725 // prior inline two-hop chain silently short-circuits on the
37726 // outer `.get(field)` hop when the axis is absent; pin the
37727 // helper's None return so the prior no-panic contract holds.
37728 // Also verify on non-Mapping outer Value shapes an external
37729 // YAML document may parse into.
37730 let value = serde_yaml::Value::Mapping(serde_yaml::Mapping::new());
37731 assert_eq!(
37732 kube_root_str_field(&value, KUBE_KEY_API_VERSION),
37733 None,
37734 "kube_root_str_field must short-circuit to None when the \
37735 requested top-level axis-key is absent — the prior inline \
37736 `.get(field)` outer hop returned None here, and every \
37737 routed caller (test pin + filter predicate) depends on \
37738 that None-arm reaching through"
37739 );
37740 assert_eq!(
37741 kube_root_str_field(&value, KUBE_KEY_KIND),
37742 None,
37743 "kube_root_str_field must short-circuit to None on a \
37744 missing top-level kind axis-key — every routed \
37745 caixa-mesh find-predicate compares against Some(<KIND>) \
37746 and must reject None-shaped entries silently"
37747 );
37748
37749 for shape in [
37750 serde_yaml::Value::Null,
37751 serde_yaml::Value::String("scalar".into()),
37752 serde_yaml::Value::Sequence(vec![]),
37753 serde_yaml::Value::Number(0.into()),
37754 serde_yaml::Value::Bool(false),
37755 ] {
37756 assert_eq!(
37757 kube_root_str_field(&shape, KUBE_KEY_KIND),
37758 None,
37759 "kube_root_str_field({shape:?}, KUBE_KEY_KIND) must \
37760 return None on non-Mapping shapes — the prior inline \
37761 `.get(field)` hop yields None on every non-Mapping \
37762 Value, and the lift must preserve that contract"
37763 );
37764 }
37765 }
37766
37767 #[test]
37768 fn kube_root_str_field_returns_none_when_field_carries_non_string_type() {
37769 // A top-level `<field>` axis-key present but carrying a non-
37770 // string YAML type — schema-invalid per the K8s apiserver's
37771 // OpenAPI schema but tolerated here as None so the readback
37772 // stays a total function. The prior inline chain's trailing
37773 // `.and_then(|n| n.as_str())` shape gate silently short-
37774 // circuits here; pin the helper's None-arm so a future
37775 // refactor that reaches for `.as_str().unwrap()` (which would
37776 // panic on a numeric axis-value) is a test-visible break, not
37777 // a runtime regression at the first schema-invalid CR the
37778 // reader sees.
37779 for non_string in [
37780 serde_yaml::Value::Null,
37781 serde_yaml::Value::Number(42.into()),
37782 serde_yaml::Value::Bool(true),
37783 serde_yaml::Value::Sequence(vec![]),
37784 serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
37785 ] {
37786 let mut cr = serde_yaml::Mapping::new();
37787 cr.insert_str_key(KUBE_KEY_KIND, non_string.clone());
37788 let value = serde_yaml::Value::Mapping(cr);
37789 assert_eq!(
37790 kube_root_str_field(&value, KUBE_KEY_KIND),
37791 None,
37792 "kube_root_str_field must return None when top-level \
37793 kind carries a non-string YAML type ({non_string:?}) \
37794 — the prior inline `.and_then(|n| n.as_str())` shape \
37795 gate short-circuited here, and every routed caller \
37796 depends on that None-arm to keep the readback total"
37797 );
37798 }
37799 }
37800
37801 #[test]
37802 fn kube_root_str_field_matches_prior_inline_chain() {
37803 // Cross-check the helper's output byte-for-byte against the
37804 // prior inline two-hop chain both routed renderers previously
37805 // carried. A drift between the helper's return and the inline
37806 // chain would silently regress every routed test-side filter's
37807 // equality comparison + the caixa-flux production-shape
37808 // per-document apiVersion / kind pin — pin the byte-
37809 // equivalence so the helper remains a drop-in replacement for
37810 // every routed site's prior two-line block.
37811 let mut cr = serde_yaml::Mapping::new();
37812 cr.insert_str_key(
37813 KUBE_KEY_API_VERSION,
37814 serde_yaml::Value::String(FLUX_HELMRELEASE_API_VERSION.into()),
37815 );
37816 cr.insert_str_key(
37817 KUBE_KEY_KIND,
37818 serde_yaml::Value::String(FLUX_KIND_HELM_RELEASE.into()),
37819 );
37820 let value = serde_yaml::Value::Mapping(cr);
37821
37822 for field in [KUBE_KEY_API_VERSION, KUBE_KEY_KIND] {
37823 let via_helper = kube_root_str_field(&value, field);
37824 let via_inline = value.get(field).and_then(|n| n.as_str());
37825 assert_eq!(
37826 via_helper, via_inline,
37827 "kube_root_str_field(_, {field:?}) must yield the same \
37828 Option<&str> as the prior inline two-hop chain — \
37829 otherwise every routed caller's equality-filter / \
37830 discriminator-pin drifts silently at readback time"
37831 );
37832 }
37833 }
37834
37835 #[test]
37836 fn kube_root_str_field_and_kube_metadata_str_field_bracket_the_readback_surface() {
37837 // Peer-pin: the two lifted K8s-CR readback primitives cover
37838 // orthogonal axes on the same document. Given a full K8s CR
37839 // (top-level `apiVersion:` + `kind:` discriminator pair,
37840 // sub-`metadata.name:` + `metadata.namespace:` identity pair),
37841 // each helper reaches through its own axis and the two
37842 // together enumerate every documented top-level string
37843 // scalar the substrate emits + reads back. Pin the pairing so
37844 // a future refactor that collapses the two into a single
37845 // navigation primitive (or splits one further) surfaces here
37846 // as a test-visible break, not a silent regression at the
37847 // first routed caller's per-CR readback drift.
37848 let mut metadata = serde_yaml::Mapping::new();
37849 metadata.insert_str_key(
37850 KUBE_KEY_NAME,
37851 serde_yaml::Value::String("checkout-cart-to-catalog".into()),
37852 );
37853 metadata.insert_str_key(
37854 KUBE_KEY_NAMESPACE,
37855 serde_yaml::Value::String(DEFAULT_NAMESPACE.into()),
37856 );
37857 let mut cr = serde_yaml::Mapping::new();
37858 cr.insert_str_key(
37859 KUBE_KEY_API_VERSION,
37860 serde_yaml::Value::String(CILIUM_API_VERSION.into()),
37861 );
37862 cr.insert_str_key(
37863 KUBE_KEY_KIND,
37864 serde_yaml::Value::String(CILIUM_KIND_NETWORK_POLICY.into()),
37865 );
37866 cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
37867 let value = serde_yaml::Value::Mapping(cr);
37868
37869 assert_eq!(
37870 kube_root_str_field(&value, KUBE_KEY_API_VERSION),
37871 Some(CILIUM_API_VERSION)
37872 );
37873 assert_eq!(
37874 kube_root_str_field(&value, KUBE_KEY_KIND),
37875 Some(CILIUM_KIND_NETWORK_POLICY)
37876 );
37877 assert_eq!(
37878 kube_metadata_str_field(&value, KUBE_KEY_NAME),
37879 Some("checkout-cart-to-catalog")
37880 );
37881 assert_eq!(
37882 kube_metadata_str_field(&value, KUBE_KEY_NAMESPACE),
37883 Some(DEFAULT_NAMESPACE)
37884 );
37885 }
37886
37887 #[test]
37888 fn kube_kind_is_matches_lifted_kube_root_str_field_equality_shape() {
37889 // Byte-equivalence pin: the lifted predicate reproduces the
37890 // three-token composition (`kube_root_str_field(v,
37891 // KUBE_KEY_KIND) == Some(<KIND>)`) the 15 caixa-mesh test-side
37892 // `.find`/`.filter` sites previously carried inline. Closes the
37893 // "did the lift accidentally rename the pinned scalar-key axis
37894 // to KUBE_KEY_API_VERSION or drop the `Some(...)` wrap" drift
37895 // class every future re-lift on the peer-axis surface (a
37896 // hypothetical `kube_api_version_is` peer, `kube_group_is` on a
37897 // multi-group router harness) would otherwise reopen.
37898 let mut cr = serde_yaml::Mapping::new();
37899 cr.insert_str_key(
37900 KUBE_KEY_KIND,
37901 serde_yaml::Value::String(GATEWAY_API_KIND_GATEWAY.into()),
37902 );
37903 let value = serde_yaml::Value::Mapping(cr);
37904
37905 assert!(kube_kind_is(&value, GATEWAY_API_KIND_GATEWAY));
37906 assert_eq!(
37907 kube_kind_is(&value, GATEWAY_API_KIND_GATEWAY),
37908 kube_root_str_field(&value, KUBE_KEY_KIND) == Some(GATEWAY_API_KIND_GATEWAY),
37909 );
37910 }
37911
37912 #[test]
37913 fn kube_kind_is_false_on_mismatched_kind_and_missing_kind() {
37914 // Complement-side pin: the predicate returns `false` when
37915 // either the kind axis carries a different discriminator or the
37916 // top-level `kind:` scalar is absent altogether (the same
37917 // vacuous-`None` short-circuit the parent
37918 // `kube_root_str_field` closes on the underlying two-hop
37919 // navigation). Consumer sites (`docs.iter().find(|d|
37920 // kube_kind_is(d, X))`) rely on the false-on-mismatch shape to
37921 // skip the wrong CRs across the multi-doc mesh emission and
37922 // land on the intended per-kind document.
37923 let mut cr_wrong_kind = serde_yaml::Mapping::new();
37924 cr_wrong_kind.insert_str_key(
37925 KUBE_KEY_KIND,
37926 serde_yaml::Value::String(GATEWAY_API_KIND_HTTP_ROUTE.into()),
37927 );
37928 assert!(!kube_kind_is(
37929 &serde_yaml::Value::Mapping(cr_wrong_kind),
37930 GATEWAY_API_KIND_GATEWAY,
37931 ));
37932
37933 let cr_no_kind = serde_yaml::Mapping::new();
37934 assert!(!kube_kind_is(
37935 &serde_yaml::Value::Mapping(cr_no_kind),
37936 GATEWAY_API_KIND_GATEWAY,
37937 ));
37938 }
37939
37940 #[test]
37941 fn find_by_kind_matches_inline_iter_find_kube_kind_is_shape() {
37942 // Byte-equivalence pin: the lifted navigator reproduces the
37943 // three-token combinator chain (`docs.iter().find(|d|
37944 // kube_kind_is(d, <KIND>))`) the 14 caixa-mesh test-side
37945 // per-Gateway / per-HTTPRoute find-by-kind sites previously
37946 // carried inline. Closes the "did the lift accidentally
37947 // widen the receiver, drop the closure, or swap `find` for
37948 // `filter`" drift class every future re-lift on the sibling
37949 // multi-doc-navigator axis (a hypothetical
37950 // `filter_by_kind` peer that carries the same underlying
37951 // predicate but returns an iterator) would otherwise reopen.
37952 let mut gateway = serde_yaml::Mapping::new();
37953 gateway.insert_str_key(
37954 KUBE_KEY_KIND,
37955 serde_yaml::Value::String(GATEWAY_API_KIND_GATEWAY.into()),
37956 );
37957 let mut route = serde_yaml::Mapping::new();
37958 route.insert_str_key(
37959 KUBE_KEY_KIND,
37960 serde_yaml::Value::String(GATEWAY_API_KIND_HTTP_ROUTE.into()),
37961 );
37962 let docs = vec![
37963 serde_yaml::Value::Mapping(gateway),
37964 serde_yaml::Value::Mapping(route),
37965 ];
37966
37967 // Lifted navigator agrees with the inline combinator chain
37968 // on every existing member of the multi-doc slice.
37969 assert_eq!(
37970 find_by_kind(&docs, GATEWAY_API_KIND_GATEWAY),
37971 docs.iter()
37972 .find(|d| kube_kind_is(d, GATEWAY_API_KIND_GATEWAY)),
37973 );
37974 assert_eq!(
37975 find_by_kind(&docs, GATEWAY_API_KIND_HTTP_ROUTE),
37976 docs.iter()
37977 .find(|d| kube_kind_is(d, GATEWAY_API_KIND_HTTP_ROUTE)),
37978 );
37979
37980 // And on the miss path: absent kind → None, matching the
37981 // inline `.find` short-circuit that consumer sites rely on
37982 // to distinguish "no such CR in this emission" from "wrong
37983 // shape" in their `.unwrap()` / `.expect(...)` follow-ups.
37984 assert_eq!(find_by_kind(&docs, CILIUM_KIND_NETWORK_POLICY), None);
37985 let empty: Vec<serde_yaml::Value> = Vec::new();
37986 assert_eq!(find_by_kind(&empty, GATEWAY_API_KIND_GATEWAY), None);
37987 }
37988
37989 #[test]
37990 fn find_by_kind_returns_first_match_on_duplicate_kind() {
37991 // Order-preservation pin: the lifted navigator returns the
37992 // first document of the matching kind (the same short-
37993 // circuit `Iterator::find` exposes). Multi-doc mesh
37994 // emissions never carry two documents of the same kind at
37995 // V0 (`gateway_routes` emits exactly one `Gateway` + one
37996 // `HTTPRoute` per Aplicacao), but the M4 cross-cluster
37997 // fan-out will (one `HelmRelease` per cluster). Pinning the
37998 // first-match contract keeps the M4 caller-side "the first
37999 // hit is the primary" convention aligned with the helper's
38000 // combinator half.
38001 let mut gateway_a = serde_yaml::Mapping::new();
38002 gateway_a.insert_str_key(
38003 KUBE_KEY_KIND,
38004 serde_yaml::Value::String(GATEWAY_API_KIND_GATEWAY.into()),
38005 );
38006 let mut meta_a = serde_yaml::Mapping::new();
38007 meta_a.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("primary".into()));
38008 gateway_a.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(meta_a));
38009 let mut gateway_b = serde_yaml::Mapping::new();
38010 gateway_b.insert_str_key(
38011 KUBE_KEY_KIND,
38012 serde_yaml::Value::String(GATEWAY_API_KIND_GATEWAY.into()),
38013 );
38014 let mut meta_b = serde_yaml::Mapping::new();
38015 meta_b.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("secondary".into()));
38016 gateway_b.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(meta_b));
38017 let docs = vec![
38018 serde_yaml::Value::Mapping(gateway_a),
38019 serde_yaml::Value::Mapping(gateway_b),
38020 ];
38021
38022 let first = find_by_kind(&docs, GATEWAY_API_KIND_GATEWAY).unwrap();
38023 assert_eq!(
38024 kube_metadata_str_field(first, KUBE_KEY_NAME),
38025 Some("primary"),
38026 );
38027 }
38028
38029 #[test]
38030 fn kube_name_matches_lifted_kube_metadata_str_field_readback_shape() {
38031 // Byte-equivalence pin: the lifted accessor reproduces the
38032 // two-token composition (`kube_metadata_str_field(v,
38033 // KUBE_KEY_NAME)`) the 12 caixa-mesh (9) + caixa-flux (3)
38034 // test-side per-CR readback sites previously carried inline
38035 // around the readback intent "what name did the emitter write
38036 // into this CR?". Closes the "did the lift accidentally
38037 // rename the pinned scalar-key axis to KUBE_KEY_NAMESPACE
38038 // (silently pulling the peer identity coordinate instead of
38039 // the primary), drop the axis-key argument, or widen the
38040 // return type" drift class every future re-lift on the peer-
38041 // axis surface (a hypothetical `kube_namespace` peer on the
38042 // per-CR namespace-scoping coordinate, a `kube_uid` peer for
38043 // ownerReference bookkeeping) would otherwise reopen. Peer of
38044 // the sibling `kube_name_is_matches_lifted_kube_metadata_str_field_equality_shape`
38045 // pin on the predicate-arity half of the same axis: the
38046 // accessor pin asserts the readback intent, the predicate pin
38047 // asserts the equality-wrap intent, together bracketing the
38048 // two-arity closure the identity axis carries at V0.
38049 let mut metadata = serde_yaml::Mapping::new();
38050 metadata.insert_str_key(
38051 KUBE_KEY_NAME,
38052 serde_yaml::Value::String("checkout-cart-to-catalog".into()),
38053 );
38054 let mut cr = serde_yaml::Mapping::new();
38055 cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
38056 let value = serde_yaml::Value::Mapping(cr);
38057
38058 assert_eq!(kube_name(&value), Some("checkout-cart-to-catalog"));
38059 assert_eq!(
38060 kube_name(&value),
38061 kube_metadata_str_field(&value, KUBE_KEY_NAME),
38062 "kube_name must byte-agree with the parametric \
38063 `kube_metadata_str_field(v, KUBE_KEY_NAME)` composition \
38064 it replaces at every consumer site — drift on either \
38065 half silently opens a per-CR identity readback that no \
38066 longer routes through the pinned KUBE_KEY_NAME axis-key",
38067 );
38068 }
38069
38070 #[test]
38071 fn kube_name_none_when_metadata_block_absent_or_name_absent() {
38072 // Complement-side pin: the accessor returns `None` when
38073 // either the enclosing `metadata:` block is absent (root-
38074 // level CR with no metadata mapping at all — the vacuous
38075 // shape the operator-side "not-yet-materialized" CR readback
38076 // might momentarily observe under a partial apply) or the
38077 // sub-`name:` scalar is absent inside a present `metadata:`
38078 // block (a partially-authored CR the K8s API-server would
38079 // reject at admission but that this readback tolerates as
38080 // `None` so the accessor stays a total function). Consumer
38081 // sites (`.expect(...)`, `.unwrap()`, `Some(...) == expected`
38082 // equality wraps) rely on the None-on-absence short-circuit
38083 // to distinguish "no such name on this doc" from "wrong
38084 // shape" in the follow-up. Peer of the sibling
38085 // `kube_name_is_false_on_mismatched_name_and_missing_name`
38086 // pin on the predicate-arity half — the accessor short-
38087 // circuits to `None`, the predicate short-circuits through it
38088 // to `false` — same underlying vacuous-`None` gate.
38089 let cr_no_metadata = serde_yaml::Mapping::new();
38090 assert_eq!(
38091 kube_name(&serde_yaml::Value::Mapping(cr_no_metadata)),
38092 None,
38093 "kube_name must return None when the enclosing metadata: \
38094 block is absent",
38095 );
38096
38097 let empty_meta = serde_yaml::Mapping::new();
38098 let mut cr_no_name = serde_yaml::Mapping::new();
38099 cr_no_name.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(empty_meta));
38100 assert_eq!(
38101 kube_name(&serde_yaml::Value::Mapping(cr_no_name)),
38102 None,
38103 "kube_name must return None when the sub-name: scalar is \
38104 absent inside a present metadata: block",
38105 );
38106 }
38107
38108 #[test]
38109 fn kube_name_none_when_metadata_name_carries_non_string_type() {
38110 // Type-gate pin: the accessor returns `None` when the sub-
38111 // `metadata.name:` scalar is present but carries a non-string
38112 // YAML type (a numeric, boolean, or nested mapping — invalid
38113 // K8s CR shape per the K8s API-machinery OpenAPI schema, but
38114 // tolerated here as `None` so the readback stays a total
38115 // function and defers the diagnostic to the caller's own
38116 // `.expect(...)` / `.unwrap()` follow-up which names the
38117 // caller's schema axis). Pins the type-gate half of the
38118 // accessor's contract — the axis-key pin is asserted by the
38119 // sibling byte-agreement test — so a hypothetical future
38120 // widening (accepting numeric `metadata.name: 42` as the
38121 // stringified `"42"`, an aliased YAML integer under a fresh
38122 // `Value::from` conversion) is caught before it lands.
38123 let mut metadata_int = serde_yaml::Mapping::new();
38124 metadata_int.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::from(42u64));
38125 let mut cr_int = serde_yaml::Mapping::new();
38126 cr_int.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata_int));
38127 assert_eq!(
38128 kube_name(&serde_yaml::Value::Mapping(cr_int)),
38129 None,
38130 "kube_name must return None when metadata.name carries a \
38131 non-string YAML type (numeric here)",
38132 );
38133
38134 let mut inner = serde_yaml::Mapping::new();
38135 inner.insert_str_key("nested", serde_yaml::Value::String("value".into()));
38136 let mut metadata_map = serde_yaml::Mapping::new();
38137 metadata_map.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::Mapping(inner));
38138 let mut cr_map = serde_yaml::Mapping::new();
38139 cr_map.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata_map));
38140 assert_eq!(
38141 kube_name(&serde_yaml::Value::Mapping(cr_map)),
38142 None,
38143 "kube_name must return None when metadata.name carries a \
38144 nested mapping (invalid CR shape per K8s API-machinery)",
38145 );
38146 }
38147
38148 #[test]
38149 fn kube_namespace_matches_lifted_kube_metadata_str_field_readback_shape() {
38150 // Byte-equivalence pin: the lifted accessor reproduces the
38151 // two-token composition (`kube_metadata_str_field(v,
38152 // KUBE_KEY_NAMESPACE)`) the 4 caixa-flux (1 production + 1
38153 // test) + caixa-mesh (2 test) per-CR namespace-scoping readback
38154 // sites previously carried inline around the readback intent
38155 // "what namespace did the emitter write into this CR?". Closes
38156 // the "did the lift accidentally rename the pinned scalar-key
38157 // axis to KUBE_KEY_NAME (silently pulling the peer identity
38158 // coordinate instead of the namespace-scoping one), drop the
38159 // axis-key argument, or widen the return type" drift class
38160 // every future re-lift on the peer-axis surface (a hypothetical
38161 // `kube_uid` peer for ownerReference bookkeeping, a
38162 // `kube_resource_version` peer for optimistic-concurrency
38163 // readback) would otherwise reopen. Peer of the sibling
38164 // `kube_name_matches_lifted_kube_metadata_str_field_readback_shape`
38165 // pin on the identity-axis half of the same
38166 // `metadata.{name, namespace}` per-CR coordinate pair: the
38167 // accessor pins the readback intent on both halves of the
38168 // canonical K8s API-machinery per-CR disambiguation pair
38169 // together, bracketing the two coordinates the emit-side
38170 // `kube_resource_skeleton` writes into every rendered CR.
38171 let mut metadata = serde_yaml::Mapping::new();
38172 metadata.insert_str_key(
38173 KUBE_KEY_NAMESPACE,
38174 serde_yaml::Value::String(DEFAULT_NAMESPACE.into()),
38175 );
38176 let mut cr = serde_yaml::Mapping::new();
38177 cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
38178 let value = serde_yaml::Value::Mapping(cr);
38179
38180 assert_eq!(kube_namespace(&value), Some(DEFAULT_NAMESPACE));
38181 assert_eq!(
38182 kube_namespace(&value),
38183 kube_metadata_str_field(&value, KUBE_KEY_NAMESPACE),
38184 "kube_namespace must byte-agree with the parametric \
38185 `kube_metadata_str_field(v, KUBE_KEY_NAMESPACE)` \
38186 composition it replaces at every consumer site — drift on \
38187 either half silently opens a per-CR namespace-scoping \
38188 readback that no longer routes through the pinned \
38189 KUBE_KEY_NAMESPACE axis-key",
38190 );
38191 }
38192
38193 #[test]
38194 fn kube_namespace_none_when_metadata_block_absent_or_namespace_absent() {
38195 // Complement-side pin: the accessor returns `None` when either
38196 // the enclosing `metadata:` block is absent (root-level CR with
38197 // no metadata mapping at all — the vacuous shape the operator-
38198 // side "not-yet-materialized" CR readback might momentarily
38199 // observe under a partial apply) or the sub-`namespace:` scalar
38200 // is absent inside a present `metadata:` block (a
38201 // cluster-scoped CR that legally omits the namespace-scoping
38202 // coordinate, a partially-authored CR the K8s API-server would
38203 // materialize with a `default` namespace at admission but that
38204 // this readback tolerates as `None` so the accessor stays a
38205 // total function). Consumer sites (`.expect(...)`,
38206 // `.unwrap_or(DEFAULT_NAMESPACE)` fallback, `Some(...) ==
38207 // expected` equality wraps) rely on the None-on-absence short-
38208 // circuit — the caixa-flux `programs_yaml_entry` production
38209 // fallback path in particular depends on the None-arm to
38210 // substitute [`DEFAULT_NAMESPACE`] when the source
38211 // ComputeUnit YAML omits `metadata.namespace`. Peer of the
38212 // sibling `kube_name_none_when_metadata_block_absent_or_name_absent`
38213 // pin on the identity-axis half.
38214 let cr_no_metadata = serde_yaml::Mapping::new();
38215 assert_eq!(
38216 kube_namespace(&serde_yaml::Value::Mapping(cr_no_metadata)),
38217 None,
38218 "kube_namespace must return None when the enclosing \
38219 metadata: block is absent — the caixa-flux \
38220 programs_yaml_entry production fallback relies on this \
38221 None-arm to substitute DEFAULT_NAMESPACE",
38222 );
38223
38224 let empty_meta = serde_yaml::Mapping::new();
38225 let mut cr_no_namespace = serde_yaml::Mapping::new();
38226 cr_no_namespace.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(empty_meta));
38227 assert_eq!(
38228 kube_namespace(&serde_yaml::Value::Mapping(cr_no_namespace)),
38229 None,
38230 "kube_namespace must return None when the \
38231 sub-namespace: scalar is absent inside a present \
38232 metadata: block (the cluster-scoped-CR / \
38233 partially-authored-CR arm)",
38234 );
38235 }
38236
38237 #[test]
38238 fn kube_namespace_none_when_metadata_namespace_carries_non_string_type() {
38239 // Type-gate pin: the accessor returns `None` when the sub-
38240 // `metadata.namespace:` scalar is present but carries a non-
38241 // string YAML type (a numeric, boolean, or nested mapping —
38242 // invalid K8s CR shape per the K8s API-machinery OpenAPI
38243 // schema, but tolerated here as `None` so the readback stays a
38244 // total function and defers the diagnostic to the caller's own
38245 // `.unwrap_or(...)` fallback / `.expect(...)` follow-up which
38246 // names the caller's schema axis). Pins the type-gate half of
38247 // the accessor's contract — the axis-key pin is asserted by
38248 // the sibling byte-agreement test — so a hypothetical future
38249 // widening (accepting numeric `metadata.namespace: 42` as the
38250 // stringified `"42"`, an aliased YAML integer under a fresh
38251 // `Value::from` conversion) is caught before it lands. Peer
38252 // of the sibling
38253 // `kube_name_none_when_metadata_name_carries_non_string_type`
38254 // pin on the identity-axis half.
38255 let mut metadata_int = serde_yaml::Mapping::new();
38256 metadata_int.insert_str_key(KUBE_KEY_NAMESPACE, serde_yaml::Value::from(42u64));
38257 let mut cr_int = serde_yaml::Mapping::new();
38258 cr_int.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata_int));
38259 assert_eq!(
38260 kube_namespace(&serde_yaml::Value::Mapping(cr_int)),
38261 None,
38262 "kube_namespace must return None when metadata.namespace \
38263 carries a non-string YAML type (numeric here)",
38264 );
38265
38266 let mut inner = serde_yaml::Mapping::new();
38267 inner.insert_str_key("nested", serde_yaml::Value::String("value".into()));
38268 let mut metadata_map = serde_yaml::Mapping::new();
38269 metadata_map.insert_str_key(KUBE_KEY_NAMESPACE, serde_yaml::Value::Mapping(inner));
38270 let mut cr_map = serde_yaml::Mapping::new();
38271 cr_map.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata_map));
38272 assert_eq!(
38273 kube_namespace(&serde_yaml::Value::Mapping(cr_map)),
38274 None,
38275 "kube_namespace must return None when metadata.namespace \
38276 carries a nested mapping (invalid CR shape per K8s \
38277 API-machinery)",
38278 );
38279 }
38280
38281 #[test]
38282 fn kube_namespace_agrees_with_kube_metadata_str_field_across_permutations() {
38283 // Load-bearing cross-check pin: the accessor and the parametric
38284 // helper it delegates to must byte-agree on every closed
38285 // permutation of the (metadata-present, sub-namespace-present,
38286 // scalar-shape) product — the same cross-product the sibling
38287 // parent `kube_metadata_str_field_matches_prior_inline_chain`
38288 // pin bracket-tests on the parametric helper for both
38289 // KUBE_KEY_NAME and KUBE_KEY_NAMESPACE arg permutations, here
38290 // extended one layer up onto the pinned accessor's own axis-
38291 // key pinning. Closes the "did the pinned accessor
38292 // silently rewire itself off the parametric helper (open-coding
38293 // a fresh two-hop walk instead of composing on the substrate
38294 // primitive)" drift class every future accessor-family
38295 // extension (a peer `kube_uid` on the ownerReference axis, a
38296 // future `kube_labels` composite-return accessor) would
38297 // otherwise reopen. Peer of the sibling
38298 // `kube_name_is_composes_on_lifted_kube_name_accessor` pin on
38299 // the predicate-arity's underlying accessor delegation.
38300 let namespaces = [
38301 "tatara-system",
38302 DEFAULT_NAMESPACE,
38303 "default",
38304 "kube-system",
38305 "flux-system",
38306 ];
38307 for ns in namespaces {
38308 let mut metadata = serde_yaml::Mapping::new();
38309 metadata.insert_str_key(
38310 KUBE_KEY_NAMESPACE,
38311 serde_yaml::Value::String(ns.to_string()),
38312 );
38313 let mut cr = serde_yaml::Mapping::new();
38314 cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
38315 let value = serde_yaml::Value::Mapping(cr);
38316 assert_eq!(
38317 kube_namespace(&value),
38318 kube_metadata_str_field(&value, KUBE_KEY_NAMESPACE),
38319 "kube_namespace must byte-agree with \
38320 kube_metadata_str_field(_, KUBE_KEY_NAMESPACE) across \
38321 every canonical namespace-scoping value (ns={ns:?}) — \
38322 drift here silently splits the two readback paths",
38323 );
38324 assert_eq!(
38325 kube_namespace(&value),
38326 Some(ns),
38327 "kube_namespace must return the authored namespace-\
38328 scoping value verbatim (ns={ns:?})",
38329 );
38330 }
38331 }
38332
38333 #[test]
38334 fn kube_namespace_borrows_from_input_value_storage() {
38335 // Borrow-not-copy pin: the accessor returns a `&str` that
38336 // borrows into the input `Value`'s own storage — pointer-equal
38337 // to the underlying `String::as_str()` on the sub-
38338 // `metadata.namespace:` scalar. Rules out a hypothetical
38339 // future rewrite that returned a fresh `String` (via `.clone()`
38340 // / `.to_string()`) or an owning `Cow` conversion, either of
38341 // which would silently double-allocate at every per-CR readback
38342 // consumer's fast path (the caixa-flux `programs_yaml_entry`
38343 // production readback fans onto every `programs.yaml` entry
38344 // emit at V0, so a per-entry allocation would compound across
38345 // the whole fleet-programs render). Peer of the sibling
38346 // per-storage-borrow pin discipline the sibling accessor family
38347 // ([`Placement::shard_key`], [`Placement::affinity`],
38348 // [`Membro::nome`], [`Entrada::destination`],
38349 // [`Entrada::hostname`]) carries on their respective per-slot
38350 // `&str`-return accessors.
38351 let ns = "tatara-system".to_string();
38352 let mut metadata = serde_yaml::Mapping::new();
38353 metadata.insert_str_key(KUBE_KEY_NAMESPACE, serde_yaml::Value::String(ns.clone()));
38354 let mut cr = serde_yaml::Mapping::new();
38355 cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
38356 let value = serde_yaml::Value::Mapping(cr);
38357
38358 // Accessor must return the same byte-string as the underlying
38359 // parametric helper's readback — the composition contract.
38360 let via_accessor = kube_namespace(&value).expect("namespace present");
38361 let via_helper = kube_metadata_str_field(&value, KUBE_KEY_NAMESPACE)
38362 .expect("namespace present via helper");
38363 assert_eq!(
38364 via_accessor.as_ptr(),
38365 via_helper.as_ptr(),
38366 "kube_namespace must return the same borrowed slice as \
38367 kube_metadata_str_field(_, KUBE_KEY_NAMESPACE) — a \
38368 pointer-drift signals a hidden clone / owning conversion \
38369 layer between the accessor and its delegate",
38370 );
38371 assert_eq!(via_accessor.len(), via_helper.len());
38372 }
38373
38374 #[test]
38375 fn kube_name_is_composes_on_lifted_kube_name_accessor() {
38376 // Composition pin: after the accessor lift, the peer
38377 // predicate `kube_name_is(v, n)` must resolve exactly as
38378 // `kube_name(v) == Some(n)` — i.e. the predicate no longer
38379 // carries an inline `kube_metadata_str_field(v,
38380 // KUBE_KEY_NAME) == Some(n)` composition but composes on the
38381 // sibling accessor. Pins the structural link between the
38382 // three-arity closure (accessor / predicate / navigator) on
38383 // the identity axis: a future re-implementation of `kube_name`
38384 // (e.g. a caching short-circuit for repeated readback on the
38385 // same document, a hypothetical alias-table dispatch on a
38386 // `metadata.identity` sub-axis) reaches the predicate through
38387 // one lift, not a second co-ordinated inline rewrite. Peer of
38388 // the sibling `find_by_name_matches_inline_iter_find_kube_name_is_shape`
38389 // pin on the navigator arity — the navigator composes on the
38390 // predicate, the predicate composes on the accessor.
38391 let mut metadata = serde_yaml::Mapping::new();
38392 metadata.insert_str_key(
38393 KUBE_KEY_NAME,
38394 serde_yaml::Value::String("checkout-cart-to-payment".into()),
38395 );
38396 let mut cr = serde_yaml::Mapping::new();
38397 cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
38398 let value = serde_yaml::Value::Mapping(cr);
38399
38400 assert_eq!(
38401 kube_name_is(&value, "checkout-cart-to-payment"),
38402 kube_name(&value) == Some("checkout-cart-to-payment"),
38403 "kube_name_is must byte-agree with the peer \
38404 `kube_name(v) == Some(n)` composition it now delegates \
38405 to — the predicate carries no more inline navigation, \
38406 only the equality-wrap semantic distinct from the \
38407 sibling accessor arity",
38408 );
38409 assert!(kube_name_is(&value, "checkout-cart-to-payment"));
38410 assert!(!kube_name_is(&value, "checkout-cart-to-catalog"));
38411 }
38412
38413 #[test]
38414 fn kube_name_is_matches_lifted_kube_metadata_str_field_equality_shape() {
38415 // Byte-equivalence pin: the lifted predicate reproduces the
38416 // three-token composition (`kube_metadata_str_field(v,
38417 // KUBE_KEY_NAME) == Some(<NAME>)`) the 6 caixa-mesh test-side
38418 // `.find`/`.filter` sites previously carried inline. Closes the
38419 // "did the lift accidentally rename the pinned scalar-key axis
38420 // to KUBE_KEY_NAMESPACE or drop the `Some(...)` wrap" drift
38421 // class every future re-lift on the peer-axis surface (a
38422 // hypothetical `kube_namespace_is` peer on a per-namespace
38423 // router harness, a `kube_uid_is` for ownerReference
38424 // bookkeeping) would otherwise reopen. Peer of the sibling
38425 // `kube_kind_is_matches_lifted_kube_root_str_field_equality_shape`
38426 // pin on the `kind:` discriminator axis.
38427 let mut metadata = serde_yaml::Mapping::new();
38428 metadata.insert_str_key(
38429 KUBE_KEY_NAME,
38430 serde_yaml::Value::String("checkout-cart-to-catalog".into()),
38431 );
38432 let mut cr = serde_yaml::Mapping::new();
38433 cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
38434 let value = serde_yaml::Value::Mapping(cr);
38435
38436 assert!(kube_name_is(&value, "checkout-cart-to-catalog"));
38437 assert_eq!(
38438 kube_name_is(&value, "checkout-cart-to-catalog"),
38439 kube_metadata_str_field(&value, KUBE_KEY_NAME) == Some("checkout-cart-to-catalog"),
38440 );
38441 }
38442
38443 #[test]
38444 fn kube_name_is_false_on_mismatched_name_and_missing_name() {
38445 // Complement-side pin: the predicate returns `false` when
38446 // either the name axis carries a different identity or the
38447 // sub-`metadata.name:` scalar (or the enclosing `metadata:`
38448 // block) is absent altogether (the same vacuous-`None`
38449 // short-circuit the parent `kube_metadata_str_field` closes on
38450 // the underlying two-hop navigation). Consumer sites
38451 // (`docs.iter().find(|d| kube_name_is(d, X))`) rely on the
38452 // false-on-mismatch shape to skip the wrong CRs across the
38453 // multi-doc mesh emission and land on the intended per-name
38454 // document. Peer of the sibling
38455 // `kube_kind_is_false_on_mismatched_kind_and_missing_kind` pin
38456 // on the `kind:` discriminator axis.
38457 let mut wrong_meta = serde_yaml::Mapping::new();
38458 wrong_meta.insert_str_key(
38459 KUBE_KEY_NAME,
38460 serde_yaml::Value::String("checkout-payment-to-cart".into()),
38461 );
38462 let mut cr_wrong_name = serde_yaml::Mapping::new();
38463 cr_wrong_name.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(wrong_meta));
38464 assert!(!kube_name_is(
38465 &serde_yaml::Value::Mapping(cr_wrong_name),
38466 "checkout-cart-to-catalog",
38467 ));
38468
38469 let cr_no_metadata = serde_yaml::Mapping::new();
38470 assert!(!kube_name_is(
38471 &serde_yaml::Value::Mapping(cr_no_metadata),
38472 "checkout-cart-to-catalog",
38473 ));
38474
38475 let empty_meta = serde_yaml::Mapping::new();
38476 let mut cr_no_name = serde_yaml::Mapping::new();
38477 cr_no_name.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(empty_meta));
38478 assert!(!kube_name_is(
38479 &serde_yaml::Value::Mapping(cr_no_name),
38480 "checkout-cart-to-catalog",
38481 ));
38482 }
38483
38484 #[test]
38485 fn find_by_name_matches_inline_iter_find_kube_name_is_shape() {
38486 // Byte-equivalence pin: the lifted navigator reproduces the
38487 // three-token combinator chain (`docs.iter().find(|d|
38488 // kube_name_is(d, <NAME>))`) the 5 caixa-mesh test-side
38489 // per-CNP-name find-by-name sites previously carried inline.
38490 // Closes the "did the lift accidentally widen the receiver,
38491 // drop the closure, or swap `find` for `filter`" drift class
38492 // every future re-lift on the sibling multi-doc-navigator axis
38493 // (a hypothetical `filter_by_name` peer that carries the same
38494 // underlying predicate but returns an iterator) would otherwise
38495 // reopen. Peer of the sibling
38496 // `find_by_kind_matches_inline_iter_find_kube_kind_is_shape`
38497 // pin on the `kind:` discriminator axis.
38498 let mut meta_a = serde_yaml::Mapping::new();
38499 meta_a.insert_str_key(
38500 KUBE_KEY_NAME,
38501 serde_yaml::Value::String("checkout-cart-to-catalog".into()),
38502 );
38503 let mut policy_a = serde_yaml::Mapping::new();
38504 policy_a.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(meta_a));
38505 let mut meta_b = serde_yaml::Mapping::new();
38506 meta_b.insert_str_key(
38507 KUBE_KEY_NAME,
38508 serde_yaml::Value::String("checkout-payment-to-cart".into()),
38509 );
38510 let mut policy_b = serde_yaml::Mapping::new();
38511 policy_b.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(meta_b));
38512 let docs = vec![
38513 serde_yaml::Value::Mapping(policy_a),
38514 serde_yaml::Value::Mapping(policy_b),
38515 ];
38516
38517 assert_eq!(
38518 find_by_name(&docs, "checkout-cart-to-catalog"),
38519 docs.iter()
38520 .find(|d| kube_name_is(d, "checkout-cart-to-catalog")),
38521 );
38522 assert_eq!(
38523 find_by_name(&docs, "checkout-payment-to-cart"),
38524 docs.iter()
38525 .find(|d| kube_name_is(d, "checkout-payment-to-cart")),
38526 );
38527
38528 // Miss path: absent name → None, matching the inline `.find`
38529 // short-circuit that consumer sites rely on to distinguish
38530 // "no such CR in this emission" from "wrong shape" in their
38531 // `.unwrap()` / `.expect(...)` follow-ups.
38532 assert_eq!(find_by_name(&docs, "checkout-cart-to-payment"), None);
38533 let empty: Vec<serde_yaml::Value> = Vec::new();
38534 assert_eq!(find_by_name(&empty, "checkout-cart-to-catalog"), None);
38535 }
38536
38537 #[test]
38538 fn find_by_name_returns_first_match_on_duplicate_name() {
38539 // Order-preservation pin: the lifted navigator returns the
38540 // first document of the matching name (the same short-circuit
38541 // `Iterator::find` exposes). Multi-doc mesh emissions never
38542 // carry two documents with identical `metadata.name` at V0
38543 // (`cilium_network_policies` fans distinct `(:de, :para)`
38544 // pairs into distinct CNP names — see the sibling
38545 // `cilium_http_contracts_fan_multiple_edges_into_one_policy`
38546 // fan-in pin), but the M4 cross-cluster fan-out will produce
38547 // per-cluster CR duplicates on the identity axis (one
38548 // `HelmRelease` per cluster carrying the same base name). Pin
38549 // the first-match contract keeps the M4 caller-side "the
38550 // first hit is the primary" convention aligned with the
38551 // helper's combinator half. Peer of the sibling
38552 // `find_by_kind_returns_first_match_on_duplicate_kind` pin on
38553 // the `kind:` discriminator axis.
38554 let mut meta_a = serde_yaml::Mapping::new();
38555 meta_a.insert_str_key(
38556 KUBE_KEY_NAME,
38557 serde_yaml::Value::String("checkout-cart-to-catalog".into()),
38558 );
38559 meta_a.insert_str_key(
38560 KUBE_KEY_NAMESPACE,
38561 serde_yaml::Value::String("cluster-a".into()),
38562 );
38563 let mut policy_a = serde_yaml::Mapping::new();
38564 policy_a.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(meta_a));
38565 let mut meta_b = serde_yaml::Mapping::new();
38566 meta_b.insert_str_key(
38567 KUBE_KEY_NAME,
38568 serde_yaml::Value::String("checkout-cart-to-catalog".into()),
38569 );
38570 meta_b.insert_str_key(
38571 KUBE_KEY_NAMESPACE,
38572 serde_yaml::Value::String("cluster-b".into()),
38573 );
38574 let mut policy_b = serde_yaml::Mapping::new();
38575 policy_b.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(meta_b));
38576 let docs = vec![
38577 serde_yaml::Value::Mapping(policy_a),
38578 serde_yaml::Value::Mapping(policy_b),
38579 ];
38580
38581 let first = find_by_name(&docs, "checkout-cart-to-catalog").unwrap();
38582 assert_eq!(
38583 kube_metadata_str_field(first, KUBE_KEY_NAMESPACE),
38584 Some("cluster-a"),
38585 );
38586 }
38587
38588 #[test]
38589 fn kube_namespace_is_composes_on_lifted_kube_namespace_accessor() {
38590 // Composition pin: the peer predicate `kube_namespace_is(v, n)`
38591 // must resolve exactly as `kube_namespace(v) == Some(n)` — no
38592 // inline `kube_metadata_str_field(v, KUBE_KEY_NAMESPACE) ==
38593 // Some(n)` composition, only the accessor + equality-wrap two-
38594 // token shape. Pins the structural link between the three-arity
38595 // closure (accessor / predicate / navigator) on the namespace-
38596 // scoping axis: a future re-implementation of `kube_namespace`
38597 // (a caching short-circuit for repeated readback on the same
38598 // document, a hypothetical alias-table dispatch on a
38599 // `metadata.tenant` sub-axis) reaches the predicate through one
38600 // lift, not a second co-ordinated inline rewrite. Peer of the
38601 // sibling `kube_name_is_composes_on_lifted_kube_name_accessor`
38602 // pin on the identity axis.
38603 let mut metadata = serde_yaml::Mapping::new();
38604 metadata.insert_str_key(
38605 KUBE_KEY_NAMESPACE,
38606 serde_yaml::Value::String("tatara-system".into()),
38607 );
38608 let mut cr = serde_yaml::Mapping::new();
38609 cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
38610 let value = serde_yaml::Value::Mapping(cr);
38611
38612 assert_eq!(
38613 kube_namespace_is(&value, "tatara-system"),
38614 kube_namespace(&value) == Some("tatara-system"),
38615 "kube_namespace_is must byte-agree with the peer \
38616 `kube_namespace(v) == Some(n)` composition it delegates to \
38617 — the predicate carries no more inline navigation, only \
38618 the equality-wrap semantic distinct from the sibling \
38619 accessor arity",
38620 );
38621 assert!(kube_namespace_is(&value, "tatara-system"));
38622 assert!(!kube_namespace_is(&value, "flux-system"));
38623 }
38624
38625 #[test]
38626 fn kube_namespace_is_matches_lifted_kube_metadata_str_field_equality_shape() {
38627 // Byte-equivalence pin: the lifted predicate reproduces the
38628 // three-token composition (`kube_metadata_str_field(v,
38629 // KUBE_KEY_NAMESPACE) == Some(<NS>)`) every future per-tenant
38630 // `.find`/`.filter` site would otherwise carry inline. Closes
38631 // the "did the lift accidentally rename the pinned scalar-key
38632 // axis to KUBE_KEY_NAME (silently pulling the peer identity
38633 // coordinate instead of the namespace-scoping one), drop the
38634 // `Some(...)` wrap, or invert the comparator direction" drift
38635 // class every future re-lift on the peer-axis surface (a
38636 // hypothetical `kube_uid_is` for ownerReference bookkeeping,
38637 // a `kube_resource_version_is` for optimistic-concurrency
38638 // bookkeeping) would otherwise reopen. Peer of the sibling
38639 // `kube_name_is_matches_lifted_kube_metadata_str_field_equality_shape`
38640 // pin on the identity axis.
38641 let mut metadata = serde_yaml::Mapping::new();
38642 metadata.insert_str_key(
38643 KUBE_KEY_NAMESPACE,
38644 serde_yaml::Value::String(DEFAULT_NAMESPACE.into()),
38645 );
38646 let mut cr = serde_yaml::Mapping::new();
38647 cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
38648 let value = serde_yaml::Value::Mapping(cr);
38649
38650 assert!(kube_namespace_is(&value, DEFAULT_NAMESPACE));
38651 assert_eq!(
38652 kube_namespace_is(&value, DEFAULT_NAMESPACE),
38653 kube_metadata_str_field(&value, KUBE_KEY_NAMESPACE) == Some(DEFAULT_NAMESPACE),
38654 "kube_namespace_is must byte-agree with the parametric \
38655 `kube_metadata_str_field(v, KUBE_KEY_NAMESPACE) == \
38656 Some(<NS>)` three-token composition — drift here silently \
38657 splits the per-tenant router harness's namespace-scoping \
38658 filter from the sibling accessor's readback",
38659 );
38660 }
38661
38662 #[test]
38663 fn kube_namespace_is_false_on_mismatched_namespace_and_missing_namespace() {
38664 // Complement-side pin: the predicate returns `false` when
38665 // either the namespace-scoping axis carries a different
38666 // coordinate or the sub-`metadata.namespace:` scalar (or the
38667 // enclosing `metadata:` block) is absent altogether (the same
38668 // vacuous-`None` short-circuit the parent
38669 // `kube_metadata_str_field` closes on the underlying two-hop
38670 // navigation). Consumer sites (`docs.iter().find(|d|
38671 // kube_namespace_is(d, <NS>))`) rely on the false-on-mismatch
38672 // shape to skip the wrong-namespace CRs across the multi-doc
38673 // fleet emission and land on the intended per-tenant slice.
38674 // Peer of the sibling
38675 // `kube_name_is_false_on_mismatched_name_and_missing_name` pin
38676 // on the identity axis.
38677 let mut wrong_meta = serde_yaml::Mapping::new();
38678 wrong_meta.insert_str_key(
38679 KUBE_KEY_NAMESPACE,
38680 serde_yaml::Value::String("flux-system".into()),
38681 );
38682 let mut cr_wrong_ns = serde_yaml::Mapping::new();
38683 cr_wrong_ns.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(wrong_meta));
38684 assert!(!kube_namespace_is(
38685 &serde_yaml::Value::Mapping(cr_wrong_ns),
38686 DEFAULT_NAMESPACE,
38687 ));
38688
38689 let cr_no_metadata = serde_yaml::Mapping::new();
38690 assert!(!kube_namespace_is(
38691 &serde_yaml::Value::Mapping(cr_no_metadata),
38692 DEFAULT_NAMESPACE,
38693 ));
38694
38695 let empty_meta = serde_yaml::Mapping::new();
38696 let mut cr_no_ns = serde_yaml::Mapping::new();
38697 cr_no_ns.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(empty_meta));
38698 assert!(!kube_namespace_is(
38699 &serde_yaml::Value::Mapping(cr_no_ns),
38700 DEFAULT_NAMESPACE,
38701 ));
38702 }
38703
38704 #[test]
38705 fn find_by_namespace_matches_inline_iter_find_kube_namespace_is_shape() {
38706 // Byte-equivalence pin: the lifted navigator reproduces the
38707 // three-token combinator chain (`docs.iter().find(|d|
38708 // kube_namespace_is(d, <NS>))`) every future per-tenant fleet-
38709 // slice site would otherwise carry inline. Closes the "did the
38710 // lift accidentally widen the receiver, drop the closure, or
38711 // swap `find` for `filter`" drift class every future re-lift on
38712 // the sibling multi-doc-navigator axis (a hypothetical
38713 // `filter_by_namespace` peer that returns an iterator across
38714 // every matching per-tenant CR rather than the first hit) would
38715 // otherwise reopen. Peer of the sibling
38716 // `find_by_name_matches_inline_iter_find_kube_name_is_shape`
38717 // pin on the identity axis.
38718 let mut meta_a = serde_yaml::Mapping::new();
38719 meta_a.insert_str_key(
38720 KUBE_KEY_NAMESPACE,
38721 serde_yaml::Value::String("tatara-system".into()),
38722 );
38723 let mut policy_a = serde_yaml::Mapping::new();
38724 policy_a.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(meta_a));
38725 let mut meta_b = serde_yaml::Mapping::new();
38726 meta_b.insert_str_key(
38727 KUBE_KEY_NAMESPACE,
38728 serde_yaml::Value::String("flux-system".into()),
38729 );
38730 let mut policy_b = serde_yaml::Mapping::new();
38731 policy_b.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(meta_b));
38732 let docs = vec![
38733 serde_yaml::Value::Mapping(policy_a),
38734 serde_yaml::Value::Mapping(policy_b),
38735 ];
38736
38737 assert_eq!(
38738 find_by_namespace(&docs, "tatara-system"),
38739 docs.iter().find(|d| kube_namespace_is(d, "tatara-system")),
38740 );
38741 assert_eq!(
38742 find_by_namespace(&docs, "flux-system"),
38743 docs.iter().find(|d| kube_namespace_is(d, "flux-system")),
38744 );
38745
38746 // Miss path: absent namespace-scoping coordinate → None,
38747 // matching the inline `.find` short-circuit that consumer
38748 // sites rely on to distinguish "no such per-tenant slice in
38749 // this emission" from "wrong shape" in their `.unwrap()` /
38750 // `.expect(...)` follow-ups. Picked a namespace-scoping value
38751 // outside the two-fixture set so the miss-path answer is
38752 // structurally None rather than coincidentally so — a fixture
38753 // whose per-tenant coordinate happened to match one of the
38754 // emitted CRs would silently short-circuit as `Some(...)` and
38755 // never exercise the None-arm.
38756 assert_eq!(find_by_namespace(&docs, "kube-system"), None);
38757 let empty: Vec<serde_yaml::Value> = Vec::new();
38758 assert_eq!(find_by_namespace(&empty, "tatara-system"), None);
38759 }
38760
38761 #[test]
38762 fn find_by_namespace_returns_first_match_on_duplicate_namespace() {
38763 // Order-preservation pin: the lifted navigator returns the
38764 // first document of the matching namespace-scoping coordinate
38765 // (the same short-circuit `Iterator::find` exposes). Every
38766 // per-tenant CR emission legally carries many CRs sharing a
38767 // single `metadata.namespace` (a per-tenant namespace slices
38768 // many `HelmRelease` + many `CiliumNetworkPolicy` +
38769 // `Gateway` / `HTTPRoute` under one namespace-scoping
38770 // coordinate), unlike the peer identity axis where each
38771 // `metadata.name` is unique per namespace-scope. Pin the
38772 // first-match contract keeps the M4 caller-side "the first hit
38773 // is the primary per-tenant CR" convention aligned with the
38774 // helper's combinator half. Peer of the sibling
38775 // `find_by_name_returns_first_match_on_duplicate_name` pin on
38776 // the identity axis.
38777 let mut meta_a = serde_yaml::Mapping::new();
38778 meta_a.insert_str_key(
38779 KUBE_KEY_NAME,
38780 serde_yaml::Value::String("checkout-cart-to-catalog".into()),
38781 );
38782 meta_a.insert_str_key(
38783 KUBE_KEY_NAMESPACE,
38784 serde_yaml::Value::String("tatara-system".into()),
38785 );
38786 let mut policy_a = serde_yaml::Mapping::new();
38787 policy_a.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(meta_a));
38788 let mut meta_b = serde_yaml::Mapping::new();
38789 meta_b.insert_str_key(
38790 KUBE_KEY_NAME,
38791 serde_yaml::Value::String("checkout-payment-to-cart".into()),
38792 );
38793 meta_b.insert_str_key(
38794 KUBE_KEY_NAMESPACE,
38795 serde_yaml::Value::String("tatara-system".into()),
38796 );
38797 let mut policy_b = serde_yaml::Mapping::new();
38798 policy_b.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(meta_b));
38799 let docs = vec![
38800 serde_yaml::Value::Mapping(policy_a),
38801 serde_yaml::Value::Mapping(policy_b),
38802 ];
38803
38804 let first = find_by_namespace(&docs, "tatara-system").unwrap();
38805 assert_eq!(
38806 kube_name(first),
38807 Some("checkout-cart-to-catalog"),
38808 "find_by_namespace must return the first per-namespace \
38809 CR in emission order — the M4 cross-cluster fan-out's \
38810 per-tenant slicer treats the first-hit CR as the primary \
38811 per-tenant coordinate, matching the peer navigator's \
38812 first-match contract on the identity axis",
38813 );
38814 }
38815
38816 // ── contrato-edge-label + cilium-network-policy-name lifts ──────────
38817
38818 #[test]
38819 fn contrato_edge_label_separator_pin() {
38820 // Load-bearing byte-string pin: the M3 `:contratos`
38821 // edge-direction separator every caixa-mesh emitter that
38822 // encodes a typed edge as a K8s-name-shaped scalar reads from.
38823 // Any future rebrand (e.g. `-to-` → `_to_`) lands here as a
38824 // one-const edit; the peer `contrato_edge_label` /
38825 // `cilium_network_policy_name` composers pick up the new
38826 // encoding by construction. A drift on this const would silently
38827 // split the CNP `metadata.name` from its own
38828 // `metadata.labels.pleme.pleme.io/contrato` value, orphaning
38829 // every operator-side grep-by-label query far from the source
38830 // caixa.lisp.
38831 assert_eq!(CONTRATO_EDGE_LABEL_SEPARATOR, "-to-");
38832 }
38833
38834 #[test]
38835 fn contrato_edge_label_matches_inline_de_to_para_encoding() {
38836 // Byte-shape pin: the composer produces the same
38837 // `format!("{de}-to-{para}")` byte-string every caixa-mesh
38838 // per-`(:de, :para)` `CiliumNetworkPolicy` emitter previously
38839 // inlined at its `labels.insert(LABEL_CONTRATO, …)` call. So a
38840 // future rewire of the composer's internals (multi-hop typed
38841 // edges once the M4 per-edge WIT registry lands, unicode
38842 // arrow-shape rebrand for operator display) reaches every
38843 // consumer through one canonical function-pointer edit.
38844 assert_eq!(contrato_edge_label("cart", "catalog"), "cart-to-catalog");
38845 assert_eq!(contrato_edge_label("cart", "payment"), "cart-to-payment");
38846 }
38847
38848 #[test]
38849 fn contrato_edge_label_threads_separator_between_de_and_para() {
38850 // Composition pin: the composer's shape is
38851 // `de + CONTRATO_EDGE_LABEL_SEPARATOR + para`, so a future
38852 // separator rebrand at [`CONTRATO_EDGE_LABEL_SEPARATOR`]
38853 // reaches the composer through one const-edit and every
38854 // consumer picks up the new encoding by construction. Pin the
38855 // structural equation (not just the byte value) so a future
38856 // reorder of the composer's `format!` argument list (a
38857 // `format!("{para}-{sep}-{de}")` typo mid-refactor) fires here
38858 // rather than silently emitting reversed-direction CNP labels.
38859 let de = "svc-a";
38860 let para = "svc-b";
38861 assert_eq!(
38862 contrato_edge_label(de, para),
38863 format!("{de}{CONTRATO_EDGE_LABEL_SEPARATOR}{para}"),
38864 );
38865 }
38866
38867 #[test]
38868 fn cilium_network_policy_name_matches_inline_aplicacao_de_to_para_encoding() {
38869 // Byte-shape pin: the composer produces the same
38870 // `format!("{aplicacao}-{de}-to-{para}")` byte-string every
38871 // caixa-mesh `cilium_network_policies` per-`(:de, :para)`
38872 // group's `kube_resource_skeleton` `name:` argument previously
38873 // inlined. So a future rewire of the composer's internals
38874 // reaches the CNP renderer through one canonical function-
38875 // pointer edit rather than a coordinated two-site rewrite of
38876 // the [`LABEL_CONTRATO`] labels.insert(...) call and the CNP
38877 // name argument.
38878 assert_eq!(
38879 cilium_network_policy_name("checkout", "cart", "catalog"),
38880 "checkout-cart-to-catalog",
38881 );
38882 assert_eq!(
38883 cilium_network_policy_name("checkout", "cart", "payment"),
38884 "checkout-cart-to-payment",
38885 );
38886 }
38887
38888 #[test]
38889 fn cilium_network_policy_name_composes_on_contrato_edge_label() {
38890 // Composition pin: the CNP name is the parent Aplicacao's
38891 // `:nome` joined to the contrato-edge-label by a canonical `-`
38892 // separator (`format!("{aplicacao}-{edge}")`), so the two
38893 // writer-side helpers close the canonical
38894 // `(LABEL_CONTRATO-value, metadata.name)` per-CNP identity
38895 // pair on one shared edge-encoding source of truth
38896 // ([`CONTRATO_EDGE_LABEL_SEPARATOR`]). Pin the structural
38897 // equation so a future refactor of either composer's internals
38898 // that accidentally desynchronizes the two (a CNP-name
38899 // rebrand landing on `format!("{aplicacao}_{edge}")` while
38900 // the label-value composer stays on `{de}-to-{para}`, or a
38901 // label-composer rebrand landing on `->` while the CNP-name
38902 // composer stays on `-to-`) fires here rather than silently
38903 // orphaning every operator-side grep-by-label query at apply
38904 // time.
38905 let aplicacao = "checkout";
38906 let de = "cart";
38907 let para = "catalog";
38908 let edge = contrato_edge_label(de, para);
38909 assert_eq!(
38910 cilium_network_policy_name(aplicacao, de, para),
38911 format!("{aplicacao}-{edge}"),
38912 );
38913 }
38914
38915 // ── gateway-api-http-route-name lift ────────────────────────────────
38916
38917 #[test]
38918 fn gateway_api_http_route_name_matches_inline_aplicacao_para_encoding() {
38919 // Byte-shape pin: the composer produces the same
38920 // `format!("{aplicacao}-{para}")` byte-string the caixa-mesh
38921 // `gateway_routes` per-`:entrada` `kube_resource_skeleton`
38922 // `name:` argument previously inlined as
38923 // `format!("{}-{}", caixa.nome, entrada.para)`. So a future
38924 // rewire of the composer's internals reaches the HTTPRoute
38925 // renderer through one canonical function-pointer edit rather
38926 // than a hand-agreement between the emitter and every
38927 // test-side probe pinning the expected `<aplicacao>-<para>`
38928 // byte-shape at the HTTPRoute `metadata.name` axis.
38929 assert_eq!(
38930 gateway_api_http_route_name("checkout", "cart"),
38931 "checkout-cart",
38932 );
38933 assert_eq!(gateway_api_http_route_name("orders", "cart"), "orders-cart",);
38934 }
38935
38936 #[test]
38937 fn rendered_file_carries_path_and_contents_fields() {
38938 // Field-shape pin: the canonical [`RenderedFile`] every
38939 // per-target `caixa-<target>` renderer's per-artifact leaf
38940 // resolves through carries exactly the `(path, contents)` pair
38941 // the prior per-crate `BundleFile { path: PathBuf, contents:
38942 // String }` (`caixa-flux`) / `ChartFile { path: PathBuf,
38943 // contents: String }` (`caixa-helm`) clones each carried
38944 // verbatim. A future refactor that adds a per-artifact
38945 // hash / provenance / write-mode discriminator on the record
38946 // must land at the canonical struct definition (this file) —
38947 // the two type aliases at `caixa-flux::BundleFile` /
38948 // `caixa-helm::ChartFile` re-export the canonical unchanged, so
38949 // an addition here reaches both per-target renderers at once,
38950 // and a struct-literal drift that inlines the pre-lift shape
38951 // at either alias trips this pin at caixa-core build time
38952 // rather than surfacing as a divergent per-target renderer's
38953 // record shape far from the source.
38954 let f = RenderedFile {
38955 path: PathBuf::from("Chart.yaml"),
38956 contents: "apiVersion: v2\n".to_string(),
38957 };
38958 assert_eq!(f.path, PathBuf::from("Chart.yaml"));
38959 assert_eq!(f.contents, "apiVersion: v2\n");
38960 }
38961
38962 #[test]
38963 fn rendered_file_derives_pattern_pin() {
38964 // Derive-shape pin: the canonical [`RenderedFile`] carries the
38965 // `Debug + Clone + PartialEq + Eq` derive tuple the two per-
38966 // renderer clones (`caixa-flux::BundleFile` /
38967 // `caixa-helm::ChartFile`) each carried verbatim before the
38968 // lift. `Clone::clone` returns a byte-equal record + the
38969 // `PartialEq::eq` impl returns `true` on the round-trip; a
38970 // future refactor that drops one of the four derives (say,
38971 // removes `PartialEq` on a per-artifact-hash addition) trips
38972 // this pin at caixa-core build time and surfaces the
38973 // per-alias downstream `assert_eq!(bundle_file_a,
38974 // bundle_file_b)` / `assert_eq!(chart_file_a, chart_file_b)`
38975 // navigators in `caixa-flux` / `caixa-helm` — every
38976 // per-alias derive-fed navigator threads through this
38977 // canonical derive tuple by construction.
38978 let f = RenderedFile {
38979 path: PathBuf::from("values.yaml"),
38980 contents: "pleme-computeunit:\n enabled: false\n".to_string(),
38981 };
38982 let clone = f.clone();
38983 assert_eq!(f, clone);
38984 let dbg = format!("{f:?}");
38985 assert!(
38986 dbg.contains("RenderedFile"),
38987 "Debug output must name the canonical type, got: {dbg:?}",
38988 );
38989 }
38990
38991 #[test]
38992 fn rendered_file_new_matches_struct_literal_shape() {
38993 // Constructor pin: [`RenderedFile::new(FILENAME, contents)`]
38994 // (the canonical lifted `impl Into<PathBuf>` / `impl Into<String>`
38995 // inherent constructor every per-target renderer's per-artifact
38996 // leaf now routes through) produces the byte-identical record
38997 // the six prior inline struct-literal call sites (three
38998 // per-artifact leaves in
38999 // [`caixa_helm::render_chart_for_servico_with`],
39000 // three per-CR leaves in [`caixa_flux::cluster_bundle`]) each
39001 // open-coded as `<Xxx>File { path: PathBuf::from(FILENAME_CONST),
39002 // contents: <body> }`. Pin the equation on a
39003 // `HELM_VALUES_YAML_FILENAME`-shaped input so a future rebrand
39004 // of the constructor's internals (a per-artifact hash /
39005 // provenance field addition, an
39006 // [`is_sandboxed_relative_path`] check at construction time
39007 // once per-cluster-writer sandboxing lands) fires here rather
39008 // than silently splitting the per-target renderer's per-CR
39009 // record shape from the substrate-canonical `(path, contents)`
39010 // pair at the caixa-core canonical.
39011 let via_new = RenderedFile::new(HELM_VALUES_YAML_FILENAME, "pleme-computeunit:\n");
39012 let via_literal = RenderedFile {
39013 path: PathBuf::from(HELM_VALUES_YAML_FILENAME),
39014 contents: "pleme-computeunit:\n".to_string(),
39015 };
39016 assert_eq!(via_new, via_literal);
39017 // Peer path-side pin: `impl Into<PathBuf>` accepts a `PathBuf`
39018 // directly (the future per-target renderer surface where the
39019 // path is composed from author input rather than picked from a
39020 // substrate-canonical `&'static str` filename constant) —
39021 // exercised so a drift onto a stricter `&str`-only bound
39022 // trips this pin at caixa-core build time rather than at the
39023 // first per-target renderer that reaches for the wider bound.
39024 let via_new_from_pathbuf = RenderedFile::new(
39025 PathBuf::from(FLUX_HELMRELEASE_YAML_FILENAME),
39026 String::from("kind: HelmRelease\n"),
39027 );
39028 assert_eq!(
39029 via_new_from_pathbuf.path,
39030 PathBuf::from(FLUX_HELMRELEASE_YAML_FILENAME),
39031 );
39032 assert_eq!(via_new_from_pathbuf.contents, "kind: HelmRelease\n");
39033 }
39034
39035 #[test]
39036 fn gateway_api_http_route_name_composes_on_canonical_dash_separator() {
39037 // Composition pin: the HTTPRoute `metadata.name` is the parent
39038 // Aplicacao's `:nome` joined to the `:entrada :para`
39039 // destination Servico's `:nome` by a canonical `-` separator
39040 // (`format!("{aplicacao}-{para}")`) — the same
39041 // "aplicacao-prefixed sub-identity" discipline the peer
39042 // [`cilium_network_policy_name`] composer materializes on the
39043 // sibling per-CR K8s-name-shaped-identity-scalar axis
39044 // ([`format!("{aplicacao}-{edge}")`]). Pin the structural
39045 // equation so a future refactor of either composer's internals
39046 // that accidentally desynchronizes the two (an HTTPRoute-name
39047 // rebrand landing on `format!("{aplicacao}.{para}")` while
39048 // the CNP-name composer stays on `{aplicacao}-{edge}`, or a
39049 // per-Aplicacao-K8s-CR-name shared-separator rebrand landing
39050 // on the CNP-name composer without a coordinated edit here)
39051 // fires here rather than silently splitting the two per-CR
39052 // name-encoding axes across the caixa-mesh renderer.
39053 let aplicacao = "checkout";
39054 let para = "cart";
39055 assert_eq!(
39056 gateway_api_http_route_name(aplicacao, para),
39057 format!("{aplicacao}-{para}"),
39058 );
39059 }
39060}