//! Render-side helpers shared by every per-Servico renderer
//! ([`caixa-helm`], [`caixa-flux`]) — the canonical place for "if the
//! M2 typed slot is non-empty, emit its camelCase YAML fragment under
//! the agreed key" patterns to live exactly once.
//!
//! Until this module landed both renderers carried an inline ~20-line
//! block per render entry-point that:
//!
//! 1. Checked `caixa.limits.is_some() && !limits.is_empty()`.
//! 2. Called `serde_yaml::to_value(limits).unwrap_or(Value::Null)` —
//! silently swallowing every serialization error as a `null`-shaped
//! fragment that would render as `limits: null` in the values block,
//! indistinguishable from "the author omitted the slot" downstream.
//! 3. Inserted under the camelCase key `"limits"` with `or_insert`
//! semantics so explicit `spec.*` fields from the ComputeUnit YAML
//! take precedence over the manifest-derived overlay.
//! 4. Repeated the same shape for `:behavior` → `"behavior"` and
//! `:upgrade-from` → `"upgradeFrom"`.
//!
//! That's the duplication budget violated three ways: same emptiness
//! check, same camelCase key, same precedence rule, written twice
//! verbatim. THEORY.md §I.3.5 ("Generation first, composition second,
//! hand-authoring last; the duplication budget is zero") promotes that
//! to a build-time concern: every recurring shape lives in a typed
//! helper before its third occurrence — and PRIME DIRECTIVE work is
//! exactly that lift.
//!
//! [`servico_m2_overlay`] is that helper. Renderers iterate the map it
//! returns and merge each `(key, value)` pair into their target with
//! their own map type's `entry().or_insert()` (so `spec.*` precedence
//! is preserved by construction).
use std::collections::BTreeMap;
use std::path::{Component, Path, PathBuf};
use thiserror::Error;
use crate::{Caixa, CaixaKind};
/// Errors the render helpers can raise.
#[derive(Debug, Error)]
pub enum RenderError {
/// `serde_yaml::to_value` failed for one of the M2 typed slots —
/// theoretically impossible for the canonical
/// [`crate::LimitsSpec`] / [`crate::BehaviorSpec`] /
/// [`crate::UpgradeFromEntry`] types (all derive Serialize without
/// fallible custom impls), but surfaced rather than swallowed so a
/// future slot whose Serialize impl gains a fallible branch
/// surfaces the failure to the caller instead of silently rendering
/// as `null` (the prior inline block's behavior).
#[error("yaml serialization of M2 slot {slot}: {source}")]
Yaml {
slot: &'static str,
#[source]
source: serde_yaml::Error,
},
}
/// Typed kind-mismatch view: the canonical surface every per-kind
/// `caixa-<target>` renderer raises when it's handed a [`Caixa`] whose
/// `:kind` doesn't match the kind that renderer is targeting. Carries
/// the offending caixa's `:nome` alongside the expected/actual kinds,
/// so the diagnostic reads `caixa "<nome>": expected :kind <expected>,
/// got <actual>` — naming which caixa needs author attention, not just
/// which kind the renderer rejected.
///
/// Lifted from three identical-shape per-renderer arms in
/// `caixa-helm` ([`Error::NotAServico`][helm-err]), `caixa-flux`
/// ([`Error::NotAServico`][flux-err]) and `caixa-mesh`
/// ([`Error::NotAnAplicacao`][mesh-err]). The prior arms each carried
/// only the actual [`CaixaKind`], leaving the user to grep for which
/// `caixa.lisp` triggered the mismatch — exactly the
/// "feira verb whose error path doesn't name the offending caixa"
/// punch-list item the compounding-mandate protocol calls out.
///
/// Renderers wrap this view in their own [`thiserror`] `Error` enum
/// via `#[from]`; the `?` operator at every kind-checking call site
/// turns the [`require_kind`] result into the renderer's local error
/// type with no manual conversion.
///
/// [helm-err]: https://docs.rs/caixa-helm
/// [flux-err]: https://docs.rs/caixa-flux
/// [mesh-err]: https://docs.rs/caixa-mesh
#[derive(Debug, Clone, PartialEq, Eq, Error)]
#[error("caixa {nome:?}: expected :kind {expected:?}, got {actual:?}")]
pub struct KindMismatch {
/// The offending caixa's `:nome` — names which `caixa.lisp` the
/// renderer was handed, so the diagnostic doesn't require the
/// user to grep for it.
pub nome: String,
/// The `:kind` this renderer targets.
pub expected: CaixaKind,
/// The `:kind` the offending caixa actually carries.
pub actual: CaixaKind,
}
/// Predicate: assert that `caixa.kind == expected`, returning a typed
/// [`KindMismatch`] view (carrying [`Caixa::nome`]) on rejection. The
/// canonical entry-point every per-kind renderer wraps in its own
/// [`thiserror`] `Error` variant via `#[from]` — the call site
/// becomes a single `caixa_core::require_kind(caixa, CaixaKind::X)?;`
/// in place of the prior inline `if caixa.kind != CaixaKind::X {
/// return Err(Error::NotAnX(caixa.kind)); }` block.
///
/// Lifted to a single helper so a future per-kind renderer
/// (`caixa-otel`, the future per-Aplicacao CR materializer the M3.x
/// roadmap acknowledges, the future per-Supervisor reconciler
/// renderer) gets the same naming-the-offending-caixa diagnostic for
/// free, and a future change to the diagnostic format (e.g. adding
/// a [`Caixa::versao`] suffix once multi-version-skew authoring lands)
/// is one edit here, not a coordinated rewrite of every renderer.
///
/// # Errors
///
/// Returns [`KindMismatch`] when `caixa.kind != expected`. The error
/// carries the caixa's `:nome` so the diagnostic names the offending
/// `caixa.lisp` — same shape every renderer's `Error::From<KindMismatch>`
/// converts into the renderer's local error type.
pub fn require_kind(caixa: &Caixa, expected: CaixaKind) -> Result<(), KindMismatch> {
if caixa.kind() == expected {
Ok(())
} else {
Err(KindMismatch {
nome: caixa.nome().to_string(),
expected,
actual: caixa.kind(),
})
}
}
/// Typed `:ci`-slot-absence view: the canonical surface every per-`Acao`
/// consumer raises when it's handed a `:kind Acao` [`Caixa`] whose `:ci`
/// slot is absent. Carries the offending caixa's `:nome` so the diagnostic
/// reads `caixa "<nome>": :kind Acao requires a :ci slot` — naming which
/// `caixa.lisp` needs author attention, not just the axis the consumer
/// rejected.
///
/// Lifted from `caixa-actions`' inline
/// `.ok_or_else(|| Error::MissingCi { nome: caixa.nome().to_string() })`
/// gate so a future per-`Acao` consumer (the deferred
/// `sui-supercacheci::canteiro::emit_gha` workflow renderer, the future
/// per-`Acao` CR materializer that mirrors the sibling per-`Servico` and
/// per-`Aplicacao` materializers the M4 roadmap acknowledges) reaches for
/// the same typed view via `#[from]` instead of re-inlining the same
/// `.ok_or_else(...)` construction.
///
/// Peer of [`KindMismatch`] on the per-renderer kind-gate axis and
/// [`ServicoCountMismatch`] on the per-Servico V0-count-gate axis — the
/// third typed named-caixa entry-gate view every per-kind
/// `caixa-<target>` renderer wraps via `#[from]` in its own
/// [`thiserror`] `Error` enum.
#[derive(Debug, Clone, PartialEq, Eq, Error)]
#[error("caixa {nome:?}: :kind Acao requires a :ci slot")]
pub struct MissingCiSlot {
/// The offending caixa's `:nome` — names which `caixa.lisp` the
/// consumer was handed, so the diagnostic doesn't require the user
/// to grep for it.
pub nome: String,
}
/// Predicate: assert that `caixa.ci().is_some()`, returning the borrowed
/// [`canteiro_types::CiRun`] on success and a typed [`MissingCiSlot`] view
/// (carrying [`Caixa::nome`]) on rejection. The canonical entry-point
/// every per-`Acao` consumer wraps in its own [`thiserror`] `Error`
/// variant via `#[from]` — the call site becomes a single
/// `let ci = caixa_core::require_ci(caixa)?;` in place of the prior
/// two-line
/// `let ci = caixa.ci().ok_or_else(|| Error::MissingCi { nome: caixa.nome().to_string() })?;`
/// block.
///
/// Returns `&CiRun` (rather than `()` like the peer [`require_kind`] and
/// [`require_single_servico`] predicates on the same substrate entry-gate
/// axis) because every caller then reaches for the borrowed `:ci` slot's
/// [`canteiro_types::CiRun`] to decompose / render / emit — projecting
/// the successful borrow through the same `?` step folds the check and
/// the bind onto one call site, matching how every present + roadmapped
/// per-`Acao` consumer uses the slot.
///
/// Lifted to a single helper so the `:ci`-slot-presence gate — the same
/// axis the [`crate::LayoutError::MissingCi`] emission gates on at
/// `feira build` time — lives in exactly one place across every future
/// per-`Acao` consumer: a future `sui-supercacheci::canteiro::emit_gha`
/// workflow renderer (the deferred `caixa-actions` next step named in
/// its own crate docs), a future per-`Acao` CR materializer, and every
/// consumer downstream reaches for the same typed helper and gets the
/// same named-the-offending-caixa diagnostic for free.
///
/// Same trajectory as [`require_kind`] / [`KindMismatch`] on the peer
/// per-renderer kind-gate axis and [`require_single_servico`] /
/// [`ServicoCountMismatch`] on the peer per-Servico V0-count-gate axis:
/// one `caixa_core::require_*` helper per typed entry-gate axis, so the
/// diagnostic shape (named caixa, named field) is uniform across the
/// substrate, and every per-kind renderer's `Error::From<*>` `#[from]`
/// arm gets the diagnostic-naming-the-offending-caixa contract for free.
///
/// # Errors
///
/// Returns [`MissingCiSlot`] when `caixa.ci().is_none()` — every
/// non-`Acao` kind lands here (the sibling
/// [`crate::LayoutError::CiOnNonAcao`] gate refuses a declared `:ci` on
/// any other kind at `feira build` time, so a callsite that gates on
/// `:kind Acao` first via [`require_kind`] will only ever surface this
/// arm for a `:kind Acao` caixa that hasn't declared its `:ci` yet).
/// The error carries the caixa's `:nome` so the diagnostic names the
/// offending `caixa.lisp` — same shape every consumer's
/// `Error::From<MissingCiSlot>` converts into the consumer's local error
/// type.
pub fn require_ci(caixa: &Caixa) -> Result<&canteiro_types::CiRun, MissingCiSlot> {
caixa.ci().ok_or_else(|| MissingCiSlot {
nome: caixa.nome().to_string(),
})
}
/// Typed `:ci`-decompose-failure view: the canonical surface every
/// per-`Acao` consumer raises when [`canteiro_types::decompose`] refuses
/// the caixa's declared `:ci` run (a duplicate node name, a dependency
/// on an undeclared node, a dependency cycle — every failure mode the
/// sibling [`canteiro_types::DecomposeError`] enumerates). Carries the
/// offending caixa's `:nome` alongside the borrowed
/// [`canteiro_types::DecomposeError`] source so the diagnostic reads
/// `caixa "<nome>": :ci decompose failed: <source>` — naming which
/// `caixa.lisp` needs author attention, not just the axis the consumer
/// rejected.
///
/// Lifted from `caixa-actions`' inline `Error::Decompose { nome: String,
/// #[source] source: DecomposeError }` variant so a future per-`Acao`
/// consumer (the deferred `sui-supercacheci::canteiro::emit_gha`
/// workflow renderer named in the `caixa-actions` crate docs, the
/// future per-`Acao` CR materializer that mirrors the sibling
/// per-`Servico` / per-`Aplicacao` materializers the M4 roadmap
/// acknowledges) reaches for the same typed view via `#[from]` instead
/// of re-inlining the same `nome: String, #[source] source:
/// DecomposeError` construction on its own call site — the second
/// typed named-caixa diagnostic axis on the per-`Acao` consumer surface
/// after the peer [`MissingCiSlot`] presence-gate axis.
///
/// Peer of [`MissingCiSlot`] on the per-`Acao` `:ci`-slot diagnostic
/// axis (the presence gate reaches for [`MissingCiSlot`] via
/// [`require_ci`]; the decompose gate reaches for [`CiDecomposeFailure`]
/// on the borrowed [`canteiro_types::CiRun`] the presence gate returns).
/// Peer of [`KindMismatch`] / [`ServicoCountMismatch`] on the sibling
/// per-renderer entry-gate diagnostic axes — extends the same "one
/// typed view per axis, carrying the offending caixa's `:nome` +
/// axis-specific detail, wrapped by every consumer via `#[from]`"
/// discipline onto the [`canteiro_types::decompose`] axis on the
/// per-`Acao` consumer surface.
///
/// The `source` field carries the borrowed
/// [`canteiro_types::DecomposeError`] verbatim (rather than collapsing
/// to a single opaque axis) so a future consumer that wants to fan on
/// the specific decompose-failure arm — a `feira lint` sub-diagnostic
/// that offers a `:deps`-repair suggestion on the `MissingDependency`
/// arm but not the `Cycle` arm, a future per-`Acao` CR materializer's
/// admission webhook that surfaces the cycle path on rejection —
/// reaches for `err.source` directly rather than re-parsing the Display
/// bytes.
///
/// [`DecomposeError`]: canteiro_types::DecomposeError
#[derive(Debug, Error)]
#[error("caixa {nome:?}: :ci decompose failed: {source}")]
pub struct CiDecomposeFailure {
/// The offending caixa's `:nome` — names which `caixa.lisp` the
/// consumer was handed, so the diagnostic doesn't require the user
/// to grep for it. Constructed via the lifted [`crate::Caixa::nome`]
/// accessor's `.to_string()` extension, matching the peer
/// [`MissingCiSlot::nome`] / [`KindMismatch::nome`] /
/// [`ServicoCountMismatch::nome`] `nome`-carrying axes.
pub nome: String,
/// The [`canteiro_types::decompose`] error the caixa's `:ci` run
/// tripped on — carried verbatim so a consumer that fans on the
/// specific arm (`Cycle` / `MissingDependency` / `DuplicateNode` /
/// …) reaches for the typed source rather than re-parsing the
/// Display bytes.
#[source]
pub source: canteiro_types::DecomposeError,
}
/// Predicate: decompose a borrowed [`canteiro_types::CiRun`] into its
/// typed [`canteiro_types::CanteiroDag`] via
/// [`canteiro_types::decompose`], wrapping any
/// [`canteiro_types::DecomposeError`] in a typed [`CiDecomposeFailure`]
/// view (carrying [`Caixa::nome`]) on rejection. The canonical
/// entry-point every per-`Acao` consumer wraps in its own
/// [`thiserror`] `Error` variant via `#[from]` — the call site becomes
/// a single `let cd = caixa_core::decompose_ci(caixa, ci)?;` in place
/// of the prior inline
/// `let cd = canteiro_types::decompose(ci).map_err(|source| CiDecomposeFailure { nome: caixa.nome().to_string(), source })?;`
/// block.
///
/// Takes the borrowed [`canteiro_types::CiRun`] as a separate argument
/// (rather than re-borrowing it through [`require_ci`] internally) so
/// the axis stays single-purpose — the sibling [`require_ci`] presence
/// gate returns the borrowed slot, this predicate consumes it, and the
/// two together form the substrate-canonical two-line per-`Acao` prelude
/// `let ci = caixa_core::require_ci(caixa)?; let cd = caixa_core::decompose_ci(caixa, ci)?;`
/// every present + roadmapped per-`Acao` consumer runs at its
/// entry-point (matching how the sibling per-Servico entry-gate axes
/// keep [`require_kind`] and [`require_single_servico`] as separate
/// primitives, then compose them into the V0-shape
/// [`require_v0_servico_shape`] helper — the compound `require + decompose`
/// helper is a peer-lift for a later commit when a second per-`Acao`
/// consumer arrives). The `caixa: &Caixa` argument is what makes the
/// diagnostic name the offending `caixa.lisp` — the borrowed
/// [`Caixa::nome`] accessor projects through the typed-view
/// constructor unchanged, matching the peer [`require_ci`] /
/// [`require_kind`] / [`require_single_servico`] typed-view constructors.
///
/// Lifted to a single helper so the [`canteiro_types::decompose`]
/// axis — the same axis every per-`Acao` consumer runs on its declared
/// `:ci` slot — lives in exactly one place across every future
/// per-`Acao` consumer: a future `sui-supercacheci::canteiro::emit_gha`
/// workflow renderer (the deferred `caixa-actions` next step named in
/// its own crate docs), a future per-`Acao` CR materializer's admission
/// webhook, a future `feira lint` sub-diagnostic that offers a
/// `:deps`-repair suggestion on the [`canteiro_types::DecomposeError::MissingDependency`]
/// arm but not the [`canteiro_types::DecomposeError::Cycle`] arm — every
/// consumer reaches for the same one-liner + `#[from]` and gets the
/// diagnostic-naming-the-offending-caixa contract for free.
///
/// Same trajectory as [`require_kind`] / [`KindMismatch`] on the peer
/// per-renderer kind-gate axis, [`require_single_servico`] /
/// [`ServicoCountMismatch`] on the peer per-Servico V0-count-gate
/// axis, and [`require_ci`] / [`MissingCiSlot`] on the peer per-`Acao`
/// presence-gate axis: one `caixa_core::require_*`/`decompose_ci`
/// helper per typed axis, so the diagnostic shape (named caixa, named
/// field) is uniform across the substrate, and every consumer's
/// `Error::From<*>` `#[from]` arm gets the diagnostic-naming-the-
/// offending-caixa contract for free.
///
/// # Errors
///
/// Returns [`CiDecomposeFailure`] when [`canteiro_types::decompose`]
/// refuses the borrowed `:ci` run — every failure mode the sibling
/// [`canteiro_types::DecomposeError`] enumerates (a duplicate node
/// name, a dependency on an undeclared node, a dependency cycle) lands
/// on this arm. The error carries the caixa's `:nome` + the underlying
/// [`canteiro_types::DecomposeError`] verbatim so the diagnostic names
/// the offending `caixa.lisp` and a consumer that fans on the specific
/// arm reaches for `err.source` directly rather than re-parsing the
/// Display bytes — same shape every consumer's
/// `Error::From<CiDecomposeFailure>` converts into the consumer's local
/// error type.
pub fn decompose_ci(
caixa: &Caixa,
ci: &canteiro_types::CiRun,
) -> Result<canteiro_types::CanteiroDag, CiDecomposeFailure> {
canteiro_types::decompose(ci).map_err(|source| CiDecomposeFailure {
nome: caixa.nome().to_string(),
source,
})
}
/// Substrate-canonical per-`Acao` declared-edge-count projection every
/// consumer of a borrowed [`canteiro_types::CiRun`] that needs the total
/// number of author-declared `deps` edges across every
/// [`canteiro_types::CiNode`] keys off — returns the plain [`usize`] sum
/// `ci.nodes.iter().map(|n| n.deps.len()).sum()` verbatim, without
/// running [`canteiro_types::decompose`] again (the count is a property
/// of the borrowed run's shape, not of the owned
/// [`canteiro_types::CanteiroDag`] the sibling [`decompose_ci`] returns
/// — an author-declared cycle carries the same edge count as an
/// author-declared linear DAG of the same node-and-dep list).
///
/// The declared-edge-count axis carries the "how many `deps` edges did
/// this repo's CI author write?" projection every per-`Acao` consumer
/// downstream fans on: the `caixa_actions::RenderedAcao::edge_count`
/// artifact the M0 renderer's `validate` returns (paired with the
/// topological node-name list from `cd.topo_order()`), the deferred
/// `sui-supercacheci::canteiro::emit_gha` workflow renderer's
/// per-workflow `jobs.<job>.needs` count reconciliation pass (each
/// `needs` entry maps 1:1 to a `deps` edge, so a renderer that emits N
/// edges must have consumed exactly `declared_edge_count` `needs`
/// entries across the fan-out), a future `feira lint --acao` per-caixa
/// admission verb's per-repo declared-edge summary, a future M4
/// `acao.pleme.io/v1alpha1/Acao` CR materializer's admission webhook
/// spanning the declared edge count against a per-tenant complexity cap.
///
/// Prior to this lift the `ci.nodes.iter().map(|n| n.deps.len()).sum()`
/// expression was inlined at two sites — `caixa_actions::validate`'s
/// `edge_count` field construction at `caixa-actions/src/lib.rs:159`
/// (the M0 per-`Acao` renderer's sole production consumer) and its own
/// [`require_acao_view`] byte-parity pin at `caixa-actions/src/lib.rs:735`
/// (which reconstructs the same sum through the compound helper's
/// returned `&CiRun` to pin that the two paths agree) — two open-coded
/// arithmetic expressions that expressed no compile-time link back to
/// the typed [`canteiro_types::CiRun`] axis, so a future refactor of
/// the declared-edge-count shape (a promotion of the plain [`usize`]
/// sum to a `{intra_workspace, cross_workspace}` split once
/// [`canteiro_types::CiNode`] grows a workspace-scoped edge kind, a
/// per-`:ci` `deps`-edge-canonicalization pass that collapses duplicate
/// edges once the canteiro-types axis grows a set-shaped `deps`
/// representation, a per-env-class edge-weight overlay once the M4
/// `EnvClass` axis grows a per-edge cost model) would have had to be
/// threaded through both open-coded copies in lockstep or the M0
/// renderer's `edge_count` artifact would silently disagree with its
/// own byte-parity pin. Lifting the projection to a typed method on the
/// substrate primitive means every downstream consumer of the `Acao`'s
/// declared-edge-count surface reaches for exactly one typed
/// dispatch — the resolver's accept-set migrates as a unit on any
/// future axis addition.
///
/// The docstring on [`require_acao_view`] already named this expression
/// verbatim ("the borrowed run for per-[`canteiro_types::CiNode`] axes
/// (`ci.nodes.iter().map(|n| n.deps.len()).sum()` for the declared edge
/// count …)") but the substrate carried no primitive for it — the
/// citation was documentation-only, and the two open-coded call sites
/// re-expressed the arithmetic each time. This lift closes that gap:
/// the docstring now cites the substrate primitive by name and every
/// consumer reaches for the same [`ci_declared_edge_count`] one-liner.
///
/// Peer of the sibling [`require_ci`] / [`decompose_ci`] /
/// [`require_acao_view`] per-`Acao` primitives on the substrate's
/// per-kind renderer entry-gate surface, extended onto the "borrowed
/// [`canteiro_types::CiRun`] scalar projection" axis (the two prior
/// primitives return borrowed / owned structural artifacts; this one
/// returns a plain [`usize`] scalar over the borrowed run's node-list
/// shape). Same "one typed dispatch on the substrate primitive, thin
/// projections at each consumer" discipline the peer per-`Aplicacao`
/// [`crate::aplicacao::AplicacaoSpec::port_for_destination`] scalar
/// projection carries on the per-Aplicacao `:entrada` port-resolution
/// axis, extended onto the per-`Acao` `:ci` declared-edge-count axis.
///
/// Named `ci_declared_edge_count` (rather than `declared_edge_count`)
/// to keep the substrate-side helper namespace explicit that the input
/// axis is a `:ci` slot — matching the peer [`require_ci`] /
/// [`decompose_ci`] `ci_`-prefix-shaped naming convention the sibling
/// per-`Acao` substrate primitives already carry, so a caller reading
/// `caixa_core::ci_declared_edge_count(ci)` sees the axis at the
/// helper name rather than at a lifted-out `use` alias.
#[must_use]
pub fn ci_declared_edge_count(ci: &canteiro_types::CiRun) -> usize {
ci.nodes.iter().map(|n| n.deps.len()).sum()
}
/// Typed `:servicos`-count-mismatch view: the canonical surface every
/// per-Servico `caixa-<target>` renderer raises when it's handed a
/// [`Caixa`] whose `:servicos` list doesn't carry exactly one entry —
/// the V0 contract every Servico-kind caixa satisfies (`caixa-helm`'s
/// `render_chart_for_servico`, `caixa-flux`'s `programs_yaml_entry`, the
/// future per-Servico OCI/wasm packager). Carries the offending caixa's
/// `:nome` alongside the actual count, so the diagnostic reads `caixa
/// "<nome>": :servicos must declare exactly one entry for V0 (got
/// <count>)` — naming which `caixa.lisp` needs author attention, not
/// just the count the renderer rejected.
///
/// Lifted from two identical-shape per-renderer arms in
/// [`caixa-helm`][helm-err] and [`caixa-flux`][flux-err]
/// (`Error::UnsupportedServicoCount(usize)`). The prior arms each
/// carried only the actual count, leaving the user to grep for which
/// `caixa.lisp` triggered the mismatch — exactly the "feira verb whose
/// error path doesn't name the offending caixa" punch-list item the
/// compounding-mandate protocol calls out. Same trajectory as
/// [`KindMismatch`] (which lifted the prior `NotAServico(CaixaKind)` /
/// `NotAnAplicacao(CaixaKind)` per-renderer arms into a typed view
/// naming the offending caixa).
///
/// Renderers wrap this view in their own [`thiserror`] `Error` enum
/// via `#[from]`; the `?` operator at every count-checking call site
/// turns the [`require_single_servico`] result into the renderer's
/// local error type with no manual conversion. Peer to [`require_kind`]
/// on the V0 Servico-shape gate axis (the kind gate refuses the wrong
/// `:kind`; this gate refuses the wrong `:servicos` count) — every
/// per-Servico renderer chains both at its entry point.
///
/// [helm-err]: https://docs.rs/caixa-helm
/// [flux-err]: https://docs.rs/caixa-flux
#[derive(Debug, Clone, PartialEq, Eq, Error)]
#[error("caixa {nome:?}: :servicos must declare exactly one entry for V0 (got {count})")]
pub struct ServicoCountMismatch {
/// The offending caixa's `:nome` — names which `caixa.lisp` the
/// renderer was handed, so the diagnostic doesn't require the
/// user to grep for it.
pub nome: String,
/// The `:servicos` list length the offending caixa actually carries.
/// The expected count is fixed at 1 by the V0 contract — every
/// `:kind Servico` caixa declares exactly one `ComputeUnit` YAML
/// pointer, matching the one Helm chart / one programs.yaml entry
/// each renderer emits.
pub count: usize,
}
/// Predicate: assert that `caixa.servicos.len() == 1`, returning a typed
/// [`ServicoCountMismatch`] view (carrying [`Caixa::nome`] + the actual
/// count) on rejection. The canonical entry-point every per-Servico
/// renderer wraps in its own [`thiserror`] `Error` variant via
/// `#[from]` — the call site becomes a single
/// `caixa_core::require_single_servico(caixa)?;` in place of the prior
/// inline `if caixa.servicos.len() != 1 { return
/// Err(Error::UnsupportedServicoCount(caixa.servicos.len())); }`
/// block.
///
/// Lifted to a single helper so the V0 `:servicos`-singularity invariant
/// — the same shape the [`crate::Caixa::validate_code_paths`] doc
/// comment already names as load-bearing on caixa-helm + caixa-flux
/// (caixa-core/src/manifest.rs:4108) — lives in exactly one place across
/// every per-Servico renderer. A future per-Servico renderer
/// (`caixa-otel`, the future per-Servico OCI packager, the future M4
/// `wasm.pleme.io/v1alpha1/ComputeUnit` CR materializer) gets the same
/// naming-the-offending-caixa diagnostic for free, and a future change
/// to the V0 invariant (e.g. allowing multi-servico Servicos when the
/// component-model multi-world boundary lands in M5) is one edit here,
/// not a coordinated rewrite of every renderer's per-arm
/// `UnsupportedServicoCount` check.
///
/// Same trajectory as [`require_kind`] / [`KindMismatch`] on the peer
/// V0 Servico-shape axis: every per-Servico renderer reaches for one
/// `caixa_core::require_*` helper per V0 invariant, so the diagnostic
/// shape (named caixa, named field) is uniform across the substrate.
///
/// # Errors
///
/// Returns [`ServicoCountMismatch`] when `caixa.servicos.len() != 1`
/// (both empty and ≥ 2 land on this arm — the V0 contract requires
/// *exactly* one entry, not *at-least* one). The error carries the
/// caixa's `:nome` + the offending count so the diagnostic names the
/// offending `caixa.lisp` — same shape every renderer's
/// `Error::From<ServicoCountMismatch>` converts into the renderer's
/// local error type.
pub fn require_single_servico(caixa: &Caixa) -> Result<(), ServicoCountMismatch> {
if caixa.servicos().len() == 1 {
Ok(())
} else {
Err(ServicoCountMismatch {
nome: caixa.nome().to_string(),
count: caixa.servicos().len(),
})
}
}
/// Compound V0-shape entry gate: the canonical two-line
/// `require_kind(caixa, Servico)? + require_single_servico(caixa)?`
/// prelude every per-Servico `caixa-<target>` renderer runs at its
/// entry-point, collapsed onto one call the caller reads as intent
/// ("gate the input on the V0 Servico shape") rather than two
/// hand-spelled predicate calls.
///
/// The pair names one contract with two axes: `:kind` is `Servico`
/// (this is a per-Servico renderer's input, not a `Biblioteca` /
/// `Binario` / `Supervisor` / `Aplicacao` mis-hand-off) *and*
/// `:servicos.len() == 1` (the V0 contract every Servico caixa
/// satisfies — one `ComputeUnit` YAML pointer, matching the one Helm
/// chart / programs.yaml entry / cluster bundle each per-Servico
/// renderer emits). Both axes must hold together — a `:kind Servico`
/// caixa with two `:servicos` entries and a `:kind Aplicacao` caixa
/// with one `:servicos` entry are equally invalid at every per-Servico
/// renderer's entry-point — so lifting the pair onto one helper names
/// the compound contract at each call site the way the M2 typed slots'
/// [`servico_m2_overlay`] names the compound `:limits`+`:behavior`+
/// `:upgrade-from` overlay contract at each call site.
///
/// Three production call sites previously carried the two-line pair
/// inline:
///
/// * `caixa-flux`'s [`programs_yaml_entry`][flux-yaml] (the
/// aggregator-path programs.yaml entry emitter);
/// * `caixa-flux`'s [`cluster_bundle`][flux-bundle] (the standalone
/// `GitRepository` + `HelmRelease` + `Kustomization` trio emitter);
/// * `caixa-helm`'s
/// [`render_chart_for_servico_with`][helm-chart] (the per-program
/// `lareira-<nome>` Helm chart emitter).
///
/// Each site now reads `caixa_core::require_v0_servico_shape(caixa)?`
/// instead of the two-line pair. A future per-Servico renderer
/// (`caixa-otel`, the future per-Servico OCI packager, the future M4
/// `wasm.pleme.io/v1alpha1/ComputeUnit` CR materializer,
/// MESH-COMPOSITION §III.2 #5) gets the compound V0-shape gate for
/// free with one call, instead of re-inlining the two-line pair — and
/// a future change to the V0 contract (e.g. adding a
/// `:kind Servico`-only `:computeunits`-slot-shape gate when the
/// component-model multi-world boundary lands in M5) is one edit here,
/// not a coordinated rewrite of every renderer's inline pair.
///
/// The generic error type `E` accepts every renderer's local
/// [`thiserror`] `Error` enum that carries both [`KindMismatch`] and
/// [`ServicoCountMismatch`] via `#[from]` (`caixa_flux::Error`,
/// `caixa_helm::Error`, and every future per-Servico renderer that
/// wires both `#[from]` arms as the diagnostic-naming-the-offending-
/// caixa contract already requires). Type inference at the call site
/// resolves `E` from the caller's `?` return type, so the call reads
/// as `caixa_core::require_v0_servico_shape(caixa)?` with no explicit
/// turbofish — the same one-liner shape every peer `require_kind` /
/// `require_single_servico` call site already reads as.
///
/// Peer to [`require_kind`] on the single-axis kind gate and
/// [`require_single_servico`] on the single-axis count gate — both
/// primitives stay public because per-non-Servico renderers
/// (`caixa-mesh`'s per-Aplicacao gate, `caixa-feira`'s
/// `first_servico_path` per-verb gate that composes both predicates
/// with `anyhow::Context`) reach for the individual predicates rather
/// than the compound one. Peer to [`servico_m2_overlay`] on the
/// sibling per-Servico compound-contract surface: `servico_m2_overlay`
/// names the compound M2 emit-side contract, `require_v0_servico_shape`
/// names the compound V0 gate-side contract, both per-Servico shape.
///
/// [flux-yaml]: https://docs.rs/caixa-flux
/// [flux-bundle]: https://docs.rs/caixa-flux
/// [helm-chart]: https://docs.rs/caixa-helm
///
/// # Errors
///
/// Returns the caller's `E` wrapping a [`KindMismatch`] when
/// `caixa.kind != CaixaKind::Servico`, or a [`ServicoCountMismatch`]
/// when `caixa.servicos.len() != 1`. Order matches the two-line pair
/// this replaces: the kind gate fires first, so a
/// `:kind Aplicacao` caixa with zero `:servicos` entries surfaces the
/// kind mismatch (the more actionable diagnostic — the author has the
/// wrong `:kind`) rather than the count mismatch (a downstream
/// consequence of the mis-kinded input).
pub fn require_v0_servico_shape<E>(caixa: &Caixa) -> Result<(), E>
where
E: From<KindMismatch> + From<ServicoCountMismatch>,
{
require_kind(caixa, CaixaKind::Servico)?;
require_single_servico(caixa)?;
Ok(())
}
/// Compound per-Aplicacao entry gate: the canonical three-line
/// `require_kind(caixa, CaixaKind::Aplicacao)? +
/// caixa.aplicacao_view().expect(…) + spec.validate()?` prelude every
/// per-Aplicacao `caixa-<target>` renderer runs at its entry-point,
/// collapsed onto one call the caller reads as intent ("gate the input
/// on the V0 Aplicacao shape and hand back a validated
/// [`crate::aplicacao::AplicacaoSpec`]") rather than three hand-spelled
/// steps.
///
/// The cascade names one contract with three axes: `:kind` is
/// `Aplicacao` (this is a per-Aplicacao renderer's input, not a
/// `Biblioteca` / `Binario` / `Servico` / `Supervisor` / `Acao`
/// mis-hand-off), the [`Caixa::aplicacao_view`] fold-in succeeds (which
/// [`require_kind`]-on-`Aplicacao` guarantees per its own doc pin —
/// [`Caixa::aplicacao_view`] returns `Some` iff `caixa.kind().is_aplicacao()`),
/// *and* the folded [`crate::aplicacao::AplicacaoSpec`] passes its own
/// M3 typed-shape validation ([`crate::aplicacao::AplicacaoSpec::validate`]:
/// non-empty `:membros`, DNS-1123 member names, semver-valid `:versao`
/// requirements, `:contratos` referencing only declared members,
/// `:placement Sharded` carrying `:shard-key`, `:placement`
/// `Replicated`/`SingleNode` carrying `:clusters`, and so on across
/// every M3 typed slot). All three axes must hold together — a
/// `:kind Servico` caixa carrying a well-formed `:membros`/`:contratos`
/// stanza (the manifest field's documented "silently ignored" case)
/// and a `:kind Aplicacao` caixa with an empty `:membros` are equally
/// invalid at every per-Aplicacao renderer's entry-point — so lifting
/// the three-arm cascade onto one helper names the compound contract
/// at each call site the way the sibling per-Servico
/// [`require_v0_servico_shape`] compound gate already names the
/// two-axis compound V0 Servico-shape contract.
///
/// Three production call sites in `caixa-mesh` previously funneled
/// through the crate-local `typed_view` wrapper which itself carried
/// the three-line cascade inline:
///
/// * `caixa-mesh`'s [`programs_for_aplicacao`][mesh-programs] (the
/// `lareira-fleet-programs`-aggregator programs.yaml fan-out
/// emitter);
/// * `caixa-mesh`'s [`cilium_network_policies`][mesh-cnp] (the
/// per-`(:de, :para)` L7 Cilium CRD emitter);
/// * `caixa-mesh`'s [`gateway_routes`][mesh-gw] (the per-`:entrada`
/// K8s Gateway API v1 Gateway + HTTPRoute emitter).
///
/// The crate-local `caixa_mesh::typed_view` wrapper now reads as a
/// one-liner `caixa_core::require_aplicacao_view::<Error>(caixa)`. A
/// future per-Aplicacao renderer (`caixa-tatara`'s per-Aplicacao
/// [`process_for_aplicacao`][tatara] downstream axes when they grow a
/// spec-consuming validate arm, the deferred
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's admission
/// webhook, a future `feira validate --aplicacao` per-caixa admission
/// verb) gets the compound three-arm gate for free with one call,
/// instead of re-inlining the three-line cascade — and a future change
/// to the V0 Aplicacao contract (e.g. adding a `:kind Aplicacao`-only
/// `:membros`-cross-cluster-uniqueness gate when the M4 federated-app
/// boundary lands) is one edit here, not a coordinated rewrite of
/// every per-Aplicacao renderer's inline cascade.
///
/// The generic error type `E` accepts every per-Aplicacao renderer's
/// local [`thiserror`] `Error` enum that carries both [`KindMismatch`]
/// and [`crate::aplicacao::AplicacaoError`] via `#[from]`
/// (`caixa_mesh::Error`, and every future per-Aplicacao renderer that
/// wires both `#[from]` arms as the diagnostic-naming-the-offending-
/// caixa contract already requires). Type inference at the call site
/// resolves `E` from the caller's `?` return type, though a caller
/// that assigns the result directly to a `Result<AplicacaoSpec,
/// Error>` binding may need a turbofish
/// (`::<Error>`) — matching the sibling `require_v0_servico_shape::<Error>`
/// turbofish convention the peer per-Servico call sites already read.
///
/// Peer to [`require_v0_servico_shape`] on the sibling per-Servico
/// entry-gate axis and [`require_kind`] / [`require_ci`] /
/// [`decompose_ci`] on the sibling per-`Acao` entry-gate axis — every
/// per-kind renderer's entry-gate cascade now lives in exactly one
/// substrate primitive.
///
/// [mesh-programs]: https://docs.rs/caixa-mesh
/// [mesh-cnp]: https://docs.rs/caixa-mesh
/// [mesh-gw]: https://docs.rs/caixa-mesh
/// [tatara]: https://docs.rs/caixa-tatara
///
/// # Errors
///
/// Returns the caller's `E` wrapping a [`KindMismatch`] when
/// `caixa.kind != CaixaKind::Aplicacao`, or a
/// [`crate::aplicacao::AplicacaoError`] when the folded
/// [`crate::aplicacao::AplicacaoSpec`] fails its typed-shape
/// validation. Order matches the three-line cascade this replaces: the
/// kind gate fires first, so a `:kind Servico` caixa with a
/// well-formed `:membros` stanza surfaces the kind mismatch (the more
/// actionable diagnostic — the author has the wrong `:kind`) rather
/// than the `AplicacaoError` (which the [`Caixa::aplicacao_view`]
/// fold-in never even reaches on a non-`Aplicacao` kind).
///
/// # Panics
///
/// Never in practice — the internal [`Caixa::aplicacao_view`] unwrap
/// is guarded by the preceding [`require_kind`]-on-`Aplicacao` gate,
/// and [`Caixa::aplicacao_view`]'s own doc pin guarantees
/// `Some`-return iff `caixa.kind().is_aplicacao()`. A future
/// [`Caixa::aplicacao_view`] refactor that decouples `Some`-return
/// from `caixa.kind().is_aplicacao()` would trip this panic at the
/// first per-Aplicacao renderer call site, not silently return `Err(E)`
/// at every one — the panic message names the substrate invariant so
/// the offending edit is obvious.
pub fn require_aplicacao_view<E>(caixa: &Caixa) -> Result<crate::aplicacao::AplicacaoSpec, E>
where
E: From<KindMismatch> + From<crate::aplicacao::AplicacaoError>,
{
require_kind(caixa, CaixaKind::Aplicacao)?;
let spec = caixa
.aplicacao_view()
.expect("require_kind(Aplicacao) guarantees Caixa::aplicacao_view returns Some");
spec.validate()?;
Ok(spec)
}
/// Compound per-`Acao` entry gate: the canonical three-line
/// `require_kind(caixa, CaixaKind::Acao)? + require_ci(caixa)? +
/// decompose_ci(caixa, ci)?` prelude every per-`Acao` `caixa-<target>`
/// consumer runs at its entry-point, collapsed onto one call the caller
/// reads as intent ("gate the input on the V0 Acao shape and hand back
/// the borrowed [`canteiro_types::CiRun`] + the decomposed
/// [`canteiro_types::CanteiroDag`]") rather than three hand-spelled
/// steps.
///
/// The cascade names one contract with three axes: `:kind` is `Acao`
/// (this is a per-`Acao` consumer's input, not a `Biblioteca` /
/// `Binario` / `Servico` / `Supervisor` / `Aplicacao` mis-hand-off),
/// the `:ci` slot is present ([`require_ci`] returns the borrowed
/// [`canteiro_types::CiRun`]), *and* the declared run decomposes
/// cleanly through [`canteiro_types::decompose`] (a duplicate node
/// name, a missing dep, a cycle — every [`canteiro_types::DecomposeError`]
/// arm — surfaces via [`CiDecomposeFailure`]). All three axes must
/// hold together — so lifting the three-arm cascade onto one helper
/// names the compound contract at each call site the way the sibling
/// per-Servico [`require_v0_servico_shape`] compound gate already
/// names the two-axis compound V0 Servico-shape contract and the
/// sibling per-Aplicacao [`require_aplicacao_view`] compound gate
/// names the three-arm compound per-Aplicacao entry-gate contract.
///
/// Returns the borrowed [`canteiro_types::CiRun`] paired with the
/// owned [`canteiro_types::CanteiroDag`] `decompose_ci` produced —
/// both are the load-bearing artifacts every per-`Acao` consumer
/// reads past the gate: the borrowed run for
/// per-[`canteiro_types::CiNode`] axes (the substrate primitive
/// [`ci_declared_edge_count`] for the declared edge count, the
/// deferred `sui-supercacheci::canteiro::emit_gha` per-node YAML emit
/// surface), the owned DAG for topological order (`cd.topo_order()`,
/// which the substrate's own [`decompose_ci`] pass-through-on-success
/// contract at [`decompose_ci_accepts_valid_ci_run_and_returns_canteiro_dag`]
/// pins as infallible on the accepted arm).
///
/// The current single production call site — `caixa-actions::validate` —
/// previously carried the three-line prelude inline:
///
/// ```ignore
/// caixa_core::require_kind(caixa, CaixaKind::Acao)?;
/// let ci = caixa_core::require_ci(caixa)?;
/// let cd = caixa_core::decompose_ci(caixa, ci)?;
/// ```
///
/// It now reads as a one-liner
/// `let (ci, cd) = caixa_core::require_acao_view::<Error>(caixa)?;`.
/// Every deferred per-`Acao` consumer named in the `caixa-actions` crate
/// docs (the `sui-supercacheci::canteiro::emit_gha` workflow renderer, a
/// future `acao.pleme.io/v1alpha1/Acao` CR materializer's admission
/// webhook, a future `feira validate --acao` per-caixa admission verb)
/// gets the compound three-arm gate for free with one call, instead of
/// re-inlining the three-line prelude — and a future change to the V0
/// Acao contract (an M4 [`canteiro_types::CiRun`] `:workspace`-scoped
/// admission gate the CR materializer resolves at admission time, a
/// per-`:ci` cross-node capability-audit prelude the Pony-inspired
/// capability-typing roadmap acknowledges) is one edit here on the
/// compound helper, not a coordinated rewrite across every per-`Acao`
/// consumer's inline three-line prelude.
///
/// The generic error type `E` accepts every per-`Acao` consumer's
/// local [`thiserror`] `Error` enum that carries all three of
/// [`KindMismatch`], [`MissingCiSlot`], and [`CiDecomposeFailure`]
/// via `#[from]` (`caixa_actions::Error` today, and every future
/// per-`Acao` consumer that wires the same three `#[from]` arms as
/// the diagnostic-naming-the-offending-caixa contract already
/// requires). Type inference at the call site resolves `E` from the
/// caller's `?` return type, though a caller that assigns the result
/// directly to a `Result<(&CiRun, CanteiroDag), Error>` binding may
/// need a turbofish (`::<Error>`) — matching the sibling
/// `require_aplicacao_view::<Error>` turbofish convention the peer
/// per-Aplicacao call site already reads.
///
/// Peer to [`require_v0_servico_shape`] on the sibling per-Servico
/// entry-gate axis and [`require_aplicacao_view`] on the sibling
/// per-Aplicacao entry-gate axis — every per-kind renderer's
/// entry-gate cascade now lives in exactly one substrate primitive.
///
/// # Errors
///
/// Returns the caller's `E` wrapping a [`KindMismatch`] when
/// `caixa.kind != CaixaKind::Acao`, a [`MissingCiSlot`] when the
/// caixa's `:ci` slot is absent past the kind gate, or a
/// [`CiDecomposeFailure`] when [`canteiro_types::decompose`] refuses
/// the borrowed run. Order matches the three-line prelude this
/// replaces: the kind gate fires first (so a `:kind Servico` caixa
/// carrying a well-formed `:ci` stanza — the manifest field's
/// documented "silently ignored" case on a non-`Acao` kind —
/// surfaces the kind mismatch, the more actionable diagnostic), then
/// the presence gate, then the decompose gate.
pub fn require_acao_view<E>(
caixa: &Caixa,
) -> Result<(&canteiro_types::CiRun, canteiro_types::CanteiroDag), E>
where
E: From<KindMismatch> + From<MissingCiSlot> + From<CiDecomposeFailure>,
{
require_kind(caixa, CaixaKind::Acao)?;
let ci = require_ci(caixa)?;
let cd = decompose_ci(caixa, ci)?;
Ok((ci, cd))
}
/// One rendered artifact — a `(path, contents)` pair every per-target
/// `caixa-<target>` renderer emits at every leaf of its output tree.
/// Carries the sandboxed relative path the substrate writes the artifact
/// under (relative to the renderer-chosen output root — the per-chart
/// directory for [`caixa-helm`][cf-helm]'s `lareira-<nome>` chart tree,
/// the per-caixa `./clusters/<cluster>/services/<nome>/` sub-tree for
/// [`caixa-flux`][cf-flux]'s [`cluster_bundle`][cb] Flux v2 CR trio)
/// alongside the pre-serialized byte contents the substrate writes to it.
///
/// Lifted from two identical-shape per-renderer arms in
/// [`caixa-flux`][cf-flux] (`BundleFile { path: PathBuf, contents:
/// String }`) and [`caixa-helm`][cf-helm] (`ChartFile { path: PathBuf,
/// contents: String }`) — same field pair, same derives (`Debug + Clone
/// + PartialEq + Eq`), no per-type impls — carrying the same "one
/// rendered leaf artifact" contract twice. Every prior per-target
/// renderer had reinvented the same two-field record because there was
/// no substrate-side canonical `(path, contents)` shape to reach for;
/// the future per-target renderers the M4/M5 roadmap acknowledges
/// (`caixa-otel`'s per-collector-config emit, the future per-Aplicacao
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-CR YAML
/// emit, the future per-Supervisor reconciler renderer's per-child
/// bundle emit) would have re-added a third and fourth clone of the
/// same record — exactly the "render-side patterns recurring ≥2 times
/// across `caixa-helm` / `caixa-flux` / `caixa-mesh` become helpers.
/// Duplication is a bug. (PRIME DIRECTIVE.)" compounding-mandate slot
/// item.
///
/// Both prior arms remain as public `pub type BundleFile =
/// caixa_core::RenderedFile;` / `pub type ChartFile =
/// caixa_core::RenderedFile;` aliases at their crate boundary so every
/// existing struct-literal construction site
/// (`BundleFile { path: …, contents: … }` / `ChartFile { path: …,
/// contents: … }`), every field-access site (`.path` / `.contents`),
/// and every derive-fed navigator (`==` equality pins, `Debug`
/// formatting probes) resolves through the type alias to the canonical
/// [`RenderedFile`] with no per-call-site edit — Rust type aliases
/// carry the same `#[derive]`-generated `Debug`/`Clone`/`PartialEq`/
/// `Eq` impls as their canonical, so the shared-shape contract lives
/// at one type definition instead of two verbatim clones drifting
/// silently on any future rebrand.
///
/// Peer to the [`KindMismatch`] / [`ServicoCountMismatch`] typed-view
/// lifts on the sibling per-renderer-error-diagnostic-shape axis: both
/// families lift a per-renderer duplicated record onto a canonical
/// substrate-side type, so a future per-target renderer joins the
/// pattern by re-exporting one alias instead of open-coding another
/// clone.
///
/// The `path` axis carries the sandboxed relative path — the same
/// [`is_sandboxed_relative_path`] discipline the [`Caixa::validate_code_paths`]
/// invariant enforces at the manifest-side path axis. No renderer today
/// runs the predicate against the emit-side per-`RenderedFile.path`
/// — the paths are picked from substrate-canonical `&'static str`
/// filename constants ([`FLUX_GITREPOSITORY_YAML_FILENAME`],
/// [`FLUX_HELMRELEASE_YAML_FILENAME`], [`FLUX_KUSTOMIZATION_YAML_FILENAME`],
/// [`HELM_CHART_YAML_FILENAME`], [`HELM_VALUES_YAML_FILENAME`]) rather
/// than author input, so a per-emit-time sandbox check would be
/// belt-and-suspenders — but the shared type shape makes a future
/// sandbox-at-emit-time invariant a one-place add across every
/// per-target renderer.
///
/// [cf-flux]: https://docs.rs/caixa-flux
/// [cf-helm]: https://docs.rs/caixa-helm
/// [cb]: https://docs.rs/caixa-flux/latest/caixa_flux/fn.cluster_bundle.html
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RenderedFile {
/// Sandboxed relative path the substrate writes the artifact under
/// (relative to the renderer-chosen output root). Substrate-canonical
/// filename constants ([`FLUX_GITREPOSITORY_YAML_FILENAME`] /
/// [`FLUX_HELMRELEASE_YAML_FILENAME`] /
/// [`FLUX_KUSTOMIZATION_YAML_FILENAME`] for the `caixa-flux`
/// [`cluster_bundle`] Flux v2 CR trio, [`HELM_CHART_YAML_FILENAME`] /
/// [`HELM_VALUES_YAML_FILENAME`] for the `caixa-helm`
/// `lareira-<nome>` chart directory) source every path today.
pub path: PathBuf,
/// The rendered byte contents — a pre-serialized UTF-8 body every
/// downstream writer (`caixa-flux::cluster_bundle`'s
/// per-`GitRepository`/`HelmRelease`/`Kustomization` YAML emit,
/// `caixa-helm::render_chart_for_servico`'s per-`Chart.yaml`/
/// `values.yaml`/`README.md` chart-directory emit) hands to
/// `std::fs::write` verbatim under the paired [`Self::path`].
pub contents: String,
}
impl RenderedFile {
/// Construct a [`RenderedFile`] from its two axes — the sandboxed
/// relative `path` the substrate writes the artifact under and the
/// pre-serialized UTF-8 `contents` the paired `std::fs::write`
/// hands to that path. Accepts anything convertible into a
/// [`PathBuf`] (`&'static str` from the substrate-canonical
/// filename constants [`HELM_CHART_YAML_FILENAME`] /
/// [`HELM_VALUES_YAML_FILENAME`] / [`FLUX_GITREPOSITORY_YAML_FILENAME`]
/// / [`FLUX_HELMRELEASE_YAML_FILENAME`] /
/// [`FLUX_KUSTOMIZATION_YAML_FILENAME`] every current per-target
/// renderer picks its per-artifact leaf path from, `String` /
/// `PathBuf` for future author-supplied paths) and anything
/// convertible into [`String`] (the `serde_yaml::to_string` /
/// `format!` outputs every current renderer already threads into
/// the paired `contents` field).
///
/// Lifted from six identical-shape struct-literal construction
/// sites — three per-artifact leaves in
/// [`caixa-helm`][cf-helm]'s `render_chart_for_servico_with`
/// (`Chart.yaml`, `values.yaml`, `README.md`) and three per-CR
/// leaves in [`caixa-flux`][cf-flux]'s [`cluster_bundle`][cb]
/// (`gitrepository.yaml`, `helmrelease.yaml`,
/// `kustomization.yaml`) — each of which open-coded a four-line
/// `<Xxx>File { path: PathBuf::from(FILENAME_CONST), contents: <body> }`
/// block that re-derived the same `PathBuf::from(&str)` wrap +
/// the same two-field assembly. Every existing struct-
/// literal construction (the type-alias identity pins at
/// [`caixa_flux::tests::bundle_file_alias_resolves_to_caixa_core_rendered_file`]
/// / [`caixa_helm::tests::chart_file_alias_resolves_to_caixa_core_rendered_file`],
/// the substrate-side field-shape pins in this crate's test
/// module) continues to compile — [`RenderedFile::new`] is an
/// additive inherent constructor that leaves the `pub path` /
/// `pub contents` field visibility untouched, so a future rebrand
/// on the record shape (a per-artifact hash / provenance field
/// addition, a per-artifact write-mode discriminator once
/// per-cluster-writer sandboxing lands) still reaches every
/// per-target renderer through this canonical constructor + the
/// existing struct-literal pinning by construction. Peer to the
/// sibling substrate-side canonical-composer surface
/// ([`oci_chart_ref`] / [`cilium_network_policy_name`] /
/// [`gateway_api_http_route_name`] / [`lareira_chart_name`]) —
/// each is a canonical `&'static fn(&str, …) -> String` composer
/// that every per-target renderer routes through instead of
/// re-deriving the same encoding inline.
///
/// A future per-target renderer (`caixa-otel`'s per-collector-
/// config emit, the future `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-CR YAML emit, the future per-Supervisor
/// reconciler renderer's per-child bundle emit) that constructs a
/// [`RenderedFile`] now reaches for [`RenderedFile::new`] and
/// participates in the same substrate-side per-artifact-
/// construction contract, so any addition here (say, a
/// `sandboxed_relative_path` invariant check on `path` at
/// construction time, the `is_sandboxed_relative_path`
/// discipline the docstring above acknowledges is not yet run at
/// emit time) reaches every per-target renderer through one
/// caixa-core edit instead of a coordinated six-site rewrite.
///
/// [cf-helm]: https://docs.rs/caixa-helm
/// [cf-flux]: https://docs.rs/caixa-flux
/// [cb]: https://docs.rs/caixa-flux/latest/caixa_flux/fn.cluster_bundle.html
#[must_use]
pub fn new<P, S>(path: P, contents: S) -> Self
where
P: Into<PathBuf>,
S: Into<String>,
{
Self {
path: path.into(),
contents: contents.into(),
}
}
}
/// Predicate: find the first ASCII whitespace byte in `s`, or `None` if
/// none of the string's bytes match `u8::is_ascii_whitespace`.
///
/// The canonical drift class this closes across every typed-magnitude
/// codec in caixa-core (`limits::parse_byte_size` backing
/// `:limits :memory`, `limits::parse_duration` backing `:limits
/// :wall-clock`, `limits::parse_millicores` backing `:limits :cpu`,
/// `supervisor::duration_codec::parse` backing `:supervisor
/// :restart-window` / `:politicas :timeout` / `:politicas
/// :circuit-breaker :window`, and `aplicacao::rate_limit_codec::parse`
/// backing `:politicas :rate-limit`) is the ASCII subset of Unicode
/// `White_Space`: space (`0x20`), tab (`0x09`), LF (`0x0A`), FF
/// (`0x0C`), CR (`0x0D`) — the five WhatWG-conformant "ASCII whitespace"
/// bytes (deliberately narrower than POSIX's `[:space:]` which also
/// admits VT `0x0B`). Every downstream YAML / JSON / TOML parser can
/// feed any of these bytes through a quoted-scalar value verbatim, so
/// a paste-from-shell-history `"500m "` (trailing space), a
/// paste-from-aligned-doc `" 64MiB"` (leading space from YAML-quoted-
/// plain-scalar alignment), a paste-from-typography `"30 s"`
/// (whitespace between magnitude and unit), a paste-from-indented-doc
/// `"\t100/s"` (YAML-block-scalar tab byte), or a multi-line-paste
/// `"30s\n"` (trailing LF) all survive the top-level `s.trim()`
/// discipline and yield the same typed value at each codec — but
/// serde round-trips to a *different* canonical form on the next
/// emit, breaking the THEORY.md Part V render-determinism contract
/// every typed slot carries.
///
/// Peer of [`find_non_ascii_whitespace_char`] — the two predicates
/// together partition the full Unicode `White_Space` axis (this one
/// on the ASCII byte range, its peer on the strictly-complementary
/// non-ASCII `char` range), and every typed-magnitude codec in
/// caixa-core calls both back-to-back at parse entry so the codec's
/// accepted set matches its emitted set on the full axis,
/// structurally. Same "single lifted source of truth" discipline the
/// peer non-ASCII arm's 1b75b38 landing pinned: drift between any two
/// codec sites' ASCII-whitespace-rejection set becomes a single-edit
/// fix at this predicate rather than five independent scans
/// diverging over time, and a future stricter classification
/// (BOM `\u{FEFF}` / ZWSP `\u{200B}` / ZWJ `\u{200D}` — the
/// "invisible but not `char::is_whitespace`" class that the
/// deliberate exclusion in `find_non_ascii_whitespace_char` leaves
/// for a follow-up, if a downstream slot proves those are drift
/// classes) can extend at this shared site in one edit rather than
/// five. Peer of [`is_dns_1123_label`] / [`is_gateway_api_http_path`]
/// / [`is_git_repo_url`] — same "typed-slot's valid set matches its
/// codec's accepted set, structurally" discipline carried at the
/// codec layer.
#[must_use]
pub fn find_ascii_whitespace_byte(s: &str) -> Option<u8> {
s.bytes().find(|b| b.is_ascii_whitespace())
}
/// Predicate: find the first non-ASCII Unicode-`White_Space` character in
/// `s`, or `None` if every character lies in the ASCII byte range.
///
/// The canonical drift class this closes across every typed-magnitude
/// codec in caixa-core (`limits::parse_byte_size` backing
/// `:limits :memory`, `limits::parse_duration` backing `:limits
/// :wall-clock`, `supervisor::duration_codec::parse` backing
/// `:supervisor :restart-window` / `:politicas :timeout` /
/// `:politicas :circuit-breaker :window`, and
/// `aplicacao::rate_limit_codec::parse` backing `:politicas
/// :rate-limit`) is the non-ASCII subset of Unicode `White_Space`: NBSP
/// (`\u{00A0}`), OGHAM SPACE MARK (`\u{1680}`), the EN-QUAD /
/// EM-QUAD / EN-SPACE / EM-SPACE / THREE-PER-EM-SPACE /
/// FOUR-PER-EM-SPACE / SIX-PER-EM-SPACE / FIGURE-SPACE /
/// PUNCTUATION-SPACE / THIN-SPACE / HAIR-SPACE band
/// (`\u{2000}`..=`\u{200A}`), LINE SEPARATOR (`\u{2028}`), PARAGRAPH
/// SEPARATOR (`\u{2029}`), NARROW NBSP (`\u{202F}`), MEDIUM
/// MATHEMATICAL SPACE (`\u{205F}`), and IDEOGRAPHIC SPACE
/// (`\u{3000}`). Every one of these characters is
/// [`char::is_whitespace`]`() && !`[`char::is_ascii`]`()`, and every
/// one of them is silently stripped by [`str::trim`] at the top of
/// each codec's parse entry — `str::trim` uses `char::is_whitespace`,
/// which is Unicode `White_Space`, strictly wider than the byte-set
/// `u8::is_ascii_whitespace` the pre-gate arm on each codec already
/// refuses. So a paste-from-typography `"\u{00A0}64MiB"` (NBSP
/// leading) survives the byte-scan (none of its bytes match
/// `is_ascii_whitespace`), lands on the top-level `s.trim()` which
/// silently strips the NBSP, parses to `64 * 1024 * 1024` bytes, and
/// serde round-trips to the *different* canonical `"64MiB"` on next
/// emit — breaking the THEORY.md Part V render-determinism contract
/// every typed slot carries. Same class on every peer codec:
/// `"\u{2028}30s"` (paste-from-web-doc line-separator prefix) →
/// `Duration::from_secs(30)` → `"30s"`; `"\u{00A0}100/s"`
/// (paste-from-typography NBSP prefix on `:politicas :rate-limit`) →
/// `RateLimit { 100, 1s }` → `"100/s"`. The ASCII-whitespace-only
/// `is_ascii_whitespace` byte-scan closed on each codec by the
/// immediate predecessors (`limits::parse_byte_size` — 24a8ad4;
/// `limits::parse_duration` — ebc3a75; `supervisor::duration_codec`
/// — a7ae622; `rate_limit_codec` — 1ad7755) covers space (`0x20`),
/// tab (`0x09`), LF (`0x0A`), FF (`0x0C`), CR (`0x0D`); this
/// predicate closes the strictly-complementary non-ASCII Unicode
/// `White_Space` class in one lifted source of truth across all four
/// codec sites in one landing — the trajectory the 24a8ad4 commit
/// body's `Forward compounding` bullet explicitly named ("the next
/// canonical-form-drift trajectory … can land as a single lifted
/// predicate across all four codec sites in one follow-up run rather
/// than four independent extensions").
///
/// The predicate is deliberately narrower than "any non-ASCII
/// codepoint" — the byte-set restrictions on the accepted magnitude
/// (`b.is_ascii_digit()` on the digit-only arm, `is_ascii_alphabetic`
/// on the unit-suffix split) already refuse every non-`White_Space`
/// non-ASCII codepoint at a downstream arm with a `BadByteMagnitude`
/// / `BadDurationMagnitude` / equivalent diagnostic. This predicate's
/// job is exclusively to name the drift class — the Unicode
/// whitespace subset that survives the byte-scan but that
/// `str::trim` silently swallows — so the codec's diagnostic can
/// carry the offending [`char`] and its `U+XXXX` codepoint verbatim
/// rather than laundering the value through a generic "bad
/// magnitude" arm at a downstream site far from the paste-origin.
/// Peer of [`is_dns_1123_label`] / [`is_gateway_api_http_path`] /
/// [`is_git_repo_url`] — same "typed-slot's valid set matches its
/// codec's accepted set, structurally" discipline carried at the
/// codec layer.
///
/// Note that BOM (`\u{FEFF}`, ZERO WIDTH NO-BREAK SPACE) and ZWSP
/// (`\u{200B}`, ZERO WIDTH SPACE) are deliberately *outside* this
/// predicate's rejection set — both have `char::is_whitespace() ==
/// false` per the Unicode `White_Space` property, so `str::trim`
/// does *not* silently strip either, and both currently land on the
/// downstream `BadByteMagnitude` / `BadDurationMagnitude` arm at
/// parse time with the byte-shape diagnostic intact. Adding them
/// here would over-fire on an accepted-diagnostic class already
/// closed at a peer arm — the render-determinism contract is
/// unbroken on those inputs today.
#[must_use]
pub fn find_non_ascii_whitespace_char(s: &str) -> Option<char> {
s.chars().find(|c| c.is_whitespace() && !c.is_ascii())
}
/// Predicate: `s` carries a leading-zero-padded magnitude — its length
/// exceeds one byte and its first byte is ASCII `'0'`.
///
/// The canonical drift class this closes across every typed-magnitude
/// codec in caixa-core (`limits::parse_byte_size` backing `:limits
/// :memory` — cea9a78; `limits::parse_duration` backing `:limits
/// :wall-clock` — 39762d7; `limits::parse_millicores` backing
/// `:limits :cpu` — the sixth codec surface;
/// `supervisor::duration_codec::parse` backing `:supervisor
/// :restart-window` / `:politicas :timeout` / `:politicas
/// :circuit-breaker :window` — 9178904; and
/// `aplicacao::rate_limit_codec::parse` backing `:politicas
/// :rate-limit` — 4f46830) is the leading-zero-padded magnitude
/// shape: every downstream typed-magnitude codec's `render_*`
/// canonicalizer emits the leading-zero-stripped form, so a
/// leading-zero magnitude (`"030s"`, `"0100/s"`, `"0500m"`,
/// `"0064MiB"`, `"01h"`) round-trips through `render_*` to a
/// *different* canonical string on the next emit (`"30s"`, `"100/s"`,
/// `"500m"`, `"64MiB"`, `"1h"`) — breaking the THEORY.md Part V
/// render-determinism contract every typed slot carries the same way
/// the leading-`+` shape did before the digit-only arm landed.
///
/// The predicate deliberately admits the single-byte magnitude `"0"`
/// (returning `false`) — every codec's `render_*` canonicalizer emits
/// `"0"` / `"0s"` / `"0m"` / `"0/s"` verbatim for the zero magnitude,
/// so the single-byte form round-trips losslessly through the codec
/// layer. The downstream semantic-zero gates
/// ([`crate::LimitsError::MemoryZero`],
/// [`crate::LimitsError::WallClockZero`],
/// [`crate::LimitsError::CpuZero`],
/// [`crate::SupervisorError::ZeroRestartWindow`],
/// [`crate::AplicacaoError::PolicyTimeoutZero`],
/// [`crate::AplicacaoError::PolicyCircuitBreakerWindowZero`],
/// [`crate::AplicacaoError::PolicyRateLimitZero`]) refuse the
/// semantic-zero authoring at the typed-validate layer above; the
/// codec-layer / typed-validate-layer partition between
/// canonical-form drift (this arm) and semantic-zero (the downstream
/// gate) remains stable across every codec site.
///
/// Peer of [`find_ascii_whitespace_byte`] /
/// [`find_non_ascii_whitespace_char`] on the same
/// canonical-form-drift axis at the codec layer: those two predicates
/// close the whitespace drift class (paste-from-shell-history /
/// paste-from-typography), this one closes the leading-zero-padding
/// drift class (paste-from-fixed-width-alignment /
/// paste-from-columnar-report). Same "single lifted source of truth"
/// discipline: drift between any two codec sites' leading-zero
/// rejection set becomes a single-edit fix at this predicate rather
/// than five independent `s.len() > 1 && s.as_bytes()[0] == b'0'`
/// scans diverging over time. A future stricter classification
/// closing at this shared site (a hypothetical `"00"` shape whose
/// diagnostic distinguishes explicit-zero-padding from the accepted
/// canonical `"0"`, or a future higher base like `"0x0100"` whose
/// magnitude prefix would trip this arm before the digit-only gate
/// catches the `x`) extends at one location rather than five. Peer of
/// [`is_dns_1123_label`] / [`is_gateway_api_http_path`] /
/// [`is_git_repo_url`] — same "typed-slot's valid set matches its
/// codec's accepted set, structurally" discipline carried at the
/// codec layer.
#[must_use]
pub fn is_leading_zero_padded_magnitude(s: &str) -> bool {
s.len() > 1 && s.as_bytes()[0] == b'0'
}
/// Predicate: `s` is a non-empty digit-only magnitude — every byte is
/// an ASCII digit `[0-9]`.
///
/// The canonical drift class this closes across every typed-magnitude
/// codec in caixa-core (`limits::parse_byte_size` backing
/// `:limits :memory`, `limits::parse_duration` backing `:limits
/// :wall-clock`, `limits::parse_millicores` backing `:limits :cpu`,
/// `supervisor::duration_codec::parse` backing `:supervisor
/// :restart-window` / `:politicas :timeout` / `:politicas
/// :circuit-breaker :window`, and `aplicacao::rate_limit_codec::parse`
/// backing `:politicas :rate-limit`) is the non-digit-only magnitude
/// shape: every downstream typed-magnitude codec's `render_*`
/// canonicalizer emits a bare integer magnitude with no leading sign
/// (`+` / `-`), no decimal point, and no exponent, so a signed
/// magnitude (`"+30s"`, `"+500m"`, `"+100/s"`, `"+64MiB"`) or a
/// fractional / decimal magnitude (`"1.5s"`, `"0.5m"`, `"1.0/s"`,
/// `"1.5KiB"`) round-trips through `render_*` to a *different*
/// canonical string on the next emit (`"30s"`, `"500m"`, `"100/s"`,
/// `"64MiB"`, `"1500ms"`, `"30s"`, `"1/s"`, `"1KiB"`) — breaking the
/// THEORY.md Part V render-determinism contract every typed slot
/// carries.
///
/// The predicate deliberately treats the empty string as non-digit-only
/// (returning `false`) so an upstream codec that hasn't already
/// refused the empty-magnitude shape on its own `Empty*` / `Bad*` arm
/// still routes empty input to the non-canonical branch rather than
/// silently accepting it via the vacuous `bytes().all(_)` truth. Every
/// current codec site refuses empty magnitudes on a prior arm before
/// this predicate is consulted (`limits::parse_byte_size`'s `num_trim`
/// empty branch, `limits::parse_duration`'s `num_trim` empty branch,
/// `limits::parse_millicores`'s `magnitude.is_empty()` branch,
/// `supervisor::duration_codec::parse`'s `num_trim` empty branch,
/// `aplicacao::rate_limit_codec::parse`'s `rate_trim` empty branch),
/// so on the reachable inputs the empty-string clause is a no-op; the
/// clause is defense-in-depth for a future codec that reaches for this
/// predicate before landing its own upstream empty-magnitude arm.
///
/// The predicate deliberately admits the single-byte magnitude `"0"`
/// (returning `true`) — every codec's `render_*` canonicalizer emits
/// `"0"` / `"0s"` / `"0m"` / `"0/s"` verbatim for the zero magnitude,
/// so the single-byte form round-trips losslessly through the codec
/// layer. The downstream semantic-zero gates
/// ([`crate::LimitsError::MemoryZero`],
/// [`crate::LimitsError::WallClockZero`],
/// [`crate::LimitsError::CpuZero`],
/// [`crate::SupervisorError::ZeroRestartWindow`],
/// [`crate::AplicacaoError::PolicyTimeoutZero`],
/// [`crate::AplicacaoError::PolicyCircuitBreakerWindowZero`],
/// [`crate::AplicacaoError::PolicyRateLimitZero`]) refuse the
/// semantic-zero authoring at the typed-validate layer above; the
/// codec-layer / typed-validate-layer partition between
/// canonical-form drift (this arm) and semantic-zero (the downstream
/// gate) remains stable across every codec site.
///
/// Peer of [`find_ascii_whitespace_byte`] /
/// [`find_non_ascii_whitespace_char`] /
/// [`is_leading_zero_padded_magnitude`] on the same
/// canonical-form-drift axis at the codec layer: those three
/// predicates close the whitespace and leading-zero-padding drift
/// classes (paste-from-shell-history / paste-from-typography /
/// paste-from-fixed-width-alignment / paste-from-columnar-report),
/// this one closes the leading-sign / fractional / decimal /
/// exponent-shape drift class (paste-from-signed-report /
/// paste-from-floating-point-source / paste-from-scientific-notation).
/// Same "single lifted source of truth" discipline: drift between any
/// two codec sites' digit-only rejection set becomes a single-edit
/// fix at this predicate rather than five independent
/// `!<var>.is_empty() && <var>.bytes().all(|b| b.is_ascii_digit())`
/// scans diverging over time. Peer of [`is_dns_1123_label`] /
/// [`is_gateway_api_http_path`] / [`is_git_repo_url`] — same
/// "typed-slot's valid set matches its codec's accepted set,
/// structurally" discipline carried at the codec layer.
#[must_use]
pub fn is_digit_only_magnitude(s: &str) -> bool {
!s.is_empty() && s.bytes().all(|b| b.is_ascii_digit())
}
/// K8s DNS-1123 label rule's max length, in bytes — the floor each
/// apiserver-side schema enforces independently on every `metadata.name`
/// / Service name / label value axis a validated identifier lands in.
///
/// Per-axis breakdown of why 63 is the strictest among the rules each
/// validated DNS-1123-label-shaped identifier passes through:
///
/// * `:membros :caixa` lands as the rendered programs.yaml entry's
/// `name:` (consumed by `lareira-fleet-programs` to derive the
/// `wasm.pleme.io/v1alpha1/ComputeUnit.metadata.name`), as the K8s
/// [`Service`][svc] `metadata.name` the future `app-operator`
/// provisions per-member (DNS-1035 label rule:
/// `[a-z]([-a-z0-9]*[a-z0-9])?` max 63), as the
/// [`LABEL_PROGRAM`] label value (K8s label value rule:
/// `[a-z0-9]([-a-z0-9_.]*[a-z0-9])?` max 63), and as a component of
/// the composed `<aplicacao>-<de>-to-<para>` `CiliumNetworkPolicy`
/// `metadata.name`.
/// * `:placement :clusters` lands as the K8s context name keying
/// every per-cluster `kubeconfig`, as the `clusters[]` filter the
/// `lareira-fleet-programs` aggregator applies to scope programs
/// to their owning cluster, and as the namespace prefix /
/// `cluster.x-k8s.io/v1beta1/Cluster.metadata.name` cluster
/// identity the future M4 cross-cluster fan-out emits per entry —
/// all DNS-1123-label territory.
/// * `:children :caixa` lands as the rendered
/// `wasm.pleme.io/v1alpha1/ComputeUnit.metadata.name` per child the
/// supervisor materializes, as the [`LABEL_PROGRAM`] label value on
/// every emitted child's pod identity, and as the per-child
/// [`Service`][svc] `metadata.name` the future wasm-operator
/// provisions — every K8s apiserver-side schema on each landing site
/// enforces the same DNS-1123 label rule on admission.
///
/// Lifted to one const so a future identifier axis reaching for the
/// same rule (the future per-Servico `:nome` gate at the Caixa-load
/// boundary, the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
/// per-member / per-cluster validators, the future per-Aplicacao
/// `:nome` gate when `feira init` lands DNS-1123 enforcement on the
/// scaffold's `--nome` flag) reads the limit from one place.
///
/// [svc]: https://kubernetes.io/docs/concepts/services-networking/service/
pub const DNS_1123_LABEL_MAX_LEN: usize = 63;
/// Predicate: assert that `s` is a valid K8s DNS-1123 label. The
/// contract — exactly the regex the K8s apiserver enforces on every
/// `metadata.name` / Service name / label value via OpenAPI v3 admission
/// validation, `[a-z0-9]([-a-z0-9]*[a-z0-9])?` with a 63-byte cap:
///
/// - 1..=63 bytes ([`DNS_1123_LABEL_MAX_LEN`] cap);
/// - lowercase ASCII alphanumeric + hyphen (`[a-z0-9-]` only; no
/// uppercase — K8s rejects, no underscore — DNS-1123 forbids, no
/// dot — a single label is not a subdomain, no Unicode/IDN — must
/// be pre-encoded);
/// - non-hyphen ASCII alphanumeric at both label boundaries
/// (no `-foo`, no `foo-`).
///
/// Returns the parser-shaped reason on rejection (without wrapping in
/// any error variant) so each per-axis caller — `validate_membro_caixa`
/// for `:membros :caixa`, `validate_placement_cluster` for
/// `:placement :clusters`, `validate_child_caixa` for `:children :caixa`,
/// every future per-axis lift (the per-Servico `:nome` gate at the
/// Caixa-load boundary, the M4 CR materializer's per-member /
/// per-cluster validators) — wraps the same reason in its own typed
/// `*Error::*Invalid { <axis>, reason }` variant. The reason wording is
/// axis-agnostic ("DNS-1123 labels allow only `[a-z0-9-]`") so every
/// call site reading the same diagnostic points at the same rule —
/// drift between any two axes' rule enforcement is a build error
/// visible at this predicate, not a per-renderer "this passed validate
/// but failed admission" surprise.
///
/// Empty input is rejected at the call site (each axis has its own
/// narrower `*Empty` variant — [`crate::AplicacaoError::MembroCaixaEmpty`],
/// [`crate::AplicacaoError::PlacementClusterEmpty`],
/// [`crate::SupervisorError::EmptyChildName`]) before this predicate
/// is consulted, mirroring `validate_entrada_host`'s empty-first
/// cascade (c7d05ec). The predicate body re-checks empty defensively
/// so it can be called from any future call site without a shape-
/// mismatch footgun — the same "defensive re-check" discipline every
/// peer value-shape predicate ([`is_gateway_api_http_path`] line 730,
/// [`is_wit_world_ref`] line 937, [`is_nats_subject`] line 1387,
/// [`is_wasi_keyvalue_slot`] line 1612, [`is_git_ref_name`] line 1777)
/// carries. Without the defensive re-check, calling
/// `is_dns_1123_label("")` panics at `bytes[0]` on the empty-slice
/// index below (`bytes[0].is_ascii_alphanumeric()` — the boundary
/// arm's `s.as_bytes()[0]` access reads past the end of the empty
/// slice), a `panic!` far from the source caixa.lisp on any future
/// call site that misses the pre-check. The peer predicates all
/// return `Err("must not be empty")` on this input; this arm brings
/// `is_dns_1123_label` in line with the same defensive contract.
///
/// Lifted from `caixa-core::aplicacao` (where it was first inlined for
/// `:membros :caixa` in 3f9d7a0 and then reused for `:placement :clusters`
/// in 6cbb900) so the third axis reaching for the rule (`:children
/// :caixa` on the supervisor tree) lands as a thin five-line wrapper
/// rather than re-inlining 40 lines of regex enforcement. The
/// "before its third occurrence" boundary the PRIME DIRECTIVE
/// duplication-budget rule draws (THEORY.md §I.3.5: "the duplication
/// budget is zero") promotes the predicate to a typed substrate-side
/// primitive on the same trajectory the M2-overlay and label-selector
/// helpers (9e3a057, 9d09cfb, 9dbeafd, 31455a7, 07a4544) already follow.
///
/// # Errors
///
/// Returns the parser-shaped reason naming the specific violation
/// (length / boundary / character-class), without wrapping in any
/// error variant — every caller maps the same `String` into its own
/// typed `*Invalid { <axis>, reason }` enum variant.
pub fn is_dns_1123_label(s: &str) -> Result<(), String> {
if s.is_empty() {
return Err("must not be empty".to_string());
}
if s.len() > DNS_1123_LABEL_MAX_LEN {
return Err(format!(
"exceeds DNS-1123 label max length of {DNS_1123_LABEL_MAX_LEN} bytes \
(got {} bytes; the K8s apiserver rejects longer names at admission \
time on every Service / Pod / CR `metadata.name` axis)",
s.len()
));
}
let bytes = s.as_bytes();
if !bytes[0].is_ascii_alphanumeric() || !bytes[bytes.len() - 1].is_ascii_alphanumeric() {
return Err("must start and end with an ASCII alphanumeric character \
(no leading or trailing `-`; DNS-1123 label rule)"
.to_string());
}
for &b in bytes {
let valid = b.is_ascii_digit() || b.is_ascii_lowercase() || b == b'-';
if !valid {
let msg = if b.is_ascii_uppercase() {
format!(
"contains uppercase character {ch:?} (K8s DNS-1123 label \
names are lowercase-only; use {lower:?})",
ch = b as char,
lower = s.to_ascii_lowercase()
)
} else if b == b'_' {
"contains `_` (DNS-1123 labels allow only `[a-z0-9-]`; use `-` \
instead)"
.to_string()
} else if b == b'.' {
"contains `.` (a single DNS-1123 label is not a subdomain; \
split into separate entries or use `-` to namespace)"
.to_string()
} else {
format!(
"contains invalid character {ch:?} (DNS-1123 labels allow \
only `[a-z0-9-]`)",
ch = b as char
)
};
return Err(msg);
}
}
Ok(())
}
/// K8s Gateway API v1 `HTTPPathMatch.value` max length, in bytes —
/// the apiserver-side `OpenAPI` schema's `maxLength: 1024` cap. Lifted
/// to a typed const so a future axis reaching for the same bound (the
/// M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-path
/// validator, the future per-`HTTPRouteRule` per-path-match emission
/// when M4 lands per-rule overrides, the future `:politicas`-derived
/// per-edge HTTP path overlay's per-path validator) reads the limit
/// from one place. The two landed call sites — `:entrada :paths`
/// entries (caixa-mesh's `HTTPRoute.spec.rules[].matches[].path.value`
/// emission) and `:contratos :endpoint` (caixa-mesh's Cilium L7
/// `path:` rule emission, caixa-mesh/src/lib.rs:311) — both inherit
/// the same cap; drift between either landing site and the K8s CRD
/// schema surfaces at this one const.
pub const GATEWAY_API_HTTP_PATH_MAX_LEN: usize = 1024;
/// K8s Gateway API v1 `Listener.hostname` and
/// `HTTPRoute.spec.hostnames[]` max length, in bytes — the apiserver-side
/// `OpenAPI` schema's `maxLength: 253` cap, ultimately the RFC 1035 / RFC
/// 1123 DNS name limit (255 wire bytes minus the trailing-dot + one length
/// prefix). Lifted to a typed const so a future axis reaching for the same
/// bound (the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
/// per-`:entrada :host` validator, the future per-`Certificate` SAN emitter
/// keying off `:entrada :host` for cert-manager, the future
/// multi-`:entrada` host-collision gate when M4 lands `:entrada` as a
/// `Vec`) reads the limit from one place. The sole landed call site — the
/// `:entrada :host` axis's total-length gate at
/// [`crate::AplicacaoSpec::validate`] via `validate_entrada_host` — reads
/// this constant verbatim; drift between the landing site and the K8s CRD
/// schema surfaces at this one const rather than a per-renderer "this
/// passed validate but failed admission" surprise.
///
/// Peer of [`GATEWAY_API_HTTP_PATH_MAX_LEN`] on the sibling per-route
/// path-value cap axis — both are apiserver-side `maxLength:` bounds on
/// Gateway API v1 landing sites the pleme-io substrate emits, both lift
/// to `caixa-core::render` so the M4 CR materializer's per-axis
/// validators (per-host, per-path) read from one place. Same "typed const
/// so the bound has exactly one source of truth" discipline every peer
/// upper bound in this crate carries
/// ([`DNS_1123_LABEL_MAX_LEN`], [`NATS_SUBJECT_MAX_LEN`],
/// [`WASI_KV_SLOT_MAX_LEN`], [`WIT_IDENT_MAX_LEN`],
/// [`crate::LIMITS_MEMORY_WASM32_MAX_BYTES`],
/// [`crate::POLICY_TIMEOUT_MAX`], [`crate::POLICY_RETRIES_MAX`]).
///
/// The per-label max within the hostname is [`DNS_1123_LABEL_MAX_LEN`]
/// (63): every `.`-separated label in a Gateway API v1 Hostname is a
/// DNS-1123 label under the apiserver's OpenAPI regex
/// `[a-z0-9]([-a-z0-9]*[a-z0-9])?`, so drift between the total-length
/// cap here and the per-label cap on the peer constant is impossible by
/// construction.
pub const GATEWAY_API_HOSTNAME_MAX_LEN: usize = 253;
/// K8s Gateway API v1 `Gateway.spec.listeners[].port` — the substrate's
/// canonical port scalar every Aplicacao-level
/// [`caixa_mesh::gateway_routes`][cm] -emitted `Gateway`'s sole per-
/// listener HTTP-listener-port axis reads from. IANA-registered as the
/// well-known `http` service port (RFC 9110 §4.2.2 / RFC 3986 §3.2.3 —
/// the port implied by an `http://<host>/…` URL when the authority
/// carries no explicit `:<port>` selector), so the substrate's external
/// `:entrada` HTTP flow surfaces at `http://<entrada.host>/` with no
/// per-client port override.
///
/// Semantically distinct from [`crate::DEFAULT_SERVICO_PORT`] (8080)
/// on the sibling per-Servico L4 axis — that constant is the port each
/// in-cluster Servico's `pleme-computeunit`-emitted K8s `Service`
/// listens on (the destination side of every mesh flow); this constant
/// is the port the Aplicacao's own external Gateway listens on (the
/// external ingress side, K8s-Gateway-API-CRD-controller-visible).
/// Two axes, two lifts — a future rebrand on either axis (the
/// substrate moving external HTTP to `:443` under mTLS-terminated
/// listeners, the substrate moving in-cluster Servicos onto `:80`
/// once the well-known port is freed) lands on its own canonical
/// const without coupling either axis to the other's rebrand cycle.
///
/// Until this lift landed the value `80` lived at one production-code
/// call site: the `listener.insert(KUBE_KEY_PORT, …)` call at
/// `caixa-mesh/src/lib.rs:2588` inside
/// [`caixa_mesh::gateway_routes`][cm]'s per-Aplicacao `Gateway`
/// emitter. A future Gateway API v1 promotion moving the well-known
/// external HTTP listener to a substrate-chosen alternative — the
/// substrate moving to `:443` once cert-manager-issued
/// per-`:entrada :host` certificates land and the external listener
/// becomes HTTPS-by-default (matching the mTLS-by-default trajectory
/// [`crate::DEFAULT_SERVICO_PORT`]'s docstring names), a per-cluster
/// override the operator pins through a future `:entrada :port` slot
/// promoted from Servico-side (`:entrada :port` today's typed slot
/// names the destination Servico port, not the Gateway listener
/// port) — without a coordinated edit would silently emit a
/// `Gateway` whose per-listener HTTP-listener-port axis the K8s
/// Gateway API v1 controller admits at the drifted port and the
/// gateway-class-controller (Cilium's Envoy sidecar today) opens on
/// the drifted port too, so every external `:entrada` HTTP flow
/// drops at the first hop with no diagnostic naming the drift root
/// cause. Lifting the literal to a shared typed `u16` const closes
/// the drift footgun structurally — every consumer reads from the
/// same lifted constant, so any rebrand reaches every site by
/// construction.
///
/// Mirrors the [`crate::DEFAULT_SERVICO_PORT`] lift (a085b26) on the
/// peer per-renderer canonical-K8s-port-axis typed `u16` const — both
/// are IANA-registered service-port scalars the substrate's mesh
/// renderer emits under a K8s CRD's `port:` axis, both lift to
/// `caixa-core::render` so any future substrate-side port migration
/// (external HTTP `:80 → :443`, in-cluster Servico `:8080 → :80`)
/// lands at exactly one const per axis. Same "typed const so the
/// scalar has exactly one source of truth" discipline every peer
/// scalar in this crate carries ([`GATEWAY_API_HOSTNAME_MAX_LEN`],
/// [`GATEWAY_API_HTTP_PATH_MAX_LEN`], [`DNS_1123_LABEL_MAX_LEN`],
/// [`WIT_IDENT_MAX_LEN`]).
///
/// [cm]: ../../caixa_mesh/fn.gateway_routes.html
pub const GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT: u16 = 80;
/// K8s Gateway API v1 `Gateway.spec.listeners[].name` — the substrate's
/// canonical author-chosen listener-name scalar every Aplicacao-level
/// [`caixa_mesh::gateway_routes`][cm] -emitted `Gateway`'s sole per-
/// listener name-discriminator axis reads from. Gateway API v1's
/// `Listener.name` is `SectionName`-typed (a required DNS-1123 label
/// unique within the parent Gateway's listener list — see the upstream
/// docs at
/// <https://gateway-api.sigs.k8s.io/api-types/gateway/#listeners> and
/// the type reference at
/// <https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.SectionName>);
/// downstream `HTTPRoute.spec.parentRefs[].sectionName` selectors bind
/// to this exact byte-string when the author wants to attach a route
/// to one specific listener out of a multi-listener Gateway. The V0
/// substrate emits exactly one HTTP listener per Aplicacao, so the
/// name is arbitrary from the CRD's perspective — the substrate picks
/// the byte-string `"http"` as the canonical short name (matching the
/// listener's protocol axis [`GATEWAY_API_PROTOCOL_HTTP`] in kind, but
/// not in bytes: this is the lowercase-ASCII listener-name identifier,
/// the sibling protocol scalar is the uppercase-ASCII
/// `ProtocolType` enum value the Gateway API v1 CRD schema pins).
///
/// Semantically distinct from every peer `"http"`-shaped byte-string
/// in the substrate:
///
/// - [`crate::GATEWAY_API_PROTOCOL_HTTP`] (`"HTTP"`) — the listener's
/// `spec.listeners[].protocol` `ProtocolType` enum value the
/// Gateway API v1 CRD schema pins to the uppercase-ASCII spelling;
/// this constant names the arbitrary author-chosen listener-name
/// identifier at the sibling `spec.listeners[].name` axis instead,
/// and the two carry different case shapes on purpose;
/// - [`crate::CILIUM_KEY_HTTP`] (`"http"`) — the Cilium CRD's per-
/// `toPorts[]` L7-HTTP-rule-list-discriminator container-axis key
/// (`spec.ingress[].toPorts[].rules.http`), a CRD-schema-pinned
/// field name the Cilium project's per-CRD-schema-migration cycle
/// controls; this constant names an Aplicacao-side arbitrary
/// listener-name at a distinct K8s Gateway API CRD path, and the
/// substrate can move it without touching the Cilium schema.
///
/// Byte-identical to [`CILIUM_KEY_HTTP`] today (both spell out the
/// four ASCII bytes `h`, `t`, `t`, `p`), but the two lifted axes name
/// semantically distinct surfaces — a future substrate-side listener-
/// name rebrand (say, `"http" → "http-v1"` once the Aplicacao renders
/// multiple listeners under the HTTPS-by-default trajectory) must
/// reach this consumer without dragging the Cilium schema key with it.
///
/// Until this lift landed the value `"http"` lived at one production-
/// code call site: the `listener.insert(GATEWAY_API_KEY_NAME, "http")`
/// call inside [`caixa_mesh::gateway_routes`][cm]'s per-Aplicacao
/// `Gateway` emitter. A future Gateway API v2 rebrand of the well-
/// known short listener-name (a substrate-side migration to a longer
/// discriminator once multi-listener Gateways ship, an operator-pinned
/// override the future `:entrada :listener-name` slot promotes) —
/// without a coordinated edit — would silently emit a `Gateway`
/// whose listener carries the drifted identifier, so every downstream
/// `HTTPRoute` `sectionName` selector authored against the substrate's
/// prior canonical name misses its listener, and every external
/// `:entrada` HTTP flow drops at attachment time with no diagnostic
/// naming the listener-name drift root cause. Lifting the literal to
/// a shared typed `&'static str` const closes the drift footgun
/// structurally — every consumer reads from the same lifted constant,
/// so any rebrand reaches every site by construction.
///
/// Mirrors the [`GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT`] lift
/// (cd60fde) on the peer per-listener HTTP-listener-port scalar-axis —
/// both are Aplicacao-side substrate-canonical scalar-value pins the
/// sole per-Aplicacao `Gateway` emitter reaches for, and both lift to
/// `caixa-core::render` so a future substrate-side rebrand on either
/// listener axis (`:port` → `:443`, `:name` → `"http-v1"`) lands at
/// exactly one const per axis. Same "typed const so the scalar has
/// exactly one source of truth" discipline every peer scalar in this
/// crate carries ([`DEFAULT_GATEWAY_CLASS_NAME`],
/// [`GATEWAY_API_PROTOCOL_HTTP`],
/// [`GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX`]).
///
/// [cm]: ../../caixa_mesh/fn.gateway_routes.html
pub const GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME: &str = "http";
/// K8s Gateway API v1 `HTTPRoute.spec.rules[].matches[].path.value`
/// substrate-side catch-all path — the fallback URL path every
/// Aplicacao-level [`caixa_mesh::gateway_routes`][cm] -emitted
/// `HTTPRoute` renders when the typed `:entrada :paths` slot is
/// empty, so an author who declares an external `:entrada` but no
/// per-path rule surface still gets a route whose sole
/// `HTTPPathMatch` matches every incoming request under the
/// paired [`GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX`] discriminator.
/// K8s Gateway API v1's `PathPrefix` matcher over the bare-root
/// `"/"` is the canonical catch-all shape — the upstream docs at
/// <https://gateway-api.sigs.k8s.io/api-types/httproute/#path-based-routing>
/// pin the `PathPrefix "/"` combination as the "match anything the
/// listener admits" idiom every gateway-class controller (Cilium's
/// Envoy today, Envoy Gateway / Istio Gateway on the peer
/// controllers) treats as the equivalent of "no path predicate"
/// under the CRD schema.
///
/// Until this lift landed the value `"/"` lived at one production-
/// code call site: the `vec!["/"]` fallback arm inside
/// [`caixa_mesh::gateway_routes`][cm]'s `let paths: Vec<&str> =
/// if entrada.paths.is_empty() { vec!["/"] } else { … }` branch, the
/// sole per-Aplicacao HTTPRoute per-rule path-list resolver that
/// surfaces the catch-all URL path whenever the typed `:entrada
/// :paths` list is empty. A future substrate-side rebrand of the
/// catch-all shape — a hypothetical migration to Gateway API v2's
/// `Exact ""` idiom, an operator-pinned per-Aplicacao override the
/// future `:entrada :default-path` slot promotes, a per-controller
/// variant that treats `"/"` as a literal prefix rather than the
/// catch-all — without a coordinated edit would silently emit an
/// `HTTPRoute` whose sole path-match predicate rejects every
/// incoming request at the drifted shape, so every external
/// `:entrada` HTTP flow drops at the first hop with no diagnostic
/// naming the catch-all-path drift root cause. Lifting the literal
/// to a shared typed `&'static str` const closes the drift footgun
/// structurally — every consumer reads from the same lifted constant,
/// so any rebrand reaches every site by construction.
///
/// Semantically distinct from every peer HTTP-path byte-string in the
/// substrate. The typed [`Entrada::paths`] admission grammar
/// ([`is_gateway_api_http_path`] + [`GATEWAY_API_HTTP_PATH_MAX_LEN`])
/// admits the bare-root `"/"` at the author's slot; this constant
/// names the substrate's *emit-side* choice for the same byte-string
/// at the *no-author-input* path — the two axes carry the identical
/// shape today by design (the substrate's catch-all round-trips
/// through the same admission grammar the author's explicit `"/"`
/// would clear), and the paired
/// [`gateway_api_default_http_route_path_carries_valid_gateway_api_http_path_shape`]
/// cross-axis pin closes the invariant at build time.
///
/// Mirrors the [`GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME`] (a12dcdd) /
/// [`GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT`] (cd60fde) lifts on the
/// peer per-listener substrate-canonical scalar-value axes — all
/// three are Aplicacao-side substrate-canonical scalar-value pins the
/// sole per-Aplicacao mesh emitter reaches for at a K8s Gateway API
/// v1 CRD sub-path, and all three lift to `caixa-core::render` so a
/// future substrate-side rebrand on any one axis lands at exactly one
/// const per axis. Same "typed const so the scalar has exactly one
/// source of truth" discipline every peer scalar in this crate
/// carries ([`DEFAULT_GATEWAY_CLASS_NAME`],
/// [`GATEWAY_API_PROTOCOL_HTTP`],
/// [`GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX`]).
///
/// [cm]: ../../caixa_mesh/fn.gateway_routes.html
pub const GATEWAY_API_DEFAULT_HTTP_ROUTE_PATH: &str = "/";
/// Predicate: assert that `path` is a valid HTTP path under both the
/// K8s Gateway API v1 `HTTPPathMatch.value` admission grammar AND the
/// Cilium L7 `path:` rule grammar — the two landing sites every
/// validated pleme-io HTTP-shaped path lands in. The contract:
///
/// - 1..=[`GATEWAY_API_HTTP_PATH_MAX_LEN`] (1024) bytes;
/// - leading `/` (the `PathPrefix` invariant — pre-checked at the
/// call site by each axis's narrower `*NotAbsolute` variant;
/// re-checked here so the predicate is usable from any future
/// call site without a shape-mismatch footgun);
/// - no consecutive `/` characters (HTTP path matchers reject
/// `//` — collapse to a single `/`);
/// - no `/./` or `/../` segments (and no trailing `/.` or `/..`) —
/// path-traversal and no-op segments are rejected outright;
/// - no `?` (query separator: queries are matched separately via
/// `HTTPRoute` `queryParams`, never in the path);
/// - no `#` (fragment separator: fragments are client-side and
/// never reach the gateway);
/// - no whitespace (space, tab — must be percent-encoded as `%20`);
/// - no ASCII control characters (`0x00..0x1F`, `0x7F`);
/// - no non-ASCII bytes (`>= 0x80`) — RFC 3986 requires `%XX`
/// percent-encoding for anything outside the ASCII unreserved +
/// reserved set;
/// - no printable-ASCII byte outside the K8s Gateway API
/// `HTTPPathMatch.value` apiserver-side `OpenAPI` regex
/// `^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$`
/// accepted set — namely `"` `<` `>` `[` `\` `]` `^` `` ` `` `{`
/// `|` `}`. These eleven bytes are printable ASCII but RFC 3986's
/// `pchar = unreserved / pct-encoded / sub-delims / ":" / "@"`
/// grammar excludes them, so the apiserver rejects them at
/// admission time on every `HTTPRoute.spec.rules[].matches[].
/// path.value` landing site and the Cilium L7 path matcher
/// refuses them too. Percent-encode (`%XX`) if the literal byte
/// is intended.
///
/// Returns the parser-shaped reason on rejection (without wrapping in
/// any error variant) so each per-axis caller — `validate_entrada_path`
/// for `:entrada :paths` entries, `WitContract::target` for the HTTP-
/// shaped `:contratos :endpoint` axis, every future per-path lift
/// (the M4 CR materializer's per-path validator, the future
/// per-`HTTPRouteRule` per-path-match emission) — wraps the same
/// reason in its own typed `*Invalid { <axis>, reason }` variant. The
/// reason wording is axis-agnostic ("HTTP path matchers reject
/// `//`") so every call site reading the same diagnostic points at
/// the same rule; drift between any two axes' rule enforcement is a
/// build error visible at this predicate, not a per-renderer "this
/// passed validate but failed admission" surprise.
///
/// Empty input is rejected at the call site (each axis has its own
/// narrower `*Empty` variant — [`crate::AplicacaoError::EntradaPathEmpty`],
/// [`crate::AplicacaoError::ContratoEndpointEmpty`]) before this
/// predicate is consulted, mirroring `is_dns_1123_label`'s empty-first
/// cascade. The predicate body re-checks empty + leading-`/`
/// defensively so it can be called from any future call site without
/// a shape-mismatch footgun.
///
/// Lifted from `caixa-core::aplicacao::validate_entrada_path` (where
/// it was first inlined for `:entrada :paths` in 55410e4) at the
/// second occurrence of the HTTP-path-grammar — the `:contratos
/// :endpoint` axis (c4213a4 gated non-empty + leading-`/` only,
/// silently passing the same authoring footguns the `:entrada :paths`
/// gate catches) — so the second axis lands as a thin three-line
/// wrapper at the per-axis call site rather than re-inlining 90 lines
/// of grammar enforcement. Same compounding shape as
/// `is_dns_1123_label` (lifted at its third occurrence in 31bfa43)
/// and the M2-overlay / label-selector helpers (9e3a057, 9d09cfb,
/// 9dbeafd, 31455a7, 07a4544) on the render side — each lifted a
/// recurring shape into a typed primitive at the threshold where the
/// duplication budget would otherwise have been exceeded.
///
/// # Errors
///
/// Returns the parser-shaped reason naming the specific violation
/// (length / character-class / segment / consecutive-slash), without
/// wrapping in any error variant — every caller maps the same
/// `String` into its own typed `*Invalid { <axis>, reason }` enum
/// variant.
pub fn is_gateway_api_http_path(path: &str) -> Result<(), String> {
if path.is_empty() {
return Err("must not be empty".to_string());
}
if !path.starts_with('/') {
return Err("must start with `/` (HTTP path matchers require a leading `/`)".to_string());
}
if path.len() > GATEWAY_API_HTTP_PATH_MAX_LEN {
return Err(format!(
"exceeds HTTP path max length of {GATEWAY_API_HTTP_PATH_MAX_LEN} bytes \
(got {} bytes; both the K8s Gateway API HTTPPathMatch.value OpenAPI \
schema and the Cilium L7 path matcher reject longer values at \
admission time)",
path.len()
));
}
for &b in path.as_bytes() {
let reason = if b == b'?' {
Some(
"must not contain `?` (queries are matched separately via HTTPRoute \
`queryParams`, not in the path; drop the `?…` suffix)"
.to_string(),
)
} else if b == b'#' {
Some(
"must not contain `#` (fragments are client-side and never reach \
the gateway; drop the `#…` suffix)"
.to_string(),
)
} else if b == b' ' || b == b'\t' {
Some(format!(
"must not contain whitespace character {ch:?} (percent-encode as `%20` \
or use `-`/`_` instead)",
ch = b as char
))
} else if b < 0x20 || b == 0x7F {
Some(format!(
"must not contain control character 0x{b:02x} (HTTP path characters \
must be printable ASCII; the K8s Gateway API HTTPPathMatch.value and \
Cilium L7 path matcher both reject control characters at admission \
time)"
))
} else if b >= 0x80 {
Some(format!(
"must not contain non-ASCII byte 0x{b:02x} (RFC 3986 requires \
percent-encoding `%XX` for characters outside the ASCII unreserved \
+ reserved set)"
))
} else if matches!(
b,
b'"' | b'<' | b'>' | b'[' | b'\\' | b']' | b'^' | b'`' | b'{' | b'|' | b'}'
) {
// The eleven printable-ASCII bytes outside the K8s Gateway
// API HTTPPathMatch.value apiserver-side OpenAPI regex
// accepted set. RFC 3986 §3.3 `pchar = unreserved /
// pct-encoded / sub-delims / ":" / "@"` excludes them from
// every path-segment, so the apiserver rejects them at
// admission time on every
// `HTTPRoute.spec.rules[].matches[].path.value` landing site
// (and the Cilium L7 path matcher follows the same grammar).
// Until this gate landed `validate` only refused `?`, `#`,
// whitespace, control characters, and non-ASCII bytes; the
// canonical author-side "I wrote a path-template variable"
// / "I copied an OpenAPI route" footguns silently passed
// (`/api/cart/{id}` — Gateway API uses `:foo` for path
// parameters, not `{foo}`; `/api/cart[0]` — index-bracket
// shape; `/api/<placeholder>` — angle-bracket placeholder;
// `/api\path` — Windows path-separator typo; `/api/^foo` —
// accidental shell-regex character) and the failure surfaced
// at apply time as a Gateway API webhook rejection naming
// the offending byte but not the offending caixa.lisp slot.
// Lifting the rejection to caixa-build time makes the
// canonical Gateway API HTTPPathMatch.value accepted set a
// structural property of every validated `:entrada :paths`
// entry and every typed-HTTP `:contratos :endpoint` payload,
// mirroring the c7d05ec / 55410e4 / 4f0390b trajectory each
// brought the per-axis accepted set to match the apiserver
// accepted set verbatim.
Some(format!(
"must not contain reserved character {ch:?} (RFC 3986 \
path-segment grammar — and the K8s Gateway API \
HTTPPathMatch.value apiserver-side OpenAPI regex \
`^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{{2}})+$` — \
exclude this byte from the `pchar = unreserved / pct-encoded \
/ sub-delims / \":\" / \"@\"` set; percent-encode as \
`%{b:02X}` if the literal character is intended)",
ch = b as char
))
} else {
None
};
if let Some(r) = reason {
return Err(r);
}
}
if path.contains("//") {
return Err(
"must not contain consecutive `/` characters (HTTP path matchers reject \
`//`; collapse to a single `/`)"
.to_string(),
);
}
if path.contains("/./") || path == "/." || path.ends_with("/.") {
return Err(
"must not contain the `.` segment (`/./` or trailing `/.`); it is \
semantically a no-op and HTTP path matchers reject it"
.to_string(),
);
}
if path.contains("/../") || path == "/.." || path.ends_with("/..") {
return Err(
"must not contain the `..` parent-segment (`/../` or trailing `/..`); \
path traversal is rejected by HTTP path matchers"
.to_string(),
);
}
Ok(())
}
/// Max length, in bytes, of a single typed `:contratos :wit` world
/// reference passing the [`is_wit_world_ref`] predicate. 128 bytes —
/// roughly 8× the longest real-world WIT reference the caixa-mesh test
/// fixtures carry (`wasi:keyvalue/store` = 19 bytes) and the WIT registry
/// references its peers under (`wasi:http/proxy@0.2.0` = 21 bytes), so
/// the cap exists to reject the paste-from-binary footgun (a multi-line
/// blob accidentally landed in the `:wit` slot) rather than to constrain
/// legitimate authoring. Lifted as a typed const so a future axis
/// reaching for the same bound (the M4 per-edge WIT registry resolver,
/// the future `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
/// per-contract WIT validator) reads from one place.
pub const WIT_IDENT_MAX_LEN: usize = 128;
/// Predicate: assert that `s` is a valid WIT (WebAssembly Component
/// Model) world reference — the canonical shape every typed
/// `:contratos :wit` value carries. The contract — modeled on the
/// [WIT IDL grammar][wit] (`namespace:package(/interface)*(@version)?`)
/// restricted to the lowercase subset the pleme-io substrate dispatches
/// on:
///
/// - 1..=[`WIT_IDENT_MAX_LEN`] (128) bytes;
/// - no whitespace, no control characters, no non-ASCII bytes;
/// - exactly one `:` separator splitting the namespace from the
/// package — `wasi:http/proxy`, `nats:pub-sub`, `wasi:keyvalue/store`
/// (no `:` = there's no namespace to dispatch on; multiple `:` =
/// the package half can't parse);
/// - an optional `/`-separated interface suffix (one or more
/// segments — the WIT grammar allows `('/' id)+` after the package);
/// - an optional `@<version>` suffix (one trailing `@` only; the
/// version body is a structurally valid SemVer 2.0.0 version —
/// non-empty, restricted to the accepted set `[0-9A-Za-z.\-+]`, AND
/// round-trippable through [`semver::Version::parse`]: three-part
/// `major.minor.patch` numeric core mandatory (two-part `1.0` and
/// four-part `1.0.0.0` reject), no leading zeros in numeric
/// identifiers (`01.0.0` rejects), no empty pre-release / build-
/// metadata identifiers (`1.0.0-` and `1.0.0-.rc1` reject); the WIT
/// IDL binds `simple-version` to SemVer verbatim so every byte-set-
/// valid but shape-invalid version body fails the upstream WIT
/// parser at consume time);
/// - every identifier segment (namespace, package, each interface)
/// is a lowercase kebab-case ASCII identifier: `[a-z]([a-z0-9]|-)*`,
/// starting with a lowercase letter, no consecutive `-`, no
/// trailing `-`.
///
/// Lowercase-only is deliberate — the substrate's
/// [`crate::aplicacao::WitContract::is_http`] / `is_pubsub` / `is_store`
/// dispatch keys off the lowercase canonical prefix (`wasi:http/`,
/// `nats:`, `wasi:keyvalue/`, `kafka:`, `kv:`, `http:`). An uppercase
/// `WASI:HTTP/proxy` is structurally a valid WIT identifier under the
/// upstream IDL grammar but silently falls through every `is_*` arm and
/// renders as a capability-only L4-only edge — the canonical "I thought
/// I had L7 HTTP routing, got L4-only" footgun. Lifting the lowercase
/// rule to caixa-build time makes the dispatch reachable-by-construction:
/// every validated `:wit` value matches exactly one of the three typed
/// dispatch arms (or the explicit capability arm), structurally.
///
/// Returns the parser-shaped reason on rejection (without wrapping in
/// any error variant) so each per-axis caller — `WitContract::target`
/// for the `:contratos :wit` axis at validate time, the future M4 CR
/// materializer's per-contract WIT validator, the future per-edge WIT
/// registry resolver — wraps the same reason in its own typed
/// `*Invalid { <axis>, reason }` variant. The reason wording is
/// axis-agnostic ("WIT identifiers allow only `[a-z0-9-]`") so every
/// call site reading the same diagnostic points at the same rule;
/// drift between any two axes' rule enforcement is a build error
/// visible at this predicate, not a per-renderer "this passed validate
/// but silently demoted to capability-only" surprise.
///
/// Empty input is rejected here (defensively) and at the call site via
/// the narrower [`crate::AplicacaoError::EmptyWit`] variant — the same
/// empty-first cascade [`is_dns_1123_label`] and
/// [`is_gateway_api_http_path`] carry.
///
/// Lifted as a typed substrate-side primitive on the same trajectory
/// the M2-overlay and label-selector helpers (9e3a057, 9d09cfb, 9dbeafd,
/// 31455a7, 07a4544) and the value-shape predicates (`is_dns_1123_label`,
/// `is_gateway_api_http_path`) already follow — the typed slot's valid
/// set matches its dispatch's accepted set, structurally.
///
/// [wit]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md
///
/// # Errors
///
/// Returns the parser-shaped reason naming the specific violation
/// (length / separator / character-class / kebab-shape / SemVer 2.0.0
/// structural invariant), without wrapping in any error variant —
/// every caller maps the same `String` into its own typed `*Invalid
/// { <axis>, reason }` enum variant.
pub fn is_wit_world_ref(s: &str) -> Result<(), String> {
if s.is_empty() {
return Err("must not be empty".to_string());
}
if s.len() > WIT_IDENT_MAX_LEN {
return Err(format!(
"exceeds WIT world-reference max length of {WIT_IDENT_MAX_LEN} bytes \
(got {} bytes; legitimate WIT references rarely exceed ~32 bytes — \
this length suggests a paste-from-binary or multi-line blob landed \
in the `:wit` slot)",
s.len()
));
}
for &b in s.as_bytes() {
if b.is_ascii_whitespace() {
return Err(format!(
"must not contain whitespace character {ch:?} (WIT world references \
are single tokens with no whitespace between identifier segments)",
ch = b as char
));
}
if b < 0x20 || b == 0x7F {
return Err(format!(
"must not contain control character 0x{b:02x} (WIT world references \
are printable ASCII tokens)"
));
}
if b >= 0x80 {
return Err(format!(
"must not contain non-ASCII byte 0x{b:02x} (WIT world references \
are restricted to ASCII identifiers + the `:` / `/` / `@` / `-` \
separators)"
));
}
}
// Split off the optional `@<version>` suffix first so the
// namespace/package parse below operates on a clean
// `<ns>:<pkg>(/<iface>)*` head.
let (head, version) = match s.split_once('@') {
Some((h, v)) => (h, Some(v)),
None => (s, None),
};
if let Some(ver) = version {
if ver.is_empty() {
return Err(
"trailing `@` must be followed by a version (e.g. `@0.2.0`); drop \
the trailing `@` to omit the version pin"
.to_string(),
);
}
if ver.contains('@') {
return Err(
"must contain at most one `@` separator (the optional version suffix \
is `@<version>`, not `@<ver>@<ver>`)"
.to_string(),
);
}
if ver.contains(':') || ver.contains('/') {
return Err(format!(
"version suffix {ver:?} must not contain `:` or `/` (those separators \
are reserved for the namespace and interface axes; the version body \
is opaque)"
));
}
// Byte-set gate on the `@<version>` body: SemVer 2.0.0 restricts
// every legal version to the accepted set
// `[0-9A-Za-z.\-+]` — the digit + letter alphabet for the
// `major.minor.patch` numeric core, the `.` segment separator,
// and the `-` / `+` sigils that introduce the optional
// pre-release and build-metadata suffixes. The WIT IDL binds
// `simple-version` to SemVer verbatim (WebAssembly Component
// Model design doc `WIT.md#versions` — `version` is parsed
// through the `semver` crate), so any printable-ASCII byte
// outside that set is guaranteed to fail the upstream WIT
// parser at consume time. Until this gate landed the outer
// whitespace / control / non-ASCII loop above rejected the
// whitespace + control + non-ASCII slices of the byte axis
// and the narrower `contains('@')` / `contains(':' | '/')`
// arms above closed the WIT-reserved separator bytes, but
// every other printable-ASCII byte (`?`, `#`, `!`, `$`, `%`,
// `&`, `'`, `"`, `(`, `)`, `*`, `,`, `;`, `<`, `=`, `>`, `[`,
// `\`, `]`, `^`, `` ` ``, `{`, `|`, `}`, `~`) silently rode
// through — the canonical author-side footguns
// (`wasi:http/proxy@0.2.0?rc1` — URL-query-separator paste
// where the author copied a versioned link and the trailing
// `?ref=…` came along; `wasi:http/proxy@0.2.0#build` —
// URL-fragment paste; `wasi:http/proxy@0.2.0 alpha` — the
// outer whitespace loop already catches this, but before that
// loop landed the space rode through too; `wasi:http/proxy@
// 0.2.0!alpha` — accidental history-expansion `!`;
// `wasi:http/proxy@0.2.0(rc1)` — parenthetical annotation
// from a doc comment) all passed `validate` and failed at
// WIT-parse time far from the source caixa.lisp with a
// parser diagnostic that names the offending byte but not
// the offending `:contratos :wit` slot. Lifting the rejection
// to caixa-build time closes the byte-set axis structurally
// — every validated `@<version>` body matches the SemVer
// 2.0.0 accepted set, and drift between the typed slot's
// accepted set and the upstream WIT parser's accepted set is
// impossible-by-construction.
//
// Same top-and-bottom-edge discipline the peer axes carry —
// [`is_gateway_api_http_path`]'s eleven-byte RFC-3986-reserved
// rejection set for `:entrada :paths` / `:contratos :endpoint`,
// [`is_nats_subject`]'s strict `[A-Za-z0-9_-]` per-token
// character set for `:contratos :subject`,
// [`is_wit_kebab_id`]'s lowercase-kebab enforcement for the
// WIT namespace/package/interface segments — the typed slot's
// valid set matches the downstream parser's accepted set,
// structurally.
for &b in ver.as_bytes() {
let valid = b.is_ascii_alphanumeric() || b == b'.' || b == b'-' || b == b'+';
if !valid {
return Err(format!(
"version suffix {ver:?} contains invalid character {ch:?} \
(SemVer 2.0.0 restricts the `@<version>` body to the accepted \
set `[0-9A-Za-z.\\-+]` — digits + letters for the \
`major.minor.patch` numeric core, `.` for segment separators, \
`-` for the pre-release suffix, `+` for the build-metadata \
suffix; every other byte fails the upstream WIT parser at \
consume time)",
ch = b as char
));
}
}
// Structural SemVer 2.0.0 parse on the `@<version>` body: every
// byte-set-valid version body (`[0-9A-Za-z.\-+]`, the accepted-
// set arm above) is not necessarily a *structurally* valid
// SemVer version. SemVer 2.0.0 imposes shape rules on top of the
// byte set — three-part `major.minor.patch` mandatory (two-part
// `1.0` and four-part `1.0.0.0` reject), no leading zeros in
// numeric identifiers (`01.0.0` rejects, `10.0.0` accepts,
// `1.0.0-01` rejects while `1.0.0-alpha01` accepts because the
// pre-release identifier is alphanumeric not numeric), no empty
// identifiers (`1.0.0-` and `1.0.0+` reject; `1.0.0-.rc1` and
// `1.0.0-alpha..beta` reject; `1.0.0+.abc` and
// `1.0.0+build..42` reject). Until this gate landed the byte-set
// arm above closed only the per-byte accepted set, and every
// *shape*-invalid version body — the canonical author-side
// paste footguns (`wasi:http/proxy@1.0` two-part-numeric-core
// paste from a Node.js `"engines"` field, `wasi:http/proxy@1`
// one-part paste from a Docker `:v1` tag, `wasi:http/proxy@v0.2.0`
// `v`-prefixed git-tag paste that strayed into the version body,
// `wasi:http/proxy@01.0.0` mistaken zero-padded major from a
// date-based version scheme, `wasi:http/proxy@1.0.0.0` four-part
// paste from a Microsoft / Java build-number convention,
// `wasi:http/proxy@1.0.0-` half-typed pre-release the author
// started and left dangling, `wasi:http/proxy@1.0.0+` peer for
// build-metadata) rode through the byte-set gate and failed at
// WIT-parse time (the WIT IDL's `simple-version` binds through
// the `semver` crate at consume time — see WebAssembly Component
// Model design doc `WIT.md#versions`, and both the M4
// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-
// contract WIT validator (MESH-COMPOSITION §III.2 #5) and the
// future per-edge WIT registry resolver strict-parse the
// `@<version>` body through the same crate). Failure surfaced
// far from the source `caixa.lisp` with a bare `semver::Error`
// that names the specific structural violation but not the
// offending `:contratos :wit` slot. Lifting the parse to caixa-
// build time closes the structural axis: every validated
// `@<version>` body past this call is byte-for-byte round-
// trippable through [`semver::Version::parse`] without re-
// checking at any downstream WIT-consumer layer.
//
// Thin wrapper around [`semver::Version::parse`] — the same
// parser [`crate::Caixa::validate_versao`] (the peer top-level
// `:versao` axis) and [`crate::CaixaVersion::parse`] consume,
// so the accepted set is structurally identical across every
// `:versao`-shaped axis the substrate carries. Maps the
// `semver::Error` reason verbatim into a self-locating
// diagnostic naming the offending version body + the SemVer
// 2.0.0 canonical shape the author intended, so the failure
// is grep-locatable in the `caixa.lisp` (search for
// `:wit "…@<value>"`) and fixable in one edit. Same top-and-
// bottom-edge discipline the peer typed-codec axes carry —
// every typed slot whose accepted set the substrate reads is
// strict-parsed against the downstream consumer's canonical
// parser at build time, not at apply time.
if let Err(e) = semver::Version::parse(ver) {
return Err(format!(
"version suffix {ver:?} is not a structurally valid SemVer 2.0.0 \
version: {e} (the WIT IDL binds `@<version>` to SemVer 2.0.0 \
verbatim — three-part `major.minor.patch` numeric core, no \
leading zeros in numeric identifiers, no empty pre-release / \
build-metadata identifiers; every other shape fails the \
upstream WIT parser at consume time)"
));
}
}
// Then split the head on `:` — exactly one separator, splitting the
// namespace from the package(/interface) body.
let Some((ns, rest)) = head.split_once(':') else {
return Err(format!(
"must contain a `:` separating the namespace from the package (e.g. \
`wasi:http/proxy`); got {s:?} with no `:` — pleme-io dispatches `:wit` \
values on the canonical `<namespace>:<package>` shape and silently \
demotes unmatched shapes to a capability-only L4 edge"
));
};
if rest.contains(':') {
return Err(format!(
"must contain exactly one `:` separator (between namespace and package); \
got {s:?} with multiple `:`"
));
}
is_wit_kebab_id(ns)
.map_err(|r| format!("namespace {ns:?} is not a valid WIT identifier: {r}"))?;
let mut segments = rest.split('/');
let pkg = segments.next().unwrap_or("");
is_wit_kebab_id(pkg)
.map_err(|r| format!("package {pkg:?} is not a valid WIT identifier: {r}"))?;
for iface in segments {
is_wit_kebab_id(iface)
.map_err(|r| format!("interface {iface:?} is not a valid WIT identifier: {r}"))?;
}
Ok(())
}
/// Predicate: assert that `s` is a lowercase kebab-case ASCII identifier
/// — the WIT IDL `id ::= word ('-' word)*` rule restricted to the
/// lowercase `word ::= [a-z][a-z0-9]*` arm the pleme-io substrate
/// dispatches on. Private because every legitimate caller flows through
/// [`is_wit_world_ref`] (which segments the world reference and runs
/// this predicate per segment); exposing it directly would invite
/// per-axis WIT-shape gates that re-implement the segmenting logic
/// inline.
fn is_wit_kebab_id(s: &str) -> Result<(), String> {
if s.is_empty() {
return Err("must not be empty".to_string());
}
let bytes = s.as_bytes();
if !bytes[0].is_ascii_lowercase() {
let msg = if bytes[0].is_ascii_uppercase() {
format!(
"must start with a lowercase ASCII letter (got uppercase {ch:?}); \
pleme-io dispatches `:wit` values on the lowercase canonical shape \
— `wasi:http/proxy` is recognized, `WASI:HTTP/proxy` is silently \
demoted to a capability-only edge",
ch = bytes[0] as char
)
} else if bytes[0].is_ascii_digit() {
format!(
"must start with a lowercase ASCII letter (got digit {ch:?}); WIT \
identifiers begin with a letter, not a digit",
ch = bytes[0] as char
)
} else if bytes[0] == b'-' {
"must not start with `-` (WIT identifiers are kebab-case words; the \
leading character is a lowercase letter)"
.to_string()
} else {
format!(
"must start with a lowercase ASCII letter (got {ch:?}); WIT \
identifiers allow only `[a-z0-9-]`",
ch = bytes[0] as char
)
};
return Err(msg);
}
if bytes[bytes.len() - 1] == b'-' {
return Err(
"must not end with `-` (WIT identifiers are kebab-case words separated \
by single hyphens; no trailing `-`)"
.to_string(),
);
}
let mut prev_hyphen = false;
for &b in bytes {
if b == b'-' {
if prev_hyphen {
return Err(
"must not contain consecutive `-` characters (WIT identifiers \
join words with single hyphens, not `--`)"
.to_string(),
);
}
prev_hyphen = true;
continue;
}
let after_hyphen = prev_hyphen;
prev_hyphen = false;
if b.is_ascii_uppercase() {
return Err(format!(
"must be lowercase (got uppercase character {ch:?}); pleme-io \
dispatches `:wit` values on the lowercase canonical shape — \
`wasi:http/proxy` is recognized, `WASI:HTTP/proxy` is silently \
demoted to a capability-only edge",
ch = b as char
));
}
// Per-word first-byte gate. The doc-comment above binds this
// predicate to the WIT IDL rule `id ::= word ('-' word)*` with
// `word ::= [a-z][a-z0-9]*` — each hyphen-separated word must
// begin with a lowercase letter, not a digit. The full-id
// first-byte arm above ([`is_wit_kebab_id`] line ~974) closes
// the leading-digit / leading-hyphen / leading-uppercase footguns
// for the *first* word (`"1http"`, `"-http"`, `"Http"`); this
// arm closes the same "word must begin with a lowercase letter"
// rule for *every subsequent* word after a `-` separator. Until
// this gate landed the byte-set arm below accepted `[a-z0-9-]`
// uniformly across all positions, so an identifier like
// `"pub-1sub"` / `"proxy-2beta"` / `"cap-9"` passed the byte-set
// gate (every byte lies in `[a-z0-9-]`), passed the leading-`-`
// arm (the first byte is `p`/`c`, not `-`), passed the
// consecutive-`-` arm (no `--`), passed the trailing-`-` arm
// (last byte is a lowercase letter or digit, not `-`), and was
// silently accepted — the canonical `abc-<digit>*` word-shape
// footgun where an author's paste-from-versioned-slug (`"proxy-2"`
// from a `v2`-tagged interface hand-transcribed) or a
// programmatic string-interpolation (`format!("{stem}-{n}")` with
// a numeric `n`) landed in the `:contratos :wit` slot's segment.
// The upstream WIT parser (WebAssembly/component-model spec §WIT
// grammar; `wit-parser` crate's `id!` production) then failed at
// WIT-parse time far from the source caixa.lisp with a parser
// diagnostic that names the offending byte but not the offending
// `:contratos :wit` slot, and the typed slot's accepted set drifted
// from the upstream parser's accepted set on the exact class the
// doc-comment above already documented as rejected — a
// documentation-vs-implementation drift, not a novel rule. Lifting
// the rejection to caixa-build time closes the per-word-first-byte
// axis structurally — every validated WIT identifier past this
// predicate matches the WIT IDL word grammar per-word, not just at
// the first byte, and drift between the typed slot's accepted set
// and the upstream WIT parser's accepted set is impossible-by-
// construction on the digit-after-hyphen axis (the last remaining
// documented-but-unenforced arm on the WIT kebab predicate).
//
// Same top-and-bottom-edge discipline the peer axes carry: every
// caller ([`is_wit_world_ref`] on the `:contratos :wit` axis, and
// through it the M3 `WitContract::target` cross-check at
// [`crate::AplicacaoSpec::validate`]) now refuses the canonical
// author-side "word two starts with a version-shape digit paste"
// footgun at validate time rather than at wit-parser consume
// time. Same trajectory as bb4e6c4 (`is_wit_world_ref` byte-set
// gate on the `@<version>` suffix) and 9f7b894 (`is_wit_world_ref`
// structural SemVer 2.0.0 parse on the `@<version>` suffix) on
// the peer per-suffix axes — the same "typed-slot's accepted set
// matches the downstream parser's accepted set, structurally"
// discipline extended here from the version-body axis to the
// per-word first-byte axis of the identifier body itself.
if after_hyphen && b.is_ascii_digit() {
return Err(format!(
"word after `-` starts with digit {ch:?} (WIT identifiers are \
`id ::= word ('-' word)*` with `word ::= [a-z][a-z0-9]*` — every \
word begins with a lowercase letter, not a digit; the upstream WIT \
parser rejects an identifier of this shape at consume time. Insert \
a lowercase-letter prefix on the offending word — `pub-v1sub` \
instead of `pub-1sub`, `proxy-v2beta` instead of `proxy-2beta`)",
ch = b as char
));
}
if !(b.is_ascii_lowercase() || b.is_ascii_digit()) {
let msg = if b == b'_' {
"contains `_` (WIT identifiers are kebab-case; use `-` between \
words instead of `_`)"
.to_string()
} else if b == b'.' {
"contains `.` (WIT identifiers are single kebab-case words; split \
into separate namespace/package/interface segments via `:` and \
`/` instead of `.`)"
.to_string()
} else {
format!(
"contains invalid character {ch:?} (WIT identifiers allow only \
`[a-z0-9-]`)",
ch = b as char
)
};
return Err(msg);
}
}
Ok(())
}
/// Max length, in bytes, of a single typed `:contratos :subject` NATS
/// subject passing the [`is_nats_subject`] predicate. 256 bytes —
/// matches the upstream NATS Java client's `MAX_SUBJECT_LENGTH`
/// constant and sits well above the longest legitimate subject the
/// caixa-mesh test fixtures + example checkout-aplicacao carry
/// (`"checkout.events.charge.failed"` = 30 bytes, `"rio.events.order.charged"`
/// = 25 bytes). The cap exists to reject the paste-from-binary footgun
/// (a multi-line blob accidentally landed in the `:subject` slot)
/// rather than to constrain legitimate authoring. Lifted as a typed
/// const so a future axis reaching for the same bound (the M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-subject
/// validator, the future NATS Stream/Consumer CR emitter for the
/// `nats:pub-sub` branch of `:contratos`, the future per-edge
/// `:politicas`-derived NATS-aware policy overlay) reads from one
/// place.
pub const NATS_SUBJECT_MAX_LEN: usize = 256;
/// Predicate: assert that `s` is a valid NATS subject — the canonical
/// shape every typed `:contratos :subject` value carries. The
/// contract — modeled on the [NATS subject grammar][nats] (dot-
/// separated tokens with `*` / `>` wildcards), restricted to the
/// strict `[A-Za-z0-9_-]` per-token character set the NATS server's
/// subject parser accepts at runtime:
///
/// - 1..=[`NATS_SUBJECT_MAX_LEN`] (256) bytes;
/// - no whitespace, no control characters, no non-ASCII bytes
/// (RFC 3986 requires `%XX` percent-encoding for non-ASCII; NATS
/// subjects predate that and reject any byte outside the strict
/// ASCII identifier set);
/// - one-or-more `.`-separated tokens — no leading `.`, no trailing
/// `.`, no consecutive `.` (NATS rejects empty tokens between
/// separators);
/// - each token is one of:
/// - a concrete identifier `[A-Za-z0-9_-]+` (NATS subjects are
/// case-sensitive; unlike DNS-1123 we don't lowercase-fold,
/// and underscores are permitted since NATS itself accepts them
/// in tokens);
/// - the `*` single-token wildcard (matches exactly one token;
/// allowed at any segment position);
/// - the `>` multi-token wildcard (matches one-or-more trailing
/// tokens; allowed ONLY as the final segment — `foo.>` matches
/// `foo.bar` / `foo.bar.baz`, `foo.>.bar` is rejected outright).
///
/// Returns the parser-shaped reason on rejection (without wrapping in
/// any error variant) so each per-axis caller — `WitContract::target`
/// for the `:contratos :subject` axis at validate time, the future M4
/// CR materializer's per-subject validator, the future NATS Stream/
/// Consumer CR emitter — wraps the same reason in its own typed
/// `*Invalid { <axis>, reason }` variant. The reason wording is axis-
/// agnostic ("NATS subjects reject empty tokens between separators")
/// so every call site reading the same diagnostic points at the same
/// rule; drift between any two axes' rule enforcement is a build
/// error visible at this predicate, not a per-renderer "this passed
/// validate but the NATS server rejected at publish/subscribe" surprise.
///
/// Empty input is rejected here (defensively) and at the call site via
/// the narrower [`crate::AplicacaoError::ContratoSubjectEmpty`] variant
/// — the same empty-first cascade [`is_dns_1123_label`],
/// [`is_gateway_api_http_path`], and [`is_wit_world_ref`] carry.
///
/// Lifted as a typed substrate-side primitive on the same trajectory
/// the M2-overlay and label-selector helpers (9e3a057, 9d09cfb,
/// 9dbeafd, 31455a7, 07a4544) and the value-shape predicates
/// (`is_dns_1123_label`, `is_gateway_api_http_path`,
/// `is_wit_world_ref`) already follow — the typed slot's valid set
/// matches the NATS server's accepted set, structurally.
///
/// [nats]: https://docs.nats.io/nats-concepts/subjects
///
/// # Errors
///
/// Returns the parser-shaped reason naming the specific violation
/// (length / separator / character-class / wildcard-position), without
/// wrapping in any error variant — every caller maps the same
/// `String` into its own typed `*Invalid { <axis>, reason }` enum
/// variant.
pub fn is_nats_subject(s: &str) -> Result<(), String> {
if s.is_empty() {
return Err("must not be empty".to_string());
}
if s.len() > NATS_SUBJECT_MAX_LEN {
return Err(format!(
"exceeds NATS subject max length of {NATS_SUBJECT_MAX_LEN} bytes \
(got {} bytes; legitimate NATS subjects rarely exceed ~64 bytes — \
this length suggests a paste-from-binary or multi-line blob landed \
in the `:subject` slot)",
s.len()
));
}
for &b in s.as_bytes() {
if b == b' ' || b == b'\t' {
return Err(format!(
"must not contain whitespace character {ch:?} (NATS subjects \
are single tokens with no whitespace between dot-separated \
segments)",
ch = b as char
));
}
if b < 0x20 || b == 0x7F {
return Err(format!(
"must not contain control character 0x{b:02x} (NATS subjects \
are printable ASCII tokens; the NATS server's subject parser \
rejects control characters at publish/subscribe time)"
));
}
if b >= 0x80 {
return Err(format!(
"must not contain non-ASCII byte 0x{b:02x} (NATS subjects \
are restricted to `[A-Za-z0-9_-]` per token + the `.` \
separator and the `*` / `>` wildcards)"
));
}
}
if s.starts_with('.') {
return Err(
"must not start with `.` (NATS subjects reject empty leading \
tokens; drop the leading `.` separator)"
.to_string(),
);
}
if s.ends_with('.') {
return Err(
"must not end with `.` (NATS subjects reject empty trailing \
tokens; use the `>` multi-token wildcard to match arbitrary \
trailing segments instead)"
.to_string(),
);
}
if s.contains("..") {
return Err(
"must not contain consecutive `.` characters (NATS subjects \
reject empty tokens between separators; use the `*` single-\
token wildcard to match any one token)"
.to_string(),
);
}
let segments: Vec<&str> = s.split('.').collect();
let last_idx = segments.len() - 1;
for (i, seg) in segments.iter().enumerate() {
is_nats_subject_segment(seg, i, last_idx)?;
}
Ok(())
}
/// Predicate: assert that `seg` is a valid NATS subject token at index
/// `i` of a `total = last_idx + 1`-segment subject. Private because
/// every legitimate caller flows through [`is_nats_subject`] (which
/// splits the subject on `.` and runs this predicate per segment);
/// exposing it directly would invite per-axis NATS-segment gates that
/// re-implement the splitting logic inline.
///
/// Mirrors the [`is_wit_kebab_id`] / [`is_wit_world_ref`] private-helper
/// pair on the WIT predicate.
fn is_nats_subject_segment(seg: &str, i: usize, last_idx: usize) -> Result<(), String> {
if seg == "*" {
return Ok(());
}
if seg == ">" {
if i != last_idx {
return Err(format!(
"the `>` multi-token wildcard is only allowed as the \
final segment (got `>` at segment {one_based} of {total}; \
move to the end or use `*` for a single-token wildcard)",
one_based = i + 1,
total = last_idx + 1
));
}
return Ok(());
}
for &b in seg.as_bytes() {
let valid = b.is_ascii_alphanumeric() || b == b'_' || b == b'-';
if !valid {
let msg = if b == b'*' {
"contains `*` mid-segment (NATS wildcards are standalone \
tokens — `foo.*.bar` matches one middle token, `foo*` \
does not; split into separate `.`-separated segments)"
.to_string()
} else if b == b'>' {
"contains `>` mid-segment (NATS wildcards are standalone \
tokens — `foo.>` matches all trailing tokens, `foo>` \
does not; split into separate `.`-separated segments)"
.to_string()
} else {
format!(
"contains invalid character {ch:?} in subject segment \
(NATS subject tokens allow only `[A-Za-z0-9_-]`; use \
`_` or `-` instead)",
ch = b as char
)
};
return Err(msg);
}
}
Ok(())
}
/// Max length, in bytes, of a single typed `:contratos :slot` WASI
/// keyvalue store key/template passing the [`is_wasi_keyvalue_slot`]
/// predicate. 512 bytes — generously above the longest realistic slot
/// template (`"checkout/$orderId"` = 17 bytes, `"users:{tenant}/{id}"`
/// = 19 bytes, `"session.tokens.<sid>"` = 20 bytes) and well under any
/// canonical WASI-keyvalue backend's per-key limit (etcd: 1.5 MB,
/// DynamoDB partition+sort key: 2 KB combined, Redis: 512 MB — the cap
/// is chosen for the *template* slot a typed `:contratos` edge
/// authors, not the realized key at runtime). The cap exists to reject
/// the paste-from-binary footgun (a multi-line blob accidentally landed
/// in the `:slot` slot) rather than to constrain legitimate authoring.
/// Lifted as a typed const so a future axis reaching for the same
/// bound (the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
/// per-slot validator, the future per-Servico `:capabilities`
/// `wasi:keyvalue/store` axis's per-slot validator when M4 lands
/// per-capability typed slots, the future per-edge `:politicas`-derived
/// kv-backend-aware policy overlay's per-slot validator) reads from
/// one place. Same lift trajectory as [`NATS_SUBJECT_MAX_LEN`] (which
/// caps the peer pub-sub payload axis at 256 bytes — twice that here
/// because kv slot templates legitimately compose more `/`-separated
/// path segments + template variables than NATS subjects do
/// `.`-separated tokens).
pub const WASI_KV_SLOT_MAX_LEN: usize = 512;
/// Predicate: assert that `s` is a valid WASI keyvalue store slot
/// template — the canonical shape every typed `:contratos :slot` value
/// carries when its `:wit` dispatch resolves to the
/// [`WitTarget::Store`][st] arm (`wasi:keyvalue/store`, `kv:*`). The
/// WASI keyvalue 0.2 specification ([`bucket = string`, `key = string`,
/// both opaque][wasi-kv]) places no syntactic constraints on the key
/// shape, so the substrate enforces the canonical printable-ASCII
/// floor every realistic kv backend admits: no raw whitespace, no
/// control bytes, no non-ASCII bytes, length-bounded by
/// [`WASI_KV_SLOT_MAX_LEN`]. The grammar:
///
/// - 1..=[`WASI_KV_SLOT_MAX_LEN`] (512) bytes;
/// - no whitespace (space, tab — kv slot templates are single-token
/// identifiers / path expressions, whitespace is the canonical
/// paste-from-doc footgun whose runtime behavior varies
/// unpredictably across backends — etcd accepts, Redis accepts
/// but rejects subsequent CLI ops, DynamoDB rejects on write);
/// - no ASCII control characters (`0x00..0x1F`, `0x7F`) — every
/// kv backend either rejects on write (DynamoDB, etcd) or admits
/// and silently breaks at the next read (Redis: `\r\n` corrupts
/// the RESP protocol framing if the slot template is rendered
/// directly into a key without re-encoding);
/// - no non-ASCII bytes (`>= 0x80`) — RFC 3986-style percent-
/// encoding (`%XX`) is the substrate's canonical UTF-8 escape
/// for kv slot templates the author wants to namespace by
/// non-ASCII identifier; raw non-ASCII silently differs between
/// backends (etcd preserves bytes verbatim; Redis-via-RESP3 may
/// re-encode; DynamoDB rejects).
///
/// The predicate is intentionally permissive on structure: all
/// printable ASCII bytes (`0x21..0x7E`) are admitted, including
/// `/` (path separators), `:` (namespace separators), `.`
/// (dot-namespacing), `-`/`_` (identifier separators), `$`/`{`/`}`/`<`/`>`
/// (template-variable syntaxes — the canonical `"checkout/$orderId"`
/// shape carries `$`-prefixed identifiers, alternate `"users:{id}"` /
/// `"session.<sid>"` shapes carry `{}` / `<>` brackets), and the
/// remaining ASCII punctuation. The substrate doesn't know which kv
/// backend the runtime resolves [`WitTarget::Store`][st] to — that
/// choice is per-cluster, made by the operator's kv-provider binding
/// — so the typed slot enforces the intersection-floor every backend
/// admits rather than any one backend's stricter superset.
///
/// Returns the parser-shaped reason on rejection (without wrapping in
/// any error variant) so each per-axis caller — [`WitContract::target`]
/// for the `:contratos :slot` axis at validate time, the future M4 CR
/// materializer's per-slot validator, the future per-Servico
/// `:capabilities wasi:keyvalue/store` per-slot validator — wraps the
/// same reason in its own typed `*Invalid { <axis>, reason }` variant.
/// The reason wording is axis-agnostic ("kv slot templates reject raw
/// whitespace") so every call site reading the same diagnostic points
/// at the same rule; drift between any two axes' rule enforcement is
/// a build error visible at this predicate, not a per-renderer "this
/// passed validate but the kv backend rejected on first write"
/// surprise.
///
/// Empty input is rejected here (defensively) and at the call site
/// via the narrower [`crate::AplicacaoError::ContratoSlotEmpty`]
/// variant — the same empty-first cascade [`is_dns_1123_label`],
/// [`is_gateway_api_http_path`], [`is_wit_world_ref`], and
/// [`is_nats_subject`] all carry.
///
/// Lifted as a typed substrate-side primitive on the same trajectory
/// the peer payload-axis predicates ([`is_gateway_api_http_path`] for
/// `:endpoint`, [`is_nats_subject`] for `:subject`) already follow —
/// the typed slot's valid set matches the kv backend intersection-
/// floor's accepted set, structurally. The fifth value-shape primitive
/// to land in [`crate::render`] after [`is_dns_1123_label`],
/// [`is_gateway_api_http_path`], [`is_wit_world_ref`], and
/// [`is_nats_subject`] — and the one that closes the trajectory across
/// every typed payload axis the [`WitContract::target`] dispatch
/// carries (HTTP `:endpoint`, PubSub `:subject`, Store `:slot`).
///
/// [st]: crate::WitTarget::Store
/// [wasi-kv]: https://github.com/WebAssembly/wasi-keyvalue
///
/// # Errors
///
/// Returns the parser-shaped reason naming the specific violation
/// (length / whitespace / control / non-ASCII), without wrapping in
/// any error variant — every caller maps the same `String` into its
/// own typed `*Invalid { <axis>, reason }` enum variant.
pub fn is_wasi_keyvalue_slot(s: &str) -> Result<(), String> {
if s.is_empty() {
return Err("must not be empty".to_string());
}
if s.len() > WASI_KV_SLOT_MAX_LEN {
return Err(format!(
"exceeds WASI keyvalue slot max length of {WASI_KV_SLOT_MAX_LEN} bytes \
(got {} bytes; legitimate kv slot templates rarely exceed ~64 bytes — \
this length suggests a paste-from-binary or multi-line blob landed in \
the `:slot` slot)",
s.len()
));
}
for &b in s.as_bytes() {
if b == b' ' || b == b'\t' {
return Err(format!(
"must not contain whitespace character {ch:?} (kv slot templates \
are single-token identifiers / path expressions; raw whitespace \
behaves unpredictably across kv backends — percent-encode as `%20` \
or use `-`/`_` to namespace)",
ch = b as char
));
}
if b < 0x20 || b == 0x7F {
return Err(format!(
"must not contain control character 0x{b:02x} (kv slot templates \
are printable ASCII; control bytes either get rejected on write \
by strict backends — DynamoDB, etcd — or silently corrupt the \
next read on permissive ones — Redis RESP framing)"
));
}
if b >= 0x80 {
return Err(format!(
"must not contain non-ASCII byte 0x{b:02x} (RFC 3986 requires \
percent-encoding `%XX` for characters outside the ASCII unreserved \
+ reserved set; raw non-ASCII bytes are admitted by some kv backends \
verbatim and re-encoded by others — the typed slot's value set is \
the intersection-floor every backend admits identically)"
));
}
}
Ok(())
}
/// Max length, in bytes, of a single typed git ref name passing the
/// [`is_git_ref_name`] predicate. 255 bytes — matches the POSIX
/// `NAME_MAX` filesystem-component limit every Git porcelain ultimately
/// stores refs into (loose `refs/<category>/<name>` files under
/// `.git/refs/`, packed-refs index entries). Refs that exceed this cap
/// fail to land on disk at clone/fetch time on every realistic
/// filesystem (ext4, btrfs, xfs, APFS, NTFS), so a `:tag` / `:branch`
/// past that length is unsourceable in practice. The cap exists to
/// reject the paste-from-binary footgun (a multi-line blob accidentally
/// landed in the `:tag` slot) rather than to constrain legitimate
/// authoring — realistic tag/branch names rarely exceed ~32 bytes
/// (`"v0.1.0"` = 6 bytes, `"release-1.0-alpha.1"` = 19 bytes,
/// `"feature/checkout-rewrite"` = 24 bytes). Lifted as a typed const
/// so a future axis reaching for the same bound (the future
/// `lacre.lisp` ref-shape gate on resolved-pin axes, the future M4
/// per-dep CR materializer's per-pin validator) reads from one place.
pub const GIT_REF_NAME_MAX_LEN: usize = 255;
/// Predicate: assert that `s` is a valid Git ref name under the
/// `git check-ref-format --allow-onelevel` rule set — the canonical
/// shape every typed `:fonte (:tipo git …)` `:tag` / `:branch` value
/// carries. The contract — modeled on the [`git check-ref-format`][gcr]
/// grammar the Git porcelain enforces at clone/fetch/checkout time,
/// with the multi-component requirement waived (`:tag "v0.1.0"` and
/// `:branch "main"` are both single-component refs, the canonical
/// leaf form for caixa's `:fonte` pin axes):
///
/// - 1..=[`GIT_REF_NAME_MAX_LEN`] (255) bytes — the POSIX `NAME_MAX`
/// filesystem-component limit Git's loose-ref `.git/refs/<cat>/<name>`
/// storage tops out at;
/// - no ASCII control characters (`0x00..=0x1F`, `0x7F`) — Git's
/// refname parser rejects them, and the `\r` / `\n` arms are the
/// canonical "the paste-from-doc spans multiple lines" footgun;
/// - no whitespace (space, tab) — Git's refname parser rejects them
/// too; a `:tag "v0.1.0 "` (trailing space, from a copy-paste)
/// silently passes string emptiness checks and fails at
/// `git fetch origin tag 'v0.1.0 '` with a quoting-confused error
/// far from the source caixa.lisp;
/// - no non-ASCII bytes (`>= 0x80`) — Git's refname rules predate
/// UTF-8 normalization (NFC vs NFD on APFS silently rewrites the
/// ref body, breaking the lacre's content addressing); the
/// intersection-floor every realistic Git host accepts is ASCII
/// identifiers + the small punctuation set below;
/// - no `~`, `^`, `:`, `?`, `*`, `[`, `\` anywhere — Git reserves
/// these for revision-grammar expressions (`HEAD~3`, `HEAD^`,
/// `:/searched`, glob wildcards, refspec brackets, Windows-path
/// backslash);
/// - no `@{` sequence — Git's reflog grammar (`HEAD@{2 hours ago}`,
/// `branch@{upstream}`);
/// - the bare `@` is not a valid refname (it's the alias for `HEAD`);
/// - no `..` anywhere (Git's `<rev1>..<rev2>` range syntax + the
/// `.` / `..` parent-traversal footgun);
/// - per `/`-separated component: must not begin with `.` (Git
/// refuses to follow loose `.git/refs/<cat>/.<name>` files), must
/// not end with `.lock` (Git's atomic-rename guard suffix), must
/// not be empty (`//` rejected by the no-empty-component arm
/// below);
/// - no leading `/`, no trailing `/`, no consecutive `//`;
/// - no trailing `.` on the whole ref (Git rejects `<name>.`);
/// - no `refs/heads/` or `refs/tags/` prefix — the canonical "I
/// copied the fully-qualified ref name out of `git show-ref`
/// instead of the leaf" footgun (per [`theory/FLAKE-DEDUP.md`][fd]
/// `BranchName` constructor rules); the caixa-resolver prepends
/// the category prefix at clone time, so an author-side
/// `:branch "refs/heads/main"` resolves to a literal ref named
/// `refs/heads/refs/heads/main` on disk.
///
/// Returns the parser-shaped reason on rejection (without wrapping in
/// any error variant) so each per-axis caller — `DepSource::validate`
/// for the `:fonte :tag` / `:fonte :branch` axes at validate time,
/// the future per-pin gate on `lacre.lisp` resolved-ref axes, the
/// future M4 per-dep CR materializer's per-pin validator — wraps the
/// same reason in its own typed `*Invalid { <axis>, reason }` variant.
/// The reason wording is axis-agnostic ("git ref names reject ASCII
/// control characters") so every call site reading the same diagnostic
/// points at the same rule; drift between any two axes' rule
/// enforcement is a build error visible at this predicate, not a
/// per-renderer "this passed validate but `git fetch` rejected at
/// clone time" surprise.
///
/// Empty input is rejected here (defensively) and at each call site
/// via the narrower [`crate::DepError::FontePinEmpty`] variant — the
/// same empty-first cascade [`is_dns_1123_label`],
/// [`is_gateway_api_http_path`], [`is_wit_world_ref`],
/// [`is_nats_subject`], and [`is_wasi_keyvalue_slot`] all carry.
///
/// `:rev` is intentionally NOT routed through this predicate — its
/// author-surface shape is a hex commit-ID (`[0-9a-f]+`), not a
/// refname; a dedicated `is_git_oid` predicate on the parallel
/// hex-shape trajectory carries the reproducibility contract. Routing
/// `:rev` through `is_git_ref_name` would admit `:rev "main"`,
/// defeating the reproducibility contract `:rev` carries vs.
/// `:branch` / `:tag`. The reverse mis-slot — a canonical OID
/// (40-char SHA-1 or 64-char SHA-256 lowercase hex) pasted into the
/// `:tag` / `:branch` slot — is closed by this predicate too: a
/// pre-emption arm below rejects any value whose width and byte set
/// match the canonical OID shape, surfacing the cross-axis mis-slot
/// at validate time with a diagnostic pointing the author at the
/// `:rev` slot. The two predicates' valid sets intersect at exactly
/// the empty set, structurally.
///
/// Lifted as a typed substrate-side primitive on the same trajectory
/// the peer value-shape predicates ([`is_dns_1123_label`],
/// [`is_gateway_api_http_path`], [`is_wit_world_ref`],
/// [`is_nats_subject`], [`is_wasi_keyvalue_slot`]) already follow —
/// the typed slot's valid set matches the Git porcelain's accepted
/// set, structurally. The sixth value-shape primitive to land in
/// [`crate::render`], and the first to gate a non-K8s downstream
/// landing surface (git CLI invocation from caixa-resolver, vs. the
/// K8s apiserver / NATS server / WASI kv backend for the prior five).
///
/// [gcr]: https://git-scm.com/docs/git-check-ref-format
/// [fd]: pleme-io/theory/FLAKE-DEDUP.md §1 `BranchName`
///
/// # Errors
///
/// Returns the parser-shaped reason naming the specific violation
/// (length / control-char / forbidden-char / component-shape / prefix),
/// without wrapping in any error variant — every caller maps the same
/// `String` into its own typed `*Invalid { <axis>, reason }` enum
/// variant.
pub fn is_git_ref_name(s: &str) -> Result<(), String> {
if s.is_empty() {
return Err("must not be empty".to_string());
}
if s.len() > GIT_REF_NAME_MAX_LEN {
return Err(format!(
"exceeds git ref name max length of {GIT_REF_NAME_MAX_LEN} bytes \
(got {} bytes; legitimate tag/branch names rarely exceed ~32 bytes — \
this length suggests a paste-from-binary or multi-line blob landed \
in the `:tag` / `:branch` slot)",
s.len()
));
}
// Canonical-OID-shape pre-emption — the structural partition the
// doc-comment above promises and [`crate::DepSource::validate`]
// routes the `:fonte` pin axes through ([`is_git_ref_name`] for
// `:tag` + `:branch`, [`is_git_oid`] for `:rev`): a value that's
// exactly the canonical Git commit-OID width
// ([`GIT_OID_SHA1_LEN`] (40) lowercase-hex for SHA-1,
// [`GIT_OID_SHA256_LEN`] (64) lowercase-hex for SHA-256) is the
// shape `is_git_oid` accepts; the two predicates' valid sets must
// intersect at exactly the empty set, so a value of that shape is
// rejected here. Without this arm a canonical lowercase-hex OID of
// either canonical width passes every other refname-shape arm in
// this predicate — pure-hex strings carry none of the forbidden
// characters, no `..` / `@{` / leading-`/` / trailing-`/` /
// `.lock`-suffix / `refs/heads/`-prefix — and the cross-axis
// partition silently fails on the canonical "I copied the SHA out
// of `git show --format=%H` and pasted it into `:tag` / `:branch`"
// mis-slot footgun. The pleme-io discipline (CAIXA-SDLC §V — the
// `:rev` slot carries the reproducibility contract; `:tag` /
// `:branch` resolve to whatever the upstream has tagged / `HEAD`
// today) requires that an OID-shaped value live under `:rev`, never
// under `:tag` / `:branch`; this arm makes that discipline a typed
// structural property, not a convention.
//
// Uppercase hex (`"DEADBEEF…"` 40 chars) is intentionally NOT
// matched here — uppercase letters are legitimate in refnames per
// `git check-ref-format`, so an uppercase 40/64-char hex string is a
// valid refname (`is_git_ref_name` accepts it); the `:rev` axis
// separately rejects uppercase via [`is_git_oid`]'s lowercase-only
// contract. Off-canonical lengths (39 / 41 / 63 / 65 hex chars) are
// also intentionally NOT matched — abbreviated commit IDs are
// ambiguous across repository history but they're not canonical
// OIDs either; they remain accepted as refnames here (consistent
// with `is_git_oid` already rejecting them via its exact-width
// check).
if (s.len() == GIT_OID_SHA1_LEN || s.len() == GIT_OID_SHA256_LEN)
&& s.bytes().all(|b| matches!(b, b'0'..=b'9' | b'a'..=b'f'))
{
return Err(format!(
"looks like a canonical Git commit OID ({len} lowercase hex \
characters — the SHA-{algo} OID width); pleme-io's `:fonte` axes \
partition refnames vs. commit OIDs structurally, so a value of \
this shape belongs in the `:rev` slot (which routes through \
`is_git_oid` for the reproducibility contract — one immutable \
commit, forever), not `:tag` / `:branch` (which route through \
this predicate for human-readable refs `git fetch` resolves at \
clone time). Move the value to `:rev`; keeping it under `:tag` \
/ `:branch` is the canonical paste-from-`git show --format=%H` \
mis-slot footgun, silently demoting an OID to a refname-shaped \
pin the resolver would attempt to `git fetch tag '<sha>'` and \
fail at clone time with a quoting-confused porcelain error far \
from the source caixa.lisp.",
len = s.len(),
algo = if s.len() == GIT_OID_SHA1_LEN {
"1"
} else {
"256"
},
));
}
if s.starts_with('-') {
return Err(
"must not start with `-` (the canonical CLI-argument-injection \
footgun on the `:tag` / `:branch` axis — caixa-resolver's \
`git::checkout` invocation routes the ref name verbatim into \
`git checkout --quiet --detach <ref>` (caixa-resolver/src/git.rs:41) \
without a `--` argument-list terminator, so a leading `-` value \
(`:tag \"-stable\"`, `:branch \"-X\"`, `:tag \"-c=core.merge=ours\"`) \
silently escapes the subprocess argument boundary and gets \
reinterpreted by `git checkout`'s argument parser as a CLI flag — \
the canonical short-flag / long-option / config-injection vector. \
Git's `check-ref-format` grammar does NOT reject a leading `-` \
(it admits the byte mid-name as a legitimate kebab separator), so \
every prior shape arm on this predicate passes the value through; \
the diagnostic moves the gate to the subprocess-argument \
boundary the resolver consumes. Peer with the \
[`is_git_repo_url`] leading-`-` arm (the CLI-arg-injection \
vector on the sibling `:repo` axis where `git clone <repo>` \
reinterprets a leading `-` as a flag like `-upload-pack=…` / \
`--config=…`), [`is_cargo_feature_name`] leading-`-` arm, and \
[`is_dns_1123_label`] leading-`-` arm — every single-token typed \
string slot the substrate routes through a downstream subprocess \
/ parser rejects the same leading-byte CLI-arg-injection shape \
at validate time. Drop the leading `-`; use a kebab-separator-\
between-alphanumeric-segments form like `\"v0.1.0\"` / \
`\"feature-x\"` / `\"main\"` instead)"
.to_string(),
);
}
for &b in s.as_bytes() {
if b == b' ' || b == b'\t' {
return Err(format!(
"must not contain whitespace character {ch:?} (git ref names are \
single tokens with no whitespace — a trailing space in a `:tag` \
/ `:branch` value is the canonical paste-from-doc footgun, \
silently breaking `git fetch <remote> tag '<value> '` at \
clone time)",
ch = b as char
));
}
if b < 0x20 || b == 0x7F {
return Err(format!(
"must not contain control character 0x{b:02x} (git ref names are \
printable ASCII; `\\r` / `\\n` are the canonical \
paste-from-multiline-doc footgun and break git's refname parser \
at every porcelain entry point)"
));
}
if b >= 0x80 {
return Err(format!(
"must not contain non-ASCII byte 0x{b:02x} (git's refname rules \
predate UTF-8 normalization — APFS NFC/NFD silently rewrites the \
ref body, breaking the lacre's content addressing across \
platforms; the intersection-floor every git host admits is ASCII)"
));
}
match b {
b'~' => {
return Err("must not contain `~` (git reserves `~` for the revision \
grammar — `HEAD~3` means `parent of parent of parent of \
HEAD`; the bare character is not admitted in a refname)"
.to_string());
}
b'^' => {
return Err("must not contain `^` (git reserves `^` for the revision \
grammar — `HEAD^` means `first parent of HEAD`; the bare \
character is not admitted in a refname)"
.to_string());
}
b':' => {
return Err("must not contain `:` (git reserves `:` for revspec / \
refspec separators — `:refs/heads/...`, `<src>:<dst>`)"
.to_string());
}
b'?' => {
return Err("must not contain `?` (git reserves `?` for refspec glob \
wildcards)"
.to_string());
}
b'*' => {
return Err("must not contain `*` (git reserves `*` for refspec glob \
wildcards — `refs/heads/*:refs/remotes/origin/*`)"
.to_string());
}
b'[' => {
return Err("must not contain `[` (git reserves `[` for refspec \
bracketed-glob syntax)"
.to_string());
}
b'\\' => {
return Err("must not contain `\\` (git's refname grammar rejects \
backslash — the canonical Windows-path-leak footgun; use \
`/` for hierarchical refs)"
.to_string());
}
_ => {}
}
}
if s.contains("..") {
return Err(
"must not contain `..` (git reserves `..` for the `<rev1>..<rev2>` \
range grammar; a `..` component would also escape the loose-ref \
directory tree at clone time)"
.to_string(),
);
}
if s.contains("@{") {
return Err(
"must not contain `@{` (git reserves `@{` for the reflog grammar \
— `branch@{upstream}`, `HEAD@{2 hours ago}`)"
.to_string(),
);
}
if s == "@" {
return Err(
"must not be the bare `@` (git aliases `@` to `HEAD`; a `:tag` / \
`:branch` named `@` is unsourceable)"
.to_string(),
);
}
if s.starts_with('/') {
return Err(
"must not begin with `/` (git refnames are relative to the ref \
category prefix the resolver prepends — drop the leading `/`)"
.to_string(),
);
}
if s.ends_with('/') {
return Err(
"must not end with `/` (git refnames are leaf-or-multi-component; \
a trailing `/` would resolve to an empty final component)"
.to_string(),
);
}
if s.contains("//") {
return Err(
"must not contain consecutive `/` characters (git refnames reject \
empty components between separators)"
.to_string(),
);
}
if s.ends_with('.') {
return Err(
"must not end with `.` (git refnames reject a trailing `.` — \
`<name>.` collides with the `<name>.lock` atomic-rename guard \
suffix on case-insensitive filesystems)"
.to_string(),
);
}
if s.starts_with("refs/heads/") || s.starts_with("refs/tags/") {
return Err(format!(
"must not carry the fully-qualified `refs/heads/` or `refs/tags/` \
prefix (this is the canonical `git show-ref` output-leak footgun; \
the caixa-resolver prepends the category prefix at clone time, so \
a `:branch \"refs/heads/main\"` would resolve to a literal ref \
named `refs/heads/refs/heads/main` on disk — drop the prefix and \
pass the leaf: `{leaf:?}`)",
leaf = s
.strip_prefix("refs/heads/")
.or_else(|| s.strip_prefix("refs/tags/"))
.unwrap_or(s),
));
}
for (i, component) in s.split('/').enumerate() {
if component.starts_with('.') {
return Err(format!(
"component {component:?} (segment {one_based} of the `/`-split \
refname) must not begin with `.` (git refuses to follow loose \
`.git/refs/<cat>/.<name>` files)",
one_based = i + 1,
));
}
// Case-insensitive `.lock` check: git enforces the `.lock`
// suffix as the atomic-rename guard on case-sensitive
// filesystems (refs/heads/main.lock collides with the
// in-flight update lockfile); on case-insensitive
// filesystems (APFS default, NTFS, HFS+) the `.LOCK` /
// `.Lock` variants collide identically. Rejecting all case
// permutations matches the broader-rejection intent on the
// axis the lacre pipeline ultimately stores into.
if component.len() >= 5
&& component.as_bytes()[component.len() - 5..].eq_ignore_ascii_case(b".lock")
{
return Err(format!(
"component {component:?} (segment {one_based} of the `/`-split \
refname) must not end with `.lock` (git uses the `.lock` \
suffix as the atomic-rename guard for in-flight ref updates; \
a refname ending in `.lock` is unwritable, and the suffix is \
case-insensitive on the case-insensitive filesystems Git \
supports — APFS default, NTFS, HFS+)",
one_based = i + 1,
));
}
}
Ok(())
}
/// Length, in lowercase-hex characters, of a full Git SHA-1 commit
/// OID — the canonical commit identifier every `git rev-parse HEAD`
/// invocation emits on a SHA-1-hashed repository. `git`'s loose-object
/// store keys every object under `.git/objects/<first-2-hex>/<last-38-hex>`,
/// so the full 40-char OID is the address-of-truth the porcelain consumes
/// at `git fetch <remote> <40-hex>` and `git checkout <40-hex>` time;
/// abbreviated OIDs are admitted by the porcelain through a separate
/// prefix-lookup pass and are ambiguous across repository history (a 7-char
/// prefix that resolves to one commit today can become a collision tomorrow
/// as the repo grows). Lifted as a typed const so the `:fonte :rev`
/// validate gate, the future lacre-side resolved-rev gate, and the future
/// M4 per-dep CR materializer's per-pin validator all read from one place.
pub const GIT_OID_SHA1_LEN: usize = 40;
/// Length, in lowercase-hex characters, of a full Git SHA-256 commit
/// OID — the canonical commit identifier on a SHA-256-hashed repository
/// (Git's [`extensions.objectFormat = sha256`][gitsha256] mode, GA since
/// Git 2.42 / Oct 2023). Doubled width vs. SHA-1: 256 bits = 64 hex chars.
/// Carried alongside [`GIT_OID_SHA1_LEN`] so the typed `:rev` slot admits
/// either canonical hash-algorithm OID without per-renderer branching;
/// the lacre's BLAKE3 content-addressing (THEORY.md §IV — typed reproducibility
/// envelope) is orthogonal to the upstream git's chosen object hash and
/// neither OID width should leak into downstream code paths.
///
/// [gitsha256]: https://git-scm.com/docs/hash-function-transition
pub const GIT_OID_SHA256_LEN: usize = 64;
/// Predicate: assert that `s` is a valid Git commit OID — the canonical
/// shape the typed `:fonte (:tipo git …)` `:rev` axis carries. The
/// reproducibility contract `:rev` carries vs. `:tag` / `:branch`
/// (CAIXA-SDLC §V — Substrate; `:tag` resolves to whatever the upstream
/// has tagged today, `:branch` to whatever the upstream's HEAD points at
/// today, `:rev` to exactly one immutable commit forever — same shape
/// Unison's [content-addressed code identity][unison] gives terms by
/// construction: the hash is the address, the address never moves):
///
/// - exactly [`GIT_OID_SHA1_LEN`] (40, SHA-1) or [`GIT_OID_SHA256_LEN`]
/// (64, SHA-256) characters — the two canonical Git hash-algorithm
/// widths; anything in between is an abbreviated prefix (the
/// canonical `git log --short` / `git rev-parse --short HEAD`
/// paste-from-release-notes footgun), which is ambiguous across
/// repository history and surfaces at clone time as an
/// [`ambiguous argument`][gitambig] error far from the source
/// caixa.lisp;
/// - every byte in `[0-9a-f]` (lowercase ASCII hex) — `git rev-parse`
/// and `git show --format=%H` both emit lowercase exclusively, so an
/// uppercase-bearing `:rev` round-trips inconsistently across the
/// resolver's `git fetch <remote> <:rev>` ↔ `git rev-parse HEAD`
/// equality-check pipeline and fails the lacre's content-addressing
/// equality probe with a confusing case-only diff;
/// - no whitespace, no control bytes, no non-ASCII, no refname
/// punctuation (`~ ^ : ? * [ \`), no `/` separators — every
/// character outside `[0-9a-f]` is rejected on the same predicate
/// arm, so a `:rev "main"` (the canonical "I conflated `:rev`
/// and `:branch`" footgun) lands at the same gate as a
/// `:rev "v0.1.0"` (`:tag` mis-slot) or a `:rev "c0ffee:scratch"`
/// (refname-shape leak); the typed `:rev` slot's valid set
/// intersects the `:tag` / `:branch` slot's valid set at exactly
/// the empty set, structurally — every refname is rejected here,
/// every OID is rejected by [`is_git_ref_name`].
/// - not the all-zero null-OID sentinel (`"0000…0000"` — 40 zeros
/// at SHA-1 width, 64 zeros at SHA-256 width). Git reserves this
/// value as the "no commit" sentinel in `git update-ref` /
/// pre-receive hook flows (`<old-value>` for create, `<new-value>`
/// for delete) and no commit in any object database has this OID,
/// so a `:rev "0000…0000"` is structurally impossible to resolve.
/// The canonical "I copy-pasted the sentinel out of `git
/// update-ref --stdin` docs / pre-receive hook example" footgun
/// would otherwise pass every other shape arm (canonical length,
/// lowercase hex) and surface at `git fetch <remote> 0000…0000`
/// time with a quoting-confused "couldn't find remote ref" error
/// far from the source caixa.lisp, with the lacre's content-
/// address locked to a `git:0000…0000` closure that never equals
/// any upstream's actual `HEAD`. Mirrors `is_git_ref_name`'s
/// canonical-OID-shape pre-emption arm (line 1322) — both
/// predicates carry one self-aware arm that catches values
/// structurally valid for the alphabet but operationally
/// meaningless on the typed axis.
///
/// Returns the parser-shaped reason on rejection (without wrapping in
/// any error variant) so each per-axis caller — [`crate::DepError::FontePinShape`]
/// at validate time on the `:fonte :rev` axis, the future per-pin gate
/// on `lacre.lisp` resolved-rev axes, the future M4 per-dep CR
/// materializer's per-pin validator — wraps the same reason in its own
/// typed `*Invalid { axis, reason }` variant. The reason wording is
/// axis-agnostic ("git commit OIDs are lowercase hex (`[0-9a-f]`)") so
/// every call site reading the same diagnostic points at the same rule.
///
/// Empty input is rejected here (defensively) and at each call site via
/// the narrower [`crate::DepError::FontePinEmpty`] variant — the same
/// empty-first cascade [`is_dns_1123_label`], [`is_gateway_api_http_path`],
/// [`is_wit_world_ref`], [`is_nats_subject`], [`is_wasi_keyvalue_slot`],
/// and [`is_git_ref_name`] all carry.
///
/// Sibling of [`is_git_ref_name`]: the two predicates together bracket
/// the `:fonte` pin axes — refname-shaped (`:tag` / `:branch`) vs.
/// hex-OID-shaped (`:rev`) — so an authored value lands in exactly one
/// of the two valid sets, and a cross-axis mis-slot (`:rev "main"` /
/// `:tag "deadbeef…"`) is a build error at the offending axis's
/// predicate, not a clone-time surprise.
///
/// [unison]: https://www.unison-lang.org/docs/the-big-idea/
/// [gitambig]: https://git-scm.com/docs/git-rev-parse#_specifying_revisions
///
/// # Errors
///
/// Returns the parser-shaped reason naming the specific violation
/// (length / character-class), without wrapping in any error variant —
/// every caller maps the same `String` into its own typed
/// `*Invalid { axis, reason }` enum variant.
pub fn is_git_oid(s: &str) -> Result<(), String> {
if s.is_empty() {
return Err("must not be empty".to_string());
}
let len = s.len();
if len != GIT_OID_SHA1_LEN && len != GIT_OID_SHA256_LEN {
return Err(format!(
"git commit OIDs are exactly {GIT_OID_SHA1_LEN} hex chars (SHA-1) or \
{GIT_OID_SHA256_LEN} hex chars (SHA-256); got {len} chars (an \
abbreviated commit ID is ambiguous across repository history — \
`git log --short` / `git rev-parse --short HEAD` emit prefixes for \
human display only, not as reproducible commit addresses; pin the \
full OID so the resolver's `git fetch <remote> <:rev>` and the \
lacre's content-addressing equality probe both resolve to exactly \
one immutable commit, forever)"
));
}
for (i, b) in s.bytes().enumerate() {
match b {
b'0'..=b'9' | b'a'..=b'f' => {}
b'A'..=b'F' => {
return Err(format!(
"git commit OIDs are lowercase hex (`[0-9a-f]`); got \
uppercase character {ch:?} at byte {i} (git porcelain \
emits OIDs lowercase exclusively — `git rev-parse HEAD` \
and `git show --format=%H` both lowercase on output; a \
`:rev` value with `[A-F]` round-trips inconsistently \
across the resolver's fetch ↔ `git rev-parse HEAD` \
equality-check pipeline and fails the lacre's \
content-addressing probe with a confusing case-only diff)",
ch = b as char
));
}
_ => {
return Err(format!(
"git commit OIDs are lowercase hex (`[0-9a-f]`); got non-hex \
character {ch:?} at byte {i} (the `:rev` slot's value-shape \
contract is a hex commit ID — for refname-shaped pins \
(`v0.1.0`, `main`, `feature/checkout`) use `:tag` or \
`:branch`, not `:rev`; the substrate's `is_git_ref_name` \
and `is_git_oid` predicates partition the `:fonte` axes \
structurally, so a cross-axis mis-slot lands at the \
offending axis's predicate, not at clone time)",
ch = b as char
));
}
}
}
// Null-OID sentinel pre-emption — the all-zero hex string is git's
// canonical "no commit" sentinel (used in `git update-ref` /
// pre-receive hook flows as the old-value side of ref-create and the
// new-value side of ref-delete) and never names a real commit in any
// repo's object database. A `:rev "0000000000000000000000000000000000000000"`
// (SHA-1 width) or `:rev "0000…0000"` (SHA-256 width) is the canonical
// "I copy-pasted the no-such-commit sentinel out of `git
// update-ref --stdin` docs / pre-receive hook example" footgun: it's
// shape-valid hex of canonical width but resolves to nothing at
// `git fetch <remote> 0000…0000` time and surfaces as a fetch failure
// far from the source caixa.lisp, with the lacre's
// content-addressing probe locked to a non-resolvable `git:0000…0000`
// closure that never equals any upstream's actual `HEAD`. Rejecting
// at the predicate keeps the `:rev` slot's accepted set aligned with
// its documented reproducibility contract — "exactly one immutable
// commit, forever" — by structurally refusing the only OID-shaped
// value the contract cannot uphold (no commit means no immutable
// resolution). Same pre-emption shape `is_git_ref_name`'s canonical-
// OID-shape pre-emption arm (caixa-core/src/render.rs:1322) carries
// — both predicates carry one self-aware arm that catches values
// structurally valid for the alphabet but operationally meaningless
// on the typed axis.
if s.bytes().all(|b| b == b'0') {
return Err(format!(
"must not be the all-zero null-OID sentinel ({len} `0` \
characters — git's canonical `no-such-commit` value used by \
`git update-ref` / pre-receive hook flows to indicate ref \
create/delete; no commit in any object database has this OID, \
so the resolver's `git fetch <remote> 0000…0000` would fail \
far from the source caixa.lisp and the lacre would lock to a \
`git:0000…0000` closure that never equals any upstream's \
actual `HEAD`. The `:rev` slot's reproducibility contract \
requires a *real* commit OID — the canonical authoring shape \
is the lowercase-hex value `git rev-parse HEAD` emits for an \
actual commit, like `\"c99fdb36abc7d3e1f4a5b6789012345678901234\"`)"
));
}
Ok(())
}
/// `:fonte (:tipo git :repo …)` value max length, in bytes — a generous
/// URL-shaped cap covering every documented author surface (the
/// `github:org/repo` shorthand, the `https://` / `ssh://` / `git://` /
/// `file://` URL schemes, the `git@host:path` scp-style SSH form). The
/// cap mirrors the conservative ceiling typical HTTP gateways and git
/// porcelain entries enforce on URL inputs (the OWASP-recommended URL
/// max of 2048 bytes); a `:repo` value above this bound is structurally
/// untenable on every realistic landing site — the caixa-resolver's
/// `git clone <repo>` invocation, the future M4
/// `mesh.pleme.io/v1alpha1/Caixa` CR materializer's per-dep `repo:`
/// axis, the future lacre BLAKE3 closure's resolved-repo identity — and
/// a value of that length is almost certainly a paste-from-binary slug
/// or a multi-line blob that landed in the slot.
///
/// Lifted as a typed `pub const` (rather than an inline literal at the
/// [`is_git_repo_url`] call site) so a future axis reaching for the same
/// bound (the future lacre-side resolved-repo gate, the M4 CR
/// materializer's per-dep `repo:` admission webhook) reads from one
/// place. Same shape every other typed bound in this module carries
/// ([`DNS_1123_LABEL_MAX_LEN`], [`GATEWAY_API_HTTP_PATH_MAX_LEN`],
/// [`NATS_SUBJECT_MAX_LEN`], [`WASI_KV_SLOT_MAX_LEN`],
/// [`GIT_REF_NAME_MAX_LEN`]).
pub const GIT_REPO_URL_MAX_LEN: usize = 2048;
/// Predicate: assert that `s` is a value-shape-valid `:fonte (:tipo git
/// :repo …)` value — the canonical shape every typed `:deps :fonte`
/// (and future `:deps-dev :fonte`) git-source carries. The contract —
/// modeled on the intersection of (a) the git porcelain's URL-parser
/// accepted set the caixa-resolver invokes at `git clone <repo>` time,
/// (b) the OWASP URL-shape guidance for author-surface inputs that flow
/// to a CLI subprocess, and (c) the typed slot's documented accepted
/// shapes ([`crate::DepSource::Git`] doc comment: `github:org/repo`
/// shorthand, `https://…` / `ssh://…` / `git://…` / `file://…` URL
/// schemes, `git@host:path` scp-style SSH):
///
/// - 1..=[`GIT_REPO_URL_MAX_LEN`] (2048) bytes;
/// - must not start with `-` (the canonical CLI-argument-injection
/// footgun — `git clone <repo>` interprets a leading `-` as a CLI
/// flag, so a `:repo "-upload-pack=evil"` value escapes the
/// subprocess argument boundary and runs an attacker-controlled
/// command; the `--` separator workaround does not fix the typed
/// slot's accepted set, the gate rejects the shape upstream);
/// - no whitespace (space, tab) — every documented form is a single
/// token without whitespace; a `:repo "github:p/x "` (trailing
/// space, paste-from-doc) silently passes the empty check and
/// surfaces at `git clone` time with a quoting-confused error far
/// from the source caixa.lisp;
/// - no ASCII control characters (`0x00..=0x1F`, `0x7F`) — the `\r`
/// / `\n` arms are the canonical "the paste-from-multiline-doc
/// spans multiple lines" footgun, and CRLF injection at the URL
/// boundary is a class of subprocess-arg attack;
/// - no non-ASCII bytes (`>= 0x80`) — IDN hosts must be pre-encoded
/// as Punycode (`xn--…`); raw non-ASCII silently breaks at git's
/// URL parser and may round-trip inconsistently across NFC/NFD
/// normalization on APFS / case-folding filesystems, the same
/// intersection-floor [`is_git_ref_name`] enforces on the peer
/// refname axes;
/// - no `#` URL-fragment-identifier byte (RFC 3986 §3.5) — every
/// documented `:repo` shape (`github:org/repo` shorthand,
/// `https://…` / `ssh://…` / `git://…` / `file://…` URL schemes,
/// `git@host:path` scp-style SSH) carries none; libcurl's URL
/// parser (the layer `git clone <https-url>` invokes) and git's
/// own URL handlers strip the `#fragment` tail before opening
/// the transport, so the byte rides verbatim into the lacre's
/// per-dep content-address (`conteudo: format!("git:{repo}…")`,
/// caixa-resolver/src/resolve.rs) but is silently dropped on the
/// wire — two repos whose values differ only in their fragment
/// anchor (`":repo "https://github.com/foo/bar#readme"` vs
/// `":repo "https://github.com/foo/bar#L42"`) resolve to the
/// byte-identical upstream `git clone` but lock to two distinct
/// BLAKE3 closures, defeating the THEORY.md §V.2 render-
/// determinism contract. The canonical "I copy-pasted the
/// permalink-to-line / anchor-to-README URL out of the browser
/// address bar and forgot to trim the `#`-tail" footgun, and the
/// symmetric "I confused the Nix flake-ref idiom (`github:foo/
/// bar#packageName`) with the bare git `:repo` shape" footgun;
/// `:repo` is a git URL, not a Nix flake reference, so the `#`-
/// suffix is structurally meaningless on this axis;
/// - no `?` URL-query-component byte (RFC 3986 §3.4) — every
/// documented `:repo` shape (`github:org/repo` shorthand,
/// `https://…` / `ssh://…` / `git://…` / `file://…` URL schemes,
/// `git@host:path` scp-style SSH) carries none; GitHub /
/// GitLab / Bitbucket all silently ignore the `?query` tail on
/// a repo URL (the canonical `https://github.com/foo/bar?
/// tab=readme-ov-file` browser-tab deep-link, the `?ref=main`
/// GitHub-tree-URL parameter, the `?utm_source=…` campaign-
/// tracker shape every social-share / newsletter / Slack
/// unfurl appends) and serve the same repo regardless, so the
/// byte rides verbatim into the lacre's per-dep content-
/// address but is silently masked at the wire — two repos
/// whose values differ only in their query tail
/// (`":repo "https://github.com/foo/bar?tab=readme-ov-file"` vs
/// `":repo "https://github.com/foo/bar?utm_source=twitter"`)
/// resolve to the byte-identical upstream `git clone` but lock
/// to two distinct BLAKE3 closures, defeating the THEORY.md
/// §V.2 render-determinism contract on the same axis the `#`
/// fragment arm closes. The Smart-HTTP transport (the layer
/// `git clone <https-url>` uses) appends its own
/// `?service=git-upload-pack` query internally; an
/// author-supplied `?` byte additionally collides with that
/// internal axis at every git porcelain entry-point. The
/// canonical "I copy-pasted the GitHub tree-URL out of the
/// browser address bar and forgot to trim the `?tab=…` /
/// `?ref=…` tail" footgun, peer with the `#` fragment arm on
/// the same paste-from-browser-address-bar trajectory;
/// - no embedded `\` byte (RFC 3986 §3.3 reserves `/` as the path-
/// segment separator; no URL grammar admits `\`) — every
/// documented `:repo` shape (`github:org/repo` shorthand,
/// `https://…` / `ssh://…` / `git://…` / `file://…` URL schemes,
/// `git@host:path` scp-style SSH) uses `/` as the path separator.
/// The canonical Windows-path-confusion footgun: an author
/// pastes `file:///C:\Users\me\repo` from a Windows Explorer
/// address bar / PowerShell `Get-Location` output, or
/// `https://github.com\foo\bar` after a Win32 shell mangled
/// the slashes, or the bare Windows-rooted path `C:\repo` into
/// a slot expecting a `file://` URL. libcurl's URL parser
/// (the layer `git clone <https-url>` invokes) silently
/// translates `\` → `/` on some platforms and refuses it on
/// others — the byte rides verbatim into the lacre's per-dep
/// content-address but is silently rewritten or rejected at
/// the wire, defeating the THEORY.md §V.2 render-determinism
/// contract on the same axis the `#` fragment / `?` query arms
/// close. The peer [`DepError::FonteCaminhoBackslash`] arm
/// (commit 3a4e1d7) closes the same byte on the sibling
/// `:fonte :caminho` path-fonte axis; this arm closes the
/// URL-grammar axis so every byte past `is_git_repo_url`
/// reaches `git clone`'s wire-format intact;
/// - no embedded `{` / `}` byte — RFC 3986 §2 excludes the pair
/// from URL syntax (they sit in the 'delims' / 'unwise' byte
/// set every URL parser is required to refuse or percent-
/// encode), and RFC 6570 reserves the matched pair for URI
/// Template placeholders (the canonical
/// `https://{host}/{org}/{repo}` substitution shape every
/// `OpenAPI` / Swagger / Postman / GitHub Octokit client library
/// / Helm chart-URL fragment carries). The canonical 'I forgot
/// to resolve the template placeholder' footgun: an author
/// pastes `:repo "https://github.com/{org}/{repo}"` from a
/// README quick-start snippet, an `OpenAPI` `servers:` URL, a
/// Helm chart's `home:` template, or the Mustache / Handlebars
/// `{{org}}/{{repo}}` doubled-brace substitution form every
/// CI / `IaC` templating engine emits, expecting the substrate
/// to resolve the placeholder downstream. libcurl percent-
/// encodes `{` / `}` to `%7B` / `%7D` on the wire so the byte
/// round-trips inconsistently between the lacre's per-dep
/// content-address and the resolver's `git clone <repo>`
/// invocation, defeating the THEORY.md §V.2 render-
/// determinism contract on the same axis the `#` fragment /
/// `?` query / `\` backslash arms close; every git porcelain
/// entry-point additionally fetches a nonexistent
/// `{placeholder}`-named path far from the source caixa.lisp;
/// - no embedded `<` / `>` byte — RFC 3986 §2 excludes the pair
/// from URL syntax under the same 'delims' / 'unwise' banner the
/// `{` / `}` arm cites, and no git URL grammar admits either byte:
/// the WHATWG URL spec's 'fragment percent-encode set' maps `<`
/// → `%3C` and `>` → `%3E` so every conformant URL parser
/// refuses or rewrites the literal byte on the wire. Beyond the
/// URL-grammar violation, every POSIX shell lexes `<` as the
/// input-redirection operator and `>` as the output-redirection
/// operator — the canonical paste-from-shell-prompt footgun the
/// peer [`DepError::FonteCaminhoShellRedirection`] arm
/// (commit e457141) closes on the sibling `:fonte :caminho`
/// path-fonte axis. The byte rides verbatim into the lacre's
/// per-dep content-address while libcurl percent-encodes it on
/// the wire — two authors whose `:repo` values differ only in
/// `<`/`>` presence resolve to the byte-identical upstream
/// `git clone` but lock to two distinct BLAKE3 closures,
/// defeating the THEORY.md §V.2 render-determinism contract on
/// the same axis the `#` fragment / `?` query / `\` backslash /
/// `{` / `}` template arms close;
/// - no embedded `` ` `` (backtick) byte — RFC 3986 §2 lists the
/// backtick in the 'delims' / 'unwise' set every URL parser is
/// required to refuse or percent-encode, and no git URL grammar
/// admits the byte: the WHATWG URL spec's 'fragment percent-
/// encode set' maps `` ` `` → `%60` so every conformant URL
/// parser refuses or rewrites the literal byte on the wire.
/// Beyond the URL-grammar violation, every POSIX shell lexes the
/// backtick as the legacy command-substitution operator
/// (`` `<cmd>` `` runs `<cmd>` in a subshell and substitutes its
/// stdout) — the canonical paste-from-shell-prompt RCE-class
/// footgun the peer [`crate::DepError::FonteCaminhoShellCommandSubstitution`]
/// arm (commit c4d62b3) closes on the sibling `:fonte :caminho`
/// path-fonte axis. The byte rides verbatim into the lacre's
/// per-dep content-address while libcurl percent-encodes it on
/// the wire — two authors whose `:repo` values differ only in
/// backtick presence resolve to the byte-identical upstream `git
/// clone` but lock to two distinct BLAKE3 closures, defeating
/// the THEORY.md §V.2 render-determinism contract on the same
/// axis the `#` fragment / `?` query / `\` backslash / `{` / `}`
/// template / `<` / `>` shell-redirection arms close;
/// - must contain a `:` separator at a non-leading position — every
/// documented form carries one (`github:org/repo`, `https://…`,
/// `ssh://…`, `git://…`, `file://…`, `git@host:path`); the
/// bare `org/repo` (no scheme) shape is ambiguous (could be a
/// filesystem path or a missing scheme) and silently passes
/// downstream git porcelain as a local relative path rather than
/// the intended GitHub-shorthand expansion. A leading `:` (`":foo"`)
/// is the canonical "empty scheme" footgun and is rejected too.
///
/// Returns the parser-shaped reason on rejection (without wrapping in
/// any error variant) so each per-axis caller — [`crate::DepError::FonteRepoShape`]
/// at validate time on the `:fonte :repo` axis, the future per-pin gate
/// on `lacre.lisp` resolved-repo axes, the future M4 per-dep CR
/// materializer's per-repo validator — wraps the same reason in its
/// own typed `*Invalid { axis, reason }` variant. The reason wording is
/// axis-agnostic ("git repo URLs reject whitespace") so every call site
/// reading the same diagnostic points at the same rule; drift between
/// any two axes' rule enforcement is a build error visible at this
/// predicate, not a per-resolver "this passed validate but `git clone`
/// rejected" surprise.
///
/// Empty input is rejected here (defensively) and at each call site via
/// the narrower [`crate::DepError::FonteRepoEmpty`] variant — the same
/// empty-first cascade [`is_dns_1123_label`], [`is_gateway_api_http_path`],
/// [`is_wit_world_ref`], [`is_nats_subject`], [`is_wasi_keyvalue_slot`],
/// [`is_git_ref_name`], and [`is_git_oid`] all carry.
///
/// Lifted as the seventh value-shape primitive in this module, peer with
/// [`is_git_ref_name`] (the `:fonte :tag` / `:fonte :branch` refname-
/// shaped axes) and [`is_git_oid`] (the `:fonte :rev` commit-OID axis) —
/// together they bracket the typed `:fonte` slot end-to-end: the
/// `:repo` URL axis (gate here), the refname-pin axes (gate via
/// `is_git_ref_name`), the OID-pin axis (gate via `is_git_oid`). Every
/// validated `:fonte (:tipo git …)` past `DepSource::validate` is
/// guaranteed-acceptable by the caixa-resolver's `git clone`/`git
/// fetch`/`git checkout` invocations, structurally — the parser-of-
/// record divergence the prior trajectory closed on the pin axes is
/// now closed on the last unsealed `:fonte` axis.
///
/// # Errors
///
/// Returns the parser-shaped reason naming the specific violation
/// (length / leading-`-` / whitespace / control-char / non-ASCII /
/// fragment-`#` / query-`?` / backslash-`\` / template-`{`-or-`}` /
/// shell-redirection-`<`-or-`>` / shell-command-substitution-backtick /
/// missing-`:` separator / leading-`:`), without wrapping in any error
/// variant — every caller maps the same `String` into its own typed
/// `*Invalid { axis, reason }` enum variant.
#[allow(
clippy::too_many_lines,
reason = "the per-byte rejection cascade is structurally flat by design — \
every arm carries its own self-locating diagnostic with the offending \
byte named verbatim plus the canonical paste-from-shape footgun the \
gate closes, so collapsing onto a single shared `for &b in …` loop \
would regress the per-arm `feira lint` consumer surface — peer with \
the `clippy::too_many_lines` allow on `DepSource::validate_caminho` \
(caixa-core/src/dep.rs:323) on the same cascade-shape rationale"
)]
pub fn is_git_repo_url(s: &str) -> Result<(), String> {
if s.is_empty() {
return Err("must not be empty".to_string());
}
if s.len() > GIT_REPO_URL_MAX_LEN {
return Err(format!(
"exceeds git repo URL max length of {GIT_REPO_URL_MAX_LEN} bytes \
(got {} bytes; legitimate `github:org/repo` shorthands and \
`https://…` / `ssh://…` / `git://…` / `file://…` URLs rarely \
exceed ~128 bytes — this length suggests a paste-from-binary or \
multi-line blob landed in the `:repo` slot)",
s.len()
));
}
if s.starts_with('-') {
return Err(
"must not start with `-` (the canonical CLI-argument-injection \
footgun — `git clone <repo>` interprets a leading `-` as a CLI \
flag, so a `-upload-pack=…` / `--config=…` value escapes the \
subprocess argument boundary; use a scheme prefix like \
`github:org/repo`, `https://host/path`, `ssh://[user@]host/path`, \
`git://host/path`, `git@host:path`, or `file:///path` for the \
intended source)"
.to_string(),
);
}
for &b in s.as_bytes() {
if b == b' ' || b == b'\t' {
return Err(format!(
"must not contain whitespace character {ch:?} (git repo URLs \
are single tokens with no whitespace — a trailing space in a \
`:repo` value is the canonical paste-from-doc footgun, \
silently breaking `git clone '<value> '` at clone time)",
ch = b as char
));
}
if b < 0x20 || b == 0x7F {
return Err(format!(
"must not contain control character 0x{b:02x} (git repo URLs \
are printable ASCII; `\\r` / `\\n` are the canonical paste-\
from-multiline-doc footgun and break git's URL parser at \
every porcelain entry point, plus CRLF at the URL boundary \
is a class of subprocess-arg injection)"
));
}
if b >= 0x80 {
return Err(format!(
"must not contain non-ASCII byte 0x{b:02x} (IDN hosts must be \
pre-encoded as Punycode `xn--…`; raw non-ASCII silently \
breaks at git's URL parser and round-trips inconsistently \
across NFC/NFD normalization on APFS / case-folding \
filesystems)"
));
}
if b == b'#' {
return Err("must not contain `#` (RFC 3986 §3.5 URL fragment \
identifier; libcurl's URL parser — the layer `git \
clone <https-url>` invokes — strips the `#fragment` \
tail before opening the transport, so the byte rides \
verbatim into the lacre's per-dep content-address but \
is silently dropped on the wire, defeating the \
THEORY.md §V.2 render-determinism contract: two \
authors whose `:repo` values differ only in their \
fragment anchor (`#readme` vs `#L42`) resolve to the \
byte-identical upstream `git clone` but lock to two \
distinct BLAKE3 closures. The canonical \
paste-from-browser-address-bar footgun (every web URL \
to a README section / line-permalink carries one), \
and the canonical \"I confused the Nix flake-ref \
idiom (`github:foo/bar#packageName`) with the bare \
git `:repo` shape\" footgun — `:repo` is a git URL, \
not a Nix flake reference, so the `#`-suffix is \
structurally meaningless on this axis. Drop the \
`#fragment` tail; pin the ref via the typed `:tag` / \
`:branch` / `:rev` slot instead)"
.to_string());
}
if b == b'?' {
return Err("must not contain `?` (RFC 3986 §3.4 URL query \
component; every documented `:fonte :repo` shape \
(`github:org/repo` shorthand, `https://…` / \
`ssh://…` / `git://…` / `file://…` URL schemes, \
`git@host:path` scp-style SSH) carries none. GitHub / \
GitLab / Bitbucket all silently ignore the `?query` \
tail on a repo URL and serve the same repo \
regardless, so the byte rides verbatim into the \
lacre's per-dep content-address but is silently \
masked at the wire — two authors whose `:repo` \
values differ only in their query tail \
(`?tab=readme-ov-file` vs `?utm_source=twitter`) \
resolve to the byte-identical upstream `git clone` \
but lock to two distinct BLAKE3 closures, defeating \
the THEORY.md §V.2 render-determinism contract on \
the same axis the fragment-`#` arm closes. The \
Smart-HTTP transport (the layer \
`git clone <https-url>` uses) additionally appends \
its own `?service=git-upload-pack` query internally; \
an author-supplied `?` byte collides with that \
internal axis at every git porcelain entry-point. \
The canonical paste-from-browser-address-bar \
footgun (`?tab=readme-ov-file` GitHub-tab deep-link, \
`?ref=main` GitHub-tree-URL parameter, \
`?utm_source=…` campaign-tracker every social-share / \
newsletter / Slack-unfurl appends). Drop the \
`?query` tail; pin the ref via the typed `:tag` / \
`:branch` / `:rev` slot instead)"
.to_string());
}
if b == b'\\' {
return Err("must not contain `\\` (RFC 3986 §3.3 reserves \
`/` as the URL path-segment separator; no URL grammar \
admits `\\`. Every documented `:fonte :repo` shape \
(`github:org/repo` shorthand, `https://…` / \
`ssh://…` / `git://…` / `file://…` URL schemes, \
`git@host:path` scp-style SSH) uses `/` as the path \
separator. The canonical Windows-path-confusion \
footgun: an author pastes `file:///C:\\Users\\me\\repo` \
from a Windows Explorer address bar / PowerShell \
`Get-Location` output, `https://github.com\\foo\\bar` \
after a Win32 shell mangled the slashes, or the bare \
Windows-rooted path `C:\\repo` into a slot expecting a \
`file://` URL. libcurl's URL parser (the layer \
`git clone <https-url>` invokes) silently translates \
`\\` to `/` on some platforms and refuses it on others, \
so the byte rides verbatim into the lacre's per-dep \
content-address but is silently rewritten or rejected \
at the wire, defeating the THEORY.md §V.2 render-\
determinism contract on the same axis the fragment-`#` \
and query-`?` arms close. The peer \
`DepError::FonteCaminhoBackslash` arm (commit 3a4e1d7) \
closes the same byte on the sibling `:fonte :caminho` \
path-fonte axis; this arm closes the URL-grammar axis. \
Drop the `\\` — use `/` for URL path separators, or \
author the `file:///C:/path` form with forward slashes \
(the canonical RFC 8089 file-URI shape on Windows-\
rooted paths))"
.to_string());
}
if b == b'{' || b == b'}' {
return Err(format!(
"must not contain `{ch}` (RFC 3986 §2 excludes `{{` / `}}` \
from URL syntax — they sit in the 'delims' / 'unwise' \
byte set every URL parser is required to refuse or \
percent-encode; RFC 6570 reserves the matched pair for \
URI Template placeholders (the canonical \
`https://{{host}}/{{org}}/{{repo}}` substitution shape \
every OpenAPI / Swagger / Postman / GitHub Octokit \
client library / Helm chart-URL fragment carries). The \
canonical 'I forgot to resolve the template \
placeholder' footgun: an author pastes \
`:repo \"https://github.com/{{org}}/{{repo}}\"` from a \
README's quick-start snippet, an OpenAPI spec's \
`servers:` URL, a Helm chart's `home:` template, or \
the Mustache / Handlebars `{{{{org}}}}/{{{{repo}}}}` \
doubled-brace substitution form every CI / IaC \
templating engine emits, expecting the substrate to \
resolve the placeholder downstream. libcurl percent-\
encodes `{{` / `}}` to `%7B` / `%7D` on the wire (so \
the byte round-trips inconsistently between the \
lacre's per-dep content-address and the resolver's \
`git clone <repo>` invocation, defeating the THEORY.md \
§V.2 render-determinism contract on the same axis the \
fragment-`#`, query-`?`, and backslash-`\\` arms close) \
while every git porcelain entry-point fetches a \
nonexistent literal-`{{placeholder}}`-named path far \
from the source caixa.lisp. Resolve the placeholder at \
author time — substitute the literal org / repo name \
(`https://github.com/pleme-io/hello-rio`), or use \
`:fonte (:tipo path :caminho \"<local-path>\")` for a \
local workspace dep)",
ch = b as char
));
}
if b == b'<' || b == b'>' {
return Err(format!(
"must not contain `{ch}` (RFC 3986 §2 excludes `<` / `>` \
from URL syntax — they sit in the 'delims' / 'unwise' \
byte set every URL parser is required to refuse or \
percent-encode, peer with the `{{` / `}}` URI Template \
arm on the same paragraph of the same RFC. No git URL \
grammar admits either byte: the `github:org/repo` \
shorthand carries an alphanumeric / `-` / `_` / `/` \
alphabet, every `https://` / `ssh://` / `git://` / \
`file://` URL scheme percent-encodes `<` to `%3C` and \
`>` to `%3E` on the wire (the WHATWG URL spec's \
'fragment percent-encode set' canonical mapping every \
conformant URL parser applies), and the `git@host:path` \
scp-style SSH shape names a POSIX path component that \
carries no shell-metachar bytes. Beyond the URL-grammar \
violation, every POSIX shell (sh / bash / zsh / dash / \
ksh / fish / nushell) lexes `<` as the input-redirection \
operator and `>` as the output-redirection operator — \
a `:repo \"https://github.com/foo/bar>build.log\"` (the \
canonical 'I pasted a shell pipeline that wrote build \
output and forgot to trim the redirect' footgun) or \
`:repo \"<README.md\"` (the symmetric input-redirection \
paste idiom every doc-quick-start `git clone <…>` line \
footnotes) is the canonical paste-from-shell-prompt \
footgun the typed slot's accepted set must exclude. The \
byte rides verbatim into the lacre's per-dep content-\
address (`conteudo: format!(\"git:{{repo}}\")` peer of \
the path-axis embedding at caixa-resolver/src/resolve.rs:189) \
and into the resolver's `git clone <repo>` \
(caixa-resolver/src/git.rs:21) subprocess invocation, \
where libcurl's URL parser percent-encodes the byte on \
the wire — so two authors whose `:repo` values differ \
only in their `<`/`>` presence (one paste-trimmed the \
redirect tail, the other didn't) resolve to the byte-\
identical upstream `git clone` but lock to two distinct \
BLAKE3 closures, defeating the THEORY.md §V.2 render-\
determinism contract on the same axis the fragment-`#`, \
query-`?`, backslash-`\\`, and template-`{{` / `}}` arms \
close. The peer `:fonte :caminho` axis (e457141) closes \
the same `<` / `>` byte under the shell-redirection \
banner via `DepError::FonteCaminhoShellRedirection`; the \
peer `:entrada :paths` axis closes the same bytes as part \
of `is_gateway_api_http_path`'s eleven-byte RFC-3986-\
reserved set; the peer `:fonte :tag` / `:fonte :branch` \
axes (e70d213) close the same bytes as part of \
`is_git_ref_name`'s shell-metachar-injection cascade. \
The `:repo` URL axis was the last typed git-source \
surface still admitting these two bytes; this arm closes \
the gap so the substrate-wide 'no shell-redirection / \
RFC-3986-unwise byte anywhere in a typed git-source slot' \
invariant is now structurally consistent across every \
git-source-shaped typed surface. Drop the `<` / `>` tail \
— pin the ref via the typed `:tag` / `:branch` / `:rev` \
slot, or use `:fonte (:tipo path :caminho \"<local-path>\")` \
for a local workspace dep)",
ch = b as char
));
}
if b == b'`' {
return Err(
"must not contain `` ` `` (RFC 3986 §2 lists the backtick byte \
in the 'delims' / 'unwise' set every URL parser is required \
to refuse or percent-encode, peer with the `<` / `>` \
shell-redirection arm on the same paragraph of the same RFC. \
No git URL grammar admits the byte: the `github:org/repo` \
shorthand carries an alphanumeric / `-` / `_` / `/` alphabet, \
every `https://` / `ssh://` / `git://` / `file://` URL scheme \
percent-encodes `` ` `` to `%60` on the wire (the WHATWG URL \
spec's 'fragment percent-encode set' canonical mapping every \
conformant URL parser applies), and the `git@host:path` \
scp-style SSH shape names a POSIX path component that \
carries no shell-metachar bytes. Beyond the URL-grammar \
violation, every POSIX shell (sh / bash / zsh / dash / ksh / \
fish) lexes the backtick as the legacy command-substitution \
operator — `` `<cmd>` `` runs `<cmd>` in a subshell and \
substitutes its stdout, the canonical RCE-class injection \
vector when a string lands in a shell context. A `:repo \
\"https://github.com/foo/`whoami`/bar\"` (the canonical \
paste-from-shell-prompt footgun where the author copies a \
backtick-templated URL from a doc / README quick-start \
snippet that expected the substrate to substitute the value \
downstream) or the symmetric `:repo \"`git config user.name`\"` \
(the dynamic-config-substitution paste idiom every \
dev-environment-setup script footnotes) is the canonical \
paste-from-shell-prompt footgun the typed slot's accepted \
set must exclude. The byte rides verbatim into the lacre's \
per-dep content-address (`conteudo: format!(\"git:{repo}\")` \
peer of the path-axis embedding at \
caixa-resolver/src/resolve.rs) and into the resolver's `git \
clone <repo>` (caixa-resolver/src/git.rs) subprocess \
invocation, where libcurl's URL parser percent-encodes the \
byte on the wire — so two authors whose `:repo` values \
differ only in their backtick presence (one paste-trimmed \
the substitution wrapper, the other didn't) resolve to the \
byte-identical upstream `git clone` but lock to two distinct \
BLAKE3 closures, defeating the THEORY.md §V.2 render-\
determinism contract on the same axis the fragment-`#`, \
query-`?`, backslash-`\\`, template-`{` / `}`, and \
shell-redirection-`<` / `>` arms close. The peer `:fonte \
:caminho` axis (c4d62b3) closes the same byte under the \
shell-command-substitution banner via \
`DepError::FonteCaminhoShellCommandSubstitution`; the peer \
`:entrada :paths` axis closes the same byte as part of \
`is_gateway_api_http_path`'s eleven-byte RFC-3986-reserved \
set. Drop the backtick wrapper — substitute the literal \
value at author time, or use `:fonte (:tipo path :caminho \
\"<local-path>\")` for a local workspace dep)"
.to_string(),
);
}
if b == b'|' {
return Err("must not contain `|` (RFC 3986 §2 lists the pipe byte in \
the 'unwise' set every URL parser is required to refuse or \
percent-encode, peer with the `{` / `}` URI Template, \
`<` / `>` shell-redirection, and `` ` `` shell-command-\
substitution arms on the same paragraph of the same RFC. \
No git URL grammar admits the byte: the `github:org/repo` \
shorthand carries an alphanumeric / `-` / `_` / `/` \
alphabet, every `https://` / `ssh://` / `git://` / \
`file://` URL scheme percent-encodes `|` to `%7C` on the \
wire (the WHATWG URL spec's 'fragment percent-encode set' \
canonical mapping every conformant URL parser applies), \
and the `git@host:path` scp-style SSH shape names a POSIX \
path component that carries no shell-metachar bytes. \
Beyond the URL-grammar violation, every POSIX shell (sh / \
bash / zsh / dash / ksh / fish / nushell) lexes `|` as the \
pipe operator — `<cmd1> | <cmd2>` streams cmd1's stdout to \
cmd2's stdin, the canonical command-chaining injection \
vector when a string lands in a shell context. A `:repo \
\"https://github.com/foo/bar|tee build.log\"` (the \
canonical 'I pasted a shell pipeline that tee'd build \
output and forgot to trim the pipe tail' footgun) or \
`:repo \"github:p/x|cat\"` (the symmetric paste-from-\
shell-prompt idiom every quick-start `git clone <…> | …` \
line footnotes) is the canonical paste-from-shell-prompt \
footgun the typed slot's accepted set must exclude. The \
byte rides verbatim into the lacre's per-dep content-\
address (`conteudo: format!(\"git:{repo}\")` peer of the \
path-axis embedding at caixa-resolver/src/resolve.rs) and \
into the resolver's `git clone <repo>` \
(caixa-resolver/src/git.rs) subprocess invocation, where \
libcurl's URL parser percent-encodes the byte on the wire \
— so two authors whose `:repo` values differ only in \
their pipe presence (one paste-trimmed the pipeline tail, \
the other didn't) resolve to the byte-identical upstream \
`git clone` but lock to two distinct BLAKE3 closures, \
defeating the THEORY.md §V.2 render-determinism contract \
on the same axis the fragment-`#`, query-`?`, backslash-\
`\\`, template-`{` / `}`, shell-redirection-`<` / `>`, \
and backtick-`` ` `` arms close. The peer `:fonte \
:caminho` axis (124106f) closes the same byte under the \
shell-pipe banner via `DepError::FonteCaminhoShellPipe`; \
the peer `:entrada :paths` axis closes the same byte as \
part of `is_gateway_api_http_path`'s eleven-byte \
RFC-3986-reserved set; the peer `:fonte :tag` / `:fonte \
:branch` axes close the same byte as part of \
`is_git_ref_name`'s shell-metachar-injection cascade. \
Drop the pipe tail — substitute the literal value at \
author time, or use `:fonte (:tipo path :caminho \
\"<local-path>\")` for a local workspace dep)"
.to_string());
}
if b == b';' {
return Err("must not contain `;` (RFC 3986 §2 lists the semicolon \
byte in the 'sub-delims' / reserved set every URL parser is \
required to percent-encode at the path-segment boundary, peer \
with the `{` / `}` URI Template, `<` / `>` shell-redirection, \
`` ` `` shell-command-substitution, and `|` shell-pipe arms on \
the same paragraph of the same RFC. No git URL grammar admits \
the byte: the `github:org/repo` shorthand carries an \
alphanumeric / `-` / `_` / `/` alphabet, every `https://` / \
`ssh://` / `git://` / `file://` URL scheme percent-encodes `;` \
to `%3B` on the wire (the WHATWG URL spec's 'fragment percent-\
encode set' canonical mapping every conformant URL parser \
applies), and the `git@host:path` scp-style SSH shape names a \
POSIX path component that carries no shell-metachar bytes. \
Beyond the URL-grammar violation, every POSIX shell (sh / \
bash / zsh / dash / ksh / fish / nushell) lexes `;` as the \
sequential-command terminator — `<cmd1>; <cmd2>` fires `<cmd2>` \
regardless of `<cmd1>`'s exit status, the canonical \
command-chaining injection vector when a string lands in a \
shell context. A `:repo \
\"https://github.com/foo/bar; rm -rf build\"` (the canonical \
'I pasted a shell one-liner that chained a cleanup tail after \
the URL and forgot to trim the `; <cmd>` tail' footgun) or \
`:repo \"github:p/x;;y\"` (the symmetric paste-from-POSIX-\
`case`-arm `;;` terminator idiom every shell-snippet footnotes) \
is the canonical paste-from-shell-prompt footgun the typed \
slot's accepted set must exclude. The byte rides verbatim into \
the lacre's per-dep content-address (`conteudo: \
format!(\"git:{repo}\")` peer of the path-axis embedding at \
caixa-resolver/src/resolve.rs) and into the resolver's `git \
clone <repo>` (caixa-resolver/src/git.rs) subprocess \
invocation, where libcurl's URL parser percent-encodes the \
byte on the wire — so two authors whose `:repo` values differ \
only in their semicolon presence (one paste-trimmed the \
sequential-command tail, the other didn't) resolve to the \
byte-identical upstream `git clone` but lock to two distinct \
BLAKE3 closures, defeating the THEORY.md §V.2 render-\
determinism contract on the same axis the fragment-`#`, \
query-`?`, backslash-`\\`, template-`{` / `}`, \
shell-redirection-`<` / `>`, backtick-`` ` ``, and \
shell-pipe-`|` arms close. The peer `:fonte :caminho` axis \
(05c358e) closes the same byte under the shell-command-\
separator banner via `DepError::FonteCaminhoShellSemicolon`; \
the peer `:entrada :paths` axis closes the same byte as part \
of `is_gateway_api_http_path`'s eleven-byte RFC-3986-reserved \
set; the peer `:fonte :tag` / `:fonte :branch` axes close the \
same byte as part of `is_git_ref_name`'s shell-metachar-\
injection cascade. Drop the `;` tail — substitute the literal \
value at author time, or use `:fonte (:tipo path :caminho \
\"<local-path>\")` for a local workspace dep)"
.to_string());
}
if b == b'&' {
return Err("must not contain `&` (RFC 3986 §2 lists the ampersand \
byte in the 'sub-delims' / reserved set every URL parser is \
required to percent-encode at the path-segment boundary, peer \
with the `{` / `}` URI Template, `<` / `>` shell-redirection, \
`` ` `` shell-command-substitution, `|` shell-pipe, and `;` \
shell-command-separator arms on the same paragraph of the same \
RFC. The byte is also the canonical RFC 3986 §3.4 URL query \
`key=value` pair separator (`?a=1&b=2`), but the prior `?` arm \
already excludes any `?query` tail on a `:repo` value — every \
documented `:fonte :repo` shape (`github:org/repo` shorthand, \
`https://…` / `ssh://…` / `git://…` / `file://…` URL schemes, \
`git@host:path` scp-style SSH) carries no query component, so \
the `&` byte cannot appear in a legitimate query position past \
the `?` gate either. Every `https://` / `ssh://` / `git://` / \
`file://` URL scheme percent-encodes `&` to `%26` on the wire \
(the WHATWG URL spec's 'fragment percent-encode set' canonical \
mapping every conformant URL parser applies), and the \
`git@host:path` scp-style SSH shape names a POSIX path \
component that carries no shell-metachar bytes. Beyond the \
URL-grammar violation, every interactive shell (bash / zsh / \
fish / nushell) lexes `&` two ways: single `&` as the \
background-task terminator that detaches the prior command \
into the background and returns control to the prompt \
immediately (the canonical `cmd &` idiom every long-running \
pipeline uses), and double `&&` as the logical-AND list \
operator that fires the next command only if the prior \
command succeeded (the canonical `make && make install` idiom \
every build script carries). A `:repo \
\"https://github.com/foo/bar & sleep 1\"` (the canonical \
'I pasted a `git clone <url> & sleep 1` background-launch \
one-liner and forgot to trim the `& <cmd>` tail' footgun) or \
`:repo \"github:p/x && echo done\"` (the symmetric \
paste-from-shell-prompt `cd path && cmd` build-chain idiom \
every quick-start `git clone <…> && cd <…>` line footnotes) \
is the canonical paste-from-shell-prompt footgun the typed \
slot's accepted set must exclude. The byte rides verbatim \
into the lacre's per-dep content-address (`conteudo: \
format!(\"git:{repo}\")` peer of the path-axis embedding at \
caixa-resolver/src/resolve.rs) and into the resolver's `git \
clone <repo>` (caixa-resolver/src/git.rs) subprocess \
invocation, where libcurl's URL parser percent-encodes the \
byte on the wire — so two authors whose `:repo` values \
differ only in their ampersand presence (one paste-trimmed \
the background-launch tail, the other didn't) resolve to \
the byte-identical upstream `git clone` but lock to two \
distinct BLAKE3 closures, defeating the THEORY.md §V.2 \
render-determinism contract on the same axis the \
fragment-`#`, query-`?`, backslash-`\\`, template-`{` / `}`, \
shell-redirection-`<` / `>`, backtick-`` ` ``, shell-pipe-`|`, \
and shell-command-separator-`;` arms close. The peer `:fonte \
:caminho` axis (e12e4f3) closes the same byte under the \
shell-background / logical-AND banner via \
`DepError::FonteCaminhoShellBackground`; the peer `:entrada \
:paths` axis closes the same byte as part of \
`is_gateway_api_http_path`'s eleven-byte RFC-3986-reserved \
set; the peer `:fonte :tag` / `:fonte :branch` axes close \
the same byte as part of `is_git_ref_name`'s shell-metachar-\
injection cascade. Drop the `&` tail — substitute the literal \
value at author time, or use `:fonte (:tipo path :caminho \
\"<local-path>\")` for a local workspace dep)"
.to_string());
}
if b == b'$' {
return Err("must not contain `$` (RFC 3986 §2 lists the dollar \
byte in the 'sub-delims' / reserved set every URL parser is \
required to percent-encode at the path-segment boundary, peer \
with the `;` shell-command-separator and `&` shell-background \
arms on the same paragraph of the same RFC. No git URL grammar \
admits the byte: the `github:org/repo` shorthand carries an \
alphanumeric / `-` / `_` / `/` alphabet, every `https://` / \
`ssh://` / `git://` / `file://` URL scheme percent-encodes `$` \
to `%24` on the wire (the WHATWG URL spec's 'fragment percent-\
encode set' canonical mapping every conformant URL parser \
applies), and the `git@host:path` scp-style SSH shape names a \
POSIX path component that carries no shell-metachar bytes. \
Beyond the URL-grammar violation, every POSIX shell (sh / \
bash / zsh / dash / ksh / fish / nushell) lexes `$` as the \
variable-expansion / command-substitution operator: `$<name>` \
/ `${{<name>}}` expands a named variable, `$(<cmd>)` runs a \
subshell and substitutes its stdout, and `$((<expr>))` \
evaluates an arithmetic expression — every form is a \
host-layout / environment-state leak when the byte lands in \
a value the resolver passes to a shell-spawned subprocess. A \
`:repo \"https://github.com/$ORG/caixa-teia\"` (the canonical \
'I pasted a shell one-liner that expanded `$ORG` against the \
author's local environment and forgot to substitute the \
literal org name' footgun, identical to the f4efe9c peer arm \
on the sibling `:caminho` axis that closes `\"$HOME/work/…\"` \
/ `\"${{WORKSPACE}}/…\"`) or `:repo \"github:p/$(whoami)/x\"` \
(the symmetric paste-from-shell-prompt command-substitution \
idiom every dev-environment-setup script footnotes) is the \
canonical paste-from-shell-prompt footgun the typed slot's \
accepted set must exclude. The byte rides verbatim into the \
lacre's per-dep content-address (`conteudo: \
format!(\"git:{repo}\")` peer of the path-axis embedding at \
caixa-resolver/src/resolve.rs) and into the resolver's `git \
clone <repo>` (caixa-resolver/src/git.rs) subprocess \
invocation, where libcurl's URL parser percent-encodes the \
byte on the wire — so two authors whose `:repo` values \
differ only in their dollar presence (one substituted the \
literal value at author time, the other didn't) resolve to \
the byte-identical upstream `git clone` but lock to two \
distinct BLAKE3 closures, defeating the THEORY.md §V.2 \
render-determinism contract on the same axis the \
fragment-`#`, query-`?`, backslash-`\\`, template-`{` / `}`, \
shell-redirection-`<` / `>`, backtick-`` ` ``, shell-pipe-`|`, \
shell-command-separator-`;`, and shell-background-`&` arms \
close. Beyond the determinism axis, a value like \
`\"github:$HOME/x\"` is a structural host-layout leak: two \
authors with the same `:repo` slot but different `$HOME` \
/ `$WORKSPACE` / `$PWD` resolve different upstream URLs at \
different times — the lacre, far from being a substrate-wide \
identity, becomes a per-workstation snapshot of the author's \
shell environment. The peer `:fonte :caminho` axis (f4efe9c) \
closes the leading-`$` byte under the shell-variable-\
expansion banner via `DepError::FonteCaminhoVarExpansion`; \
the peer `:entrada :paths` axis closes the same byte as part \
of `is_gateway_api_http_path`'s eleven-byte RFC-3986-reserved \
set; the peer `:fonte :tag` / `:fonte :branch` axes close \
the same byte as part of `is_git_ref_name`'s shell-metachar-\
injection cascade — the `:caminho` axis closes only the \
leading position because absolute / tilde / var arms there \
are leading-byte sentinels, but the `:repo` URL axis closes \
the byte anywhere because every per-byte arm on this surface \
is positional-agnostic (the substitution / leak shapes \
`\"https://$HOST/p/x\"` and `\"github:p/$(whoami)\"` both \
carry the byte mid-string). Drop the `$` — substitute the \
literal value at author time, or use `:fonte (:tipo path \
:caminho \"<local-path>\")` for a local workspace dep)"
.to_string());
}
if b == b'*' {
return Err("must not contain `*` (RFC 3986 §2 lists the asterisk \
byte in the 'sub-delims' / reserved set every URL parser is \
required to percent-encode at the path-segment boundary, peer \
with the `;` shell-command-separator, `&` shell-background, \
and `$` shell-variable-expansion arms on the same paragraph of \
the same RFC. No git URL grammar admits the byte: the \
`github:org/repo` shorthand carries an alphanumeric / `-` / \
`_` / `/` alphabet, every `https://` / `ssh://` / `git://` / \
`file://` URL scheme percent-encodes `*` to `%2A` on the wire \
(the WHATWG URL spec's 'special-query percent-encode set' \
canonical mapping every conformant URL parser applies), and \
the `git@host:path` scp-style SSH shape names a POSIX path \
component that carries no shell-metachar bytes. Beyond the \
URL-grammar violation, every POSIX shell (sh / bash / zsh / \
dash / ksh / fish / nushell) lexes `*` as the \
pathname-expansion / glob wildcard operator: a single `*` \
matches any sequence of characters in a path component \
(including the empty sequence), `**` matches across `/` \
boundaries under bash's `globstar` shopt, and `foo*` resolves \
against the cwd-relative filesystem at command-substitution \
time. Beyond shell glob semantics, git itself lexes `*` as \
the refspec wildcard operator (`refs/heads/*:refs/remotes/\
origin/*` — the same byte the peer `is_git_ref_name` \
predicate refuses on `:fonte :tag` / `:fonte :branch`), so a \
`:repo` value carrying `*` is structurally ambiguous with \
every refspec parser the resolver invokes downstream. A \
`:repo \"https://github.com/pleme-io/caixa-*\"` (the canonical \
'I pasted a `ls github.com/pleme-io/caixa-*` shell-listing \
tail and forgot to substitute the literal repo name' \
footgun, identical to the cf9034b peer arm on the sibling \
`:caminho` axis that closes `\"../caixa-teia/*\"`) or `:repo \
\"github:p/*\"` (the symmetric paste-from-shell-prompt \
glob-expansion idiom every quick-listing one-liner footnotes) \
is the canonical paste-from-shell-prompt footgun the typed \
slot's accepted set must exclude. The byte rides verbatim \
into the lacre's per-dep content-address (`conteudo: \
format!(\"git:{repo}\")` peer of the path-axis embedding at \
caixa-resolver/src/resolve.rs) and into the resolver's `git \
clone <repo>` (caixa-resolver/src/git.rs) subprocess \
invocation, where libcurl's URL parser percent-encodes the \
byte on the wire — so two authors whose `:repo` values \
differ only in their asterisk presence (one substituted the \
literal repo name at author time, the other didn't) resolve \
to the byte-identical upstream `git clone` but lock to two \
distinct BLAKE3 closures, defeating the THEORY.md §V.2 \
render-determinism contract on the same axis the \
fragment-`#`, query-`?`, backslash-`\\`, template-`{` / `}`, \
shell-redirection-`<` / `>`, backtick-`` ` ``, shell-pipe-`|`, \
shell-command-separator-`;`, shell-background-`&`, and \
shell-variable-expansion-`$` arms close. The peer `:fonte \
:caminho` axis (cf9034b) closes the same byte under the \
shell-glob / pathname-expansion banner via \
`DepError::FonteCaminhoShellGlob`; the peer `:fonte :tag` / \
`:fonte :branch` axes close the same byte as part of \
`is_git_ref_name`'s refspec-wildcard cascade. Drop the `*` — \
substitute the literal repo name at author time, or use \
`:fonte (:tipo path :caminho \"<local-path>\")` for a local \
workspace dep)"
.to_string());
}
if b == b'(' || b == b')' {
return Err(format!(
"must not contain `{ch}` (RFC 3986 §2 excludes `(` / `)` \
from URL syntax — they sit in the 'sub-delims' / reserved \
byte set every URL parser is required to percent-encode at \
the path-segment boundary, peer with the `;` \
shell-command-separator, `&` shell-background, `$` \
shell-variable-expansion, and `*` shell-glob arms on the \
same paragraph of the same RFC. No git URL grammar admits \
either byte: the `github:org/repo` shorthand carries an \
alphanumeric / `-` / `_` / `/` alphabet, every `https://` / \
`ssh://` / `git://` / `file://` URL scheme percent-encodes \
`(` to `%28` and `)` to `%29` on the wire (the WHATWG URL \
spec's 'special-query percent-encode set' canonical mapping \
every conformant URL parser applies), and the \
`git@host:path` scp-style SSH shape names a POSIX path \
component that carries no shell-metachar bytes. Beyond the \
URL-grammar violation, every POSIX shell (sh / bash / zsh / \
dash / ksh / fish / nushell) lexes `(` / `)` as the \
subshell-grouping operator: `(<cmd>)` runs `<cmd>` in a \
child shell with a fresh environment scope (the canonical \
idiom for sandboxing a `cd` or variable assignment), and \
`$(<cmd>)` is the modern Bourne command-substitution shape \
the prior `$` arm closes the leading byte of — the closing \
`)` byte completes that substitution shape and must be \
refused on the same axis. The byte pair is additionally the \
canonical regex-alternation grouping operator (`(foo|bar)`) \
every doc / README quick-start snippet folds into a paste-\
from-doc footgun shape, and the bash brace-expansion \
alternation form (`{{foo,bar}}`) the prior `{{` / `}}` URI \
Template arm closes on the curly-brace axis routes the \
same alternation intent through the parenthesis axis on \
every POSIX-portable script. A `:repo \
\"https://github.com/(foo|bar)/repo\"` (the canonical 'I \
pasted a regex-alternation form from a doc / README and \
forgot to substitute one literal org' footgun) or `:repo \
\"github:p/x(date)\"` (the symmetric paste-from-shell-\
prompt subshell-grouping idiom every dynamic-config-\
substitution one-liner footnotes) is the canonical paste-\
from-shell-prompt footgun the typed slot's accepted set \
must exclude. The byte rides verbatim into the lacre's \
per-dep content-address (`conteudo: \
format!(\"git:{{repo}}\")` peer of the path-axis embedding \
at caixa-resolver/src/resolve.rs) and into the resolver's \
`git clone <repo>` (caixa-resolver/src/git.rs) subprocess \
invocation, where libcurl's URL parser percent-encodes the \
byte on the wire — so two authors whose `:repo` values \
differ only in their parenthesis presence (one paste-\
trimmed the grouping wrapper, the other didn't) resolve to \
the byte-identical upstream `git clone` but lock to two \
distinct BLAKE3 closures, defeating the THEORY.md §V.2 \
render-determinism contract on the same axis the \
fragment-`#`, query-`?`, backslash-`\\`, template-`{{` / \
`}}`, shell-redirection-`<` / `>`, backtick-`` ` ``, \
shell-pipe-`|`, shell-command-separator-`;`, shell-\
background-`&`, shell-variable-expansion-`$`, and shell-\
glob-`*` arms close. Drop the `(` / `)` wrapper — \
substitute the literal value at author time, or use \
`:fonte (:tipo path :caminho \"<local-path>\")` for a local \
workspace dep)",
ch = b as char
));
}
if b == b'"' {
return Err("must not contain `\"` (RFC 3986 §2 lists the \
double-quote byte in the 'delims' set every URL parser is \
required to refuse or percent-encode, peer with the `<` / \
`>` shell-redirection and `` ` `` shell-command-substitution \
arms on the same paragraph of the same RFC — the four-byte \
'delims' subset (`<`, `>`, `\"`, `` ` ``) is the strictest \
of the §2 reserved classes, every member structurally \
incompatible with every URL grammar at every position. No \
git URL grammar admits the byte: the `github:org/repo` \
shorthand carries an alphanumeric / `-` / `_` / `/` \
alphabet, every `https://` / `ssh://` / `git://` / \
`file://` URL scheme percent-encodes `\"` to `%22` on the \
wire (the WHATWG URL spec's 'C0 control percent-encode \
set' canonical mapping every conformant URL parser \
applies), and the `git@host:path` scp-style SSH shape \
names a POSIX path component that carries no \
shell-metachar bytes. Beyond the URL-grammar violation, \
every POSIX shell (sh / bash / zsh / dash / ksh / fish / \
nushell) lexes `\"` as the double-quote string delimiter — \
a `\"<text>\"` form suppresses word-splitting and \
pathname-expansion on `<text>` while still expanding `$`, \
`` ` ``, and `\\` substitutions inside, the canonical \
'quote the URL so the shell doesn't re-lex the bytes' \
idiom every doc / README quick-start snippet wraps the \
URL argument with. A `:repo \
\"\\\"https://github.com/pleme-io/caixa-teia\\\"\"` (the \
canonical paste-from-doc footgun where the author copies \
`$ git clone \"https://…\"` from a README's quick-start \
snippet and keeps the surrounding double-quote bytes — \
the doc quotes the URL so the shell doesn't re-lex \
metachars inside, but the typed slot is itself a \
byte-level string parser, not a shell context, so the \
quote bytes ride into the value verbatim) or `:repo \
\"github:p/x\\\"tail\"` (the symmetric stray-quote paste \
idiom every shell-history `git clone …` line footnotes) \
is the canonical paste-from-shell-quoting footgun the \
typed slot's accepted set must exclude. The byte rides \
verbatim into the lacre's per-dep content-address \
(`conteudo: format!(\"git:{repo}\")` peer of the path-\
axis embedding at caixa-resolver/src/resolve.rs) and into \
the resolver's `git clone <repo>` \
(caixa-resolver/src/git.rs) subprocess invocation, where \
libcurl's URL parser percent-encodes the byte on the wire \
— so two authors whose `:repo` values differ only in \
their double-quote presence (one paste-trimmed the quote \
wrapper, the other didn't) resolve to the byte-identical \
upstream `git clone` but lock to two distinct BLAKE3 \
closures, defeating the THEORY.md §V.2 render-determinism \
contract on the same axis the fragment-`#`, query-`?`, \
backslash-`\\`, template-`{` / `}`, shell-redirection-\
`<` / `>`, backtick-`` ` ``, shell-pipe-`|`, \
shell-command-separator-`;`, shell-background-`&`, \
shell-variable-expansion-`$`, shell-glob-`*`, and \
shell-subshell-grouping-`(` / `)` arms close. The peer \
`:entrada :paths` axis closes the same byte as part of \
`is_gateway_api_http_path`'s RFC-3986-reserved set; the \
`:fonte :tag` / `:fonte :branch` axes close the same byte \
as part of `is_git_ref_name`'s shell-metachar-injection \
cascade. Drop the `\"` wrapper — paste only the URL \
between the quotes, or use `:fonte (:tipo path :caminho \
\"<local-path>\")` for a local workspace dep)"
.to_string());
}
if b == b'\'' {
return Err("must not contain `'` (RFC 3986 §2.2 lists the \
single-quote byte in the 'sub-delims' set the URL grammar \
admits inside a path segment but every WHATWG-conformant \
special-scheme URL parser percent-encodes inside a query \
component via the 'special-query percent-encode set' — \
the peer position the prior `*` / `(` / `)` 'sub-delims' \
arms close and the partner ASCII string-delimiter to the \
`\"` 'delims' double-quote byte the prior arm closes. The \
byte is the second ASCII shell-string-delimiter — `\"` \
and `'` are the only two ASCII bytes a byte-level string \
parser sharing a value-shape with a shell argument must \
refuse on a URL-shaped slot for paste-from-doc safety. No \
documented `:fonte :repo` shape admits the byte: the \
`github:org/repo` shorthand carries an alphanumeric / `-` \
/ `_` / `/` alphabet, every `https://` / `ssh://` / \
`git://` / `file://` URL scheme keeps host / path bodies \
inside the `unreserved` alphanumeric / `-` / `.` / `_` / \
`~` set that excludes the byte, and the `git@host:path` \
scp-style SSH shape names a POSIX path component that \
carries no shell-metachar bytes. Every POSIX shell (sh / \
bash / zsh / dash / ksh / fish / nushell) lexes `'` as \
the single-quote / strong-quote string delimiter — a \
`'<text>'` form suppresses every form of expansion on \
`<text>` (no `$`, no `` ` ``, no `\\`, no glob, no \
word-splitting), the canonical 'strong-quote the URL so \
the shell doesn't re-lex anything inside' idiom every \
doc / README quick-start snippet wraps the URL argument \
with as the stricter, security-conscious alternative to \
the `\"…\"` weak-quote shape the prior arm closes. A \
`:repo \"'https://github.com/pleme-io/caixa-teia'\"` (the \
canonical paste-from-doc-shell-quoting footgun where the \
author copies `$ git clone 'https://…'` from a README's \
quick-start snippet and keeps the surrounding strong-\
quote bytes — the doc strong-quotes the URL so the shell \
doesn't re-lex any metachars inside, but the typed slot \
is itself a byte-level string parser, not a shell \
context, so the quote bytes ride into the value verbatim; \
the strong-quote idiom is more common than `\"…\"` in \
security-conscious docs because it forecloses every \
expansion the weak-quote form still admits inside) or \
`:repo \"github:p/x'tail\"` (the symmetric stray-quote \
paste idiom every shell-history `git clone …` line \
carries when the author paste-trimmed one boundary but \
not the other) is the canonical paste-from-shell-quoting \
footgun the typed slot's accepted set must exclude. The \
byte additionally carries the canonical English-\
typography apostrophe footgun: an author writes `:repo \
\"github:p/repo's-fork\"` (the possessive-form paste-\
from-prose idiom every README / commit-message / chat-\
thread reference to a repo carries) expecting the \
substrate to coerce it to a kebab-case slug; the byte \
rides verbatim into the lacre's per-dep content-address \
(`conteudo: format!(\"git:{repo}\")` peer of the path-\
axis embedding at caixa-resolver/src/resolve.rs) and \
into the resolver's `git clone <repo>` (caixa-resolver/\
src/git.rs) subprocess invocation, where the upstream \
host's git porcelain fetches a literal apostrophe-bearing \
path that no host's repo registry resolves (GitHub / \
GitLab / Bitbucket / Codeberg / sourcehut all reject `'` \
in repo slugs at admission time) — so the lacre locks \
to a `git:github:p/repo's-fork` closure that never \
resolves at clone time, surfacing as a quoting-confused \
'remote ref not found' porcelain error far from the \
source caixa.lisp, defeating the THEORY.md §V.2 render-\
determinism contract on the same axis the fragment-`#`, \
query-`?`, backslash-`\\`, template-`{` / `}`, \
shell-redirection-`<` / `>`, backtick-`` ` ``, \
shell-pipe-`|`, shell-command-separator-`;`, shell-\
background-`&`, shell-variable-expansion-`$`, shell-\
glob-`*`, shell-subshell-grouping-`(` / `)`, and shell-\
double-quote-`\"` arms close. Together with the prior \
`\"` arm, this arm closes both ASCII shell-string-\
delimiter bytes on the typed `:repo` URL axis — every \
byte the canonical `git clone <repo>` doc-paste idiom \
wraps the URL argument with is now refused at validate \
time, before the byte rides into the lacre or the \
resolver subprocess. Drop the `'` wrapper — paste only \
the URL between the quotes, or use `:fonte (:tipo path \
:caminho \"<local-path>\")` for a local workspace dep)"
.to_string());
}
if b == b'!' {
return Err("must not contain `!` (RFC 3986 §2.2 lists the bang byte \
in the 'sub-delims' set the URL grammar admits inside a \
path segment but every WHATWG-conformant special-scheme \
URL parser percent-encodes inside a query component via \
the 'special-query percent-encode set' — the peer position \
the prior `*` / `(` / `)` / `'` 'sub-delims' arms close. \
No documented `:fonte :repo` shape admits the byte: the \
`github:org/repo` shorthand carries an alphanumeric / `-` \
/ `_` / `/` alphabet, every `https://` / `ssh://` / \
`git://` / `file://` URL scheme keeps host / path bodies \
inside the RFC 3986 `unreserved` alphanumeric / `-` / \
`.` / `_` / `~` set that excludes the byte, and the \
`git@host:path` scp-style SSH shape names a POSIX path \
component that carries no shell-metachar bytes. Beyond \
the URL-grammar question, every interactive POSIX shell \
with history enabled (bash / ksh / zsh's `bashcompat` \
mode / csh / tcsh) lexes `!` as the history-expansion \
prefix — `!command` re-runs the most recent history \
entry beginning with `command`, `!!` re-runs the prior \
command verbatim, `!$` substitutes the last word of the \
prior command, `!:N` substitutes the Nth word, the \
canonical RCE-class injection vector when a string lands \
in a shell context with `set -o histexpand` (bash's \
default for interactive sessions). A `:repo \
\"https://github.com/foo/bar!sudo\"` (the canonical \
paste-from-shell-history footgun where the author copies \
a `git clone <url>!sudo make install` one-liner from a \
README's quick-start snippet, intending the trailing \
`!sudo` as a shell-history reference but the typed slot \
is itself a byte-level string parser, not a shell \
context, so the bytes ride into the value verbatim) or \
`:repo \"github:p/repo!!\"` (the symmetric `!!` repeat-\
prior-command paste idiom every shell-history `git \
clone …` retry line carries) is the canonical paste-\
from-shell-history footgun the typed slot's accepted \
set must exclude. Beyond shell-history, the bang byte \
carries the canonical English-typography emphasis \
footgun: an author writes `:repo \
\"github:p/awesome-repo!\"` (the exclamation-form paste-\
from-prose idiom every README / chat-thread / commit-\
message reference to an enthusiastically-named repo \
carries) expecting the substrate to coerce it to a \
kebab-case slug; the byte rides verbatim into the \
lacre's per-dep content-address (`conteudo: \
format!(\"git:{repo}\")` peer of the path-axis \
embedding at caixa-resolver/src/resolve.rs) and into \
the resolver's `git clone <repo>` (caixa-resolver/\
src/git.rs) subprocess invocation, where the upstream \
host's git porcelain fetches a literal bang-bearing \
path that no host's repo registry resolves (GitHub / \
GitLab / Bitbucket / Codeberg / sourcehut all reject \
`!` in repo slugs at admission time) — so the lacre \
locks to a `git:github:p/awesome-repo!` closure that \
never resolves at clone time, surfacing as a 'remote \
ref not found' porcelain error far from the source \
caixa.lisp, defeating the THEORY.md §V.2 render-\
determinism contract on the same axis the fragment-\
`#`, query-`?`, backslash-`\\`, template-`{` / `}`, \
shell-redirection-`<` / `>`, backtick-`` ` ``, shell-\
pipe-`|`, shell-command-separator-`;`, shell-\
background-`&`, shell-variable-expansion-`$`, shell-\
glob-`*`, shell-subshell-grouping-`(` / `)`, shell-\
double-quote-`\"`, and shell-single-quote-`'` arms \
close. The peer `:fonte :tag` / `:fonte :branch` axes \
(`is_git_ref_name`) deliberately admit `!` (git's \
`check-ref-format` accepts it as a printable byte and \
the bang carries no refname-grammar meaning); the \
`:entrada :paths` axis (`is_gateway_api_http_path`) \
similarly admits it (K8s Gateway API HTTPPathMatch.value \
OpenAPI regex accepts it). `:repo` is substrate-\
internal and strictly narrower than its upstream \
grammar by design, so the divergence is intentional: \
the shell-history-expansion footgun is real on the \
typed `:fonte :repo` axis (every `git clone <url>` \
invocation crosses a shell boundary at the caixa-\
resolver / `Command::new(\"git\")` subprocess layer) \
in a way it isn't on the refname / HTTP-path axes that \
never reach shell context. Drop the trailing `!` — \
author the bare alphanumeric / `-` / `_` slug, or use \
`:fonte (:tipo path :caminho \"<local-path>\")` for a \
local workspace dep)"
.to_string());
}
if b == b',' {
return Err("must not contain `,` (RFC 3986 §2.2 lists the comma byte \
in the 'sub-delims' set the URL grammar admits inside a \
path segment but every WHATWG-conformant special-scheme \
URL parser percent-encodes it inside both the path and \
query percent-encode sets — the peer position the prior \
`!` / `*` / `(` / `)` / `'` 'sub-delims' arms close. No \
documented `:fonte :repo` shape admits the byte: the \
`github:org/repo` shorthand carries an alphanumeric / `-` \
/ `_` / `/` alphabet, every `https://` / `ssh://` / \
`git://` / `file://` URL scheme keeps host / path bodies \
inside the RFC 3986 `unreserved` alphanumeric / `-` / \
`.` / `_` / `~` set that excludes the byte, and the \
`git@host:path` scp-style SSH shape names a POSIX path \
component that carries no list-separator bytes (every \
forge — GitHub / GitLab / Bitbucket / Codeberg / \
sourcehut — refuses `,` in repo slugs at admission time). \
Beyond the URL-grammar question, the comma byte carries \
the canonical list-separator-belongs-to-list-grammar \
footgun across every parser-of-record `:fonte :repo` \
lands in: an author copies a `git clone <urlA>, <urlB>` \
paste-from-CSV-list one-liner from a multi-repo \
bootstrap doc (the canonical `git clone --recurse-\
submodules <a>, <b>, <c>` README-quickstart idiom every \
mono-repo carries) or pastes a JSON-array literal `[\"a\", \
\"b\", \"c\"]` from a tooling-config snippet stripped \
of its brackets, intending the comma to separate \
multiple repo entries but the typed `:repo` slot names \
*one* repo (the list-separator belongs to the list \
grammar of the enclosing `:deps` slot, not to the \
individual `:repo` value). A `:repo \
\"github:p/a,github:p/b\"` silently passed every prior \
arm and rode into the lacre's per-dep content-address \
(`conteudo: format!(\"git:{repo}\")` peer of the path-\
axis embedding at caixa-resolver/src/resolve.rs) and \
into the resolver's `git clone <repo>` (caixa-\
resolver/src/git.rs) subprocess invocation, where the \
upstream host's git porcelain fetched a literal comma-\
bearing path that no host's repo registry resolves — \
so the lacre locks to a `git:github:p/a,github:p/b` \
closure that never resolves at clone time, surfacing as \
a 'remote ref not found' porcelain error far from the \
source caixa.lisp, defeating the THEORY.md §V.2 render-\
determinism contract on the same axis the fragment-\
`#`, query-`?`, backslash-`\\`, template-`{` / `}`, \
shell-redirection-`<` / `>`, backtick-`` ` ``, shell-\
pipe-`|`, shell-command-separator-`;`, shell-\
background-`&`, shell-variable-expansion-`$`, shell-\
glob-`*`, shell-subshell-grouping-`(` / `)`, shell-\
double-quote-`\"`, shell-single-quote-`'`, and shell-\
history-`!` arms close. Beyond the multi-repo paste, \
the byte carries the canonical English-typography \
trailing-`,` paste-from-prose footgun: an author writes \
`:repo \"github:pleme-io/caixa-feira,\"` (the trailing \
comma every README-prose list-of-projects sentence \
carries, mistakenly retained when the slug is pasted \
mid-sentence) expecting the substrate to coerce it to \
a kebab-case slug; the byte rides verbatim. The peer \
`:fonte :tag` / `:fonte :branch` axes \
(`is_git_ref_name`) deliberately admit `,` (git's \
`check-ref-format` accepts it as a printable byte and \
the comma carries no refname-grammar meaning); the \
`:entrada :paths` axis (`is_gateway_api_http_path`) \
similarly admits it (K8s Gateway API HTTPPathMatch.value \
OpenAPI regex accepts it). `:repo` is substrate-\
internal and strictly narrower than its upstream \
grammar by design, so the divergence is intentional: \
the list-separator-belongs-to-list-grammar footgun is \
real on the typed `:fonte :repo` axis (every `:deps` \
entry names exactly one repo and the comma between \
entries belongs to the `:deps` list grammar, never to \
the value) in a way it isn't on the refname / HTTP-\
path axes whose grammars admit the byte without \
confusion. Drop the trailing `,` — author the bare \
alphanumeric / `-` / `_` slug, or split into multiple \
`:deps` entries to express multiple repos)"
.to_string());
}
if b == b'=' {
return Err("must not contain `=` (RFC 3986 §2.2 lists the equals byte \
in the 'sub-delims' set — the URL grammar admits the byte \
inside a path segment, but every WHATWG-conformant special-\
scheme URL parser percent-encodes it inside a query \
component via the 'special-query percent-encode set' (the \
same set the prior `,` / `!` / `*` / `(` / `)` / `'` sub-\
delims arms close on, peer with the immediately prior `,` \
arm on the same paragraph of the same RFC). No documented \
`:fonte :repo` shape admits the byte: the `github:org/repo` \
shorthand carries an alphanumeric / `-` / `_` / `/` \
alphabet, every `https://` / `ssh://` / `git://` / \
`file://` URL scheme keeps host / path bodies inside the \
RFC 3986 `unreserved` alphanumeric / `-` / `.` / `_` / `~` \
set that excludes the byte, and the `git@host:path` scp-\
style SSH shape names a POSIX path component that carries \
no key-value-separator bytes (every forge — GitHub / \
GitLab / Bitbucket / Codeberg / sourcehut — refuses `=` in \
repo slugs at admission time). Beyond the URL-grammar \
question, the equals byte carries three canonical paste-\
from-doc footguns the typed `:repo` slot's accepted set \
must exclude. First, the URL-query key-value-separator \
paste: an author copies `https://github.com/p/x?ref=main` \
from a browser address bar / GitHub-tree-URL deep-link / \
`?utm_source=…` campaign-tracker query string; the prior \
`?` arm (a68f818) closes the query-prefix byte but every \
paste-from-doc snippet that lost its `?` prefix (a copy-\
paste that started mid-query, a shell-pipeline that \
stripped the leading `?` via `cut -d?`, a docs example \
that documented the bare `key=value` pairs without the \
leading `?`) lands a `:repo \"github:p/x ref=main\"` whose \
`=` byte is now the load-bearing footgun. Second, the \
shell env-var-assignment paste: every POSIX shell (sh / \
bash / zsh / dash / ksh / fish) lexes `KEY=VALUE` at the \
start of a command line as a one-shot env-var assignment \
scoped to that command (`GIT_TERMINAL_PROMPT=0 git clone \
<url>` runs `git clone` with the prompt suppressed, \
`GIT_SSL_NO_VERIFY=1 git clone <url>` skips TLS \
verification, `HTTPS_PROXY=… git clone <url>` overrides \
the proxy) — the canonical paste-from-shell-history idiom \
every git-troubleshooting README documents. An author \
copies `:repo \"GIT_TERMINAL_PROMPT=0 https://github.com/\
p/x\"` from a shell-prompt one-liner and the env-var \
prefix rides verbatim into the value, defeating the \
substrate's typed `:repo` axis (the env-var prefix \
belongs to the shell context, not to the URL). Third, the \
git-CLI-flag paste: every `git` porcelain entry-point \
accepts `--config <key>=<value>` (`git -c \
protocol.file.allow=always clone …`, `git -c \
http.extraHeader=…`) and `git config --get <key>` outputs \
`<key>=<value>`-shaped lines; an author copies \
`url=https://github.com/p/x` from `git config --get-all \
remote.origin.url` output or a `.gitconfig` `[remote \
\"origin\"] url = https://…` ini-stanza paste and the \
`url=` prefix rides verbatim into the typed `:repo` slot \
(the ini-key-prefix belongs to the gitconfig grammar, not \
to the URL value). A `:repo \"GIT_TERMINAL_PROMPT=0 \
https://github.com/p/x\"` or `:repo \"url=https://github.\
com/p/x\"` silently passed every prior arm; the byte rode \
into the lacre's per-dep content-address (`conteudo: \
format!(\"git:{repo}\")` peer of the path-axis embedding \
at caixa-resolver/src/resolve.rs) and into the resolver's \
`git clone <repo>` (caixa-resolver/src/git.rs) subprocess \
invocation, where libcurl's URL parser percent-encodes \
the byte to `%3D` on the wire — so two authors whose \
`:repo` values differ only in their `=` presence resolve \
to the byte-identical upstream `git clone` but lock to \
two distinct BLAKE3 closures, defeating the THEORY.md \
§V.2 render-determinism contract on the same axis the \
fragment-`#`, query-`?`, backslash-`\\`, template-`{` / \
`}`, shell-redirection-`<` / `>`, backtick-`` ` ``, shell-\
pipe-`|`, shell-command-separator-`;`, shell-background-\
`&`, shell-variable-expansion-`$`, shell-glob-`*`, shell-\
subshell-grouping-`(` / `)`, shell-double-quote-`\"`, \
shell-single-quote-`'`, shell-history-`!`, and list-\
separator-`,` arms close. The peer `:fonte :tag` / \
`:fonte :branch` axes (`is_git_ref_name`) deliberately \
admit `=` (git's `check-ref-format` accepts it as a \
printable byte and the equals carries no refname-grammar \
meaning); the `:entrada :paths` axis \
(`is_gateway_api_http_path`) similarly admits it (K8s \
Gateway API HTTPPathMatch.value OpenAPI regex accepts \
it). `:repo` is substrate-internal and strictly narrower \
than its upstream grammar by design, so the divergence is \
intentional: the URL-query / shell-env-var-assignment / \
git-config-ini key-value-separator footgun is real on the \
typed `:fonte :repo` axis (every `git clone <repo>` \
invocation crosses a shell boundary at the caixa-\
resolver subprocess layer, and the lacre's per-dep \
content-address must be byte-identical to the wire form) \
in a way it isn't on the refname / HTTP-path axes whose \
grammars admit the byte without confusion. Drop the `=` — \
strip the env-var / config-key prefix from the value \
before the URL, or author the bare alphanumeric / `-` / \
`_` slug)"
.to_string());
}
if b == b'%' {
return Err(
"must not contain `%` (RFC 3986 §2.1 reserves the percent byte \
as the URL percent-encoding escape — `%HH` is the \
mandatory encoding mechanism for every byte outside the \
`unreserved` alphanumeric / `-` / `.` / `_` / `~` set, \
and `%` itself must be percent-encoded as `%25` to appear \
literally inside a URL value, peer with the immediately \
prior `=` / `,` / `!` / `*` / `(` / `)` / `'` 'sub-delims' \
arms on the same RFC. No documented `:fonte :repo` shape \
admits the byte: the `github:org/repo` shorthand carries \
an alphanumeric / `-` / `_` / `/` alphabet, every \
`https://` / `ssh://` / `git://` / `file://` URL scheme \
keeps host / path bodies inside the RFC 3986 `unreserved` \
set that excludes the byte and every percent-encoded \
byte (alphanumeric / `-` / `.` / `_` / `~` — no member \
needs percent-encoding), and the `git@host:path` scp-\
style SSH shape names a POSIX path component that \
carries no percent-encoded bytes (every forge — GitHub / \
GitLab / Bitbucket / Codeberg / sourcehut — refuses `%` \
in repo slugs at admission time, and IDN host labels \
must be pre-encoded as Punycode `xn--…` rather than as \
percent-encoded UTF-8 bytes). Beyond the URL-grammar \
question, the percent byte is the canonical render-\
determinism axis-of-non-determinism the typed `:repo` \
slot must close at the manifest layer. First, the \
paste-from-browser-address-bar percent-encoded-space \
footgun: an author copies \
`https://github.com/p/x%20test` from a browser address \
bar or a percent-encoded README hyperlink, intending \
the `%20` as the URL encoding of a literal space; \
libcurl's URL parser (the layer `git clone <https-url>` \
invokes) re-percent-encodes the `%` byte to `%25` on \
the wire (since `%` is reserved as the escape sequence \
lead-in and must itself be encoded for a literal byte), \
so the wire request becomes \
`https://github.com/p/x%2520test` — a different path \
than the lacre's content-address records, defeating \
the THEORY.md §V.2 render-determinism contract \
directly on the encoding-mechanism axis itself (the \
most direct violation of every prior render-\
determinism arm — `#`, `?`, `\\`, `{`/`}`, `<`/`>`, \
`` ` ``, `|`, `;`, `&`, `$`, `*`, `(`/`)`, `\"`, `'`, \
`!`, `,`, `=` — since `%` is the very encoding step \
those arms reason about). Second, the lone-percent \
malformed-escape footgun: an author writes `:repo \
\"https://github.com/p/x%foo\"` (the `%` not followed \
by two hex digits) — every WHATWG-conformant URL \
parser rejects the value at parse time per RFC 3986 \
§2.1 (`%HH` requires exactly two hex digits to follow), \
but the byte rides into the lacre's per-dep content-\
address (`conteudo: format!(\"git:{repo}\")` peer of \
the path-axis embedding at caixa-resolver/src/resolve.\
rs) before the resolver subprocess fails far from the \
source caixa.lisp. Third, the over-encoded path \
footgun: an author writes `:repo \
\"https://github.com/p%2Fx\"` intending the `%2F` as \
the URL encoding of `/`; the GitHub Smart-HTTP \
transport rejects percent-encoded path-separator bytes \
in repo URLs (the URL's path-segment grammar is \
resolved before the percent-decoding pass), but the \
byte rides verbatim into the lacre and locks a \
`git:https://github.com/p%2Fx` closure that diverges \
from the byte-identical `https://github.com/p/x` form \
every other author authored — two authors whose \
`:repo` values differ only in their `/` vs `%2F` \
presence resolve to the byte-identical upstream `git \
clone` but lock to two distinct BLAKE3 closures, the \
canonical render-determinism violation. The peer \
`:fonte :tag` / `:fonte :branch` axes \
(`is_git_ref_name`) deliberately admit `%` (git's \
`check-ref-format` accepts it as a printable byte and \
the percent carries no refname-grammar meaning); the \
`:entrada :paths` axis (`is_gateway_api_http_path`) \
similarly admits it (K8s Gateway API \
HTTPPathMatch.value OpenAPI regex accepts it as a \
path-segment byte). `:repo` is substrate-internal and \
strictly narrower than its upstream grammar by design, \
so the divergence is intentional: the percent-encoding \
axis is the load-bearing render-determinism axis on \
the typed `:fonte :repo` slot (every byte the wire \
differs from the lacre by even a single `%`-escape \
round-trip violates the substrate's content-addressed-\
closure contract) in a way it isn't on the refname / \
HTTP-path axes whose grammars admit the byte without \
confusion. Drop the `%` — substitute the literal byte \
directly (the typed slot admits the same `unreserved` \
byte-set the URL grammar's percent-decoding pass \
produces, so the percent-encoded form is structurally \
redundant), or split the encoded value into the typed \
slot it belongs in (e.g., a host with non-ASCII bytes \
must be pre-encoded as Punycode `xn--…` rather than \
percent-encoded UTF-8))"
.to_string(),
);
}
if b == b'^' {
return Err(
"must not contain `^` (RFC 3986 §2 lists the circumflex byte \
in the 'unwise' set every URL parser is required to refuse \
or percent-encode at the path-segment boundary, peer with \
the `{` / `}` URI Template, `<` / `>` shell-redirection, \
`` ` `` shell-command-substitution, and `|` shell-pipe arms \
on the same paragraph of the same RFC — the 'unwise' \
four-byte subset (`{`, `}`, `|`, `\\`, `^`) is the strictest \
of the §2 reserved classes, every member structurally \
incompatible with every URL grammar at every position. No \
git URL grammar admits the byte: the `github:org/repo` \
shorthand carries an alphanumeric / `-` / `_` / `/` \
alphabet, every `https://` / `ssh://` / `git://` / \
`file://` URL scheme percent-encodes `^` to `%5E` on the \
wire (the WHATWG URL spec's 'fragment percent-encode set' \
canonical mapping every conformant URL parser applies), \
and the `git@host:path` scp-style SSH shape names a POSIX \
path component that carries no shell-metachar bytes. \
Beyond the URL-grammar violation, every interactive POSIX \
shell with history enabled (bash / ksh / zsh's \
`bashcompat` mode) lexes `^old^new^` as the quick history-\
substitution shorthand — `^foo^bar` re-runs the most \
recent history entry with the first `foo` substituted by \
`bar`, the canonical RCE-class injection vector when a \
string lands in a shell context with `set -o histexpand` \
(bash's default for interactive sessions, peer with the \
`!` history-expansion arm). csh / tcsh lex `^` as the \
history-substitution prefix (`^old^new` substitutes `old` \
with `new` in the prior command's first occurrence). Beyond \
shell history, every regular-expression engine (POSIX BRE \
/ ERE, PCRE, RE2, the rust `regex` crate, JavaScript's \
`RegExp`) lexes `^` two ways: leading-position `^` anchors \
the match to the start of the line (the canonical `^foo` \
anchored-prefix idiom every grep / sed / awk one-liner \
carries), and inside-class `[^abc]` negates the character \
class (the canonical exclusion idiom every regex carries). \
PowerShell (Windows / cross-platform) lexes `^` as the \
escape character — `cmd ^> file` escapes the redirection \
operator into a literal byte, the canonical paste-from-\
PowerShell-prompt footgun on a cross-platform caixa.lisp. \
A `:repo \"https://github.com/p/x^old^new\"` (the \
canonical paste-from-shell-history footgun where the \
author copies a `git clone <url>` line followed by a \
`^typo^fix` quick-edit-and-rerun shell-history shorthand \
and forgot to trim the `^...^...` tail) or `:repo \
\"github:p/^archived\"` (the symmetric regex-anchor / \
negation paste idiom every doc-quick-start grep-pipeline \
footnotes) is the canonical paste-from-shell-prompt \
footgun the typed slot's accepted set must exclude. The \
byte rides verbatim into the lacre's per-dep content-\
address (`conteudo: format!(\"git:{repo}\")` peer of the \
path-axis embedding at caixa-resolver/src/resolve.rs) and \
into the resolver's `git clone <repo>` \
(caixa-resolver/src/git.rs) subprocess invocation, where \
libcurl's URL parser percent-encodes the byte on the wire \
— so two authors whose `:repo` values differ only in \
their caret presence (one paste-trimmed the history-\
substitution shorthand, the other didn't) resolve to the \
byte-identical upstream `git clone` but lock to two \
distinct BLAKE3 closures, defeating the THEORY.md §V.2 \
render-determinism contract on the same axis the \
fragment-`#`, query-`?`, backslash-`\\`, template-`{` / \
`}`, shell-redirection-`<` / `>`, backtick-`` ` ``, \
shell-pipe-`|`, shell-command-separator-`;`, shell-\
background-`&`, shell-variable-expansion-`$`, shell-glob-\
`*`, subshell-grouping-`(` / `)`, shell-double-quote-`\"`, \
shell-single-quote-`'`, history-expansion-`!`, list-\
separator-`,`, env-var-assignment-`=`, and percent-\
encoding-`%` arms close. Drop the `^...^...` tail — \
substitute the literal value at author time, or use \
`:fonte (:tipo path :caminho \"<local-path>\")` for a \
local workspace dep)"
.to_string(),
);
}
}
if s.starts_with(':') {
return Err(
"must not start with `:` (the canonical empty-scheme footgun — \
`:foo` parses as a zero-length scheme that no git porcelain \
entry-point accepts; use a non-empty scheme prefix like \
`github:`, `https://`, `ssh://`, `git://`, `file://`, or the \
`git@host:path` scp-style SSH form)"
.to_string(),
);
}
if !s.contains(':') {
return Err(
"must contain a `:` separator (every documented `:fonte :repo` \
shape carries one: `github:org/repo` shorthand, `https://…` / \
`ssh://…` / `git://…` / `file://…` URL schemes, or \
`git@host:path` scp-style SSH; a bare `org/repo` form is \
ambiguous — `git clone` reads it as a relative filesystem path \
rather than the GitHub-shorthand expansion the author probably \
intended — so prefix it with `github:` for the registry-\
shorthand resolver convention)"
.to_string(),
);
}
Ok(())
}
/// Practical cap on a `:caracteristicas` (Cargo-feature-name-shaped)
/// entry, in bytes. Cargo itself enforces no length cap on feature
/// names — its `restricted_names::validate_feature_name` accepts any
/// length — but every realistic feature in the Cargo ecosystem is
/// well under this bound (`derive` 6, `serde_json` 10, the
/// `__private_…` doubled-underscore convention rarely exceeds 32).
/// 64 bytes is the substrate's catch-the-paste-from-binary cap on the
/// peer trajectory `is_dns_1123_label` (63), `is_wit_world_ref` (128),
/// `is_nats_subject` (256), `is_wasi_keyvalue_slot` (512),
/// `is_git_ref_name` (255), `is_git_oid` (40/64),
/// `is_git_repo_url` (2048) carry: an axis-appropriate ceiling above
/// every legitimate authoring shape, tight enough to surface the
/// "paste-from-binary" / "multi-line blob landed in a single-token
/// slot" footgun at validate time.
pub const CARGO_FEATURE_NAME_MAX_LEN: usize = 64;
/// Predicate: assert that `s` is a valid Cargo feature name. The
/// contract — modeled on Cargo's
/// `restricted_names::validate_feature_name` grammar (the parser the
/// Cargo resolver routes every `[dependencies.<dep>.features]` entry
/// through at `cargo metadata` time), narrowed to the strict ASCII
/// subset every realistic feature in the Cargo ecosystem uses:
///
/// - 1..=[`CARGO_FEATURE_NAME_MAX_LEN`] (64) bytes;
/// - first byte: ASCII alphanumeric or `_` (Cargo's parser admits
/// Unicode XID-start characters too; pleme-io narrows to the
/// ASCII subset for the same reason every peer value-shape
/// predicate above narrows — drift between NFC-vs-NFD
/// normalization across filesystems silently rewrites the
/// feature-key, breaking the lacre's content-addressing
/// invariant). Leading `-` / `+` / `.` are explicitly named —
/// each is the canonical "I copy-pasted the
/// `+optional-feature` enablement form from a Cargo doc" /
/// "I confused the dotted-form with feature-name shape"
/// footgun the predicate's diagnostic remediation points at;
/// - remaining bytes: ASCII alphanumeric, `_`, `-`, `+`, or `.`
/// (the Cargo-accepted continuation set). Whitespace, control
/// characters, non-ASCII bytes, `/` / `?` / `#` / `,` /
/// other punctuation are each surfaced with a self-locating
/// reason naming the canonical authoring footgun (multi-token
/// blob, CR/LF paste-from-doc, `/` segment-separator confusion
/// with namespaced-dep features the predicate's call site
/// explicitly does not enable, list-separator-belongs-to-list-
/// grammar miscomprehension).
///
/// Returns the parser-shaped reason on rejection (without wrapping in
/// any error variant) so each per-axis caller — [`crate::Dep::validate`]
/// for the `:deps`/`:deps-dev :caracteristicas` axis at validate time,
/// every future per-feature axis (M4 caixa-resolver's `lacre.lisp`
/// resolved-feature-set materializer, the future per-WitContract
/// `:caracteristicas`-shaped capability-set axis if WIT worlds grow a
/// typed feature toggle, the future per-`UpgradeInstruction` per-
/// capability set axis the §V.2 mes-build extension would carry) —
/// wraps the same reason in its own typed `*Invalid { <axis>, reason }`
/// variant. The reason wording is axis-agnostic ("Cargo feature names
/// reject leading `-`") so every call site reading the same diagnostic
/// points at the same rule; drift between any two axes' rule
/// enforcement is a build error visible at this predicate, not a
/// per-renderer "this passed validate but Cargo rejected at metadata
/// time" surprise.
///
/// Empty input is rejected here (defensively) and at each call site
/// via the narrower [`crate::DepError::CaracteristicaEmpty`] variant —
/// the same empty-first cascade [`is_dns_1123_label`],
/// [`is_gateway_api_http_path`], [`is_wit_world_ref`],
/// [`is_nats_subject`], [`is_wasi_keyvalue_slot`], [`is_git_ref_name`],
/// [`is_git_oid`], and [`is_git_repo_url`] all carry.
///
/// Lifted as a typed substrate-side primitive on the same trajectory
/// the peer value-shape predicates already follow — the typed slot's
/// valid set matches the downstream consumer's accepted set (here,
/// Cargo's TOML-feature-name parser at `cargo metadata` time),
/// structurally. The ninth value-shape primitive to land in
/// [`crate::render`], closing the typed `:deps`/`:deps-dev` surface
/// value-shape trajectory on its last unsealed axis (`:caracteristicas`
/// entries; the per-entry `:nome` / `:versao` / `:fonte` axes are
/// already routed through their respective shape predicates).
///
/// # Errors
///
/// Returns the parser-shaped reason naming the specific violation
/// (length / first-byte-class / continuation-byte-class / whitespace /
/// control-char / non-ASCII / `/`-segment-separator-confusion /
/// `,`-list-separator-confusion), without wrapping in any error
/// variant — every caller maps the same `String` into its own typed
/// `*Invalid { <axis>, reason }` enum variant.
pub fn is_cargo_feature_name(s: &str) -> Result<(), String> {
if s.is_empty() {
return Err("must not be empty".to_string());
}
if s.len() > CARGO_FEATURE_NAME_MAX_LEN {
return Err(format!(
"exceeds Cargo feature name max length of {CARGO_FEATURE_NAME_MAX_LEN} bytes \
(got {} bytes; legitimate Cargo feature names rarely exceed ~24 bytes — \
this length suggests a paste-from-binary or multi-token blob landed in \
the `:caracteristicas` slot)",
s.len()
));
}
let bytes = s.as_bytes();
let first = bytes[0];
if !(first.is_ascii_alphanumeric() || first == b'_') {
let msg = if first == b'+' {
"must not start with `+` (Cargo's feature-name grammar reserves a leading \
`+` for the activation-syntax inside a `[dependencies.<dep>.features]` \
list — `:caracteristicas` entries name the feature itself, not its \
enablement form; drop the leading `+` and author the bare feature name, \
e.g. `\"http\"` not `\"+http\"`)"
.to_string()
} else if first == b'-' {
"must not start with `-` (Cargo's feature-name grammar rejects a leading \
hyphen — `-` is a legitimate continuation character between alphanumeric \
segments but the canonical CLI-argument-injection / kebab-leak footgun at \
the start; drop the leading `-`, e.g. `\"json\"` not `\"-json\"`)"
.to_string()
} else if first == b'.' {
"must not start with `.` (Cargo's feature-name grammar rejects a leading \
dot; `.` is a legitimate continuation character but the canonical \
leading-dot-as-version-suffix / hidden-file footgun at the start. Drop \
the leading `.`)"
.to_string()
} else if first == b' ' || first == b'\t' {
"must not start with whitespace (Cargo's feature-name grammar rejects \
whitespace anywhere; the leading-whitespace arm is the canonical \
paste-from-aligned-doc footgun)"
.to_string()
} else if first < 0x20 || first == 0x7F {
format!(
"must not start with control character 0x{first:02x} (Cargo's feature-name \
grammar rejects ASCII control characters; the CR/LF arm is the canonical \
paste-from-multiline-doc footgun)"
)
} else if first >= 0x80 {
format!(
"must not start with non-ASCII byte 0x{first:02x} (Cargo accepts Unicode \
XID-start characters but pleme-io narrows to the strict ASCII subset every \
realistic feature name uses; legitimate features are kebab-case ASCII \
identifiers like `\"http\"`, `\"json\"`, `\"derive\"`)"
)
} else {
format!(
"must start with an ASCII alphanumeric character or `_`, got {ch:?} \
(Cargo's `restricted_names::validate_feature_name` rejects feature names \
whose first character is outside the XID-start + `_` + digit set; \
pleme-io narrows to the strict ASCII alphanumeric + `_` subset)",
ch = first as char
)
};
return Err(msg);
}
for &b in &bytes[1..] {
let valid = b.is_ascii_alphanumeric() || b == b'_' || b == b'-' || b == b'+' || b == b'.';
if !valid {
let msg = if b == b' ' || b == b'\t' {
format!(
"must not contain whitespace character {ch:?} (Cargo's feature-name \
grammar rejects whitespace; feature names are single-token identifiers \
— use `-` or `_` to separate kebab-case / snake-case segments instead)",
ch = b as char
)
} else if b == b',' {
"must not contain `,` (the comma separator belongs to the \
`:caracteristicas` list grammar between entries, not to the feature-name \
grammar within an entry — split the value into two separate list entries)"
.to_string()
} else if b == b'/' {
"must not contain `/` (Cargo's `dep/feat` syntax for namespaced-dep \
features applies inside `[dependencies.<dep>.features]` list entries that \
already name the parent dep — `:caracteristicas` entries are per-dep \
already, so the segment separator within a feature name must be `-`, \
`_`, `+`, or `.`)"
.to_string()
} else if b == b'?' {
"must not contain `?` (Cargo's feature-name grammar rejects URL-reserved \
punctuation; use `-`, `_`, `+`, or `.` as a segment separator instead)"
.to_string()
} else if b == b'#' {
"must not contain `#` (Cargo's feature-name grammar rejects URL-reserved \
punctuation; use `-`, `_`, `+`, or `.` as a segment separator instead)"
.to_string()
} else if b < 0x20 || b == 0x7F {
format!(
"must not contain control character 0x{b:02x} (Cargo's feature-name \
grammar rejects ASCII control characters; the CR/LF arm is the \
canonical paste-from-multiline-doc footgun)"
)
} else if b >= 0x80 {
format!(
"must not contain non-ASCII byte 0x{b:02x} (Cargo accepts Unicode \
XID-continue characters but pleme-io narrows to the strict ASCII \
subset every realistic feature name uses; raw non-ASCII silently \
round-trips inconsistently across NFC/NFD normalization on APFS / \
case-folding filesystems, breaking the lacre's content-addressing \
invariant)"
)
} else {
format!(
"contains invalid character {ch:?} (Cargo's feature-name grammar \
allows only `[A-Za-z0-9_+\\-.]` after the first character)",
ch = b as char
)
};
return Err(msg);
}
}
Ok(())
}
/// Practical cap on a `:licenca` (SPDX-expression-shaped) value, in
/// bytes. The SPDX specification places no length cap on expressions
/// — the grammar admits arbitrarily-nested composite expressions —
/// but every realistic pleme-io fixture stays well under this bound
/// (`MIT` 3, `Apache-2.0` 10, `Apache-2.0 OR MIT` 17, the longest
/// SPDX dual-license-with-exception shape `Apache-2.0 WITH
/// LLVM-exception` 31; a `(MIT OR Apache-2.0) AND BSD-3-Clause AND
/// ISC` composite caps near 50). 256 bytes is the substrate's
/// catch-the-paste-from-binary cap on the peer trajectory
/// `is_dns_1123_label` (63), `is_cargo_feature_name` (64),
/// `is_wit_world_ref` (128), `is_nats_subject` (256),
/// `is_wasi_keyvalue_slot` (512), `is_git_ref_name` (255),
/// `is_git_oid` (40/64), `is_git_repo_url` (2048) carry: an
/// axis-appropriate ceiling above every legitimate authoring shape,
/// tight enough to surface the "paste-from-license-text" /
/// "multi-line license blob landed in the `:licenca` slot" footgun
/// at validate time.
pub const SPDX_EXPRESSION_MAX_LEN: usize = 256;
/// Predicate: assert that `s` is a valid SPDX-expression shape. The
/// contract — modeled on the SPDX 2.1 expression grammar
/// (`compound-expression = simple-expression | "(" compound-expression
/// ")" | compound-expression "WITH" exception-id | compound-expression
/// "AND" compound-expression | compound-expression "OR"
/// compound-expression`; `simple-expression = license-id | license-id
/// "+" | "LicenseRef-" idstring | "DocumentRef-" idstring ":"
/// "LicenseRef-" idstring`; `idstring = 1*(ALPHA / DIGIT / "-" /
/// ".")`), narrowed to the structural alphabet floor every realistic
/// SPDX expression in the wild uses:
///
/// - 1..=[`SPDX_EXPRESSION_MAX_LEN`] (256) bytes;
/// - no leading whitespace (paste-from-aligned-doc footgun);
/// - no trailing whitespace (paste-from-doc footgun — every
/// downstream SPDX parser splits on exact token boundaries and
/// a trailing space breaks the `WITH` / `AND` / `OR` keyword
/// match);
/// - every byte in the SPDX expression alphabet: ASCII alphanumeric
/// plus `.`, `-`, `+`, `(`, `)`, `:` (the `DocumentRef-…:LicenseRef-…`
/// separator), and a single ASCII space (token separator). Tabs,
/// control characters, non-ASCII bytes, `_` (not in `idstring`),
/// `,` (SPDX uses `AND` / `OR` keywords, not comma), `/` (the
/// `dual-license/A` colloquial idiom is non-SPDX), and every other
/// punctuation byte are each surfaced with a self-locating reason
/// naming the canonical authoring footgun.
///
/// The predicate is a *structural* floor — it enforces the alphabet +
/// length the SPDX grammar's character class admits, not the full
/// expression-parse (compound-expression nesting, `AND`/`OR`/`WITH`
/// keyword placement, parenthesis balance, idstring well-formedness
/// per simple-expression production). A future tightening on the
/// `:licenca` axis can extend past this shape predicate into a full
/// SPDX parser + license-id allowlist (peer with how
/// [`is_git_repo_url`] is the structural floor on `:repositorio` and
/// a future flake-resolver might tighten the per-URL-scheme arm into
/// scheme-specific shape predicates). This gate closes the
/// `_`/`,`/`/`/tab/CR/LF/non-ASCII/multi-line-blob footguns
/// structurally at the manifest layer; the parser-shape arms remain
/// for a follow-up routine once a real SPDX-parser dep is justified.
///
/// Returns the parser-shaped reason on rejection (without wrapping in
/// any error variant) so each per-axis caller —
/// [`crate::Caixa::validate_licenca`] for the universal `:licenca`
/// axis at validate time, every future per-license axis (a future
/// `:fonte :license` per-dep license-pin axis, a future
/// per-`UpgradeInstruction` per-component license-compatibility axis,
/// a future `Lacre` per-resolved-dep license-closure axis) — wraps the
/// same reason in its own typed `*Invalid { <axis>, reason }` variant.
/// The reason wording is axis-agnostic ("SPDX expressions reject
/// leading whitespace") so every call site reading the same diagnostic
/// points at the same rule; drift between any two axes' rule
/// enforcement is a build error visible at this predicate, not a
/// per-renderer "this passed validate but `helm lint` rejected the
/// `Chart.yaml license:` value" surprise.
///
/// Empty input is rejected here (defensively) and at each call site
/// via the narrower [`crate::ManifestError::LicencaEmpty`] variant —
/// the same empty-first cascade [`is_dns_1123_label`],
/// [`is_gateway_api_http_path`], [`is_wit_world_ref`],
/// [`is_nats_subject`], [`is_wasi_keyvalue_slot`], [`is_git_ref_name`],
/// [`is_git_oid`], [`is_git_repo_url`], and [`is_cargo_feature_name`]
/// all carry.
///
/// Lifted as a typed substrate-side primitive on the same trajectory
/// the peer value-shape predicates already follow — the typed slot's
/// valid set matches the downstream consumer's accepted set (here,
/// the `caixa-helm` chart `README.md` `## License` section + a
/// future SPDX-aware Chart.yaml `license:` emitter + the future
/// per-resolved-dep license-closure axis a forthcoming `Lacre`
/// extension would carry), structurally.
///
/// # Errors
///
/// Returns the parser-shaped reason naming the specific violation
/// (length / leading-whitespace / trailing-whitespace /
/// alphabet-class / tab / control-char / non-ASCII / `_` /
/// `,`-list-separator-confusion / `/`-dual-license-idiom), without
/// wrapping in any error variant — every caller maps the same
/// `String` into its own typed `*Invalid { <axis>, reason }` enum
/// variant.
pub fn is_spdx_expression_shape(s: &str) -> Result<(), String> {
if s.is_empty() {
return Err("must not be empty".to_string());
}
if s.len() > SPDX_EXPRESSION_MAX_LEN {
return Err(format!(
"exceeds SPDX expression max length of {SPDX_EXPRESSION_MAX_LEN} bytes \
(got {} bytes; realistic SPDX expressions like `\"Apache-2.0 WITH \
LLVM-exception\"` rarely exceed ~64 bytes — this length suggests a \
paste-from-license-text or multi-line blob landed in the `:licenca` \
slot)",
s.len()
));
}
let bytes = s.as_bytes();
if bytes[0] == b' ' {
return Err(
"must not start with whitespace (SPDX expressions are single tokens \
or token sequences separated by *internal* single ASCII spaces; a \
leading space is the canonical paste-from-aligned-doc footgun and \
breaks every downstream SPDX parser that splits on exact token \
boundaries)"
.to_string(),
);
}
if *bytes.last().expect("non-empty checked above") == b' ' {
return Err(
"must not end with whitespace (SPDX expressions don't terminate with \
trailing whitespace; the trailing-space arm is the canonical \
paste-from-doc footgun that breaks downstream parsers which split \
on exact `AND` / `OR` / `WITH` keyword boundaries)"
.to_string(),
);
}
for &b in bytes {
let valid = b.is_ascii_alphanumeric()
|| b == b'.'
|| b == b'-'
|| b == b'+'
|| b == b'('
|| b == b')'
|| b == b':'
|| b == b' ';
if !valid {
let msg = if b == b'\t' {
"must not contain tab character (SPDX expressions use a single \
ASCII space between tokens — tabs are the canonical \
paste-from-aligned-doc footgun and break downstream parsers \
that split on exact `\" \"` boundaries)"
.to_string()
} else if b < 0x20 || b == 0x7F {
format!(
"must not contain control character 0x{b:02x} (SPDX \
expressions are printable ASCII; the CR/LF arm is the \
canonical paste-from-multiline-doc footgun and lands as a \
malformed line in the rendered chart `README.md` `## \
License` section)"
)
} else if b >= 0x80 {
format!(
"must not contain non-ASCII byte 0x{b:02x} (SPDX identifiers \
are ASCII per the `idstring = 1*(ALPHA / DIGIT / \"-\" / \
\".\")` production; raw non-ASCII silently round-trips \
inconsistently across NFC/NFD normalization on APFS / \
case-folding filesystems and breaks at every downstream \
SPDX-aware tool)"
)
} else if b == b'_' {
"must not contain `_` (SPDX `idstring` grammar — license-id, \
LicenseRef, exception-id — is `1*(ALPHA / DIGIT / \"-\" / \
\".\")`; `_` is not in the SPDX alphabet, use `-` as the \
segment separator instead, e.g. `\"Apache-2.0\"` not \
`\"Apache_2.0\"`)"
.to_string()
} else if b == b',' {
"must not contain `,` (SPDX expressions compose multiple \
licenses via the `AND` / `OR` keywords, not the comma \
separator; e.g. `\"MIT OR Apache-2.0\"` not `\"MIT, \
Apache-2.0\"`)"
.to_string()
} else if b == b'/' {
"must not contain `/` (the `dual-license/A` slash form is a \
non-SPDX colloquial idiom; SPDX uses the `OR` keyword to \
compose: `\"MIT OR Apache-2.0\"` not `\"MIT/Apache-2.0\"`)"
.to_string()
} else if b == b';' {
"must not contain `;` (SPDX expressions compose multiple \
licenses via the `AND` / `OR` keywords, not the semicolon \
separator; e.g. `\"MIT AND Apache-2.0\"` not `\"MIT; \
Apache-2.0\"`)"
.to_string()
} else {
format!(
"contains invalid character {ch:?} (the SPDX expression \
alphabet is `[A-Za-z0-9.+\\-():]` plus single ASCII space; \
license IDs / exception IDs are `idstring` `1*(ALPHA / \
DIGIT / \"-\" / \".\")`, composition uses `AND` / `OR` / \
`WITH` keywords + `(`/`)` grouping)",
ch = b as char
)
};
return Err(msg);
}
}
Ok(())
}
/// Maximum byte length of a chart-description-shaped string. The
/// 512-byte cap is the axis-appropriate ceiling for the free-form
/// prose summary the `:descricao` axis carries: every realistic
/// chart description in the wild (`"Canonical Rust→wasm32-wasip2
/// caixa Servico."`, `"Checkout flow."`, `"AWS provider caixa for
/// tatara-lisp"`) sits well under 256 bytes, and the 512-byte cap
/// surfaces the "paste-from-doc multi-paragraph blob landed in the
/// `:descricao` slot" footgun at validate time. Peer with
/// [`WASI_KV_SLOT_MAX_LEN`] (512) on the sibling longer-than-
/// identifier axis; tighter than [`GIT_REPO_URL_MAX_LEN`] (2048)
/// which carries a different axis-class ceiling, and looser than
/// [`SPDX_EXPRESSION_MAX_LEN`] (256) which is the canonical
/// short-identifier-class axis.
pub const CHART_DESCRIPTION_MAX_LEN: usize = 512;
/// Scan `s` for the Unicode bidirectional-override / isolate format
/// codepoints UAX #9 names as the structural prerequisite of the
/// "Trojan Source" attack class (CVE-2021-42574 / Boucher & Anderson
/// 2021): nine codepoints in two contiguous blocks that flip the
/// rendered visual order of every following character until a
/// matching pop, so a string visible to a human reader and the same
/// string consumed by a parser/renderer can disagree on the order of
/// its content bytes.
///
/// The accepted set (rejection list):
///
/// - U+202A `LRE` LEFT-TO-RIGHT EMBEDDING
/// - U+202B `RLE` RIGHT-TO-LEFT EMBEDDING
/// - U+202C `PDF` POP DIRECTIONAL FORMATTING
/// - U+202D `LRO` LEFT-TO-RIGHT OVERRIDE
/// - U+202E `RLO` RIGHT-TO-LEFT OVERRIDE
/// - U+2066 `LRI` LEFT-TO-RIGHT ISOLATE
/// - U+2067 `RLI` RIGHT-TO-LEFT ISOLATE
/// - U+2068 `FSI` FIRST STRONG ISOLATE
/// - U+2069 `PDI` POP DIRECTIONAL ISOLATE
///
/// Returns the first offending codepoint in document order, or
/// `None` when `s` carries none of them. Iterates `chars()` once
/// (single UTF-8 decode pass, peer of every other UTF-8-aware
/// predicate in this module) — the per-predicate caller folds the
/// `Some(c)` into its axis-specific reason wording with the
/// offending codepoint named verbatim as `U+XXXX`.
///
/// Lifted as a shared helper rather than inlined into each per-axis
/// predicate (the PRIME DIRECTIVE duplication-budget rule —
/// THEORY.md §I.3.5: "every recurring shape becomes a generator
/// before it becomes a pattern; every pattern becomes a library
/// before it becomes duplicated code. The duplication budget is
/// zero.") because two predicates ([`is_chart_description_shape`],
/// [`is_chart_maintainer_name_shape`]) carry the same UTF-8
/// free-form-prose accepted set and would otherwise inline the same
/// nine-codepoint match arm verbatim. The third caller — every
/// future per-axis free-form-prose surface (a future Aplicacao-
/// level `:descricao` summary axis, a future per-`:contratos` edge
/// `:descricao` annotation, the future per-`:autores`-email-suffix
/// shape gate) — lands as a thin `if let Some(c) =
/// find_unicode_bidi_override(s) { … }` wrapper rather than
/// re-inlining the same codepoint match.
///
/// The arm is structurally distinct from the per-byte control-char
/// arm `[is_chart_description_shape]` already carries: ASCII control
/// bytes (`0x00..=0x1F` plus `0x7F`) are caught at the per-byte
/// pass; the bidi codepoints all decode to non-ASCII three-byte
/// UTF-8 sequences (`E2 80 AA..=E2 80 AE` for U+202A..=U+202E,
/// `E2 81 A6..=E2 81 A9` for U+2066..=U+2069) — every byte ≥ 0x80
/// per UTF-8 grammar — that the per-byte non-ASCII pass deliberately
/// accepts (Unicode letters, em-dash, arrows are canonical
/// `:descricao` shapes). Only the typed codepoint scan catches them.
fn find_unicode_bidi_override(s: &str) -> Option<char> {
s.chars().find(|c| {
matches!(
*c,
'\u{202A}'
| '\u{202B}'
| '\u{202C}'
| '\u{202D}'
| '\u{202E}'
| '\u{2066}'
| '\u{2067}'
| '\u{2068}'
| '\u{2069}'
)
})
}
/// Scan `s` for any of the three non-ASCII Unicode line-break
/// codepoints UAX #14 (Unicode Line Breaking Algorithm) and the
/// YAML 1.1 §4.1 b-char production both treat as line terminators
/// outside the two single-byte ASCII shapes (`\n` LF / `\r` CR) the
/// per-byte arm on the calling predicate already closes:
///
/// - U+0085 `NEL` NEXT LINE
/// - U+2028 `LS` LINE SEPARATOR
/// - U+2029 `PS` PARAGRAPH SEPARATOR
///
/// YAML 1.2 §5.4 ("Line Break Characters") explicitly retired these
/// three from the YAML line-break set per the UTR #20 recommendation,
/// so a YAML 1.2-strict parser (the `serde_yaml` / `yaml-rust2` family)
/// preserves them as literal codepoints inside the rendered Chart.yaml
/// scalar — but YAML 1.1 parsers (go-yaml v2 which Helm v3 / kubectl /
/// every Kubernetes client library transitively links, and `ruamel.yaml`
/// in compat mode) still treat them as line terminators per the YAML 1.1
/// b-char production, so the same `:descricao` / `:autores` value
/// authored with an embedded U+2028 parses as a single-line plain-style
/// scalar through one downstream consumer and a multi-line block scalar
/// through another. The cross-parser line-break disagreement breaks the
/// THEORY.md §V.2 render-determinism contract every typed slot carries
/// on the same axis the per-byte `\n` / `\r` arms close for ASCII; the
/// substrate refuses the three codepoints at validate time so the
/// rendered Chart.yaml carries the single-line shape every conformant
/// YAML parser agrees on. Independently, every UAX #14 conformant text
/// consumer (editors, terminals, web UIs like `helm list` /
/// `helm search` / Artifact Hub) breaks the visual line at these
/// codepoints regardless of YAML version, so the author's editor view
/// of `caixa.lisp` disagrees with the chart-consumer's rendered view
/// even when both YAML parsers agree on the byte-level shape.
///
/// Returns the first offending codepoint in document order, or `None`
/// when `s` carries none of them. Iterates `chars()` once (single
/// UTF-8 decode pass, peer of [`find_unicode_bidi_override`] and every
/// other UTF-8-aware predicate in this module) — the per-predicate
/// caller folds the `Some(c)` into its axis-specific reason wording
/// with the offending codepoint named verbatim as `U+XXXX`.
///
/// Lifted as a shared helper rather than inlined into each per-axis
/// predicate (the PRIME DIRECTIVE duplication-budget rule —
/// THEORY.md §I.3.5: "every recurring shape becomes a generator
/// before it becomes a pattern; every pattern becomes a library
/// before it becomes duplicated code. The duplication budget is
/// zero.") because two predicates ([`is_chart_description_shape`],
/// [`is_chart_maintainer_name_shape`]) carry the same UTF-8
/// free-form-prose accepted set and would otherwise inline the same
/// three-codepoint match arm verbatim — sibling lift to the
/// [`find_unicode_bidi_override`] helper one trajectory earlier on
/// the same two predicates. The third caller — every future
/// per-axis free-form-prose surface (a future Aplicacao-level
/// `:descricao` summary axis, a future per-`:contratos` edge
/// `:descricao` annotation, the future per-`:autores`-email-suffix
/// shape gate) — lands as a thin `if let Some(c) =
/// find_unicode_line_break(s) { … }` wrapper rather than re-inlining
/// the same codepoint match.
///
/// The arm is structurally distinct from the per-byte control-char
/// arm `[is_chart_description_shape]` already carries: the ASCII
/// line-break bytes `\n` (`0x0A`) and `\r` (`0x0D`) are caught at the
/// per-byte pass; the three non-ASCII line-break codepoints all
/// decode to multi-byte UTF-8 sequences (`C2 85` for U+0085, `E2 80
/// A8` for U+2028, `E2 80 A9` for U+2029) — every byte ≥ 0x80 per
/// UTF-8 grammar — that the per-byte non-ASCII pass deliberately
/// accepts (Unicode letters, em-dash, arrows are canonical
/// `:descricao` shapes). Only the typed codepoint scan catches them.
fn find_unicode_line_break(s: &str) -> Option<char> {
s.chars()
.find(|c| matches!(*c, '\u{0085}' | '\u{2028}' | '\u{2029}'))
}
/// Scan `s` for any of the eight BMP Unicode invisible-format
/// codepoints — the Cf-category zero-width codepoints that have no
/// visible glyph in any conforming font yet ride verbatim through
/// string equality and parser lookup:
///
/// - U+00AD `SHY` SOFT HYPHEN
/// - U+200B `ZWSP` ZERO WIDTH SPACE
/// - U+2060 `WJ` WORD JOINER
/// - U+2061 `FA` FUNCTION APPLICATION
/// - U+2062 `IT` INVISIBLE TIMES
/// - U+2063 `IS` INVISIBLE SEPARATOR
/// - U+2064 `IP` INVISIBLE PLUS
/// - U+FEFF `ZWNBSP` ZERO WIDTH NO-BREAK SPACE (BOM)
///
/// These codepoints break the THEORY.md §V.2 render-determinism
/// contract on a third axis from the visual-order class the sibling
/// [`find_unicode_bidi_override`] helper closes (the nine UAX #9
/// explicit-direction codepoints flip the rendered visual order) and
/// the single-line/multi-line class the sibling
/// [`find_unicode_line_break`] helper closes (the three UAX #14
/// non-ASCII line-break codepoints split a YAML 1.1 scalar): the
/// *invisible-identity* divergence. The author's editor view of
/// `caixa.lisp`, the chart-consumer's `helm list` / `helm search` /
/// Artifact Hub maintainer column, and every conformant terminal /
/// browser / editor agree on the visible glyph sequence (the
/// codepoint renders as nothing, so `"alice"` and
/// `"alice\u{200B}"` look identical end-to-end) — but the byte
/// sequence the YAML-plain-style-scalar carries verbatim differs
/// from the byte sequence the same author intends to read back, so
/// every byte-level grep / diff / equality comparison over the
/// rendered Chart.yaml disagrees with the visible-glyph match, the
/// Artifact Hub maintainer / description search index lookup misses
/// the authored identity entry because the byte sequence carries
/// invisible codepoints between letters, and a future per-author
/// CLA-signer lookup matches a visually-identical-but-byte-distinct
/// identity (the canonical "invisible-codepoint homograph" footgun).
/// The canonical authoring shapes that introduce these codepoints:
/// paste-from-Microsoft-Word (SHY auto-inserted at every hyphenation
/// candidate), paste-from-text-editor-saved-as-UTF-8-with-BOM (BOM
/// leading byte from Notepad / older VS Code defaults / Excel CSV
/// export), paste-from-typesetting-doc (ZWSP / WJ invisible word-
/// break hints from InDesign / LaTeX-rendered PDF copy-paste).
///
/// Returns the first offending codepoint in document order, or
/// `None` when `s` carries none of them. Iterates `chars()` once
/// (single UTF-8 decode pass, peer of [`find_unicode_bidi_override`]
/// and [`find_unicode_line_break`]) — the per-predicate caller folds
/// the `Some(c)` into its axis-specific reason wording with the
/// offending codepoint named verbatim as `U+XXXX`.
///
/// Excluded from the rejected set, on purpose:
///
/// - U+200C `ZWNJ` ZERO WIDTH NON-JOINER and U+200D `ZWJ` ZERO
/// WIDTH JOINER — both carry semantic compositional load in
/// Devanagari / Bengali / Persian script clusters (the
/// canonical "Persian name authoring" shape relies on ZWNJ to
/// break inappropriate ligatures) and in modern emoji ZWJ
/// sequences (👨💻 is `MAN` + U+200D `ZWJ` + `LAPTOP`); the
/// `:autores` / `:descricao` axes admit Unicode prose where
/// such sequences are the canonical authoring shape and a ban
/// would regress legitimate maintainer-name fixtures.
/// - U+200E `LRM` LEFT-TO-RIGHT MARK and U+200F `RLM`
/// RIGHT-TO-LEFT MARK — both are legitimate single-character
/// direction *hints* (not overrides) in mixed-script prose
/// (the canonical "Arabic name with embedded ASCII email"
/// shape relies on RLM to render the visual order reliably
/// across YAML / HTML consumers); the visible-order risk on
/// these axes is closed by the bidi-*override* helper (the 9
/// codepoints UAX #9 names as the Trojan Source vector), not
/// by the bidi-*marks*, so LRM/RLM remain accepted natively.
/// - Codepoints outside the BMP — Variation Selectors
/// Supplement (U+E0100..U+E01EF), Tag characters
/// (U+E0001..U+E007F) — sit outside the BMP and rarely
/// surface in realistic Helm chart metadata pasted from
/// editors; the BMP-restricted set captures the canonical
/// paste-from-Word / paste-from-BOM-editor / paste-from-
/// typesetting-doc / paste-from-math-formula class without
/// committing to a full Unicode `Default_Ignorable_Code_Point`
/// table.
///
/// Lifted as a shared helper rather than inlined into each per-axis
/// predicate (the PRIME DIRECTIVE duplication-budget rule —
/// THEORY.md §I.3.5: "every recurring shape becomes a generator
/// before it becomes a pattern; every pattern becomes a library
/// before it becomes duplicated code. The duplication budget is
/// zero.") because two predicates ([`is_chart_description_shape`],
/// [`is_chart_maintainer_name_shape`]) carry the same UTF-8
/// free-form-prose accepted set and would otherwise inline the same
/// eight-codepoint match arm verbatim — third lift in the UAX-driven
/// render-determinism trio (peer of [`find_unicode_bidi_override`]
/// on the visual-order axis and [`find_unicode_line_break`] on the
/// single-line/multi-line axis). The third caller — every future
/// per-axis free-form-prose surface (a future Aplicacao-level
/// `:descricao` summary axis, a future per-`:contratos` edge
/// `:descricao` annotation, the future per-`:autores`-email-suffix
/// shape gate) — lands as a thin `if let Some(c) =
/// find_unicode_invisible_format(s) { … }` wrapper rather than
/// re-inlining the same codepoint match.
///
/// The arm is structurally distinct from every prior arm on the
/// calling predicates: the per-byte control-char arm catches ASCII
/// `0x00..=0x1F` plus `0x7F` DEL; the per-byte non-ASCII pass
/// admits multi-byte UTF-8 sequences (Unicode letters, em-dash,
/// arrows are canonical shapes); the bidi-override helper catches
/// the 9 visual-order codepoints; the line-break helper catches
/// the 3 single-line-vs-multi-line codepoints. None overlap the
/// eight invisible-format codepoints here — each decodes to a
/// distinct multi-byte UTF-8 sequence (`C2 AD` for U+00AD,
/// `E2 80 8B` for U+200B, `E2 81 A0` for U+2060, `E2 81 A1` for
/// U+2061, `E2 81 A2` for U+2062, `E2 81 A3` for U+2063, `E2 81
/// A4` for U+2064, `EF BB BF` for U+FEFF) the per-byte non-ASCII
/// pass deliberately accepts; only the typed codepoint scan catches
/// them.
///
/// The four math-invisible operators U+2061..=U+2064 carry their
/// semantic load only inside mathematical typesetting (MathML
/// `<mo>` invisible operators, LaTeX `\,\,` thin-space-as-invisible-
/// times) — no realistic Helm chart `:descricao` or `:autores`
/// value is a math formula. The canonical authoring footgun is the
/// paste-from-MathJax-rendered-doc / paste-from-LaTeX-equation /
/// paste-from-InDesign-math-equation shape where MathJax /
/// LaTeX2RTF / InDesign export an invisible-operator codepoint
/// between adjacent symbols to preserve the semantic operator
/// reading for screen readers, and the codepoint silently rides
/// into the YAML scalar — same invisible-identity divergence class
/// the BMP four (SHY / ZWSP / WJ / BOM) close on the paste-from-
/// Word / paste-from-BOM-editor / paste-from-typesetting-doc class.
fn find_unicode_invisible_format(s: &str) -> Option<char> {
s.chars().find(|c| {
matches!(
*c,
'\u{00AD}'
| '\u{200B}'
| '\u{2060}'
| '\u{2061}'
| '\u{2062}'
| '\u{2063}'
| '\u{2064}'
| '\u{FEFF}'
)
})
}
/// Predicate: assert that `s` is a valid chart-description shape.
/// The `:descricao` axis is a free-form prose summary that lands in
/// the rendered `lareira-<nome>` Helm chart's `Chart.yaml`
/// `description:` field (a YAML scalar consumed by `helm list`,
/// `helm search`, Artifact Hub, and every chart-aware UI) and in
/// the chart's `README.md` header paragraph
/// (`caixa-helm/src/lib.rs:232`, `caixa-helm/src/lib.rs:333`).
/// The contract — modeled on the YAML 1.2 plain-style scalar
/// grammar and the Helm chart spec's expectation that
/// `description:` is a one-line summary:
///
/// - 1..=[`CHART_DESCRIPTION_MAX_LEN`] (512) bytes;
/// - no leading whitespace (paste-from-aligned-doc footgun —
/// YAML plain-style scalars round-trip trim-and-restore on
/// leading whitespace, so an authored `" foo"` lands as `"foo"`
/// in the rendered Chart.yaml and the round-trip back through
/// `caixa.lisp` silently drops the space);
/// - no trailing whitespace (paste-from-doc footgun — every YAML
/// dumper trims trailing whitespace from plain-style scalars,
/// so an authored `"foo "` round-trips inconsistently);
/// - no ASCII control characters anywhere (`0x00..=0x1F` plus
/// `0x7F` DEL) — tabs, newlines, carriage returns, and every
/// other control byte break the single-line YAML scalar shape
/// and the README header paragraph. The newline / CR arms are
/// the canonical paste-from-multiline-doc footgun; the tab arm
/// is the canonical paste-from-aligned-doc footgun; the
/// other-control-byte arm catches every more-exotic
/// paste-from-binary-blob shape (`0x00` NUL, `0x07` BEL,
/// `0x1B` ESC) that would silently land in the rendered
/// `Chart.yaml` as a YAML-illegal byte sequence and fail at
/// `helm lint` time far from the source caixa.lisp;
/// - non-ASCII bytes (UTF-8 continuation sequences) are
/// accepted — the canonical author shapes (`"Canonical
/// Rust→wasm32-wasip2 caixa Servico."`, `"FIXME — describe
/// this caixa"`) carry `→` (U+2192) and `—` (U+2014) and every
/// downstream consumer (YAML 1.2, Helm v3, every chart-aware
/// UI) round-trips Unicode losslessly;
/// - no Unicode bidirectional-override / isolate format
/// codepoints (U+202A `LRE`, U+202B `RLE`, U+202C `PDF`,
/// U+202D `LRO`, U+202E `RLO`, U+2066 `LRI`, U+2067 `RLI`,
/// U+2068 `FSI`, U+2069 `PDI`) — the nine codepoints UAX #9
/// names as the structural prerequisite of the "Trojan Source"
/// attack class (CVE-2021-42574 / Boucher & Anderson 2021)
/// that flip the rendered visual order of every following
/// character until a matching pop. Routed through the lifted
/// [`find_unicode_bidi_override`] helper so the same
/// nine-codepoint accepted set is shared with
/// [`is_chart_maintainer_name_shape`] on the sibling
/// YAML-plain-style-scalar surface, structurally consistent.
/// The non-ASCII byte arm above admits Unicode letters /
/// em-dash / arrows because YAML 1.2 + Helm v3 + every
/// chart-aware UI round-trip them losslessly; the bidi-override
/// codepoints break that round-trip discipline by class
/// (the byte sequence rides verbatim into the rendered
/// `Chart.yaml`'s `description:` value but renders differently
/// in `helm show chart` / Artifact Hub / `helm list` vs the
/// author's editor view of `caixa.lisp`), defeating the
/// THEORY.md §V.2 render-determinism contract every typed
/// slot carries on the same axis the per-byte CR/LF/control
/// arms above close for ASCII.
/// - no non-ASCII Unicode line-break codepoints (U+0085 `NEL`,
/// U+2028 `LS`, U+2029 `PS`) — the three codepoints UAX #14
/// (Unicode Line Breaking Algorithm) and the YAML 1.1 §4.1
/// b-char production both treat as line terminators outside
/// the ASCII `\n` / `\r` arms above. YAML 1.2 §5.4 retired
/// them per UTR #20, so YAML 1.2-strict parsers preserve them
/// verbatim while YAML 1.1 parsers (go-yaml v2 which Helm v3 /
/// kubectl link, `ruamel.yaml` in compat mode) split the
/// scalar on them — the same `:descricao` value parses as
/// single-line through one consumer and multi-line through
/// another, breaking cross-parser determinism on the same
/// axis the per-byte `\n` / `\r` arms close for ASCII.
/// Independently, every UAX #14 conformant text consumer
/// (editors, terminals, `helm list` / Artifact Hub web UIs)
/// breaks the visual line at these codepoints regardless of
/// YAML version, so the author's editor view of `caixa.lisp`
/// and the chart-consumer's rendered view diverge even when
/// both YAML parsers agree on the byte-level shape. Routed
/// through the lifted [`find_unicode_line_break`] helper so
/// the same three-codepoint accepted set is shared with
/// [`is_chart_maintainer_name_shape`], peer of the
/// [`find_unicode_bidi_override`] lift on the same two
/// predicates one trajectory earlier.
/// - no Unicode invisible-format codepoints (U+00AD `SHY`,
/// U+200B `ZWSP`, U+2060 `WJ`, U+2061 `FA` FUNCTION
/// APPLICATION, U+2062 `IT` INVISIBLE TIMES, U+2063 `IS`
/// INVISIBLE SEPARATOR, U+2064 `IP` INVISIBLE PLUS, U+FEFF
/// `ZWNBSP` / BOM) — the eight BMP Cf-category zero-width
/// codepoints with no visible glyph in any conforming font.
/// The author's editor view of `caixa.lisp` and the chart-
/// consumer's `helm list` / Artifact Hub description column
/// agree on the visible glyph sequence (`"Canonical Servico"`
/// and `"Canonical\u{200B}Servico"` render identically), but
/// the byte sequence the YAML-plain-style-scalar carries
/// verbatim differs — every byte-level grep / diff / equality
/// comparison and the Artifact Hub description-search index
/// lookup disagree silently with the visible-glyph match.
/// Closes the canonical paste-from-Microsoft-Word (SHY auto-
/// inserted at hyphenation candidates), paste-from-text-
/// editor-saved-as-UTF-8-with-BOM (leading BOM byte),
/// paste-from-typesetting-doc (ZWSP / WJ invisible word-break
/// hints), and paste-from-MathJax/LaTeX-rendered-formula
/// (FUNCTION APPLICATION / INVISIBLE TIMES / INVISIBLE
/// SEPARATOR / INVISIBLE PLUS — the four math-formula
/// invisible operators MathJax / LaTeX export between
/// adjacent symbols for screen-reader operator semantics)
/// footguns. Routed through the lifted
/// [`find_unicode_invisible_format`] helper so the same
/// eight-codepoint accepted set is shared with
/// [`is_chart_maintainer_name_shape`], third lift in the
/// UAX-driven render-determinism trio (peer of
/// [`find_unicode_bidi_override`] on the visual-order axis
/// and [`find_unicode_line_break`] on the single-line/multi-
/// line axis). The eight-codepoint set excludes U+200C
/// `ZWNJ` / U+200D `ZWJ` (legitimate compositional load in
/// Indic / Persian scripts and emoji ZWJ sequences) and
/// U+200E `LRM` / U+200F `RLM` (legitimate single-character
/// direction hints in mixed-script prose); the visible-order
/// risk on bidi overrides — not marks — is closed by the
/// prior helper.
///
/// The predicate is a *structural* floor — it enforces the
/// single-line printable-UTF-8 shape every realistic chart
/// description carries, not a per-byte alphabet check (which would
/// regress every non-ASCII canonical fixture). Same trajectory as
/// [`is_spdx_expression_shape`] (the ASCII-alphabet floor on the
/// `:licenca` axis) and [`is_git_repo_url`] (the URL-shape floor on
/// the `:repositorio` axis): the typed validator refuses the
/// downstream consumer's would-also-refuse shapes at the source
/// caixa.lisp boundary with the offending value named verbatim.
///
/// Returns the parser-shaped reason on rejection (without wrapping
/// in any error variant) so each per-axis caller —
/// [`crate::Caixa::validate_descricao`] for the universal
/// `:descricao` axis at validate time, every future per-description
/// axis (a future Aplicacao-level `:descricao` summary axis on
/// `mesh.pleme.io/v1alpha1/Caixa` CRs, a future Servico-level
/// per-`:contratos` edge `:descricao` annotation) — wraps the same
/// reason in its own typed `*Invalid { <axis>, reason }` variant.
/// The reason wording is axis-agnostic ("chart descriptions reject
/// leading whitespace") so every call site reading the same
/// diagnostic points at the same rule; drift between any two axes'
/// rule enforcement is a build error visible at this predicate, not
/// a per-renderer "this passed validate but `helm lint` rejected
/// the Chart.yaml `description:` value" surprise.
///
/// Empty input is rejected here (defensively) and at each call
/// site via the narrower [`crate::ManifestError::DescricaoEmpty`]
/// variant — the same empty-first cascade [`is_dns_1123_label`],
/// [`is_gateway_api_http_path`], [`is_wit_world_ref`],
/// [`is_nats_subject`], [`is_wasi_keyvalue_slot`],
/// [`is_git_ref_name`], [`is_git_oid`], [`is_git_repo_url`],
/// [`is_cargo_feature_name`], and [`is_spdx_expression_shape`] all
/// carry.
///
/// # Errors
///
/// Returns the parser-shaped reason naming the specific violation
/// (length / leading-whitespace / trailing-whitespace /
/// tab / newline / carriage-return / other-control-byte /
/// Unicode-bidi-override-codepoint / Unicode-line-break-codepoint),
/// without wrapping in any error variant — every caller maps the
/// same `String` into its own typed `*Invalid { <axis>, reason }`
/// enum variant.
pub fn is_chart_description_shape(s: &str) -> Result<(), String> {
if s.is_empty() {
return Err("must not be empty".to_string());
}
if s.len() > CHART_DESCRIPTION_MAX_LEN {
return Err(format!(
"exceeds chart description max length of {CHART_DESCRIPTION_MAX_LEN} bytes \
(got {} bytes; realistic chart descriptions like `\"Canonical \
Rust→wasm32-wasip2 caixa Servico.\"` rarely exceed ~64 bytes — this \
length suggests a paste-from-doc multi-paragraph blob landed in the \
`:descricao` slot)",
s.len()
));
}
let bytes = s.as_bytes();
if bytes[0] == b' ' {
return Err(
"must not start with whitespace (chart descriptions are single-line YAML \
plain-style scalars; a leading space is the canonical \
paste-from-aligned-doc footgun and round-trips inconsistently — every \
YAML dumper trims leading whitespace from plain-style scalars, so the \
authored space silently drops in the rendered Chart.yaml)"
.to_string(),
);
}
if *bytes.last().expect("non-empty checked above") == b' ' {
return Err(
"must not end with whitespace (chart descriptions don't terminate with \
trailing whitespace; every YAML dumper trims trailing whitespace from \
plain-style scalars, so the authored space round-trips inconsistently \
back through `caixa.lisp`)"
.to_string(),
);
}
for &b in bytes {
if b == b'\t' {
return Err(
"must not contain tab character (chart descriptions are single-line \
YAML plain-style scalars; tabs are the canonical \
paste-from-aligned-doc footgun and break the single-line scalar \
shape — every downstream YAML 1.2 parser is forbidden from \
emitting indentation tabs and tabs in plain-style scalars are \
implementation-defined)"
.to_string(),
);
}
if b == b'\n' {
return Err(
"must not contain newline (chart descriptions are single-line YAML \
plain-style scalars; an embedded newline is the canonical \
paste-from-multiline-doc footgun and lands as a multi-line YAML \
block scalar in the rendered Chart.yaml — every chart-aware UI \
(`helm list`, `helm search`, Artifact Hub) renders the description \
in a single-line column, so the embedded newline is silently \
dropped at every downstream consumer)"
.to_string(),
);
}
if b == b'\r' {
return Err("must not contain carriage return (chart descriptions are \
single-line YAML plain-style scalars; a `\\r` byte is the canonical \
paste-from-Windows-CRLF-doc footgun and lands as a literal CR in \
the rendered Chart.yaml — every YAML 1.2 parser treats CR as a \
line terminator equivalent to LF, so the embedded CR is silently \
normalized to a newline at every downstream consumer)"
.to_string());
}
if b < 0x20 || b == 0x7F {
return Err(format!(
"must not contain control character 0x{b:02x} (chart descriptions \
are printable UTF-8 single-line scalars; the control-byte arm \
catches paste-from-binary-blob footguns like `0x00` NUL, `0x07` \
BEL, `0x1b` ESC that would silently land in the rendered \
Chart.yaml as a YAML-illegal byte sequence and fail at `helm lint` \
time far from the source caixa.lisp)"
));
}
}
if let Some(c) = find_unicode_bidi_override(s) {
return Err(format!(
"must not contain Unicode bidirectional-override codepoint U+{cp:04X} \
(the nine codepoints UAX #9 names as the structural prerequisite of \
the \"Trojan Source\" attack class — CVE-2021-42574 / Boucher & \
Anderson 2021: U+202A `LRE`, U+202B `RLE`, U+202C `PDF`, U+202D `LRO`, \
U+202E `RLO`, U+2066 `LRI`, U+2067 `RLI`, U+2068 `FSI`, U+2069 `PDI` \
— flip the rendered visual order of every following character until a \
matching pop, so a `:descricao` string visible to a human reading \
`caixa.lisp` and the same string consumed by `helm show chart` / \
`helm list` / Artifact Hub / every chart-aware UI disagree on the \
order of the displayed content bytes. The byte sequence \
({utf8_seq}) rides verbatim into the rendered Chart.yaml's \
`description:` value at the same axis the per-byte CR/LF/control \
arms close for ASCII, but renders differently across consumers, \
defeating the THEORY.md §V.2 render-determinism contract every typed \
slot carries. The non-ASCII byte arm above admits Unicode letters / \
em-dash / arrows because YAML 1.2 + Helm v3 round-trip them \
losslessly; this codepoint breaks that round-trip discipline by \
class. Drop the bidi-override codepoint; pure visual right-to-left \
text (Hebrew, Arabic) is accepted natively without explicit \
direction marks)",
cp = c as u32,
utf8_seq = c
.encode_utf8(&mut [0u8; 4])
.bytes()
.map(|b| format!("0x{b:02X}"))
.collect::<Vec<_>>()
.join(" "),
));
}
if let Some(c) = find_unicode_line_break(s) {
return Err(format!(
"must not contain Unicode line-break codepoint U+{cp:04X} (the three \
codepoints UAX #14 / YAML 1.1 §4.1 name as line terminators outside \
the ASCII `\\n` / `\\r` arms above: U+0085 `NEL` NEXT LINE, U+2028 \
`LS` LINE SEPARATOR, U+2029 `PS` PARAGRAPH SEPARATOR. YAML 1.2 §5.4 \
retired them per UTR #20 so YAML 1.2-strict parsers preserve them \
verbatim, but YAML 1.1 parsers (go-yaml v2 which Helm v3 / kubectl / \
every Kubernetes client library transitively links, `ruamel.yaml` in \
compat mode) still split scalars on them — the same `:descricao` \
value parses as a single-line plain-style scalar through one \
downstream consumer and a multi-line block scalar through another, \
breaking cross-parser determinism on the same axis the per-byte \
`\\n` / `\\r` arms close for ASCII. Independently, every UAX #14 \
conformant text consumer (editors, terminals, `helm list` / \
`helm search` / Artifact Hub web UIs) breaks the visual line at \
these codepoints regardless of YAML version, so the author's editor \
view of `caixa.lisp` and the chart-consumer's rendered view of the \
`description:` field diverge even when both YAML parsers agree on \
the byte-level shape, defeating the THEORY.md §V.2 render-\
determinism contract every typed slot carries. The byte sequence \
({utf8_seq}) rides verbatim into the rendered Chart.yaml at the \
same axis the per-byte `\\n` / `\\r` arms close for ASCII. Routed \
through the shared [`find_unicode_line_break`] helper so the same \
three-codepoint accepted set lives in exactly one place across the \
[`is_chart_maintainer_name_shape`] sibling YAML-plain-style-scalar \
surface, peer of the [`find_unicode_bidi_override`] lift on the \
same two predicates one trajectory earlier. Drop the non-ASCII \
line-break codepoint; split the value into separate logical lines \
at the source if a multi-line summary is intended (the \
`:descricao` axis is single-line by contract — the multi-paragraph \
shape belongs in the chart `README.md` body, not the YAML \
`description:` scalar))",
cp = c as u32,
utf8_seq = c
.encode_utf8(&mut [0u8; 4])
.bytes()
.map(|b| format!("0x{b:02X}"))
.collect::<Vec<_>>()
.join(" "),
));
}
if let Some(c) = find_unicode_invisible_format(s) {
return Err(format!(
"must not contain Unicode invisible-format codepoint U+{cp:04X} (the \
eight BMP Cf-category zero-width codepoints with no visible glyph in \
any conforming font: U+00AD `SHY` SOFT HYPHEN, U+200B `ZWSP` ZERO \
WIDTH SPACE, U+2060 `WJ` WORD JOINER, U+2061 `FA` FUNCTION \
APPLICATION, U+2062 `IT` INVISIBLE TIMES, U+2063 `IS` INVISIBLE \
SEPARATOR, U+2064 `IP` INVISIBLE PLUS, U+FEFF `ZWNBSP` ZERO WIDTH \
NO-BREAK SPACE / BOM. The invisible-identity divergence: the \
author's editor view of `caixa.lisp`, the chart-consumer's \
`helm list` / `helm search` / Artifact Hub description column, \
and every conformant terminal / browser / editor agree on the \
visible glyph sequence (the codepoint renders as nothing, so \
`\"Canonical Servico\"` and `\"Canonical\\u{{200B}}Servico\"` look \
identical end-to-end), but the byte sequence the YAML-plain-style-\
scalar carries verbatim differs — every byte-level grep / diff / \
equality comparison over the rendered Chart.yaml `description:` \
value disagrees with the visible-glyph match, and the Artifact Hub \
description-search index lookup misses the authored entry because \
the byte sequence carries an extra invisible codepoint between \
letters. The canonical authoring shapes that silently introduce \
these codepoints: paste-from-Microsoft-Word (SHY auto-inserted at \
every hyphenation candidate), paste-from-text-editor-saved-as-UTF-8-\
with-BOM (BOM leading byte from Notepad / older VS Code defaults), \
paste-from-typesetting-doc (ZWSP / WJ invisible word-break hints \
from InDesign / LaTeX-rendered PDF copy-paste), and paste-from-\
MathJax/LaTeX-rendered-formula (FUNCTION APPLICATION / INVISIBLE \
TIMES / INVISIBLE SEPARATOR / INVISIBLE PLUS — MathJax / LaTeX2RTF \
/ InDesign math-equation export emit one of these between adjacent \
symbols to preserve operator semantics for screen readers, and the \
codepoint silently rides into the YAML scalar with no visible \
trace). The byte sequence ({utf8_seq}) rides verbatim into the \
rendered Chart.yaml at the same axis the per-byte CR/LF/control \
arms close for ASCII, but renders as nothing across consumers, \
defeating the THEORY.md §V.2 render-determinism contract on a \
third axis from the bidi-override (visual-order) and line-break \
(single-line vs multi-line) classes the prior arms close. Routed \
through the shared [`find_unicode_invisible_format`] helper so \
the eight-codepoint accepted set lives in exactly one place \
across the [`is_chart_maintainer_name_shape`] sibling \
YAML-plain-style-scalar surface, third lift in the UAX-driven \
render-determinism trio (peer of [`find_unicode_bidi_override`] \
on the visual-order axis and [`find_unicode_line_break`] on the \
single-line/multi-line axis). Drop the invisible codepoint; emoji \
ZWJ sequences (U+200D for the 👨💻 family) and bidi direction-mark \
codepoints (U+200E `LRM` / U+200F `RLM`) are accepted natively — \
only the eight zero-semantic-content codepoints are rejected)",
cp = c as u32,
utf8_seq = c
.encode_utf8(&mut [0u8; 4])
.bytes()
.map(|b| format!("0x{b:02X}"))
.collect::<Vec<_>>()
.join(" "),
));
}
Ok(())
}
/// Maximum byte length of a chart-maintainer-name-shaped string. The
/// 128-byte cap is the axis-appropriate ceiling for the per-entry
/// identifier the `:autores` Vec axis carries: every realistic Helm
/// chart maintainer name in the wild (`"pleme-io"`, `"Pleme
/// Contributors"`, `"alice <alice@example.com>"`, `"François
/// Dupont"`) sits well under 64 bytes, and the 128-byte cap surfaces
/// the "paste-from-doc multi-paragraph blob landed in a single
/// `:autores` entry" footgun at validate time. Tighter than
/// [`CHART_DESCRIPTION_MAX_LEN`] (512) on the sibling free-form-prose
/// axis where multi-sentence summaries are the canonical shape;
/// peer with [`WIT_IDENT_MAX_LEN`] (128) on the sibling
/// short-identifier-class axis.
pub const CHART_MAINTAINER_NAME_MAX_LEN: usize = 128;
/// Predicate: assert that `s` is a valid chart-maintainer-name shape.
/// The `:autores` axis is a per-entry maintainer identifier that lands
/// in the rendered `lareira-<nome>` Helm chart's `Chart.yaml`
/// `maintainers: [{name: …, email: null}]` array via
/// [`caixa-helm`]'s `build_chart_yaml` (`caixa-helm/src/lib.rs:251`);
/// each entry becomes the `name:` value of a single `Maintainer`
/// record (a YAML scalar consumed by `helm list`, `helm search`,
/// Artifact Hub's maintainer index, and every chart-aware UI). The
/// contract — modeled on the same YAML 1.2 plain-style scalar
/// grammar [`is_chart_description_shape`] enforces on the sibling
/// `:descricao` axis, with a tighter length cap for the per-entry
/// identifier class:
///
/// - 1..=[`CHART_MAINTAINER_NAME_MAX_LEN`] (128) bytes;
/// - no leading whitespace (paste-from-aligned-doc footgun —
/// YAML plain-style scalars round-trip trim-and-restore on
/// leading whitespace, so an authored `" pleme-io"` lands as
/// `"pleme-io"` in the rendered Chart.yaml and the round-trip
/// back through `caixa.lisp` silently drops the space);
/// - no trailing whitespace (paste-from-doc footgun — every YAML
/// dumper trims trailing whitespace from plain-style scalars,
/// so an authored `"pleme-io "` round-trips inconsistently);
/// - no ASCII control characters anywhere (`0x00..=0x1F` plus
/// `0x7F` DEL) — tabs, newlines, carriage returns, and every
/// other control byte break the single-line YAML scalar shape
/// and the `helm list` / `helm search` / Artifact Hub
/// maintainer-column rendering. The newline / CR arms are the
/// canonical paste-from-multiline-doc footgun (the author
/// pasted a multi-line block of author records into one
/// `:autores` entry instead of splitting them into one entry
/// per author); the tab arm is the canonical
/// paste-from-aligned-doc footgun; the other-control-byte
/// arm catches every more-exotic paste-from-binary-blob shape;
/// - non-ASCII bytes (UTF-8 continuation sequences) are accepted
/// — realistic maintainer names carry Unicode (`"François"`,
/// `"日本語"`, `"naïve"`) and every downstream consumer
/// (YAML 1.2, Helm v3, every chart-aware UI) round-trips
/// Unicode losslessly;
/// - no Unicode bidirectional-override / isolate format
/// codepoints (U+202A `LRE`, U+202B `RLE`, U+202C `PDF`,
/// U+202D `LRO`, U+202E `RLO`, U+2066 `LRI`, U+2067 `RLI`,
/// U+2068 `FSI`, U+2069 `PDI`) — the nine codepoints UAX #9
/// names as the structural prerequisite of the "Trojan Source"
/// attack class (CVE-2021-42574). A maintainer-name with an
/// embedded `RLO` flips the visual order of every trailing
/// byte, so an `:autores "alice\u{202E}example.com<bob@"` (the
/// paste-from-attacker-crafted-doc footgun) renders in
/// `helm list`'s maintainer column / Artifact Hub as
/// `alice<@bob>moc.elpmaxe` but rides verbatim into the
/// rendered Chart.yaml `maintainers:` array — same Trojan
/// Source class [`is_chart_description_shape`] closes on the
/// sibling `:descricao` axis. Routed through the same lifted
/// [`find_unicode_bidi_override`] helper so the nine-codepoint
/// accepted set is shared, structurally consistent.
/// - no non-ASCII Unicode line-break codepoints (U+0085 `NEL`,
/// U+2028 `LS`, U+2029 `PS`) — the three codepoints UAX #14
/// (Unicode Line Breaking Algorithm) and YAML 1.1 §4.1 b-char
/// production both treat as line terminators outside the
/// ASCII `\n` / `\r` arms above. YAML 1.2 §5.4 retired them
/// per UTR #20 so the cross-parser line-break disagreement
/// (go-yaml v2 / YAML 1.1 still splits; YAML 1.2-strict
/// parsers preserve) breaks the THEORY.md §V.2 render-
/// determinism contract on the same axis the per-byte `\n` /
/// `\r` arms close for ASCII. A maintainer-name with an
/// embedded U+2028 parses as one entry through a YAML 1.2
/// parser and as two `maintainers:` array entries through a
/// YAML 1.1 parser — same paste-from-multiline-doc class the
/// `\n` arm above closes, extended to the non-ASCII line-break
/// codepoints the per-byte non-ASCII pass deliberately
/// admits for Unicode letters. Routed through the same lifted
/// [`find_unicode_line_break`] helper so the three-codepoint
/// accepted set is shared with [`is_chart_description_shape`]
/// on the sibling YAML-plain-style-scalar surface,
/// structurally consistent.
/// - no Unicode invisible-format codepoints (U+00AD `SHY`,
/// U+200B `ZWSP`, U+2060 `WJ`, U+2061 `FA` FUNCTION
/// APPLICATION, U+2062 `IT` INVISIBLE TIMES, U+2063 `IS`
/// INVISIBLE SEPARATOR, U+2064 `IP` INVISIBLE PLUS, U+FEFF
/// `ZWNBSP` / BOM) — the eight BMP Cf-category zero-width
/// codepoints with no visible glyph. A maintainer-name with
/// an embedded U+200B (`"alice\u{200B}"`) renders identically
/// to `"alice"` in `helm list` / Artifact Hub's maintainer
/// column, yet the byte sequence is distinct — the Artifact
/// Hub maintainer-index lookup misses the authored `"alice"`
/// entry, and a future CLA-signer lookup matches a visually-
/// identical-but-byte-distinct identity (the canonical
/// invisible-codepoint homograph footgun on the maintainer-
/// identity axis). Closes the canonical paste-from-Microsoft-
/// Word (SHY), paste-from-text-editor-saved-as-UTF-8-with-BOM
/// (BOM), paste-from-typesetting-doc (ZWSP / WJ), and
/// paste-from-MathJax/LaTeX-rendered-formula (FUNCTION
/// APPLICATION / INVISIBLE TIMES / INVISIBLE SEPARATOR /
/// INVISIBLE PLUS — math-formula invisible operators
/// MathJax / LaTeX2RTF / InDesign emit between symbols for
/// screen-reader operator semantics) footguns. Routed through
/// the same lifted [`find_unicode_invisible_format`] helper
/// so the eight-codepoint accepted set is shared with
/// [`is_chart_description_shape`], third lift in the UAX-
/// driven render-determinism trio (peer of
/// [`find_unicode_bidi_override`] on the visual-order axis
/// and [`find_unicode_line_break`] on the single-line/multi-
/// line axis). The eight-codepoint set excludes U+200C
/// `ZWNJ` / U+200D `ZWJ` (emoji ZWJ sequences are canonical
/// for modern maintainer-display names) and U+200E `LRM` /
/// U+200F `RLM` (mixed-script direction hints are canonical
/// for "Arabic name with embedded ASCII email" shapes).
///
/// Same structural single-line printable-UTF-8 floor as
/// [`is_chart_description_shape`] — both `:descricao` and `:autores`
/// land as YAML plain-style scalars in the same `Chart.yaml` and
/// share every paste-from-doc footgun the YAML 1.2 grammar refuses
/// at parse time. The two predicates differ only on the byte
/// length cap: 512 bytes for `:descricao` (multi-sentence prose
/// shape) vs 128 bytes for `:autores` entries (short-identifier
/// shape). Returns the parser-shaped reason on rejection (without
/// wrapping in any error variant) so each per-axis caller —
/// [`crate::Caixa::validate_autores`] for the universal `:autores`
/// axis at validate time, every future per-maintainer-name axis (a
/// future caixa-registry maintainer-index entry, a future
/// chart-author CLA-signer lookup) — wraps the same reason in its
/// own typed `*Invalid { <axis>, reason }` variant.
///
/// Empty input is rejected here (defensively) and at each call
/// site via the narrower [`crate::ManifestError::AutorEmpty`]
/// variant — the same empty-first cascade [`is_dns_1123_label`],
/// [`is_gateway_api_http_path`], [`is_wit_world_ref`],
/// [`is_nats_subject`], [`is_wasi_keyvalue_slot`],
/// [`is_git_ref_name`], [`is_git_oid`], [`is_git_repo_url`],
/// [`is_cargo_feature_name`], [`is_spdx_expression_shape`], and
/// [`is_chart_description_shape`] all carry.
///
/// # Errors
///
/// Returns the parser-shaped reason naming the specific violation
/// (length / leading-whitespace / trailing-whitespace / tab /
/// newline / carriage-return / other-control-byte /
/// Unicode-bidi-override-codepoint), without wrapping in any error
/// variant — every caller maps the same `String` into its own typed
/// `*Invalid { <axis>, reason }` enum variant.
pub fn is_chart_maintainer_name_shape(s: &str) -> Result<(), String> {
if s.is_empty() {
return Err("must not be empty".to_string());
}
if s.len() > CHART_MAINTAINER_NAME_MAX_LEN {
return Err(format!(
"exceeds chart maintainer name max length of \
{CHART_MAINTAINER_NAME_MAX_LEN} bytes (got {} bytes; realistic chart \
maintainer names like `\"pleme-io\"`, `\"Pleme Contributors\"`, \
`\"alice <alice@example.com>\"` rarely exceed ~64 bytes — this \
length suggests a paste-from-doc multi-paragraph blob landed in a \
single `:autores` entry instead of being split into one entry per \
author)",
s.len()
));
}
let bytes = s.as_bytes();
if bytes[0] == b' ' {
return Err(
"must not start with whitespace (chart maintainer names are \
single-line YAML plain-style scalars; a leading space is the \
canonical paste-from-aligned-doc footgun and round-trips \
inconsistently — every YAML dumper trims leading whitespace from \
plain-style scalars, so the authored space silently drops in the \
rendered Chart.yaml)"
.to_string(),
);
}
if *bytes.last().expect("non-empty checked above") == b' ' {
return Err(
"must not end with whitespace (chart maintainer names don't \
terminate with trailing whitespace; every YAML dumper trims \
trailing whitespace from plain-style scalars, so the authored \
space round-trips inconsistently back through `caixa.lisp`)"
.to_string(),
);
}
for &b in bytes {
if b == b'\t' {
return Err(
"must not contain tab character (chart maintainer names are \
single-line YAML plain-style scalars; tabs are the canonical \
paste-from-aligned-doc footgun and break the single-line \
scalar shape — every downstream YAML 1.2 parser is forbidden \
from emitting indentation tabs and tabs in plain-style scalars \
are implementation-defined)"
.to_string(),
);
}
if b == b'\n' {
return Err("must not contain newline (chart maintainer names are \
single-line YAML plain-style scalars; an embedded newline is \
the canonical paste-from-multiline-doc footgun — the author \
pasted a multi-line block of author records into one \
`:autores` entry instead of splitting them into one entry per \
author, and the result lands as a multi-line YAML block scalar \
in the rendered Chart.yaml `maintainers:` array)"
.to_string());
}
if b == b'\r' {
return Err("must not contain carriage return (chart maintainer \
names are single-line YAML plain-style scalars; a `\\r` byte \
is the canonical paste-from-Windows-CRLF-doc footgun and \
lands as a literal CR in the rendered Chart.yaml — every YAML \
1.2 parser treats CR as a line terminator equivalent to LF, \
so the embedded CR is silently normalized to a newline at \
every downstream consumer)"
.to_string());
}
if b < 0x20 || b == 0x7F {
return Err(format!(
"must not contain control character 0x{b:02x} (chart \
maintainer names are printable UTF-8 single-line scalars; the \
control-byte arm catches paste-from-binary-blob footguns like \
`0x00` NUL, `0x07` BEL, `0x1b` ESC that would silently land \
in the rendered Chart.yaml as a YAML-illegal byte sequence \
and fail at `helm lint` time far from the source caixa.lisp)"
));
}
}
if let Some(c) = find_unicode_bidi_override(s) {
return Err(format!(
"must not contain Unicode bidirectional-override codepoint U+{cp:04X} \
(the nine codepoints UAX #9 names as the structural prerequisite of \
the \"Trojan Source\" attack class — CVE-2021-42574 / Boucher & \
Anderson 2021: U+202A `LRE`, U+202B `RLE`, U+202C `PDF`, U+202D `LRO`, \
U+202E `RLO`, U+2066 `LRI`, U+2067 `RLI`, U+2068 `FSI`, U+2069 `PDI` \
— flip the rendered visual order of every following character until a \
matching pop, so an `:autores` entry visible to a human reading \
`caixa.lisp` and the same entry consumed by `helm list` / Artifact \
Hub's maintainer column disagree on the order of the displayed \
content bytes. The byte sequence ({utf8_seq}) rides verbatim into \
the rendered Chart.yaml `maintainers:` array at the same axis the \
per-byte CR/LF/control arms close for ASCII, but renders \
differently across consumers, defeating the THEORY.md §V.2 \
render-determinism contract every typed slot carries. Routed through \
the shared [`find_unicode_bidi_override`] helper so the same \
nine-codepoint accepted set lives in exactly one place across the \
[`is_chart_description_shape`] sibling YAML-plain-style-scalar \
surface, structurally consistent. Drop the bidi-override codepoint; \
pure visual right-to-left maintainer names (Hebrew, Arabic) are \
accepted natively without explicit direction marks)",
cp = c as u32,
utf8_seq = c
.encode_utf8(&mut [0u8; 4])
.bytes()
.map(|b| format!("0x{b:02X}"))
.collect::<Vec<_>>()
.join(" "),
));
}
if let Some(c) = find_unicode_line_break(s) {
return Err(format!(
"must not contain Unicode line-break codepoint U+{cp:04X} (the three \
codepoints UAX #14 / YAML 1.1 §4.1 name as line terminators outside \
the ASCII `\\n` / `\\r` arms above: U+0085 `NEL` NEXT LINE, U+2028 \
`LS` LINE SEPARATOR, U+2029 `PS` PARAGRAPH SEPARATOR. YAML 1.2 §5.4 \
retired them per UTR #20 so YAML 1.2-strict parsers preserve them \
verbatim, but YAML 1.1 parsers (go-yaml v2 which Helm v3 / kubectl / \
every Kubernetes client library transitively links, `ruamel.yaml` in \
compat mode) still split scalars on them — an `:autores` entry with \
an embedded U+2028 parses as one `maintainers:` array entry through \
a YAML 1.2 parser and as two entries through a YAML 1.1 parser, \
breaking cross-parser determinism on the same axis the per-byte \
`\\n` / `\\r` arms close for ASCII. Independently, every UAX #14 \
conformant text consumer (editors, terminals, `helm list` / \
Artifact Hub's maintainer column) breaks the visual line at these \
codepoints regardless of YAML version, so the author's editor view \
of `caixa.lisp` and the chart-consumer's rendered view of the \
`maintainers:` entry diverge even when both YAML parsers agree on \
the byte-level shape, defeating the THEORY.md §V.2 render-\
determinism contract every typed slot carries. The byte sequence \
({utf8_seq}) rides verbatim into the rendered Chart.yaml at the \
same axis the per-byte `\\n` / `\\r` arms close for ASCII. Routed \
through the shared [`find_unicode_line_break`] helper so the same \
three-codepoint accepted set lives in exactly one place across the \
[`is_chart_description_shape`] sibling YAML-plain-style-scalar \
surface, peer of the [`find_unicode_bidi_override`] lift on the \
same two predicates one trajectory earlier. Drop the non-ASCII \
line-break codepoint; split the value into separate `:autores` \
list entries at the source — the per-entry shape is single-line by \
contract)",
cp = c as u32,
utf8_seq = c
.encode_utf8(&mut [0u8; 4])
.bytes()
.map(|b| format!("0x{b:02X}"))
.collect::<Vec<_>>()
.join(" "),
));
}
if let Some(c) = find_unicode_invisible_format(s) {
return Err(format!(
"must not contain Unicode invisible-format codepoint U+{cp:04X} (the \
eight BMP Cf-category zero-width codepoints with no visible glyph: \
U+00AD `SHY` SOFT HYPHEN, U+200B `ZWSP` ZERO WIDTH SPACE, U+2060 \
`WJ` WORD JOINER, U+2061 `FA` FUNCTION APPLICATION, U+2062 `IT` \
INVISIBLE TIMES, U+2063 `IS` INVISIBLE SEPARATOR, U+2064 `IP` \
INVISIBLE PLUS, U+FEFF `ZWNBSP` ZERO WIDTH NO-BREAK SPACE / BOM. \
The maintainer-identity divergence: the author's editor view of \
`caixa.lisp` and the `helm list` / Artifact Hub maintainer column \
agree on the visible glyph sequence (`\"alice\"` and \
`\"alice\\u{{200B}}\"` render identically as `alice`), but the byte \
sequence the YAML-plain-style-scalar carries verbatim differs — \
the Artifact Hub maintainer-index lookup misses the authored \
`\"alice\"` entry because the byte sequence carries an extra \
invisible codepoint, a future per-maintainer CLA-signer lookup \
matches a visually-identical-but-byte-distinct identity (the \
canonical invisible-codepoint homograph footgun), and every \
byte-level diff / grep / equality comparison over the Chart.yaml \
`maintainers:` array disagrees with the visible-glyph match. The \
canonical authoring shapes that silently introduce these \
codepoints: paste-from-Microsoft-Word (SHY auto-inserted at \
every hyphenation candidate), paste-from-text-editor-saved-as-\
UTF-8-with-BOM (BOM leading byte from Notepad / older VS Code \
defaults / Excel CSV export), paste-from-typesetting-doc (ZWSP / \
WJ invisible word-break hints from InDesign / LaTeX-rendered PDF \
copy-paste), and paste-from-MathJax/LaTeX-rendered-formula \
(FUNCTION APPLICATION / INVISIBLE TIMES / INVISIBLE SEPARATOR / \
INVISIBLE PLUS — MathJax / LaTeX2RTF / InDesign math-equation \
export emit one of these between adjacent symbols to preserve \
operator semantics for screen readers, and the codepoint silently \
rides into the YAML scalar with no visible trace). The byte \
sequence ({utf8_seq}) rides verbatim into the rendered \
Chart.yaml, but renders as nothing across consumers, defeating \
the THEORY.md §V.2 render-determinism contract on a third axis \
from the bidi-override (visual-order) and line-break (single-\
line vs multi-line) classes the prior arms close. Routed through \
the shared [`find_unicode_invisible_format`] helper so the \
eight-codepoint accepted set is shared with \
[`is_chart_description_shape`], third lift in the UAX-driven \
render-determinism trio (peer of [`find_unicode_bidi_override`] \
on the visual-order axis and [`find_unicode_line_break`] on the \
single-line/multi-line axis). Drop the invisible codepoint; emoji \
ZWJ sequences (U+200D for the 👨💻 family) and bidi direction-mark \
codepoints (U+200E `LRM` / U+200F `RLM`) are accepted natively \
for mixed-script maintainer names — only the eight zero-semantic-\
content codepoints are rejected)",
cp = c as u32,
utf8_seq = c
.encode_utf8(&mut [0u8; 4])
.bytes()
.map(|b| format!("0x{b:02X}"))
.collect::<Vec<_>>()
.join(" "),
));
}
Ok(())
}
/// Maximum byte length of a chart-keyword-shaped string. The 20-byte
/// cap matches Cargo's `[package] keywords` rule
/// (<https://doc.rust-lang.org/cargo/reference/manifest.html#the-keywords-field>:
/// "Each keyword should be ASCII text, start with a letter, and only
/// contain letters, numbers, _ or -. Keywords are case-insensitive and
/// limited to a maximum length of 20 characters.") — the same parser
/// crates.io routes its `keywords:` array entries through at publish
/// time. Tighter than every peer length cap on the typed Caixa surface
/// ([`CHART_MAINTAINER_NAME_MAX_LEN`] 128 on the sibling chart-metadata
/// `Vec<String>` axis, [`CARGO_FEATURE_NAME_MAX_LEN`] 64 on the sibling
/// `:caracteristicas` per-entry axis, [`CHART_DESCRIPTION_MAX_LEN`] 512
/// on the free-form-prose axis); the search-tag class is the tightest
/// short-identifier shape on the typed surface — every realistic
/// `:etiquetas` entry in the wild (`"iac"`, `"aws"`, `"pangea"`,
/// `"hello-world"`, `"tatara-lisp"`, `"caixa-servico"`,
/// `"infrastructure"`, `"pangea-native"`) sits well under 20 bytes,
/// and the 20-byte cap surfaces the "paste-from-doc multi-tag blob
/// landed in a single `:etiquetas` entry" footgun (`"web-service web
/// app"`, `"mesh,http,grpc"`) at validate time.
pub const CHART_KEYWORD_MAX_LEN: usize = 20;
/// Predicate: assert that `s` is a valid chart-keyword shape. The
/// `:etiquetas` axis is a per-entry registry-search-tag identifier
/// that lands in the rendered `lareira-<nome>` Helm chart's
/// `Chart.yaml` `keywords:` array via [`caixa-helm`]'s
/// `build_chart_yaml` (folded through a [`std::collections::BTreeSet`]
/// alongside the four substrate-fixed tags `lareira` / `wasm` /
/// `tatara-lisp` / `caixa-servico`) and indexes the chart through
/// Artifact Hub's keyword-search axis + the future caixa-registry's
/// keyword index. The contract — modeled on Cargo's crates.io
/// `[package] keywords` grammar (the parser the crates.io publish API
/// routes every `keywords:` entry through at publish time), narrowed
/// to the strict ASCII subset every realistic search tag uses:
///
/// - 1..=[`CHART_KEYWORD_MAX_LEN`] (20) bytes;
/// - first byte: ASCII letter (`A-Z` or `a-z`). Leading digit, `-`,
/// `_`, whitespace, control, and non-ASCII are each surfaced with
/// a self-locating reason naming the canonical authoring footgun
/// (paste-from-numbered-list `"1foo"`, kebab-leak `"-foo"`,
/// snake-leak `"_foo"`, paste-from-aligned-doc whitespace,
/// paste-from-Unicode-doc non-ASCII);
/// - remaining bytes: ASCII alphanumeric, `_`, or `-` (Cargo's
/// crates.io-accepted continuation set; tighter than
/// [`is_cargo_feature_name`]'s `_`/`-`/`+`/`.` continuation set —
/// `+` and `.` are not part of the keyword grammar). Whitespace,
/// `,` / `/` / `;` / `.` list-separator confusions, control bytes,
/// and non-ASCII bytes are each surfaced with a self-locating
/// reason naming the canonical authoring footgun (multi-tag blob
/// in one entry, CSV-list-belongs-to-list-grammar miscomprehension,
/// CR/LF paste-from-doc, NFC/NFD normalization drift).
///
/// Returns the parser-shaped reason on rejection (without wrapping in
/// any error variant) so each per-axis caller —
/// [`crate::Caixa::validate_etiquetas`] for the universal `:etiquetas`
/// axis at validate time, every future per-keyword axis (a future
/// caixa-registry keyword-index lookup, a future Artifact Hub-keyword
/// scraper validator, a future per-Aplicacao aggregated keyword set)
/// — wraps the same reason in its own typed `*Invalid { <axis>, reason }`
/// variant.
///
/// Empty input is rejected here (defensively) and at each call site
/// via the narrower [`crate::ManifestError::EtiquetaEmpty`] variant —
/// the same empty-first cascade [`is_dns_1123_label`],
/// [`is_gateway_api_http_path`], [`is_wit_world_ref`],
/// [`is_nats_subject`], [`is_wasi_keyvalue_slot`], [`is_git_ref_name`],
/// [`is_git_oid`], [`is_git_repo_url`], [`is_cargo_feature_name`],
/// [`is_spdx_expression_shape`], [`is_chart_description_shape`], and
/// [`is_chart_maintainer_name_shape`] all carry at their call sites.
///
/// # Errors
///
/// Returns the parser-shaped reason naming the specific violation
/// (length / first-byte-class / continuation-byte-class / whitespace /
/// control-char / non-ASCII / `,`-list-separator-confusion /
/// `/`-path-separator-confusion / `;`-list-separator-confusion /
/// `.`-namespace-confusion), without wrapping in any error variant —
/// every caller maps the same `String` into its own typed
/// `*Invalid { <axis>, reason }` enum variant.
pub fn is_chart_keyword_shape(s: &str) -> Result<(), String> {
if s.is_empty() {
return Err("must not be empty".to_string());
}
if s.len() > CHART_KEYWORD_MAX_LEN {
return Err(format!(
"exceeds chart keyword max length of {CHART_KEYWORD_MAX_LEN} bytes (got \
{} bytes; legitimate `:etiquetas` search tags rarely exceed ~12 bytes — \
this length suggests a paste-from-doc multi-tag blob landed in a single \
`:etiquetas` entry instead of being split into one entry per tag, e.g. \
`(\"mesh\" \"http\" \"grpc\")` not `(\"mesh-http-grpc-rpc-wasm\")`. \
Cargo's crates.io publish API enforces the same 20-byte cap on its \
`keywords:` array at publish time)",
s.len()
));
}
let bytes = s.as_bytes();
let first = bytes[0];
if !first.is_ascii_alphabetic() {
let msg = if first == b' ' || first == b'\t' {
"must not start with whitespace (chart keywords are single-token \
search-tag identifiers; the leading-whitespace arm is the canonical \
paste-from-aligned-doc footgun and round-trips inconsistently — every \
YAML 1.2 dumper trims leading whitespace from plain-style scalars, so \
the authored space silently drops in the rendered Chart.yaml \
`keywords:` array)"
.to_string()
} else if first == b'-' {
"must not start with `-` (Cargo's crates.io keyword grammar rejects a \
leading hyphen — `-` is a legitimate continuation character between \
alphanumeric segments but the canonical CLI-argument-injection / \
kebab-leak footgun at the start; drop the leading `-`, e.g. \
`\"tatara-lisp\"` not `\"-tatara-lisp\"`)"
.to_string()
} else if first == b'_' {
"must not start with `_` (Cargo's crates.io keyword grammar requires the \
first character be an ASCII letter — `_` is a legitimate continuation \
character between alphanumeric segments but the canonical \
snake-leak / hidden-identifier footgun at the start; drop the leading \
`_`, e.g. `\"caixa-servico\"` not `\"_caixa_servico\"`)"
.to_string()
} else if first.is_ascii_digit() {
format!(
"must not start with digit {ch:?} (Cargo's crates.io keyword grammar \
requires the first character be an ASCII letter — a digit at the \
start is the canonical paste-from-numbered-list footgun, e.g. the \
author copied `1. mesh` from a numbered doc and the `1` leaked \
into the tag; drop the leading digit, e.g. `\"v2\"` not `\"2v\"`)",
ch = first as char
)
} else if first < 0x20 || first == 0x7F {
format!(
"must not start with control character 0x{first:02x} (Cargo's \
crates.io keyword grammar rejects ASCII control characters; the \
CR/LF arm is the canonical paste-from-multiline-doc footgun)"
)
} else if first >= 0x80 {
format!(
"must not start with non-ASCII byte 0x{first:02x} (Cargo's \
crates.io keyword grammar is strict ASCII; the non-ASCII arm \
catches the canonical paste-from-Unicode-doc footgun — every \
legitimate search tag is a kebab-case ASCII identifier like \
`\"mesh\"`, `\"wasm\"`, `\"tatara-lisp\"`. Raw non-ASCII silently \
round-trips inconsistently across NFC/NFD normalization on APFS / \
case-folding filesystems and breaks the Artifact Hub keyword \
search index lookup)"
)
} else {
format!(
"must start with an ASCII letter, got {ch:?} (Cargo's crates.io \
keyword grammar rejects every non-letter first character — the \
canonical search tags are kebab-case ASCII identifiers starting \
with a letter, like `\"mesh\"`, `\"wasm\"`, `\"hello-world\"`)",
ch = first as char
)
};
return Err(msg);
}
for &b in &bytes[1..] {
let valid = b.is_ascii_alphanumeric() || b == b'_' || b == b'-';
if !valid {
let msg = if b == b' ' || b == b'\t' {
format!(
"must not contain whitespace character {ch:?} (Cargo's \
crates.io keyword grammar rejects whitespace; search tags are \
single-token identifiers — use `-` or `_` to separate \
kebab-case / snake-case segments instead, or split into \
separate `:etiquetas` entries: `(\"web\" \"service\")` not \
`(\"web service\")`)",
ch = b as char
)
} else if b == b',' {
"must not contain `,` (the comma separator belongs to the \
`:etiquetas` list grammar between entries, not to the keyword \
grammar within an entry — split the value into separate list \
entries: `(\"mesh\" \"http\" \"grpc\")` not `(\"mesh,http,grpc\")`. \
The author confused the CSV-style list-separator convention with \
the list grammar)"
.to_string()
} else if b == b'/' {
"must not contain `/` (Cargo's crates.io keyword grammar rejects \
path-style separators within a tag; the segment separator within \
a search tag is `-` or `_`, and multi-segment paths belong as \
separate `:etiquetas` entries: `(\"caixa\" \"servico\")` not \
`(\"caixa/servico\")`)"
.to_string()
} else if b == b';' {
"must not contain `;` (the semicolon separator is not part of the \
`:etiquetas` list grammar — split the value into separate list \
entries: `(\"mesh\" \"http\")` not `(\"mesh;http\")`. The author \
confused another lisp-list-style separator with the list \
grammar)"
.to_string()
} else if b == b'.' {
"must not contain `.` (Cargo's crates.io keyword grammar excludes \
`.` from the continuation set — the canonical \
namespace-confusion / version-suffix footgun, e.g. `\"http.1\"` \
/ `\"v1.0\"`; use `-` instead, e.g. `\"http-1\"` / `\"v1-0\"`)"
.to_string()
} else if b == b'\n' {
"must not contain newline (chart keywords are single-line \
single-token identifiers; an embedded newline is the canonical \
paste-from-multiline-doc footgun — the author pasted a multi-tag \
block into one `:etiquetas` entry instead of splitting into one \
entry per tag)"
.to_string()
} else if b == b'\r' {
"must not contain carriage return (chart keywords are single-line \
single-token identifiers; a `\\r` byte is the canonical \
paste-from-Windows-CRLF-doc footgun and lands as a literal CR in \
the rendered Chart.yaml `keywords:` array)"
.to_string()
} else if b < 0x20 || b == 0x7F {
format!(
"must not contain control character 0x{b:02x} (Cargo's \
crates.io keyword grammar rejects ASCII control characters; \
the control-byte arm catches paste-from-binary-blob footguns \
like `0x00` NUL, `0x07` BEL, `0x1b` ESC, `0x7f` DEL that \
would silently land in the rendered Chart.yaml \
`keywords:` array as a YAML-illegal byte sequence)"
)
} else if b >= 0x80 {
format!(
"must not contain non-ASCII byte 0x{b:02x} (Cargo's crates.io \
keyword grammar is strict ASCII; the non-ASCII arm catches \
the canonical paste-from-Unicode-doc footgun — raw non-ASCII \
silently round-trips inconsistently across NFC/NFD \
normalization on APFS / case-folding filesystems and breaks \
the Artifact Hub keyword search index lookup)"
)
} else {
format!(
"contains invalid character {ch:?} (Cargo's crates.io keyword \
grammar allows only `[A-Za-z0-9_-]` after the first \
character)",
ch = b as char
)
};
return Err(msg);
}
}
Ok(())
}
/// Tagged reason a caixa-author-supplied path can fail the
/// sandboxed-relative shape gate every callback / script path must
/// pass for the layout checker's `root.join(p)` to stay inside the
/// caixa root.
///
/// Returned by [`is_sandboxed_relative_path`] so each per-axis caller
/// — [`crate::BehaviorSpec::validate`] on `:behavior :on-*` paths
/// (b0c8389), [`crate::UpgradeInstruction::validate`]'s `StateChange`
/// arm on `:upgrade-from :state-change :script` (26da2c7), every
/// future axis admitting a user-supplied path — match-and-wraps the
/// tag into its own typed `*Invalid { slot, path }` enum variant so
/// the diagnostic still names *which slot* carried the malformed
/// value. The tag is axis-agnostic; the wrapping per-axis variant
/// carries the slot identity.
///
/// Sibling discriminator-style of the per-arm reason substrings every
/// value-shape predicate already exposes (`is_dns_1123_label`,
/// `is_gateway_api_http_path`, …) — but typed rather than string-
/// shaped, because the per-axis variants for path violations were
/// already split three ways (`EmptyPath` / `AbsolutePath` /
/// `ParentEscape` in `BehaviorError`; `EmptyScript` / `AbsoluteScript`
/// / `ParentEscapeScript` in `UpgradeError`), so collapsing them to a
/// single `*PathInvalid { reason }` variant would *regress* the
/// diagnostic shape rather than preserve it.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, gen_platform::IsVariant)]
pub enum PathShapeViolation {
/// The path string is empty — `PathBuf::new()` or the
/// canonical "I declared the slot but left the value blank"
/// authoring footgun. `root.join(PathBuf::new())` resolves to
/// `root` itself, silently pointing the runtime's `LisleLoader`
/// at the project root rather than a file.
Empty,
/// The path is absolute — `Path::join` *replaces* the base
/// with an absolute right-hand side, so `root.join("/etc/passwd")`
/// resolves to `"/etc/passwd"` and escapes the project sandbox
/// entirely. The Lunatic-style sandbox discipline
/// ([`theory/INSPIRATIONS.md` §III.1][i31]) requires every
/// author-supplied path to live under the caixa root.
///
/// [i31]: https://github.com/pleme-io/theory/blob/main/INSPIRATIONS.md
Absolute,
/// The path contains a [`Component::ParentDir`] component anywhere
/// — `root.join("../sibling/x")` traverses above the caixa root,
/// the same sandbox-escape vector via parent-directory traversal.
/// Caught regardless of where the `..` component sits (leading,
/// mid-path, trailing) so a future relaxation that only checks
/// one position surfaces at this one predicate.
ParentEscape,
}
impl PathShapeViolation {
/// Exhaustive iteration surface for every consumer that walks the
/// closed three-arm [`PathShapeViolation`] discriminator set — the
/// paired byte-parity pin on the [`gen_platform::IsVariant`]-derived
/// per-arm `is_*` predicate family, a future `feira lint
/// --explain-path-shape=<axis>` per-arm listing of the accepted
/// violation kinds, a future `mesh.pleme.io/v1alpha1/Caixa` CR
/// materializer's per-path admission-webhook rejection body naming
/// the accepted-violation-tag set, any future property-test harness
/// that sweeps every arm to compute per-arm diagnostic coverage.
/// A future variant addition (a `Symlink` arm the future
/// symlink-escape gate would carry once `Path::is_symlink` becomes
/// part of the sandbox contract, a `TrailingSpace` arm a future
/// authoring-side whitespace-hygiene gate would raise for
/// `"lib/init.lisp "` shapes) extends this slice as one edit and
/// every consumer picks up the new entry by construction; the
/// compiler-checked exhaustiveness on the sibling `match` arms in
/// [`is_sandboxed_relative_path`] and [`require_sandboxed_lisp_path`]
/// is the build-time guarantee that no arm forgets to grow.
///
/// Peer of the sibling closed-set fieldless typed enums'
/// [`crate::CaixaKind::ALL`] (6b1f4fb) /
/// [`crate::CaixaDialeto::ALL`] (dd4f541) /
/// [`crate::aplicacao::PlacementStrategy::ALL`] (18c7342) /
/// [`crate::aplicacao::RateLimitUnit::ALL`] (6bce03d) /
/// [`crate::dep::DepList::ALL`] (45ee563) /
/// [`crate::supervisor::RestartStrategy::ALL`] (4eec29c) /
/// [`crate::supervisor::RestartPolicy::ALL`] (dd32ccf)
/// exhaustive-iteration surfaces — the tenth closed-set typed
/// enum on the caixa surface to converge onto the same
/// one-canonical-arm-list-per-enum discipline, and the first
/// render-side path-shape-diagnostic axis (as distinct from an
/// OTP-shape M2 slot or an M3 mesh slot) to reach it. Order matches
/// variant declaration order verbatim (`Empty` → `Absolute` →
/// `ParentEscape`) so the slice is the canonical ordering every
/// exhaustive dispatch site (the `Empty → Absolute → ParentEscape`
/// arm-ordering [`is_sandboxed_relative_path`] and every per-axis
/// caller in [`crate::manifest::ManifestError`] preserve for
/// diagnostic-precedence continuity) defers to.
pub const ALL: &'static [Self] = &[Self::Empty, Self::Absolute, Self::ParentEscape];
}
/// Predicate: assert that `path` is a *sandboxed-relative* path —
/// the shape every caixa-author-supplied callback / script path must
/// take so the layout checker's `root.join(p)` resolves inside the
/// caixa root sandbox. The contract:
///
/// - non-empty (`PathBuf::new()` → `Empty`);
/// - relative (absolute paths replace the base under
/// [`Path::join`] semantics → `Absolute`);
/// - no [`Component::ParentDir`] components anywhere (traversal
/// above the caixa root → `ParentEscape`).
///
/// Returns [`PathShapeViolation`] tagging the specific failure;
/// each per-axis caller match-and-wraps the variant in its own
/// typed `*Invalid { slot, path }` enum variant so the diagnostic
/// still names *which slot* carried the malformed value. The
/// arm-ordering is the same `Empty → Absolute → ParentEscape`
/// every prior inlined copy followed (b0c8389 [`crate::BehaviorSpec`],
/// 26da2c7 [`crate::UpgradeInstruction::StateChange`]), so any
/// caller migrating to the lifted predicate preserves its existing
/// per-slot diagnostic precedence by construction.
///
/// Lifted from `caixa-core::behavior` and `caixa-core::upgrade`
/// where the same three-step gate was inlined verbatim across two
/// call sites — the PRIME DIRECTIVE duplication-budget rule
/// (THEORY.md §I.3.5: "every recurring shape becomes a generator
/// before it becomes a pattern; every pattern becomes a library
/// before it becomes duplicated code. The duplication budget is
/// zero.") promotes the gate to a typed substrate-side predicate
/// on the same trajectory the M2-overlay and label-selector helpers
/// (9e3a057, 9d09cfb, 9dbeafd, 31455a7, 07a4544, 8b4db42) already
/// follow. The third caller — the future M3/M4 axis admitting a
/// user-supplied path (the future `:entrada :tls-cert` /
/// `:entrada :tls-key` PEM-file axes, the future
/// `mesh.pleme.io/v1alpha1/Caixa` CR materializer's per-path
/// validator, the future per-Servico pre-warm script axis) — lands
/// as a thin five-line wrapper rather than re-inlining the same
/// three checks.
///
/// Pairs with the per-axis empty / absolute / parent-escape variants
/// on [`crate::BehaviorError`] and [`crate::UpgradeError`] — those
/// remain the typed surface authors see; this predicate is the
/// single-source-of-truth gate the caixa-build pipeline consults to
/// produce them.
///
/// # Errors
///
/// Returns the [`PathShapeViolation`] tag identifying the specific
/// violation ([`PathShapeViolation::Empty`] / [`PathShapeViolation::Absolute`]
/// / [`PathShapeViolation::ParentEscape`]) so each per-axis caller
/// match-and-wraps it into its own typed `*Path` / `*Script` enum
/// variant (preserving the per-slot diagnostic granularity the inline
/// pre-lift gates already produced).
pub fn is_sandboxed_relative_path(path: &Path) -> Result<(), PathShapeViolation> {
if path.as_os_str().is_empty() {
return Err(PathShapeViolation::Empty);
}
if path.is_absolute() {
return Err(PathShapeViolation::Absolute);
}
if path.components().any(|c| matches!(c, Component::ParentDir)) {
return Err(PathShapeViolation::ParentEscape);
}
Ok(())
}
/// The canonical tatara-lisp source-file extension every M2 typed
/// path-slot the M2.5 wasm-engine instantiator reads through
/// `tatara_lisp::read` at instance-start time must terminate in.
///
/// Strict lowercase: the byte-size / duration codecs and every other
/// shape-gate predicate in this module are case-sensitive on unit /
/// scheme / label boundaries, so a strict `lisp` shape matches the
/// downstream accepted set without case-folding drift (an uppercase
/// `.LISP` / `.Lisp` shape that a case-insensitive volume's existence
/// check would match the on-disk file would still mismatch the
/// canonical form the codec emits, breaking the THEORY.md §V.2.7
/// render-determinism contract every typed slot carries).
pub const LISP_SOURCE_EXTENSION: &str = "lisp";
/// Predicate: assert that `path` terminates in the canonical
/// [`LISP_SOURCE_EXTENSION`] (lowercase `.lisp`) — the file-type
/// shape every M2 typed path-slot the wasm-engine instantiator reads
/// as tatara-lisp source must take. The contract:
///
/// - the path has an extension component (no-extension paths like
/// `"lib/init"` or `"a"` fail);
/// - the extension's UTF-8 string form is exactly `"lisp"` —
/// lowercase, no trailing residue, no double-extension shadow
/// like `".lisp.bak"`.
///
/// Returns `true` on accept, `false` on reject. Each per-axis caller
/// — [`crate::BehaviorSpec::validate`] on `:behavior :on-*` paths
/// (c97815a), [`crate::UpgradeInstruction::StateChange::validate`]
/// on `:upgrade-from :state-change :script` (this commit), every
/// future axis admitting a tatara-lisp source path — wraps the
/// boolean into its own typed `*NonLispExtension { slot, path }` /
/// `*NonLispExtensionScript { script }` enum variant so the
/// diagnostic still names *which slot* carried the non-`.lisp`
/// value. The predicate is axis-agnostic; the wrapping per-axis
/// variant carries the slot identity.
///
/// Lifted from `caixa-core::behavior` where the same single-line
/// gate (`path.extension().and_then(|ext| ext.to_str()) ==
/// Some("lisp")`) was inlined verbatim across the first call site
/// (`BehaviorSpec::validate_callback_path`) — the PRIME DIRECTIVE
/// duplication-budget rule (THEORY.md §I.3.5: "every recurring shape
/// becomes a generator before it becomes a pattern; every pattern
/// becomes a library before it becomes duplicated code. The
/// duplication budget is zero.") promotes the gate to a typed
/// substrate-side predicate on the same trajectory the path-shape
/// gate [`is_sandboxed_relative_path`] already follows (lifted from
/// the same two call sites once the second consumer appeared). The
/// third caller — the future `:bibliotecas` per-entry tatara-lisp
/// source-file axis (the `feira build` loop reads each through the
/// same `tatara_lisp::read` reader at parse time), the future `:exe`
/// `:kind Binario` entry-point axis (the nix-built binary's entry
/// point loads as Lisp source), the future M2.5 wasm-engine
/// pre-warm hook axis — lands as a thin two-line wrapper rather
/// than re-inlining the same extension check.
///
/// Pairs with the per-axis `*NonLispExtension` / `*NonLispExtensionScript`
/// variants on [`crate::BehaviorError`] and [`crate::UpgradeError`]
/// — those remain the typed surface authors see; this predicate is
/// the single-source-of-truth gate the caixa-build pipeline consults
/// to produce them.
#[must_use]
pub fn is_lisp_extension(path: &Path) -> bool {
path.extension().and_then(|ext| ext.to_str()) == Some(LISP_SOURCE_EXTENSION)
}
/// The canonical compound suffix every `:servicos` entry — the
/// ComputeUnit-CR axis the M2 typed-substrate caixa-helm /
/// caixa-flux renderers consume via `serde_yaml::from_str` — must
/// terminate in. Two-segment shape (`.computeunit.yaml`) rather than
/// a single `.yaml` extension: the `.computeunit` segment routes
/// authoring-time to the typed `ComputeUnit` CR shape the
/// `pleme-computeunit` library chart resolves, distinguishing the
/// slot's accepted set from the open `.yaml` universe (Helm
/// `values.yaml`, FluxCD `Kustomization.yaml`, the generic K8s
/// manifest YAML every operator emits) — same axis-discipline the
/// peer [`LISP_SOURCE_EXTENSION`] sibling carries on the tatara-lisp-
/// source axis but with a compound suffix because
/// [`Path::extension`] only returns the post-last-`.` segment
/// (`"yaml"` for `foo.computeunit.yaml`), so the predicate routes
/// through [`Path::file_name`] and a string `ends_with` check on the
/// full suffix instead.
///
/// Strict lowercase: every other shape-gate predicate in this module
/// is case-sensitive on unit / scheme / label boundaries, so a strict
/// `.computeunit.yaml` shape matches the downstream accepted set
/// without case-folding drift (an uppercase `.COMPUTEUNIT.YAML` shape
/// that a case-insensitive volume's existence check would match the
/// on-disk file would still mismatch the canonical form every in-tree
/// `:servicos` fixture and the `Caixa::template` scaffold emit,
/// breaking the THEORY.md §V.2.7 render-determinism contract every
/// typed slot carries).
pub const COMPUTEUNIT_YAML_SUFFIX: &str = ".computeunit.yaml";
/// Predicate: assert that `path` terminates in the canonical
/// [`COMPUTEUNIT_YAML_SUFFIX`] (lowercase `.computeunit.yaml`) — the
/// file-type shape every `:servicos` entry, the ComputeUnit-CR axis
/// the M2 typed-substrate caixa-helm / caixa-flux renderers consume
/// via `serde_yaml::from_str`, must take. The contract:
///
/// - the path has a final file-name component (paths ending in `/`
/// fail);
/// - the file name's UTF-8 string form ends in
/// `.computeunit.yaml` — lowercase, no case-folding;
/// - at least one byte precedes the suffix (the degenerate hidden-
/// file `.computeunit.yaml` shape — file name exactly equal to
/// the suffix — fails: the substrate identifies each ComputeUnit
/// by the file-stem segment that precedes `.computeunit.yaml`,
/// so an empty stem is structurally an unidentified Servico).
///
/// Returns `true` on accept, `false` on reject. The per-axis caller
/// — [`crate::Caixa::validate_code_paths`] on the `:servicos` axis —
/// wraps the boolean into its own typed
/// `ManifestError::CodePathNonComputeUnitYamlExtension { slot, path }`
/// variant so the diagnostic still names the offending slot and the
/// offending path verbatim. Peer of [`is_lisp_extension`] on the
/// tatara-lisp-source axis (`:bibliotecas` 64772a9); same axis-
/// agnostic predicate discipline, here on the compound-suffix axis
/// [`Path::extension`] can't express on its own. The third caller —
/// the future M2.5 caixa-operator `:servicos` admission webhook
/// keying off the same accepted set, the M4
/// `mesh.pleme.io/v1alpha1/ComputeUnit` CR materializer's per-
/// `:servicos` shape gate, the future `feira fmt`'s `:servicos`
/// canonical-form normalizer — lands as a thin wrapper rather than
/// re-inlining the same compound-suffix check.
///
/// Pairs with the per-axis
/// [`crate::ManifestError::CodePathNonComputeUnitYamlExtension`]
/// variant — that remains the typed surface authors see; this
/// predicate is the single-source-of-truth gate the caixa-build
/// pipeline consults to produce it.
#[must_use]
pub fn is_computeunit_yaml_extension(path: &Path) -> bool {
path.file_name()
.and_then(|n| n.to_str())
.is_some_and(|name| {
name.len() > COMPUTEUNIT_YAML_SUFFIX.len() && name.ends_with(COMPUTEUNIT_YAML_SUFFIX)
})
}
/// Canonical camelCase YAML key for the `:limits` slot's overlay.
pub const M2_KEY_LIMITS: &str = "limits";
/// Canonical camelCase YAML key for the `:behavior` slot's overlay.
pub const M2_KEY_BEHAVIOR: &str = "behavior";
/// Canonical camelCase YAML key for the `:upgrade-from` slot's overlay.
pub const M2_KEY_UPGRADE_FROM: &str = "upgradeFrom";
/// Canonical JSON/YAML top-level key for [`crate::Caixa`]'s runtime
/// `deps` axis — the runtime-closure dependency list every build the
/// caixa participates in reaches (peer of the dev-only `:deps-dev`
/// list [`CAIXA_KEY_DEPS_DEV`] pins). The Rust field is single-word
/// `deps`; the `#[serde(rename_all = "camelCase")]` attribute on
/// [`crate::Caixa`] is a no-op on this axis (no `_` to transform), so
/// the emitted JSON key equals the source-side field name byte-for-byte
/// and equals this constant's value.
///
/// [`crate::Caixa::to_lisp`] threads the manifest through
/// `serde_json::to_value(self) → tatara_lisp::domain::json_to_sexp`, so
/// the emitted JSON key is the load-bearing byte-string the round-trip
/// consumes on its way back to the kebab-case `:deps` author surface.
/// Until this lift landed the byte-string `"deps"` was structurally
/// implicit in the [`crate::Caixa::deps`] field name at
/// [`crate::Caixa`] with no compile-time link to any downstream
/// `.get(<key>)` consumer or drift-detection pin — a future
/// [`crate::Caixa`] field rename (`deps` → `dependencies` matching
/// Cargo's verbatim `[dependencies]` axis, `deps` → `runtime_deps`
/// matching a hypothetical per-runtime-target vocabulary flip) OR an
/// added `#[serde(rename = "…")]` explicit attribute override (either
/// of which would silently break every [`crate::Caixa::to_lisp`]
/// round-trip and the future M4 operator-side manifest ingest that
/// reaches for `deps` via `Value::get(...)`) would surface at consumer
/// parse time as a silently-absent JSON key defaulting to
/// [`Vec::new()`], far from the rename's commit and with no field
/// naming the drift.
///
/// Peer of [`CAIXA_KEY_DEPS_DEV`] on the two-list dep-graph
/// serialized-key axis: this const names the runtime-closure dep-list
/// wire key, [`CAIXA_KEY_DEPS_DEV`] names the dev-only dep-list wire
/// key. Byte-identical to the peer [`DEP_AUTHOR_KEY_DEPS`] author-facing
/// kebab-case label modulo the leading `:` — the two consts split on
/// the axis every dep-graph slot carries (author-facing kebab-case
/// label vs. renderer-side wire key). Same "one canonical byte-string
/// per typed axis" discipline every peer [`M2_KEY_*`] /
/// [`M3_KEY_PLACEMENT`] / [`SUPERVISOR_KEY_*`] const carries.
pub const CAIXA_KEY_DEPS: &str = "deps";
/// Canonical camelCase JSON/YAML top-level key for [`crate::Caixa`]'s
/// `deps_dev` axis — the dev-only dependency list that the M0 base
/// package model already exposes (peer of the runtime `:deps` list, but
/// excluded from published lacres and consumer builds). The Rust field
/// is `snake_case` `deps_dev`; the `#[serde(rename_all = "camelCase")]`
/// attribute on [`crate::Caixa`] maps it to the camelCase JSON key
/// `"depsDev"` this constant pins.
///
/// [`crate::Caixa::to_lisp`] threads the manifest through
/// `serde_json::to_value(self) → tatara_lisp::domain::json_to_sexp`, so
/// the emitted JSON key is the load-bearing byte-string the round-trip
/// consumes on its way back to the kebab-case `:deps-dev` author
/// surface. Until this lift landed the byte-string `"depsDev"` was
/// structurally implicit in the `#[serde(rename_all = "camelCase")]`
/// derive attribute at [`crate::Caixa`] with no compile-time link to any
/// downstream `.get(<key>)` consumer or drift-detection pin — a future
/// [`crate::Caixa`] field rename (`deps_dev` → `dev_deps` matching
/// Cargo's verbatim `dev-dependencies` axis, `deps_dev` → `deps_test`
/// matching a hypothetical per-test-target vocabulary flip) OR a
/// `#[serde(rename_all = "…")]` attribute flip (any of which would
/// silently break every `Caixa::to_lisp` round-trip and the future M4
/// operator-side manifest ingest that reaches for `depsDev` via
/// `Value::get(...)`) would surface at consumer parse time as a
/// silently-absent JSON key defaulting to `Vec::new()`, far from the
/// rename's commit and with no field naming the drift.
///
/// Peer of [`M2_KEY_UPGRADE_FROM`] on the sibling top-level
/// [`crate::Caixa`] multi-word camelCase-renamed serialized-key axis —
/// both are `snake_case → camelCase` renames the `rename_all` derive
/// produces on the M0 [`crate::Caixa`] surface. Alongside
/// [`SUPERVISOR_KEY_MAX_RESTARTS`] (`"maxRestarts"`, 40cc4e5) and
/// [`SUPERVISOR_KEY_RESTART_WINDOW`] (`"restartWindow"`, 40cc4e5) —
/// which pin the two supervisor-tree top-level multi-word keys the
/// [`crate::Caixa`] surface flattens up — this const closes the last of
/// the four multi-word top-level [`crate::Caixa`] serde-derived JSON
/// keys still lacking a lifted `&'static str` peer. Same "one canonical
/// byte-string per typed serialized-key axis" discipline every peer
/// [`M2_KEY_*`] / [`M3_KEY_PLACEMENT`] / [`SUPERVISOR_KEY_*`] const
/// carries.
pub const CAIXA_KEY_DEPS_DEV: &str = "depsDev";
/// Canonical author-facing kebab-case `(defcaixa … :limits (…))` top-level
/// slot label the M2 per-Servico Lunatic sandbox `:limits` slot surfaces
/// under. Peer of [`M2_KEY_LIMITS`] on the dual-axis pair every M2
/// top-level slot carries: the camelCase [`M2_KEY_*`] const names the
/// *renderer-side* overlay-container wire key the serde-derive-emitted
/// programs.yaml / values.yaml block carries under (`"limits"`, load-bearing
/// per the `#[serde(rename_all = "camelCase")]` attribute on the emit-side
/// [`servico_m2_overlay`] shape), the kebab-case [`M2_AUTHOR_KEY_*`] const
/// names the *author-facing* label the [`crate::Caixa::declared_servico_slots`]
/// tagger threads through as one of the `&'static str` entries in the
/// canonical-declaration-order slot list every kind-coherence gate consults
/// ([`crate::LayoutError::ServicoSlotsOnNonServico`] joins them into the
/// space-separated `slots:` diagnostic naming which of the three M2 slots
/// the offending caixa declared on a non-Servico kind).
///
/// Until this lift landed the three kebab-case labels sat once each in
/// [`crate::Caixa::declared_servico_slots`] as three-arm inline
/// `":limits"` / `":behavior"` / `":upgrade-from"` byte-strings the tagger
/// pushed onto its return `Vec`, plus a handful of test-side probe
/// literals asserting the diagnostic's `slots:` field carries the
/// expected per-arm value verbatim — with no compile-time link between
/// the tagger's arms and the tests' expected values. A future rebrand
/// (a hypothetical `:limits` → `:sandbox` matching the Lunatic
/// terminology INSPIRATIONS §III.1 documents at the per-process level,
/// `:behavior` → `:gen-server` matching Erlang's verbatim
/// `gen_server` name, `:upgrade-from` → `:appup` matching Erlang's
/// verbatim appup terminology, or a per-consumer disambiguation as the
/// `defcaixa` macro stabilizes) would silently desynchronize the
/// production [`crate::Caixa::declared_servico_slots`] tagger from the
/// tests until a downstream consumer surfaced the drift at build time as
/// a matches-arm miss far from the rename's commit. This lift closes
/// that gap by routing both halves (production tagger + tests) through
/// three peer consts declared adjacent to the renderer-side
/// [`M2_KEY_*`] peers, so the "one canonical declaration per arm, next
/// to the axis" discipline the peer [`M2_BEHAVIOR_AUTHOR_KEY_ON_*`]
/// sub-slot author-label consts (889dc18) established for the M2
/// `:behavior` sub-slot's per-callback kebab-case labels extends onto
/// the M2 top-level slot axis. Same "one canonical byte-string per
/// typed axis" discipline every peer M2 / M3 renderer-wire-key axis
/// carries ([`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] /
/// [`M2_KEY_UPGRADE_FROM`], [`M2_LIMITS_KEY_MEMORY`] /
/// [`M2_LIMITS_KEY_FUEL`] / [`M2_LIMITS_KEY_WALL_CLOCK`] /
/// [`M2_LIMITS_KEY_CPU`] (d8b8b4f), [`M2_BEHAVIOR_KEY_ON_INIT`] etc.
/// (21fe462), [`M2_UPGRADE_FROM_KEY_FROM`] /
/// [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`] (36ffe65)).
pub const M2_AUTHOR_KEY_LIMITS: &str = ":limits";
/// Canonical author-facing kebab-case `(defcaixa … :behavior (…))`
/// top-level slot label the M2 per-Servico OTP-shaped `:behavior`
/// gen_server-callback-set slot surfaces under. Peer of
/// [`M2_AUTHOR_KEY_LIMITS`] on the sibling M2 top-level slot dual axis;
/// see [`M2_AUTHOR_KEY_LIMITS`] for the full lift rationale.
pub const M2_AUTHOR_KEY_BEHAVIOR: &str = ":behavior";
/// Canonical author-facing kebab-case `(defcaixa … :upgrade-from (…))`
/// top-level slot label the M2 per-Servico OTP-appup `:upgrade-from`
/// hot-code-reload table slot surfaces under. Peer of
/// [`M2_AUTHOR_KEY_LIMITS`] on the sibling M2 top-level slot dual axis;
/// see [`M2_AUTHOR_KEY_LIMITS`] for the full lift rationale.
pub const M2_AUTHOR_KEY_UPGRADE_FROM: &str = ":upgrade-from";
/// Canonical camelCase YAML sub-key the `:limits :memory` per-Servico
/// linear-memory-cap scalar-axis lands under inside the [`M2_KEY_LIMITS`]
/// overlay block. Peer of [`M2_KEY_LIMITS`] on the sibling `:limits`
/// sub-slot axis: `M2_KEY_LIMITS` names the overlay-container's
/// top-level key ("limits"), the four `M2_LIMITS_KEY_*` consts name the
/// four typed sub-keys ([`LIMITS_MEMORY_WASM32_MAX_BYTES`]-bounded
/// memory cap, [`crate::LIMITS_FUEL_MAX`]-bounded fuel budget,
/// [`crate::LIMITS_WALL_CLOCK_MAX`]-bounded wall-clock cap,
/// [`crate::LIMITS_CPU_MILLICORES_MAX`]-bounded soft cgroup CPU share)
/// that the emit-side [`servico_m2_overlay`] serializes through serde
/// (`LimitsSpec` carries `#[serde(rename_all = "camelCase")]`) and every
/// substrate-side test-side navigator probes to pin the round-trip
/// through the rendered `programs.yaml` per-Servico entry / lareira
/// chart `values.yaml` per-`pleme-computeunit` block. The lower-camel
/// shape (`"memory"` / `"fuel"` / `"wallClock"` / `"cpu"`) is
/// load-bearing: the serde-derive on [`crate::LimitsSpec`] emits under
/// the same shape and the drift-detection pin in `limits.rs::tests`
/// (`limits_spec_serde_keys_match_lifted_m2_limits_key_consts`)
/// serializes a fully-populated [`crate::LimitsSpec`] and asserts each
/// canonical `M2_LIMITS_KEY_*` byte-sequence appears in the JSON — so a
/// hypothetical future `rename_all = "snake_case"` / `"kebab-case"`
/// accident at the derive attribute surfaces as a build-time test
/// failure at `limits.rs` rather than as a silent test-side
/// `.get(<stale-camelCase-const>)` returning `None` far from the
/// derive-attr drift's commit. Same "one canonical byte-string per
/// typed axis" discipline every peer M2 / M3 wire-key axis carries
/// ([`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] / [`M2_KEY_UPGRADE_FROM`],
/// [`M3_PLACEMENT_KEY_ESTRATEGIA`] etc.).
pub const M2_LIMITS_KEY_MEMORY: &str = "memory";
/// Canonical camelCase YAML sub-key the `:limits :fuel` per-Servico
/// wasm-instruction-budget scalar-axis lands under inside the
/// [`M2_KEY_LIMITS`] overlay block. Peer of [`M2_LIMITS_KEY_MEMORY`] on
/// the sibling `:limits` sub-slot axis.
pub const M2_LIMITS_KEY_FUEL: &str = "fuel";
/// Canonical camelCase YAML sub-key the `:limits :wall-clock` per-Servico
/// wall-clock-cap scalar-axis lands under inside the [`M2_KEY_LIMITS`]
/// overlay block. Peer of [`M2_LIMITS_KEY_MEMORY`] on the sibling
/// `:limits` sub-slot axis; the camelCase shape (`"wallClock"`, not
/// `"wall_clock"`) is load-bearing per the serde-derive attribute on
/// [`crate::LimitsSpec`].
pub const M2_LIMITS_KEY_WALL_CLOCK: &str = "wallClock";
/// Canonical camelCase YAML sub-key the `:limits :cpu` per-Servico
/// soft-cgroup-CPU-share millicores scalar-axis lands under inside the
/// [`M2_KEY_LIMITS`] overlay block. Peer of [`M2_LIMITS_KEY_MEMORY`] on
/// the sibling `:limits` sub-slot axis.
pub const M2_LIMITS_KEY_CPU: &str = "cpu";
/// Canonical camelCase YAML sub-key the `:behavior :on-init` per-Servico
/// OTP-shaped instance-init-callback path scalar-axis lands under inside
/// the [`M2_KEY_BEHAVIOR`] overlay block. Peer of [`M2_KEY_BEHAVIOR`] on
/// the sibling `:behavior` sub-slot axis: [`M2_KEY_BEHAVIOR`] names the
/// overlay-container's top-level key ("behavior"), the six
/// `M2_BEHAVIOR_KEY_ON_*` consts name the six typed sub-keys the M2
/// [`crate::BehaviorSpec`] struct's OTP-shaped callback fields
/// (`on_init` / `on_call` / `on_cast` / `on_info` / `on_state_change` /
/// `on_terminate`, analogs of `gen_server:init/1` / `handle_call/3` /
/// `handle_cast/2` / `handle_info/2` / `code_change/3` / `terminate/2`
/// per `theory/INSPIRATIONS.md` §II.3) serialize as under the
/// `#[serde(rename_all = "camelCase")]` derive attribute
/// (`"onInit"` / `"onCall"` / `"onCast"` / `"onInfo"` / `"onStateChange"`
/// / `"onTerminate"`). Emitted by [`servico_m2_overlay`] as sub-keys of
/// the [`M2_KEY_BEHAVIOR`] overlay block and consumed by every
/// substrate-side test-side navigator that reaches into the rendered
/// `programs.yaml` per-Servico entry / lareira chart `values.yaml`
/// per-`pleme-computeunit` block to pin the per-callback round-trip.
/// The lower-camel shape is load-bearing: the serde-derive on
/// [`crate::BehaviorSpec`] emits under the same shape and the
/// drift-detection pin in `behavior.rs::tests`
/// (`behavior_spec_serde_keys_match_lifted_m2_behavior_key_consts`)
/// serializes a fully-populated [`crate::BehaviorSpec`] and asserts each
/// canonical `M2_BEHAVIOR_KEY_ON_*` byte-sequence appears in the JSON —
/// so a hypothetical future `rename_all = "snake_case"` / `"kebab-case"`
/// accident at the derive attribute or an OTP-lineage per-callback
/// rebrand (`:on-init` → `:on-start` matching Akka's per-actor
/// preStart naming, `:on-call` → `:on-request` matching a hypothetical
/// wasi:http/incoming-handler terminology flip, `:on-state-change` →
/// `:on-code-change` matching Erlang's verbatim `code_change/3` name)
/// coordinated at the type's derive attribute surfaces as a build-time
/// test failure at `behavior.rs` rather than as a silent test-side
/// `.get(<stale-camelCase-const>)` returning `None` far from the
/// derive-attr drift's commit. Same "one canonical byte-string per typed
/// axis" discipline every peer M2 / M3 wire-key axis carries
/// ([`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] / [`M2_KEY_UPGRADE_FROM`],
/// [`M2_LIMITS_KEY_MEMORY`] / [`M2_LIMITS_KEY_FUEL`] /
/// [`M2_LIMITS_KEY_WALL_CLOCK`] / [`M2_LIMITS_KEY_CPU`] (d8b8b4f),
/// [`M3_PLACEMENT_KEY_ESTRATEGIA`] etc.).
pub const M2_BEHAVIOR_KEY_ON_INIT: &str = "onInit";
/// Canonical camelCase YAML sub-key the `:behavior :on-call` per-Servico
/// OTP-shaped sync-request-handler path scalar-axis lands under inside
/// the [`M2_KEY_BEHAVIOR`] overlay block. Peer of
/// [`M2_BEHAVIOR_KEY_ON_INIT`] on the sibling `:behavior` sub-slot axis.
pub const M2_BEHAVIOR_KEY_ON_CALL: &str = "onCall";
/// Canonical camelCase YAML sub-key the `:behavior :on-cast` per-Servico
/// OTP-shaped async-fire-and-forget-handler path scalar-axis lands under
/// inside the [`M2_KEY_BEHAVIOR`] overlay block. Peer of
/// [`M2_BEHAVIOR_KEY_ON_INIT`] on the sibling `:behavior` sub-slot axis.
pub const M2_BEHAVIOR_KEY_ON_CAST: &str = "onCast";
/// Canonical camelCase YAML sub-key the `:behavior :on-info` per-Servico
/// OTP-shaped out-of-band-message-handler path scalar-axis lands under
/// inside the [`M2_KEY_BEHAVIOR`] overlay block. Peer of
/// [`M2_BEHAVIOR_KEY_ON_INIT`] on the sibling `:behavior` sub-slot axis.
pub const M2_BEHAVIOR_KEY_ON_INFO: &str = "onInfo";
/// Canonical camelCase YAML sub-key the `:behavior :on-state-change`
/// per-Servico OTP-shaped hot-upgrade state-migration path scalar-axis
/// lands under inside the [`M2_KEY_BEHAVIOR`] overlay block. Peer of
/// [`M2_BEHAVIOR_KEY_ON_INIT`] on the sibling `:behavior` sub-slot axis;
/// the camelCase shape (`"onStateChange"`, not `"on_state_change"`) is
/// load-bearing per the serde-derive attribute on
/// [`crate::BehaviorSpec`].
pub const M2_BEHAVIOR_KEY_ON_STATE_CHANGE: &str = "onStateChange";
/// Canonical camelCase YAML sub-key the `:behavior :on-terminate`
/// per-Servico OTP-shaped graceful-shutdown-callback path scalar-axis
/// lands under inside the [`M2_KEY_BEHAVIOR`] overlay block. Peer of
/// [`M2_BEHAVIOR_KEY_ON_INIT`] on the sibling `:behavior` sub-slot axis.
pub const M2_BEHAVIOR_KEY_ON_TERMINATE: &str = "onTerminate";
/// Canonical author-facing kebab-case `(defcaixa … :behavior (:on-init …))`
/// slot label the `:behavior :on-init` per-Servico OTP-shaped instance-init
/// callback axis surfaces under. Peer of [`M2_BEHAVIOR_KEY_ON_INIT`] on the
/// dual-axis pair every M2 `:behavior` sub-slot carries: the camelCase
/// [`M2_BEHAVIOR_KEY_ON_*`] const names the *renderer-side* wire key the
/// serde-derive-emitted [`M2_KEY_BEHAVIOR`] overlay carries under
/// (`"onInit"` etc, load-bearing per the `#[serde(rename_all = "camelCase")]`
/// attribute on [`crate::BehaviorSpec`]), the kebab-case
/// [`M2_BEHAVIOR_AUTHOR_KEY_ON_*`] const names the *author-facing* label the
/// [`crate::BehaviorSpec::declared_slots`] tagger threads through as the
/// `slot: &'static str` field on every [`crate::BehaviorError`] variant
/// (`":on-init"` etc, the exact byte-string authors see in the
/// per-slot value-shape diagnostic naming which of the six typed callback
/// slots the offending path landed on).
///
/// Until this lift landed the six kebab-case labels sat once each in
/// [`crate::BehaviorSpec::declared_slots`] as the six-arm inline
/// `":on-init"` / `":on-call"` / `":on-cast"` / `":on-info"` /
/// `":on-state-change"` / `":on-terminate"` byte-strings the tagger
/// iterated over, plus roughly two dozen test-side probe literals
/// asserting the diagnostic's `slot:` field carries the expected
/// per-arm value verbatim — with no compile-time link between the
/// tagger's arms and the tests' expected values. A future OTP-lineage
/// per-callback rebrand (`:on-init` → `:on-start` matching Akka's
/// per-actor preStart naming, `:on-call` → `:on-request` matching a
/// hypothetical wasi:http/incoming-handler terminology flip,
/// `:on-state-change` → `:on-code-change` matching Erlang's verbatim
/// `code_change/3` name, `:on-terminate` → `:on-shutdown` matching a
/// generic-lifecycle rebrand) or a per-consumer disambiguation (a
/// vocabulary shift on the author surface as the `defcaixa` macro
/// stabilizes) would silently desynchronize the production
/// [`crate::BehaviorSpec::declared_slots`] tagger from the tests until
/// a downstream consumer surfaced the drift at build time as a
/// matches-arm miss. This lift closes that gap by routing both halves
/// (production tagger + tests) through six peer consts declared
/// adjacent to the renderer-side [`M2_BEHAVIOR_KEY_ON_*`] peers, so
/// the "one canonical declaration per arm, next to the axis" discipline
/// the [`WitTarget::HTTP_FIELD_NAME`] / [`WitTarget::PUBSUB_FIELD_NAME`]
/// / [`WitTarget::STORE_FIELD_NAME`] payload-arm peer consts (174e96a)
/// already established for the [`crate::WitContract::target`]'s per-arm
/// diagnostic-scalar axis extends onto the M2 `:behavior` sub-slot
/// author-facing-label axis. Same "one canonical byte-string per typed
/// axis" discipline every peer M2 / M3 wire-key axis carries
/// ([`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] / [`M2_KEY_UPGRADE_FROM`],
/// [`M2_LIMITS_KEY_MEMORY`] / [`M2_LIMITS_KEY_FUEL`] /
/// [`M2_LIMITS_KEY_WALL_CLOCK`] / [`M2_LIMITS_KEY_CPU`] (d8b8b4f),
/// [`M2_BEHAVIOR_KEY_ON_INIT`] / [`M2_BEHAVIOR_KEY_ON_CALL`] /
/// [`M2_BEHAVIOR_KEY_ON_CAST`] / [`M2_BEHAVIOR_KEY_ON_INFO`] /
/// [`M2_BEHAVIOR_KEY_ON_STATE_CHANGE`] / [`M2_BEHAVIOR_KEY_ON_TERMINATE`]
/// (21fe462), [`M2_UPGRADE_FROM_KEY_FROM`] /
/// [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`] (36ffe65),
/// [`M3_PLACEMENT_KEY_ESTRATEGIA`] etc.), extended here to close the
/// M2 `:behavior` sub-slot's *author-facing-label* axis so the same
/// discipline the renderer-side wire-key axis carries applies to the
/// author-facing side.
pub const M2_BEHAVIOR_AUTHOR_KEY_ON_INIT: &str = ":on-init";
/// Canonical author-facing kebab-case `(defcaixa … :behavior (:on-call …))`
/// slot label for the `:behavior :on-call` per-Servico OTP-shaped
/// synchronous request/response handler axis. Peer of
/// [`M2_BEHAVIOR_AUTHOR_KEY_ON_INIT`] on the sibling `:behavior` sub-slot
/// author-facing-label axis.
pub const M2_BEHAVIOR_AUTHOR_KEY_ON_CALL: &str = ":on-call";
/// Canonical author-facing kebab-case `(defcaixa … :behavior (:on-cast …))`
/// slot label for the `:behavior :on-cast` per-Servico OTP-shaped
/// asynchronous fire-and-forget handler axis. Peer of
/// [`M2_BEHAVIOR_AUTHOR_KEY_ON_INIT`] on the sibling `:behavior` sub-slot
/// author-facing-label axis.
pub const M2_BEHAVIOR_AUTHOR_KEY_ON_CAST: &str = ":on-cast";
/// Canonical author-facing kebab-case `(defcaixa … :behavior (:on-info …))`
/// slot label for the `:behavior :on-info` per-Servico OTP-shaped
/// out-of-band message handler axis. Peer of
/// [`M2_BEHAVIOR_AUTHOR_KEY_ON_INIT`] on the sibling `:behavior` sub-slot
/// author-facing-label axis.
pub const M2_BEHAVIOR_AUTHOR_KEY_ON_INFO: &str = ":on-info";
/// Canonical author-facing kebab-case
/// `(defcaixa … :behavior (:on-state-change …))` slot label for the
/// `:behavior :on-state-change` per-Servico OTP-shaped hot-upgrade
/// state-migration axis. Peer of [`M2_BEHAVIOR_AUTHOR_KEY_ON_INIT`] on the
/// sibling `:behavior` sub-slot author-facing-label axis; the kebab-case
/// shape (`":on-state-change"`, not `":on-statechange"` /
/// `":on_state_change"`) is load-bearing per the author-facing
/// `(defcaixa …)` macro's canonical form and the exact byte-string the
/// per-slot [`crate::BehaviorError`] diagnostic threads through.
pub const M2_BEHAVIOR_AUTHOR_KEY_ON_STATE_CHANGE: &str = ":on-state-change";
/// Canonical author-facing kebab-case
/// `(defcaixa … :behavior (:on-terminate …))` slot label for the
/// `:behavior :on-terminate` per-Servico OTP-shaped graceful-shutdown
/// callback axis. Peer of [`M2_BEHAVIOR_AUTHOR_KEY_ON_INIT`] on the sibling
/// `:behavior` sub-slot author-facing-label axis.
pub const M2_BEHAVIOR_AUTHOR_KEY_ON_TERMINATE: &str = ":on-terminate";
/// Canonical camelCase YAML sub-key the `:upgrade-from :from` per-entry
/// OTP-appup-shaped prior-`:versao` semver-string scalar-axis lands under
/// inside each element of the [`M2_KEY_UPGRADE_FROM`] overlay sequence.
/// Peer of [`M2_KEY_UPGRADE_FROM`] on the sibling `:upgrade-from` sub-slot
/// axis: [`M2_KEY_UPGRADE_FROM`] names the overlay-container's top-level
/// key ("upgradeFrom"), the two `M2_UPGRADE_FROM_KEY_*` consts name the
/// two typed sub-keys the M2 [`crate::UpgradeFromEntry`] struct's
/// OTP-appup-shaped per-entry fields (`from` semver-of-the-prior-`:versao`
/// / `instructions` typed [`crate::UpgradeInstruction`] list, analogs of
/// the OTP `.appup` file's `{FromVsn, [Instruction, …]}` per-entry tuple
/// per `theory/INSPIRATIONS.md` §II.4) serialize as under the
/// `#[serde(rename_all = "camelCase")]` derive attribute (`"from"` /
/// `"instructions"`). Emitted by [`servico_m2_overlay`] as sub-keys of
/// each element of the [`M2_KEY_UPGRADE_FROM`] overlay sequence and
/// consumed by every substrate-side test-side navigator that reaches into
/// the rendered `programs.yaml` per-Servico entry / lareira chart
/// `values.yaml` per-`pleme-computeunit` block to pin the per-entry
/// round-trip. The lower-camel shape (`"from"` / `"instructions"`) is
/// load-bearing: the serde-derive on [`crate::UpgradeFromEntry`] emits
/// under the same shape and the drift-detection pin in
/// `upgrade.rs::tests`
/// (`upgrade_from_entry_serde_keys_match_lifted_m2_upgrade_from_key_consts`)
/// serializes a fully-populated [`crate::UpgradeFromEntry`] and asserts
/// each canonical `M2_UPGRADE_FROM_KEY_*` byte-sequence appears in the
/// JSON — so a hypothetical future `rename_all = "snake_case"` /
/// `"kebab-case"` accident at the derive attribute or an OTP-lineage
/// per-entry-key rebrand (`:from` → `:prior-versao` matching a hypothetical
/// verbatim-Erlang `FromVsn` collapse, `:instructions` → `:steps` matching
/// a hypothetical Akka appup-shape rebrand) coordinated at the type's
/// derive attribute surfaces as a build-time test failure at `upgrade.rs`
/// rather than as a silent test-side `.get(<stale-camelCase-const>)`
/// returning `None` far from the derive-attr drift's commit. Same "one
/// canonical byte-string per typed axis" discipline every peer M2 / M3
/// wire-key axis carries ([`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] /
/// [`M2_KEY_UPGRADE_FROM`], [`M2_LIMITS_KEY_MEMORY`] /
/// [`M2_LIMITS_KEY_FUEL`] / [`M2_LIMITS_KEY_WALL_CLOCK`] /
/// [`M2_LIMITS_KEY_CPU`] (d8b8b4f), [`M2_BEHAVIOR_KEY_ON_INIT`] /
/// [`M2_BEHAVIOR_KEY_ON_CALL`] / [`M2_BEHAVIOR_KEY_ON_CAST`] /
/// [`M2_BEHAVIOR_KEY_ON_INFO`] / [`M2_BEHAVIOR_KEY_ON_STATE_CHANGE`] /
/// [`M2_BEHAVIOR_KEY_ON_TERMINATE`] (21fe462),
/// [`M3_PLACEMENT_KEY_ESTRATEGIA`] etc.). Closes the M2 sub-slot camelCase
/// key axis: with this lift the three M2 typed slots (`:limits` /
/// `:behavior` / `:upgrade-from`) all have their canonical camelCase
/// sub-slot key constants pinned into caixa-core.
pub const M2_UPGRADE_FROM_KEY_FROM: &str = "from";
/// Canonical camelCase YAML sub-key the `:upgrade-from :instructions`
/// per-entry OTP-appup-shaped typed [`crate::UpgradeInstruction`] list
/// axis lands under inside each element of the [`M2_KEY_UPGRADE_FROM`]
/// overlay sequence. Peer of [`M2_UPGRADE_FROM_KEY_FROM`] on the sibling
/// `:upgrade-from` sub-slot axis.
pub const M2_UPGRADE_FROM_KEY_INSTRUCTIONS: &str = "instructions";
/// Canonical `#[serde(tag = "…")]` discriminator-key byte-sequence the
/// M2 `:upgrade-from :instructions` per-entry OTP-appup
/// [`crate::UpgradeInstruction`] enum surfaces its variant tag under
/// on serde emission — the internally-tagged wire key downstream
/// consumers navigate to (`serde_json::to_value(&instr).get("kind")`
/// / `serde_yaml::Value::Mapping.get("kind")` / hand-authored `{"kind":
/// "load-module", "module": "…"}` JSON) to disambiguate which of the
/// five OTP-shaped variants they hold. The `#[serde(tag = "kind",
/// rename_all = "kebab-case")]` attribute on
/// [`crate::UpgradeInstruction`] emits exactly this byte-sequence as
/// the tag-slot key, and this const names the same byte-string one
/// altitude above the derive attribute so every downstream consumer
/// that reaches for the tag (the reflection-vs-serde round-trip check
/// in [`caixa-core/tests/dispatcher_registration.rs`] that probes
/// `v.get("kind")` against every variant's expected kebab-case tag,
/// the future M4 `mesh.pleme.io/v1alpha1/Caixa` CR materializer's
/// upgrade-instruction admission webhook, any wasm-operator dispatch
/// step that navigates the serialized instruction blob to route by
/// variant) routes through one canonical `&'static str` rather than
/// re-inlining the literal.
///
/// Lifted as a typed `pub const` (rather than an inline literal at
/// the `#[serde(tag = "…")]` attribute site + every consumer probe)
/// so the tag-key axis has exactly one source of truth — a future
/// serde-shape rebrand (`tag = "kind"` → `tag = "type"` matching a
/// JSON-Schema `discriminator` convention, `tag = "kind"` → `tag = "op"`
/// matching a hypothetical OTP-abbreviation collapse, `tag = "kind"`
/// → `tag = "instruction"` matching a hypothetical author-surface
/// self-description flip as the `defcaixa` macro stabilizes) lands as
/// an edit to exactly one const, and every consumer that reaches for
/// the tag picks it up at build time rather than at runtime as a
/// silent `.get(<stale-tag-key>)` returning `None` far from the
/// derive-attr drift's commit. Same "one canonical byte-string per
/// typed axis" discipline every peer M2 sub-slot wire-key axis
/// carries ([`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] /
/// [`M2_KEY_UPGRADE_FROM`], [`M2_LIMITS_KEY_MEMORY`] etc. (d8b8b4f),
/// [`M2_BEHAVIOR_KEY_ON_INIT`] etc. (21fe462),
/// [`M2_UPGRADE_FROM_KEY_FROM`] / [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`]
/// (36ffe65)), now extending the lift onto the last remaining
/// un-lifted wire-key axis on the M2 `:upgrade-from :instructions`
/// typed slot: the internally-tagged variant-discriminator key that
/// pairs with the [`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`] etc.
/// (56120ef) per-variant kebab-case *values* the same
/// `#[serde(tag = "kind", rename_all = "kebab-case")]` attribute
/// emits. With this lift the `:upgrade-from :instructions` axis has
/// its dual (`key = "kind"` + five variant-value tags) fully lifted
/// into caixa-core.
pub const M2_UPGRADE_INSTRUCTION_KEY_KIND: &str = "kind";
/// Canonical per-variant data-field JSON key the M2 `:upgrade-from
/// :instructions` per-entry OTP-appup
/// [`crate::UpgradeInstruction::LoadModule`] / [`crate::UpgradeInstruction::SoftPurge`]
/// / [`crate::UpgradeInstruction::Purge`] variants surface their
/// module-name payload under on serde emission — the internally-tagged
/// per-variant field byte-string every downstream consumer reading the
/// module string reaches for
/// (`serde_json::to_value(&instr).get("module")` /
/// `serde_yaml::Value::Mapping.get("module")` / hand-authored
/// `{"kind": "load-module", "module": "hello-rio"}` JSON blobs the
/// wasm-operator's upgrade-dispatch step consumes to route the
/// per-module load / soft-purge / purge action). The three variants
/// carrying a `module: String` field
/// ([`crate::UpgradeInstruction::LoadModule`], [`crate::UpgradeInstruction::SoftPurge`],
/// [`crate::UpgradeInstruction::Purge`]) all emit this exact
/// byte-sequence as the data-field JSON key alongside the
/// [`M2_UPGRADE_INSTRUCTION_KEY_KIND`] tag-key on the same instruction
/// blob — the `#[serde(tag = "kind", rename_all = "kebab-case")]`
/// attribute on [`crate::UpgradeInstruction`] promotes each variant's
/// struct-field name to a sibling JSON key at the same nesting level as
/// the tag, so a `LoadModule { module: "hello-rio" }` serializes to
/// `{"kind": "load-module", "module": "hello-rio"}` — one tag axis, one
/// data-field axis, both live on the same JSON object and both must be
/// pinned into caixa-core so a future rebrand at either axis surfaces
/// as a build-time test failure rather than an apply-time
/// `.get(<stale-field-key>)` returning `None` far from the field-name
/// drift's commit.
///
/// Lifted as a typed `pub const` (rather than an inline literal at every
/// consumer probe) so the per-variant module-field axis has exactly one
/// source of truth — a future struct-field rebrand (`module: String` →
/// `component: String` matching a hypothetical WASI component-model
/// naming pass, `module: String` → `name: String` matching the
/// canonical `KUBE_KEY_NAME` axis, `module: String` → `target: String`
/// matching the sibling `:contratos :para` axis) lands as an edit to
/// exactly one const, and every consumer that probes the module-field
/// key picks it up at build time. Same "one canonical byte-string per
/// typed axis" discipline the sibling
/// [`M2_UPGRADE_INSTRUCTION_KEY_KIND`] (6a203d7) lift established on
/// the peer tag-slot key axis on the same
/// [`crate::UpgradeInstruction`] enum: `KEY_KIND` names the tag axis,
/// `FIELD_KEY_MODULE` names the module-payload axis, and the two must
/// be disjoint by construction (an internally-tagged serialization
/// where the tag key collides with a data-field key silently corrupts
/// every serialized blob — same failure mode
/// `m2_upgrade_instruction_key_kind_const_disjoint_from_variant_data_keys`
/// pins on the sibling axis).
///
/// With this lift and its [`M2_UPGRADE_INSTRUCTION_FIELD_KEY_SCRIPT`]
/// peer, the whole `:upgrade-from :instructions` variant-JSON dual is
/// lifted into caixa-core: the tag *key*
/// ([`M2_UPGRADE_INSTRUCTION_KEY_KIND`]), the five tag *values*
/// ([`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`] etc.), and the two
/// data-field *keys* (this const and `SCRIPT`) all sit as
/// single-source-of-truth `&'static str`s. Any future serde-shape
/// rebrand touching either axis (tag key rename, per-variant field
/// rename, `rename_all` regime flip) surfaces at build time.
pub const M2_UPGRADE_INSTRUCTION_FIELD_KEY_MODULE: &str = "module";
/// Canonical per-variant data-field JSON key the M2 `:upgrade-from
/// :instructions` per-entry [`crate::UpgradeInstruction::StateChange`]
/// variant surfaces its script-path payload under on serde emission —
/// the internally-tagged per-variant field byte-string every downstream
/// consumer reading the migration-script path reaches for
/// (`serde_json::to_value(&instr).get("script")` /
/// `serde_yaml::Value::Mapping.get("script")` / hand-authored
/// `{"kind": "state-change", "script": "lib/migrations/v01-to-v02.lisp"}`
/// JSON blobs the wasm-operator's upgrade-dispatch step consumes to
/// route the per-`gen_server` `code_change/3` migration action). Peer
/// of [`M2_UPGRADE_INSTRUCTION_FIELD_KEY_MODULE`] on the sibling
/// module-payload axis; see [`M2_UPGRADE_INSTRUCTION_FIELD_KEY_MODULE`]
/// for the full lift rationale.
///
/// The [`crate::UpgradeInstruction::StateChange`] variant is the only
/// one carrying a `script: PathBuf` field — the two module-bearing
/// variants ([`crate::UpgradeInstruction::LoadModule`],
/// [`crate::UpgradeInstruction::SoftPurge`],
/// [`crate::UpgradeInstruction::Purge`]) route through the sibling
/// [`M2_UPGRADE_INSTRUCTION_FIELD_KEY_MODULE`] const, and
/// [`crate::UpgradeInstruction::Restart`] carries no data field at all.
/// Same one-const-per-typed-axis discipline as the sibling.
pub const M2_UPGRADE_INSTRUCTION_FIELD_KEY_SCRIPT: &str = "script";
/// Canonical author-facing kebab-case tag the M2 `:upgrade-from
/// :instructions` per-entry OTP-appup [`crate::UpgradeInstruction::LoadModule`]
/// variant surfaces under — the `:kind` field the
/// [`crate::UpgradeError::ModuleEmpty`] / [`crate::UpgradeError::ModuleInvalid`]
/// / [`crate::UpgradeError::DuplicateCleanup`] / [`crate::UpgradeError::PurgeWithoutPriorLoad`]
/// diagnostics carry so the author can grep their caixa.lisp for
/// `(:load-module …)` and fix it in one edit. The
/// [`crate::UpgradeInstruction::lisp_form`] production dispatch and every
/// test-side probe that pins a `kind:` / `kinds:` / `other_kinds:` /
/// `prior_cleanup_kind:` field routes through this const, so a future
/// per-variant kebab-case rebrand (`:load-module` → `:load` matching a
/// hypothetical Erlang `code:load_module` collapse, `:load-module` →
/// `:reload` matching a hypothetical Elixir/Phoenix hot-reload rebrand,
/// or a per-consumer disambiguation as the `defcaixa` macro stabilizes)
/// lands at one const-edit per arm and reaches both surfaces
/// (production dispatch + tests) by construction. Peer of
/// [`M2_UPGRADE_FROM_KEY_FROM`] / [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`]
/// on the sibling `:upgrade-from` sub-slot renderer-wire-key axis
/// (36ffe65) — this const family extends the same "one canonical
/// byte-string per typed axis" discipline onto the *author-facing*
/// per-instruction-variant tag axis one altitude below the
/// `:instructions` container. Same "one canonical declaration per arm,
/// next to the axis" discipline the peer [`M2_BEHAVIOR_AUTHOR_KEY_ON_INIT`]
/// etc. (889dc18) established for the M2 `:behavior` sub-slot's
/// per-callback kebab-case labels, [`CONTRATO_AUTHOR_KEY_DE`] /
/// [`CONTRATO_AUTHOR_KEY_PARA`] (f50c875) for the M3 `:contratos`
/// per-entry endpoint labels, and every top-level [`M2_AUTHOR_KEY_LIMITS`]
/// (f49c8b0) / [`M3_AUTHOR_KEY_MEMBROS`] (882f498) /
/// [`SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] (be40492) family established.
pub const M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE: &str = ":load-module";
/// Canonical author-facing kebab-case tag the M2 `:upgrade-from
/// :instructions` per-entry [`crate::UpgradeInstruction::StateChange`]
/// variant surfaces under. Peer of [`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`]
/// on the sibling per-instruction-variant tag axis; see
/// [`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`] for the full lift rationale.
pub const M2_UPGRADE_INSTRUCTION_KIND_STATE_CHANGE: &str = ":state-change";
/// Canonical author-facing kebab-case tag the M2 `:upgrade-from
/// :instructions` per-entry [`crate::UpgradeInstruction::SoftPurge`]
/// variant surfaces under. Peer of [`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`]
/// on the sibling per-instruction-variant tag axis; see
/// [`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`] for the full lift rationale.
pub const M2_UPGRADE_INSTRUCTION_KIND_SOFT_PURGE: &str = ":soft-purge";
/// Canonical author-facing kebab-case tag the M2 `:upgrade-from
/// :instructions` per-entry [`crate::UpgradeInstruction::Purge`]
/// variant surfaces under. Peer of [`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`]
/// on the sibling per-instruction-variant tag axis; see
/// [`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`] for the full lift rationale.
pub const M2_UPGRADE_INSTRUCTION_KIND_PURGE: &str = ":purge";
/// Canonical author-facing kebab-case tag the M2 `:upgrade-from
/// :instructions` per-entry [`crate::UpgradeInstruction::Restart`]
/// variant surfaces under. Peer of [`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`]
/// on the sibling per-instruction-variant tag axis; see
/// [`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`] for the full lift rationale.
pub const M2_UPGRADE_INSTRUCTION_KIND_RESTART: &str = ":restart";
/// Canonical lowercase JSON/YAML discriminator-key the
/// [`crate::dep::DepSource`] enum's `#[serde(tag = "tipo", rename_all
/// = "lowercase")]` derive emits as the tag axis at each serialized
/// `Dep.fonte` block — the load-bearing byte-string every downstream
/// consumer reading a Dep source (the [`caixa_resolver`] per-`:deps`
/// git-clone dispatcher, the future `feira lock` / `feira resolve`
/// `lacre.lisp` closure writer, every test payload that reaches
/// `Value::get(DEP_SOURCE_KEY_TIPO)` to pin the variant discriminator)
/// must probe on. Peer of the two variant tag consts
/// [`DEP_SOURCE_TIPO_GIT`] and [`DEP_SOURCE_TIPO_PATH`] the sibling
/// `rename_all = "lowercase"` axis lifts on the same discriminator
/// block: the [`DEP_SOURCE_KEY_TIPO`] const names the outer tag *key*
/// (`"tipo":`) the `tag = "tipo"` attribute pins, the two
/// `DEP_SOURCE_TIPO_*` consts name the two admitted tag *values*
/// (`"git"` / `"path"`) the `rename_all = "lowercase"` attribute pins
/// as the discriminator's closed-set arms.
///
/// Until this lift landed the two load-bearing bytes at both altitudes
/// (`"tipo"` at the tag key, `"git"` / `"path"` at the two variant
/// tags) sat only as inline literals — at the `#[serde(tag = "tipo",
/// rename_all = "lowercase")]` attribute (dep.rs:59) and at one
/// round-trip test payload (`git_source_json_round_trip` pinning
/// `"tipo":"git"` inline, dep.rs:13563) — with no compile-time link
/// between the load-bearing serde-derive attribute and the downstream
/// consumers that probe the emit-side discriminator via
/// `Value::get(...)`. A future accidental `tag = "type"` /
/// `tag = "source_type"` typo at the attribute (English-uniformity
/// rebrand as the substrate publishes its typed manifest schema
/// outside pleme-io, verbatim-Cargo `"type"` alignment matching a
/// hypothetical Zig-store convergence, or per-consumer disambiguation
/// as the `defcaixa` macro stabilizes) — or a `rename_all` rebrand
/// (`"UPPERCASE"` / `"snake_case"` / `"kebab-case"`) — would silently
/// break the resolver's `Dep.fonte` dispatch and every downstream
/// `lacre.lisp` closure consumer, with the drift surfacing at fetch
/// time far from the derive-attr commit as an unknown-variant deserialize
/// failure. Pinning the three canonical byte-sequences to `&'static str`
/// consts + running the serialize-and-check drift-detection pins on
/// both variants closes the drift structurally at caixa-core build time.
///
/// Same "one canonical byte-string per typed serialized-key axis"
/// discipline the peer [`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`] etc.
/// (56120ef), [`M3_PLACEMENT_ESTRATEGIA_SINGLE_NODE`] etc., and
/// [`HELM_CHART_TYPE_APPLICATION`] / [`HELM_CHART_TYPE_LIBRARY`]
/// (1c5eb9d) closed-set variant-tag lifts carry — extended here to the
/// [`crate::dep::DepSource`] `:deps :fonte` typed slot's discriminator
/// axis at both altitudes (discriminator key + closed-set variant tags),
/// the last `#[serde(tag = ..., rename_all = ...)]` discriminator
/// family in caixa-core lacking a lifted peer.
pub const DEP_SOURCE_KEY_TIPO: &str = "tipo";
/// Canonical lowercase JSON/YAML discriminator-value the
/// [`crate::dep::DepSource::Git`] variant surfaces under — the
/// `"git"` scalar the `#[serde(tag = "tipo", rename_all =
/// "lowercase")]` derive emits at the [`DEP_SOURCE_KEY_TIPO`] axis
/// for the Git arm. Peer of [`DEP_SOURCE_TIPO_PATH`] on the sibling
/// closed-set variant-tag axis; see [`DEP_SOURCE_KEY_TIPO`] for the
/// full lift rationale. The scalar is derived from the Rust variant
/// name `Git` by the `rename_all = "lowercase"` derive; ASCII-lowercase
/// of `Git` is `git`.
pub const DEP_SOURCE_TIPO_GIT: &str = "git";
/// Canonical lowercase JSON/YAML discriminator-value the
/// [`crate::dep::DepSource::Path`] variant surfaces under — the
/// `"path"` scalar the `#[serde(tag = "tipo", rename_all =
/// "lowercase")]` derive emits at the [`DEP_SOURCE_KEY_TIPO`] axis
/// for the Path arm. Peer of [`DEP_SOURCE_TIPO_GIT`] on the sibling
/// closed-set variant-tag axis; see [`DEP_SOURCE_KEY_TIPO`] for the
/// full lift rationale.
///
/// Byte-identical to [`CILIUM_KEY_PATH`], [`FLUX_KUSTOMIZATION_KEY_PATH`],
/// and [`GATEWAY_API_KEY_PATH`] today — all four resolve to the same
/// four-byte `"path"` literal — but semantically distinct: the three
/// `*_KEY_PATH` consts name YAML container/leaf-*key* axes on their
/// respective K8s CR schemas (Cilium L7 HTTP-rule filesystem-path
/// container, Flux Kustomization git-source-subtree container, Gateway
/// API URL-path-match container), while this constant names a
/// discriminator *value* on the manifest-side [`crate::dep::DepSource`]
/// typed enum's closed-set variant tag axis (Path variant vs Git
/// variant). Splitting the four lets each axis's future rebrand land
/// independently at its canonical const definition without coupling
/// the `:deps :fonte` Path-variant discriminator axis to the three
/// K8s-CR key axes (or vice versa) — same
/// "byte-identical-but-semantically-distinct" discipline the peer
/// [`FLEET_PROGRAMS_KEY_NAME`] / [`KUBE_KEY_NAME`] and
/// [`FLEET_PROGRAMS_KEY_VERSAO`] / [`MEMBRO_KEY_VERSAO`] splits
/// established on the sibling per-entry key axes.
pub const DEP_SOURCE_TIPO_PATH: &str = "path";
/// Canonical [`crate::LayoutError::MissingEntry`] `kind: &'static str`
/// discriminator scalar the M2 `:behavior` typed slot's per-callback
/// on-disk-leaf existence gate surfaces under — the byte-string every
/// [`crate::LayoutInvariants::verify`] emission carries when a
/// `:behavior :on-init` / `:on-call` / `:on-cast` / `:on-info` /
/// `:on-state-change` / `:on-terminate` sub-slot's tatara-lisp source
/// path fails to resolve against the caixa root's on-disk layout. Names
/// the "M2 :behavior sub-slot leaf-kind" axis one altitude below the
/// [`M2_AUTHOR_KEY_BEHAVIOR`] (f49c8b0) parent-slot label: the
/// top-level [`M2_AUTHOR_KEY_BEHAVIOR`] const names the M2 slot itself
/// on the author surface (`(defcaixa … :behavior (…))`), the six
/// [`M2_BEHAVIOR_AUTHOR_KEY_ON_*`] consts (889dc18) name the per-
/// callback sub-slot labels the author writes (`(:on-init "lib/init.lisp"
/// …)`), and this const names the per-slot-family leaf-kind byte-string
/// the layout diagnostic emits when the on-disk `lib/init.lisp` file
/// doesn't exist ("MissingEntry { kind: \"behavior-callback\", path:
/// /root/lib/init.lisp }").
///
/// Peer of [`LAYOUT_MISSING_ENTRY_KIND_UPGRADE_SCRIPT`] on the sibling
/// M2 `:upgrade-from` typed slot's per-entry leaf-kind axis: the two
/// consts split the M2 slot-family's on-disk-leaf categorization axis
/// into its two per-slot arms, so the `LayoutError::MissingEntry
/// { kind: &'static str, .. }` discriminator's accept-set has one
/// canonical declaration per arm rather than two inline byte-strings
/// scattered across [`crate::layout`]'s per-slot existence gates.
///
/// Until this lift landed the byte `"behavior-callback"` sat at two
/// sites in [`crate::layout`] — once at the [`crate::LayoutInvariants::verify`]
/// per-`:behavior :on-*` sub-slot existence gate's `MissingEntry` emit
/// (production, layout.rs:902), once at the
/// [`crate::layout::tests::behavior_callback_must_exist`]
/// (or peer test) `matches!(…, MissingEntry { kind: "behavior-callback",
/// .. })` shape probe (layout.rs:3152) — with no compile-time link
/// between the two: a future per-consumer rebrand (a hypothetical
/// `"behavior-callback"` → `"m2-behavior-callback"` for altitude-explicit
/// scoping as the M3+ layout gates grow their own per-slot leaf-kind
/// labels, `"behavior-callback"` → `"gen-server-callback"` matching a
/// verbatim-OTP rebrand of the [`M2_AUTHOR_KEY_BEHAVIOR`] slot's
/// `gen_server`-lineage identity, or a per-diagnostic disambiguation as
/// the `defcaixa` macro stabilizes and per-callback shapes diverge)
/// would silently desynchronize the production `MissingEntry` emission
/// from the test's `matches!` shape probe until build time surfaced the
/// drift as a pattern-arm miss far from the rename's commit. This lift
/// closes that gap by routing both halves (production emit + test
/// probe) through one peer const declared adjacent to the M2 top-level
/// slot-label family, so the "one canonical declaration per arm, next
/// to the axis" discipline the peer [`M2_AUTHOR_KEY_LIMITS`] /
/// [`M2_AUTHOR_KEY_BEHAVIOR`] / [`M2_AUTHOR_KEY_UPGRADE_FROM`]
/// (f49c8b0), [`M2_BEHAVIOR_AUTHOR_KEY_ON_INIT`] etc. (889dc18),
/// [`M2_UPGRADE_INSTRUCTION_KIND_LOAD_MODULE`] etc. (56120ef),
/// [`M3_AUTHOR_KEY_MEMBROS`] etc. (882f498), and
/// [`SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] etc. (be40492) top-level +
/// sub-slot author-facing-label consts established for the sibling
/// M2 / M3 / Supervisor slot-family axes extends onto the M2
/// layout-check leaf-kind categorization axis.
///
/// Byte-shape note: unlike the peer author-facing kebab-case slot
/// labels (which carry the leading `:` sigil because the tatara-lisp
/// reader emits keyword tokens as `:kebab-case` and the author writes
/// them verbatim in `caixa.lisp`), this discriminator has no leading
/// `:` because the substrate consumer reading the value is the layout
/// diagnostic's downstream printer — the operator running `feira build`
/// sees `LayoutError::MissingEntry { kind: "behavior-callback", .. }`
/// as a categorization label, not as a tatara-lisp keyword to be
/// grep'd for in the source `.lisp`. Same shape distinction the peer
/// [`crate::WitTarget::HTTP_FIELD_NAME`] (= `"endpoint"`) /
/// [`crate::WitTarget::PUBSUB_FIELD_NAME`] (= `"subject"`) /
/// [`crate::WitTarget::STORE_FIELD_NAME`] (= `"slot"`) /
/// [`crate::WitTarget::CAPABILITY_EXPECTED`] (= `"none"`) consts
/// established on the sibling `:contratos` per-entry payload-field-
/// name axis: the field-name byte-strings are the downstream
/// diagnostic's format-argument scalars, prefixed by the `:` inside
/// the error format template (`":{expected}"`) rather than baked into
/// the const.
pub const LAYOUT_MISSING_ENTRY_KIND_BEHAVIOR_CALLBACK: &str = "behavior-callback";
/// Canonical [`crate::LayoutError::MissingEntry`] `kind: &'static str`
/// discriminator scalar the M2 `:upgrade-from` typed slot's per-entry
/// [`crate::UpgradeInstruction::StateChange`] script-path on-disk-leaf
/// existence gate surfaces under — the byte-string every
/// [`crate::LayoutInvariants::verify`] emission carries when a
/// `(:state-change "<script>.lisp")` instruction's tatara-lisp source
/// path fails to resolve against the caixa root's on-disk layout.
/// Peer of [`LAYOUT_MISSING_ENTRY_KIND_BEHAVIOR_CALLBACK`] on the sibling
/// M2 `:behavior` typed slot's per-callback leaf-kind axis; see
/// [`LAYOUT_MISSING_ENTRY_KIND_BEHAVIOR_CALLBACK`] for the full lift
/// rationale.
pub const LAYOUT_MISSING_ENTRY_KIND_UPGRADE_SCRIPT: &str = "upgrade-script";
/// Canonical [`crate::LayoutError::MissingEntry`] `kind: &'static str`
/// discriminator scalar the M0 `:kind Biblioteca` typed slot's
/// per-`:bibliotecas` entry on-disk-leaf existence gate surfaces under
/// — the byte-string every [`crate::LayoutInvariants::verify`]
/// emission carries when a `:bibliotecas ("lib/foo.lisp" …)` entry's
/// tatara-lisp source path fails to resolve against the caixa root's
/// on-disk layout. Peer of [`LAYOUT_MISSING_ENTRY_KIND_EXE`] /
/// [`LAYOUT_MISSING_ENTRY_KIND_SERVICO`] on the sibling M0 code-slot
/// per-directory leaf-kind axes, and of the M2-tier
/// [`LAYOUT_MISSING_ENTRY_KIND_BEHAVIOR_CALLBACK`] /
/// [`LAYOUT_MISSING_ENTRY_KIND_UPGRADE_SCRIPT`] (95c9c4c) leaf-kind
/// labels on the [`crate::LayoutError::MissingEntry`] `kind:
/// &'static str` discriminator's accept-set — completes the
/// M0-tier arm of the same per-slot leaf-kind categorization axis
/// the M2 lift established.
///
/// Byte-identical to [`crate::CaixaKind::Biblioteca`]'s
/// [`crate::CaixaKind::as_str`] output today (both resolve to the
/// same eleven-byte `"biblioteca"` scalar) — the pin test
/// `layout_missing_entry_kind_m0_consts_align_with_caixa_kind_as_str`
/// makes the coincidence load-bearing rather than accidental so a
/// future rename that touches either axis (a per-consumer
/// disambiguation as the layout diagnostic vocabulary sharpens, a
/// verbatim-Portuguese rebrand of the [`crate::CaixaKind`]'s
/// human-readable-form arm) has to reach both sites in lockstep
/// or the pin trips at build time.
pub const LAYOUT_MISSING_ENTRY_KIND_BIBLIOTECA: &str = "biblioteca";
/// Canonical [`crate::LayoutError::MissingEntry`] `kind: &'static str`
/// discriminator scalar the M0 `:kind Binario` typed slot's per-`:exe`
/// entry on-disk-leaf existence gate surfaces under — the byte-string
/// every [`crate::LayoutInvariants::verify`] emission carries when an
/// `:exe ("exe/tool.lisp" …)` entry's tatara-lisp source path fails to
/// resolve against the caixa root's on-disk layout. Peer of
/// [`LAYOUT_MISSING_ENTRY_KIND_BIBLIOTECA`] /
/// [`LAYOUT_MISSING_ENTRY_KIND_SERVICO`] on the sibling M0 code-slot
/// per-directory leaf-kind axes; see
/// [`LAYOUT_MISSING_ENTRY_KIND_BIBLIOTECA`] for the shared lift
/// rationale.
///
/// Semantically distinct from [`crate::CaixaKind::Binario`]'s
/// [`crate::CaixaKind::as_str`] output (`"binario"`) — this const
/// names the *directory-entry* leaf-kind label (the M0 `:exe`
/// per-entry axis carries source files under the `exe/` subtree),
/// not the caixa's own [`crate::CaixaKind`] discriminator. The
/// [`crate::LayoutError::MissingEntry`] `kind` emission consumer
/// (the operator running `feira build`) reads this as a per-directory
/// categorization label (`"missing exe/... entry"`), whereas
/// [`crate::CaixaKind::as_str`] names the whole caixa's runtime kind
/// (`"binario"` = "this caixa produces one or more binaries"). Two
/// axes, two lifts — the pin test
/// `layout_missing_entry_kind_m0_consts_align_with_caixa_kind_as_str`
/// asserts the *inequality* between this const and
/// [`crate::CaixaKind::Binario`]'s [`crate::CaixaKind::as_str`]
/// output, so a future accidental collapse of the two axes onto a
/// single scalar surfaces at build time.
pub const LAYOUT_MISSING_ENTRY_KIND_EXE: &str = "exe";
/// Canonical [`crate::LayoutError::MissingEntry`] `kind: &'static str`
/// discriminator scalar the M0 `:kind Servico` typed slot's
/// per-`:servicos` entry on-disk-leaf existence gate surfaces under —
/// the byte-string every [`crate::LayoutInvariants::verify`] emission
/// carries when a `:servicos ("servicos/foo.computeunit.yaml" …)`
/// entry fails to resolve against the caixa root's on-disk layout.
/// Peer of [`LAYOUT_MISSING_ENTRY_KIND_BIBLIOTECA`] /
/// [`LAYOUT_MISSING_ENTRY_KIND_EXE`] on the sibling M0 code-slot
/// per-directory leaf-kind axes; see
/// [`LAYOUT_MISSING_ENTRY_KIND_BIBLIOTECA`] for the shared lift
/// rationale. Byte-identical to [`crate::CaixaKind::Servico`]'s
/// [`crate::CaixaKind::as_str`] output today (both resolve to the
/// same seven-byte `"servico"` scalar).
pub const LAYOUT_MISSING_ENTRY_KIND_SERVICO: &str = "servico";
/// Canonical human-readable label the M0 [`crate::CaixaKind::Biblioteca`]
/// arm surfaces under [`crate::CaixaKind::as_str`] and (routed through
/// it) [`std::fmt::Display`] — the byte-string every future diagnostic
/// / graph / audit consumer that formats a `:kind` variant as
/// user-facing text lands on (the future wasm-operator's per-caixa
/// startup log line naming the loaded caixa's typed shape, the future
/// `feira app graph` per-member kind column, the future M4
/// `wasm.pleme.io/v1alpha1/ComputeUnit` / `mesh.pleme.io/v1alpha1/*` CR
/// materializer's admission-webhook rejection body naming which typed
/// kind the offending manifest carries). Peer of the sibling four
/// [`CAIXA_KIND_LABEL_BINARIO`] / [`CAIXA_KIND_LABEL_SERVICO`] /
/// [`CAIXA_KIND_LABEL_SUPERVISOR`] / [`CAIXA_KIND_LABEL_APLICACAO`]
/// consts on the same closed [`crate::CaixaKind`] enum surface —
/// together the pentad names every author-reachable arm of the
/// substrate's most fundamental typed axis (what a caixa produces),
/// mirroring the closed-enum-scalar-value trajectory the sibling
/// OTP-shaped [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ONE`] etc. (09ffb2d) and
/// [`SUPERVISOR_CHILD_RESTART_PERMANENT`] etc. (ccdf955) and the M3
/// [`M3_PLACEMENT_ESTRATEGIA_SINGLE_NODE`] etc. (3f0e21c) established
/// on the sibling closed-set typed-enum discriminator axes.
///
/// Until this lift landed the five [`crate::CaixaKind::as_str`] arms
/// each returned a hand-authored byte-string literal (`"biblioteca"`
/// / `"binario"` / `"servico"` / `"supervisor"` / `"aplicacao"`) at
/// the source-side match arm with no compile-time link to the peer
/// [`LAYOUT_MISSING_ENTRY_KIND_BIBLIOTECA`] /
/// [`LAYOUT_MISSING_ENTRY_KIND_SERVICO`] consts on the sibling
/// layout-diagnostic axis (whose bytes coincide by design), and no
/// [`std::fmt::Display`] surface at all — every consumer reaching for
/// a caixa-kind byte-string past the wire format
/// (`Serialize` → PascalCase `"Biblioteca"` etc.) had to reach for the
/// hand-authored [`crate::CaixaKind::as_str`] arm's literal or roll a
/// per-consumer `format!("{v:?}")` `Debug` route, either of which a
/// future variant rename would silently desynchronize. Lifting the
/// five arms onto peer consts + routing [`std::fmt::Display`] through
/// [`crate::CaixaKind::as_str`] closes the drift footgun structurally:
/// the human-readable byte-string (`Display` + `as_str`), the wire
/// byte-string (`Serialize`, PascalCase — intentionally distinct from
/// the human-readable form), and the layout-diagnostic byte-string
/// (`LAYOUT_MISSING_ENTRY_KIND_*`) each route through one canonical
/// declaration per axis, with pin tests
/// (`caixa_kind_as_str_returns_lifted_peer_const`,
/// `caixa_kind_display_routes_through_as_str_helper`) making any drift
/// a caixa-core-build-time failure.
///
/// Byte-identical to [`LAYOUT_MISSING_ENTRY_KIND_BIBLIOTECA`] today
/// (both resolve to the same eleven-byte `"biblioteca"` scalar) — the
/// pin test
/// [`layout_missing_entry_kind_m0_consts_align_with_caixa_kind_as_str`]
/// (fe2a898) already made the coincidence load-bearing on the sibling
/// layout-leaf-kind axis. Semantically distinct: this const names the
/// [`crate::CaixaKind`] discriminator's human-readable form (the
/// substrate's canonical `:kind` label), while
/// [`LAYOUT_MISSING_ENTRY_KIND_BIBLIOTECA`] names the
/// [`crate::LayoutError::MissingEntry`] `kind: &'static str`
/// leaf-kind discriminator (the per-`:bibliotecas`-entry on-disk-leaf
/// existence diagnostic's categorization label). Two axes, two lifts —
/// same "byte-identical-but-semantically-distinct" discipline the peer
/// [`FLEET_PROGRAMS_KEY_VERSAO`] / [`MEMBRO_KEY_VERSAO`] split (ce80ca0)
/// established on the sibling per-entry version-constraint axis.
pub const CAIXA_KIND_LABEL_BIBLIOTECA: &str = "biblioteca";
/// Canonical human-readable label the M0 [`crate::CaixaKind::Binario`]
/// arm surfaces under [`crate::CaixaKind::as_str`] and (routed through
/// it) [`std::fmt::Display`]. Peer of [`CAIXA_KIND_LABEL_BIBLIOTECA`] /
/// [`CAIXA_KIND_LABEL_SERVICO`] / [`CAIXA_KIND_LABEL_SUPERVISOR`] /
/// [`CAIXA_KIND_LABEL_APLICACAO`] on the same closed
/// [`crate::CaixaKind`] enum surface; see [`CAIXA_KIND_LABEL_BIBLIOTECA`]
/// for the shared lift rationale.
///
/// Semantically distinct from [`LAYOUT_MISSING_ENTRY_KIND_EXE`]
/// (`"exe"`) — the alignment pin
/// [`layout_missing_entry_kind_m0_consts_align_with_caixa_kind_as_str`]
/// (fe2a898) asserts the *inequality* between the layout-side leaf-kind
/// label (which names the `exe/` directory sub-tree) and this
/// [`crate::CaixaKind`] discriminator label (which names the caixa's
/// whole runtime kind). Two axes, two lifts.
pub const CAIXA_KIND_LABEL_BINARIO: &str = "binario";
/// Canonical human-readable label the M0 [`crate::CaixaKind::Servico`]
/// arm surfaces under [`crate::CaixaKind::as_str`] and (routed through
/// it) [`std::fmt::Display`]. Peer of [`CAIXA_KIND_LABEL_BIBLIOTECA`] /
/// [`CAIXA_KIND_LABEL_BINARIO`] / [`CAIXA_KIND_LABEL_SUPERVISOR`] /
/// [`CAIXA_KIND_LABEL_APLICACAO`] on the same closed
/// [`crate::CaixaKind`] enum surface; see [`CAIXA_KIND_LABEL_BIBLIOTECA`]
/// for the shared lift rationale.
///
/// Byte-identical to [`LAYOUT_MISSING_ENTRY_KIND_SERVICO`] today (both
/// resolve to the same seven-byte `"servico"` scalar) — the pin test
/// [`layout_missing_entry_kind_m0_consts_align_with_caixa_kind_as_str`]
/// (fe2a898) already made the coincidence load-bearing on the sibling
/// layout-leaf-kind axis. Semantically distinct: this const names the
/// [`crate::CaixaKind`] discriminator's human-readable form (the
/// substrate's canonical `:kind Servico` label), while
/// [`LAYOUT_MISSING_ENTRY_KIND_SERVICO`] names the per-`:servicos`-entry
/// on-disk-leaf existence diagnostic's categorization label.
pub const CAIXA_KIND_LABEL_SERVICO: &str = "servico";
/// Canonical human-readable label the M2 [`crate::CaixaKind::Supervisor`]
/// arm surfaces under [`crate::CaixaKind::as_str`] and (routed through
/// it) [`std::fmt::Display`]. Peer of [`CAIXA_KIND_LABEL_BIBLIOTECA`] /
/// [`CAIXA_KIND_LABEL_BINARIO`] / [`CAIXA_KIND_LABEL_SERVICO`] /
/// [`CAIXA_KIND_LABEL_APLICACAO`] on the same closed
/// [`crate::CaixaKind`] enum surface; see [`CAIXA_KIND_LABEL_BIBLIOTECA`]
/// for the shared lift rationale.
///
/// No layout-leaf-kind peer today — the `:kind Supervisor` typed slot
/// carries no on-disk source-file sub-tree (a supervisor is composed
/// entirely of `:children` references to other caixas), so no
/// [`crate::LayoutError::MissingEntry`] `kind:` diagnostic reaches for
/// this label. The const stands as the sole source of truth for the
/// [`crate::CaixaKind::Supervisor`] arm's human-readable form.
pub const CAIXA_KIND_LABEL_SUPERVISOR: &str = "supervisor";
/// Canonical human-readable label the M3 [`crate::CaixaKind::Aplicacao`]
/// arm surfaces under [`crate::CaixaKind::as_str`] and (routed through
/// it) [`std::fmt::Display`]. Peer of [`CAIXA_KIND_LABEL_BIBLIOTECA`] /
/// [`CAIXA_KIND_LABEL_BINARIO`] / [`CAIXA_KIND_LABEL_SERVICO`] /
/// [`CAIXA_KIND_LABEL_SUPERVISOR`] on the same closed
/// [`crate::CaixaKind`] enum surface; see [`CAIXA_KIND_LABEL_BIBLIOTECA`]
/// for the shared lift rationale.
///
/// Byte-identical to [`FLEET_PROGRAMS_KEY_APLICACAO`] today (both
/// resolve to the same nine-byte `"aplicacao"` scalar) — the coincidence
/// is deliberate but semantically distinct: this const names the
/// [`crate::CaixaKind::Aplicacao`] discriminator's human-readable form
/// (the substrate's canonical `:kind Aplicacao` label), while
/// [`FLEET_PROGRAMS_KEY_APLICACAO`] names the per-programs.yaml-entry
/// passthrough-annotation YAML key that links a member entry back to
/// its parent Aplicacao (MESH-COMPOSITION §III.4). Two axes, two lifts
/// — same "byte-identical-but-semantically-distinct" discipline every
/// peer split establishes.
pub const CAIXA_KIND_LABEL_APLICACAO: &str = "aplicacao";
/// Canonical human-readable label the [`crate::CaixaKind::Acao`] arm
/// surfaces under [`crate::CaixaKind::as_str`] and (routed through it)
/// [`std::fmt::Display`]. Sixth peer of [`CAIXA_KIND_LABEL_BIBLIOTECA`] /
/// [`CAIXA_KIND_LABEL_BINARIO`] / [`CAIXA_KIND_LABEL_SERVICO`] /
/// [`CAIXA_KIND_LABEL_SUPERVISOR`] / [`CAIXA_KIND_LABEL_APLICACAO`] on
/// the same closed [`crate::CaixaKind`] enum surface; see
/// [`CAIXA_KIND_LABEL_BIBLIOTECA`] for the shared lift rationale.
///
/// No layout-leaf-kind peer today (mirroring [`CAIXA_KIND_LABEL_SUPERVISOR`])
/// — the `:kind Acao` slot's sole payload is the `:ci` field
/// (a `canteiro_types::CiRun`), which is not a code-surface
/// path-existence check the way `:bibliotecas`/`:exe`/`:servicos` are.
pub const CAIXA_KIND_LABEL_ACAO: &str = "acao";
/// Canonical PascalCase wire byte-string the [`crate::CaixaKind::Biblioteca`]
/// arm serializes as under the un-`rename`d `#[derive(Serialize)]` on
/// [`crate::CaixaKind`] — the exact byte-shape every wire surface that
/// carries a Caixa's `:kind` outside the caixa-core boundary consumes
/// (the [`caixa_crd::caixa_cr::CaixaSpec`] `kind:` field the K8s
/// `Caixa` CR persists between apply and reconcile passes, the
/// tatara-lisp author-surface `:kind Biblioteca` symbol the sexp parser
/// binds into the typed [`crate::CaixaKind`] enum, the future M4
/// `mesh.pleme.io/v1alpha1/Caixa` CR materializer's per-CR admission-
/// webhook wire binding).
///
/// Peer of the sibling [`CAIXA_KIND_LABEL_BIBLIOTECA`] lowercase-Portuguese
/// diagnostic-form const on the sibling axis — the two byte-strings are
/// *intentionally distinct* by design (see the two-axis-split docstring
/// on [`crate::CaixaKind::as_str`] + the load-bearing pin
/// [`crate::kind::tests::caixa_kind_display_matches_as_str_and_not_serialize_wire`]
/// on the split). This wire const names the substrate's PascalCase
/// wire form; the sibling `_LABEL_*` const names the substrate's
/// lowercase-Portuguese diagnostic form. Six-arm parallel of the
/// same closed [`crate::CaixaKind`] enum surface — same "one canonical
/// byte-string per arm, per axis, next to the axis" discipline every
/// peer typed-enum const family carries.
///
/// Prior to this lift, every consumer that needed the PascalCase wire
/// byte-shape reached for one of two fragile paths: `format!("{:?}",
/// kind)` (couples the wire format to `Debug`'s stability guarantee,
/// which is *no guarantee at all* by Rust's own conventions — a
/// `#[derive(Debug)]` swap for a hand-rolled `impl Debug` that pretty-
/// prints the variant with extra context is a permitted mechanical
/// edit whose apply-time symptom would be every downstream K8s CR
/// carrying a stale wire byte-string), or `serde_json::to_string(&k)`
/// then string-trim of the outer quotes (introduces an allocation +
/// error-handling path for a byte-shape the compiler knows verbatim at
/// build time). Lifting the six arms onto peer consts routes the
/// substrate's wire byte-shape through one canonical declaration per
/// arm the paired [`crate::CaixaKind::wire_name`] +
/// [`crate::CaixaKind::from_wire`] typed dispatch consumers key off.
pub const CAIXA_KIND_WIRE_BIBLIOTECA: &str = "Biblioteca";
/// Canonical PascalCase wire byte-string the [`crate::CaixaKind::Binario`]
/// arm serializes as under the un-`rename`d `#[derive(Serialize)]` on
/// [`crate::CaixaKind`]. Peer of [`CAIXA_KIND_WIRE_BIBLIOTECA`] on the
/// same closed [`crate::CaixaKind`] enum surface; see the sibling
/// [`CAIXA_KIND_WIRE_BIBLIOTECA`] docstring for the shared lift
/// rationale.
pub const CAIXA_KIND_WIRE_BINARIO: &str = "Binario";
/// Canonical PascalCase wire byte-string the [`crate::CaixaKind::Servico`]
/// arm serializes as under the un-`rename`d `#[derive(Serialize)]` on
/// [`crate::CaixaKind`]. Peer of [`CAIXA_KIND_WIRE_BIBLIOTECA`] on the
/// same closed [`crate::CaixaKind`] enum surface; see the sibling
/// [`CAIXA_KIND_WIRE_BIBLIOTECA`] docstring for the shared lift
/// rationale.
pub const CAIXA_KIND_WIRE_SERVICO: &str = "Servico";
/// Canonical PascalCase wire byte-string the [`crate::CaixaKind::Supervisor`]
/// arm serializes as under the un-`rename`d `#[derive(Serialize)]` on
/// [`crate::CaixaKind`]. Peer of [`CAIXA_KIND_WIRE_BIBLIOTECA`] on the
/// same closed [`crate::CaixaKind`] enum surface; see the sibling
/// [`CAIXA_KIND_WIRE_BIBLIOTECA`] docstring for the shared lift
/// rationale.
pub const CAIXA_KIND_WIRE_SUPERVISOR: &str = "Supervisor";
/// Canonical PascalCase wire byte-string the [`crate::CaixaKind::Aplicacao`]
/// arm serializes as under the un-`rename`d `#[derive(Serialize)]` on
/// [`crate::CaixaKind`]. Peer of [`CAIXA_KIND_WIRE_BIBLIOTECA`] on the
/// same closed [`crate::CaixaKind`] enum surface; see the sibling
/// [`CAIXA_KIND_WIRE_BIBLIOTECA`] docstring for the shared lift
/// rationale.
pub const CAIXA_KIND_WIRE_APLICACAO: &str = "Aplicacao";
/// Canonical PascalCase wire byte-string the [`crate::CaixaKind::Acao`]
/// arm serializes as under the un-`rename`d `#[derive(Serialize)]` on
/// [`crate::CaixaKind`]. Sixth peer of [`CAIXA_KIND_WIRE_BIBLIOTECA`] /
/// [`CAIXA_KIND_WIRE_BINARIO`] / [`CAIXA_KIND_WIRE_SERVICO`] /
/// [`CAIXA_KIND_WIRE_SUPERVISOR`] / [`CAIXA_KIND_WIRE_APLICACAO`] on
/// the same closed [`crate::CaixaKind`] enum surface; see the sibling
/// [`CAIXA_KIND_WIRE_BIBLIOTECA`] docstring for the shared lift
/// rationale.
pub const CAIXA_KIND_WIRE_ACAO: &str = "Acao";
/// Canonical caixa-root-relative directory name housing every
/// [`crate::CaixaKind::Biblioteca`] caixa's `lib/<nome>.lisp` entry
/// (and every `:bibliotecas ("lib/foo.lisp" …)` per-entry source
/// path the M0 `:kind Biblioteca` typed slot admits). The single
/// source of truth every consumer that composes a caixa-root-relative
/// path pointing at the tatara-lisp library sub-tree reaches for:
///
/// - [`crate::LayoutInvariants::verify`] joins `root` with this
/// const to reconstruct the default `lib/<nome>.lisp` per-caixa
/// entry the [`crate::LayoutError::MissingLib`] emission gates on;
/// - `feira init`'s new-caixa scaffolder joins `root` with this
/// const to seed the empty `lib/` sub-tree the template's
/// `lib/<nome>.lisp` starter file lives in;
/// - `feira fmt` / `feira lint` enumerate every `.lisp` under
/// `root.join(LAYOUT_DIR_LIB)` as their default target set (their
/// `--paths`-less invocation walks the library sub-tree the
/// substrate's [`crate::LayoutInvariants::verify`] pins);
/// - `feira tofu` reads every `.lisp` under `root.join(LAYOUT_DIR_LIB)`
/// to concatenate the `(defteia …)` forms the caixa-arch invariants
/// bind on.
///
/// The `lib/` byte-shape is a Cargo-style abbreviation of the M0
/// `:kind Biblioteca` discriminator ([`crate::CaixaKind::Biblioteca`]
/// / [`LAYOUT_MISSING_ENTRY_KIND_BIBLIOTECA`], both `"biblioteca"`),
/// deliberately distinct from the discriminator's byte-shape so the
/// on-disk convention stays terse while the diagnostic label stays
/// full-form Portuguese. Peer of [`LAYOUT_DIR_EXE`] /
/// [`LAYOUT_DIR_SERVICOS`] on the sibling M0 per-`CaixaKind`
/// on-disk-directory-name axes — the three consts jointly single-source
/// the CSE-invariant layout convention every caixa the substrate accepts
/// carries. A future rebrand of the on-disk directory landing convention
/// (`"lib"` → `"src"` matching Rust's convention, `"lib"` → `"biblioteca"`
/// matching the full-form Portuguese-uniformity a per-kind consumer
/// disambiguation would prefer) lands as a one-line const-edit + the
/// paired drift-detection pin that guards the two-axis distinctness
/// (`layout_dir_bib_is_distinct_from_layout_missing_entry_kind_bib`)
/// rather than a coordinated ~40-site sweep across production +
/// tests + CI scaffolders.
///
/// Same "one canonical byte-string per typed axis + a paired
/// drift-detection pin at every load-bearing byte-shape coincidence"
/// discipline the M0 [`LAYOUT_MISSING_ENTRY_KIND_BIBLIOTECA`] /
/// [`LAYOUT_MISSING_ENTRY_KIND_EXE`] / [`LAYOUT_MISSING_ENTRY_KIND_SERVICO`]
/// (fe2a898) leaf-kind categorization triad established on the peer
/// [`crate::LayoutError::MissingEntry`] `kind:` discriminator axis.
pub const LAYOUT_DIR_LIB: &str = "lib";
/// Canonical caixa-root-relative directory name housing every
/// [`crate::CaixaKind::Binario`] caixa's `exe/<name>` entry (and
/// every `:exe ("exe/tool" …)` per-entry source path the M0
/// `:kind Binario` typed slot admits). Peer of [`LAYOUT_DIR_LIB`] /
/// [`LAYOUT_DIR_SERVICOS`] on the sibling M0 per-`CaixaKind`
/// on-disk-directory-name axes; see [`LAYOUT_DIR_LIB`] for the
/// shared lift rationale.
///
/// [`crate::LayoutInvariants::verify`] joins `root` with this const
/// to reconstruct the sandbox-root the [`crate::LayoutError::ExeOutsideDir`]
/// emission gates every declared `:exe` entry against — a `:exe`
/// entry whose resolved path escapes `root.join(LAYOUT_DIR_EXE)`
/// surfaces `ExeOutsideDir(<path>)` at `feira build` time rather than
/// silently reaching outside the caixa's sandbox at OCI-build /
/// nix-build time. Byte-identical (by design) to
/// [`LAYOUT_MISSING_ENTRY_KIND_EXE`] — the M0 `:kind Binario`
/// on-disk-directory-name and the [`crate::LayoutError::MissingEntry`]
/// `kind:` leaf-kind categorization label share the same three-byte
/// scalar because both name the same axis (the `exe/` sub-tree), a
/// coincidence the pin test
/// `layout_dir_exe_matches_layout_missing_entry_kind_exe` makes
/// load-bearing so a rebrand touching either axis without the other
/// trips at build time rather than surfacing at
/// [`crate::LayoutInvariants::verify`] time as a mismatched
/// `MissingEntry.kind` diagnostic naming a stale label.
pub const LAYOUT_DIR_EXE: &str = "exe";
/// Canonical caixa-root-relative directory name housing every
/// [`crate::CaixaKind::Servico`] caixa's
/// `servicos/<nome>.computeunit.yaml` per-CR `ComputeUnit` descriptor
/// (and every `:servicos ("servicos/foo.computeunit.yaml" …)`
/// per-entry source path the M0 `:kind Servico` typed slot admits).
/// Peer of [`LAYOUT_DIR_LIB`] / [`LAYOUT_DIR_EXE`] on the sibling M0
/// per-`CaixaKind` on-disk-directory-name axes; see [`LAYOUT_DIR_LIB`]
/// for the shared lift rationale.
///
/// [`crate::LayoutInvariants::verify`] joins `root` with this const
/// to reconstruct the sandbox-root the
/// [`crate::LayoutError::ServicoOutsideDir`] emission gates every
/// declared `:servicos` entry against — a `:servicos` entry whose
/// resolved path escapes `root.join(LAYOUT_DIR_SERVICOS)` surfaces
/// `ServicoOutsideDir(<path>)` at `feira build` time rather than
/// silently reaching outside the caixa's sandbox at
/// [`caixa_helm`][ch] / [`caixa_flux`][cf] render time or at the
/// operator's OCI-build step.
///
/// The `servicos/` byte-shape is the Portuguese *plural* of the M0
/// `:kind Servico` discriminator ([`crate::CaixaKind::Servico`] /
/// [`LAYOUT_MISSING_ENTRY_KIND_SERVICO`], both `"servico"`, singular)
/// — the on-disk directory holds one-or-more `ComputeUnit` YAML
/// descriptors per caixa, the discriminator names the caixa's kind.
/// The pin test
/// `layout_dir_servicos_is_distinct_from_layout_missing_entry_kind_servico`
/// makes the singular/plural split load-bearing so a future rebrand
/// touching either axis without the other (a per-consumer
/// disambiguation collapsing them, a hypothetical English-uniformity
/// pass renaming `"servicos"` → `"services"`) trips at build time.
///
/// [ch]: caixa_helm
/// [cf]: caixa_flux
pub const LAYOUT_DIR_SERVICOS: &str = "servicos";
/// Canonical `wasm.pleme.io/v1alpha1/ComputeUnit` CRD `spec.module`
/// per-CR wasm-module-reference sub-block key — the top-level `spec.*`
/// child every rendered `ComputeUnit` YAML carries to name the wasm
/// component (`module.source: oci://...` for OCI-hosted binaries,
/// `module.source: file://...` for locally-mounted wasm bundles) the
/// M2.5 wasm-engine instantiator loads at Servico bring-up. The single
/// source of truth every downstream consumer that reads or emits the
/// per-CR module sub-block reaches for:
///
/// - [`caixa_flux::programs_yaml_entry`] splices the ComputeUnit
/// YAML's `spec.module` verbatim through into the emitted
/// `programs[]` entry (the `lareira-fleet-programs` library chart's
/// per-entry module-source axis, populated from the ComputeUnit's
/// `spec.module` per the docstring on `programs_yaml_entry` above);
/// - [`caixa_helm::build_values_yaml`] threads the same
/// `spec.module` sub-block into the rendered `values.yaml`'s
/// [`DEFAULT_LIBRARY_NAME`]-wrapped block so the `pleme-computeunit`
/// library chart's per-Servico module axis binds to the exact
/// source the caixa.lisp's `:servicos` fixture pins;
/// - every test-fixture navigator in both crates that reaches into
/// the rendered `programs[]` entry / `values.yaml` block by the
/// module sub-block key to pin the per-Servico module-source axis
/// round-trip (six sites across [`caixa_flux`][cf]'s per-entry
/// module + module.source drift-detection sweep + [`caixa_helm`][ch]'s
/// per-values module drift-detection sweep) resolves the same
/// `&'static str` when parsing back the rendered document;
/// - every future per-Servico renderer the absorption-roadmap
/// acknowledges (the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-`:membros` module-source resolver, a future
/// per-cluster ComputeUnit-CR admission webhook keying off the same
/// accepted sub-block set, the future caixa-otel collector-pipeline
/// emitter's per-Servico module-scrape reference).
///
/// Until this lift landed the byte `"module"` lived as six verbatim
/// inline literals across [`caixa_flux`][cf] and [`caixa_helm`][ch]'s
/// test-fixture navigators (four sites in caixa-flux —
/// `programs_yaml_entry_round_trips`'s `entry.get("module")` pair +
/// `upsert_helmrelease_replaces_existing`'s `.get("module")` +
/// `upsert_into_programs_yaml`'s `.get("module")` — and two sites in
/// caixa-helm — `values_yaml_wraps_under_pleme_computeunit_key`'s
/// `cu_block.get("module")` + `values_yaml_wrap_key_follows_library_name_override`'s
/// peer navigator on the library-name-override axis). A future
/// ComputeUnit CRD schema-key rebrand on the per-CR module-reference
/// axis (the substrate moving the wasm-component reference to
/// `binary:` for parity with OCI OpenContainer Image nomenclature, to
/// `component:` for parity with WIT Component Model wire terminology,
/// to `spec.wasm.source` for schema-clarity once the ComputeUnit
/// CRD grows sibling `spec.native.*` / `spec.container.*` runtime-
/// discriminators as the ABSORPTION-ROADMAP.md M4-M5 trajectory names)
/// without a coordinated edit across all six sites would silently
/// split the schema: the emitter would write under the drifted key
/// while every downstream test would still probe `module:` — the
/// `lareira-fleet-programs` library chart's per-entry module-source
/// axis would silently receive an empty reference, the workload would
/// silently come up with no wasm module bound (the M2.5 instantiator
/// falls back to the library chart's admission-time default of a
/// hello-world stub, or fails the bring-up at wasm-engine parse time
/// with a diagnostic far from the caixa.lisp source), and the failure
/// would surface as "the Servico's pods are running but they aren't
/// running our code" far from the rebrand commit's source. Lifting
/// the literal to one `&'static str` closes the drift footgun
/// structurally — every consumer reads the same memory, so any
/// future rebrand reaches every consumer by construction.
///
/// Same "the typed constant lives in one place" discipline the peer
/// [`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] / [`M2_KEY_UPGRADE_FROM`]
/// lifts apply on the sibling caixa.lisp M2 typed-slot canonical-
/// camelCase-key surfaces — extends the discipline from the caixa-
/// source-side M2 typed-slot overlay-key triple onto the substrate-
/// side `wasm.pleme.io/v1alpha1/ComputeUnit` CRD per-`spec.*`
/// sub-block axis every rendered ComputeUnit YAML declares as its
/// top-level `(module, trigger, capabilities)` triple (the peer
/// [`COMPUTEUNIT_SPEC_KEY_TRIGGER`] +
/// [`COMPUTEUNIT_SPEC_KEY_CAPABILITIES`] siblings complete the
/// substrate-side ComputeUnit-CRD per-`spec.*` sub-block re-export
/// triple).
///
/// [cf]: ../../caixa_flux/index.html
/// [ch]: ../../caixa_helm/index.html
pub const COMPUTEUNIT_SPEC_KEY_MODULE: &str = "module";
/// Canonical `wasm.pleme.io/v1alpha1/ComputeUnit` CRD `spec.trigger`
/// per-CR invocation-trigger sub-block key — the top-level `spec.*`
/// child every rendered `ComputeUnit` YAML carries to name how the
/// wasm component is invoked (`trigger.service.{port, paths}` for
/// HTTP-triggered Servicos, `trigger.subscription.{subject}` for the
/// future NATS-triggered Servicos the M4 `:contratos` typed-mesh
/// pubsub axis will emit). Peer of [`COMPUTEUNIT_SPEC_KEY_MODULE`] on
/// the same ComputeUnit CRD per-`spec.*` sub-block surface —
/// `COMPUTEUNIT_SPEC_KEY_MODULE` names the per-CR wasm-binary
/// reference axis, this constant names the per-CR invocation-shape
/// axis every downstream trigger consumer (the `pleme-computeunit`
/// library chart's per-Servico `trigger.service.port` /
/// `trigger.service.paths` / `trigger.service.breathability` values-
/// block routing, the future M4 pubsub-subscription binding, the
/// `caixa-mesh` `CiliumNetworkPolicy` L4-port fallback that reads the
/// destination Servico's per-`trigger.service.port` axis via a future
/// resolver round-trip) reaches for. Same lift trajectory as the
/// sibling [`COMPUTEUNIT_SPEC_KEY_MODULE`] axis — three verbatim
/// inline test-side literals (one caixa-flux drift-detection navigator
/// + two caixa-helm per-values drift-detection navigators, one under
/// the canonical wrap-key + one under the library-name-override wrap-
/// key) collapsed onto the same `&'static str` so any future rebrand
/// (the substrate moving the invocation-shape axis to `invoke:`,
/// `entry:`, or splitting into `trigger.http.*` / `trigger.pubsub.*`
/// runtime-discriminators as the M4 `:contratos` axis grows) reaches
/// every consumer by construction. See [`COMPUTEUNIT_SPEC_KEY_MODULE`]
/// for the full lift rationale.
pub const COMPUTEUNIT_SPEC_KEY_TRIGGER: &str = "trigger";
/// Canonical `wasm.pleme.io/v1alpha1/ComputeUnit` CRD
/// `spec.capabilities` per-CR WASI-capability-list sub-block key — the
/// top-level `spec.*` child every rendered `ComputeUnit` YAML carries
/// to declare the wasm-component-capability tokens the M2.5 wasm-engine
/// instantiator binds at Servico bring-up (`http-in:0.0.0.0:8080` for
/// the HTTP incoming-handler, `env` for read-only environment access,
/// `sock-*` for TCP outbound, and the sibling WASI-preview-2 preview-
/// interfaces per the WIT Component Model). Peer of
/// [`COMPUTEUNIT_SPEC_KEY_MODULE`] and [`COMPUTEUNIT_SPEC_KEY_TRIGGER`]
/// on the same ComputeUnit CRD per-`spec.*` sub-block surface —
/// completes the substrate-side ComputeUnit-CRD per-`spec.*` sub-block
/// re-export triple every rendered ComputeUnit YAML declares as its
/// top-level `(module, trigger, capabilities)` axis. Same lift
/// trajectory as the sibling [`COMPUTEUNIT_SPEC_KEY_MODULE`] axis —
/// three verbatim inline test-side literals (one caixa-flux drift-
/// detection navigator + two caixa-helm per-values drift-detection
/// navigators, one under the canonical wrap-key + one under the
/// library-name-override wrap-key) collapsed onto the same
/// `&'static str` so any future rebrand (the substrate moving the
/// capability-list axis to `caps:` for terse-schema parity with the
/// WASI-preview-2 upstream naming, splitting into
/// `capabilities.wasi.*` / `capabilities.pleme.*` runtime-vs-substrate
/// discriminators, or the M4 WIT Component Model materializer moving
/// to a typed `imports:` / `exports:` split) reaches every consumer by
/// construction. See [`COMPUTEUNIT_SPEC_KEY_MODULE`] for the full lift
/// rationale.
pub const COMPUTEUNIT_SPEC_KEY_CAPABILITIES: &str = "capabilities";
/// Canonical `wasm.pleme.io/v1alpha1/ComputeUnit` CRD
/// `spec.module.source` per-CR wasm-component-reference leaf-scalar
/// sub-block key — the nested `spec.module.*` child every rendered
/// `ComputeUnit` YAML carries to name the exact wasm-component
/// artifact the M2.5 wasm-engine instantiator loads at Servico
/// bring-up. Peer of the parent [`COMPUTEUNIT_SPEC_KEY_MODULE`] on the
/// same ComputeUnit CRD per-`spec.module.*` sub-block surface —
/// `COMPUTEUNIT_SPEC_KEY_MODULE` names the top-level per-CR module-
/// reference block; this constant names the block's leaf reference-
/// value axis. Every rendered `programs[]` entry the
/// `lareira-fleet-programs` library chart consumes carries the
/// `module.source: oci://ghcr.io/pleme-io/<caixa>:<versao>` (or
/// `module.source: file://...` for locally-mounted wasm bundles;
/// `module.source: github:<owner>/<repo>` for git-hosted sources) as
/// its per-Servico wasm-artifact reference; every `spec.module.source`
/// readback across the [`caixa_flux::programs_yaml_entry`] round-trip
/// pins + the [`caixa_flux::upsert_into_programs_yaml`] /
/// [`caixa_flux::upsert_into_helmrelease_programs`] cross-upsert
/// navigators resolves the same `&'static str`.
///
/// Until this lift landed the byte `"source"` lived as three verbatim
/// inline literals across [`caixa_flux`][cf]'s test-fixture navigators
/// (`programs_yaml_entry_round_trips`'s
/// `entry.get(COMPUTEUNIT_SPEC_KEY_MODULE).and_then(|m| m.get("source"))`
/// per-`module.source` present-check +
/// `upsert_into_programs_yaml`'s
/// `arr[0].get(COMPUTEUNIT_SPEC_KEY_MODULE).get("source")` cross-
/// upsert readback + `upsert_into_helmrelease_programs`'s peer
/// navigator on the `HelmRelease`-wrapped `spec.values.programs[]`
/// path). A future ComputeUnit-CRD schema rebrand on the per-`module`
/// leaf-scalar axis (the substrate moving the reference-value axis to
/// `ref:` for parity with the OCI Distribution Spec's per-manifest
/// content-reference nomenclature, to `uri:` for parity with the WIT
/// Component Model's per-import content-reference field, to
/// `module.oci.ref` / `module.file.path` / `module.git.rev` sibling-
/// discriminator split once the ComputeUnit CRD grows typed sub-block
/// discriminators as the ABSORPTION-ROADMAP.md M4-M5 trajectory names)
/// without a coordinated three-site edit would silently split the
/// schema: the emitter would write under the drifted leaf-key while
/// every downstream navigator would still probe `source:` — the
/// `lareira-fleet-programs` library chart's per-entry module-source
/// axis would silently receive an empty reference, the workload would
/// silently come up with no wasm module bound (the M2.5 instantiator
/// falls back to the library chart's admission-time hello-world stub,
/// or fails the bring-up at wasm-engine parse time with a diagnostic
/// far from the caixa.lisp source), and the failure would surface as
/// "the Servico's pods are running but they aren't running our code"
/// far from the rebrand commit's source. Lifting the literal to one
/// `&'static str` closes the drift footgun structurally — every
/// consumer reads the same memory, so any future rebrand reaches every
/// consumer by construction.
///
/// Same "the typed constant lives in one place" discipline the peer
/// [`COMPUTEUNIT_SPEC_KEY_MODULE`] / [`COMPUTEUNIT_SPEC_KEY_TRIGGER`] /
/// [`COMPUTEUNIT_SPEC_KEY_CAPABILITIES`] lifts apply on the sibling
/// substrate-side ComputeUnit-CRD per-`spec.*` sub-block axis —
/// extends the discipline one level deeper from the top-level `spec.*`
/// container-axis surface onto the nested `spec.module.*` leaf-scalar-
/// axis every rendered ComputeUnit YAML declares under its per-CR
/// module-reference block.
///
/// [cf]: ../../caixa_flux/index.html
pub const COMPUTEUNIT_MODULE_KEY_SOURCE: &str = "source";
/// Canonical YAML key for the M3 `:placement` slot's overlay on a
/// rendered programs.yaml entry. The lareira-fleet-programs aggregator
/// (and the future `app-operator` per-Aplicacao reconciler) both key
/// off this exact spelling to filter entries by `placement.clusters`
/// for cross-cluster fanout (MESH-COMPOSITION §III.4) and to dispatch
/// on `placement.estrategia` for distributed-app takeover semantics
/// (§II.1, §V cross-cluster federation). Lifted as a const alongside
/// the M2 keys so the Aplicacao-side renderer
/// ([`crate::aplicacao::Placement`] → caixa-mesh
/// `programs_for_aplicacao`) and every consumer (the M4 cluster-fanout
/// renderer, the future `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer, the `app-operator`'s placement-strategy dispatcher)
/// spell the same key exactly the same way — drift here = a
/// programs.yaml entry whose placement is silently dropped at the
/// aggregator's filter step (visible only as "the workload doesn't
/// land where the typed slot said it should").
pub const M3_KEY_PLACEMENT: &str = "placement";
/// Canonical author-facing kebab-case `(defcaixa … :membros (…))`
/// top-level mesh slot label the M3 Aplicacao's constituent-Servico set
/// surfaces under. Peer of the four sibling M3 top-level mesh-slot
/// labels ([`M3_AUTHOR_KEY_CONTRATOS`], [`M3_AUTHOR_KEY_POLITICAS`],
/// [`M3_AUTHOR_KEY_PLACEMENT`], [`M3_AUTHOR_KEY_ENTRADA`]) on the
/// dual-axis pair every M3 top-level mesh slot carries: the
/// author-facing kebab-case `[M3_AUTHOR_KEY_*]` const names the label
/// the [`crate::Caixa::declared_mesh_slots`] tagger threads through as
/// one of the `&'static str` entries in the canonical-declaration-order
/// slot list the kind-coherence gate
/// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]) joins into the
/// space-separated `slots:` diagnostic naming which of the five mesh
/// slots the offending caixa declared on a non-Aplicacao kind. Peer of
/// the [`M3_KEY_PLACEMENT`] renderer-side wire-key const declared
/// immediately above on the sole M3 mesh slot the renderer surfaces as
/// a per-entry overlay-container key (`:membros` / `:contratos` /
/// `:politicas` / `:entrada` render as per-arm derived artifacts —
/// programs.yaml fan-out, CiliumNetworkPolicies, per-edge overlays,
/// Gateway/HTTPRoute — not as a single overlay-container key).
///
/// Until this lift landed the five kebab-case labels sat once each in
/// [`crate::Caixa::declared_mesh_slots`] as five-arm inline
/// `":membros"` / `":contratos"` / `":politicas"` / `":placement"` /
/// `":entrada"` byte-strings the tagger pushed onto its return `Vec`,
/// plus three test-side probe literals across `layout.rs` and
/// `manifest.rs::tests` — with no compile-time link between the
/// tagger's arms and the tests' expected values. A future rebrand
/// (a hypothetical `:membros` → `:members` matching English-uniformity
/// as the substrate's per-slot vocabulary stabilizes, `:contratos` →
/// `:contracts` matching the same, `:politicas` → `:policies`
/// matching the same, `:placement` → `:distribution` matching
/// MESH-COMPOSITION §II.1 vocabulary, `:entrada` → `:ingress` matching
/// K8s Gateway API's ingress-side vocabulary, or a per-consumer
/// disambiguation as the `defcaixa` macro stabilizes) would silently
/// desynchronize the production
/// [`crate::Caixa::declared_mesh_slots`] tagger from the tests until a
/// downstream consumer surfaced the drift at build time as a
/// matches-arm miss far from the rename's commit. This lift closes
/// that gap by routing both halves (production tagger + tests) through
/// five peer consts declared adjacent to the renderer-side
/// [`M3_KEY_PLACEMENT`] peer, so the "one canonical declaration per
/// arm, next to the axis" discipline the peer
/// [`M2_AUTHOR_KEY_LIMITS`] / [`M2_AUTHOR_KEY_BEHAVIOR`] /
/// [`M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot consts
/// (f49c8b0) established for the sibling per-Servico M2 slot axis
/// extends onto the M3 top-level mesh slot axis so both altitudes
/// of the typed-slot algebra (per-Servico M2 + per-Aplicacao M3)
/// route through peer author-label consts.
pub const M3_AUTHOR_KEY_MEMBROS: &str = ":membros";
/// Canonical author-facing kebab-case `(defcaixa … :contratos (…))`
/// top-level mesh slot label the M3 Aplicacao's WIT-typed inter-Servico
/// edge set surfaces under. Peer of [`M3_AUTHOR_KEY_MEMBROS`] on the
/// sibling M3 top-level mesh-slot dual axis; see
/// [`M3_AUTHOR_KEY_MEMBROS`] for the full lift rationale.
pub const M3_AUTHOR_KEY_CONTRATOS: &str = ":contratos";
/// Canonical author-facing kebab-case `(defcaixa … :politicas (…))`
/// top-level mesh slot label the M3 Aplicacao's mesh-level policy
/// overlay ([`crate::aplicacao::MeshPolicy`]: `:timeout`, `:retries`,
/// `:circuit-breaker`, `:mtls-required`, `:rate-limit`) surfaces under.
/// Peer of [`M3_AUTHOR_KEY_MEMBROS`] on the sibling M3 top-level
/// mesh-slot dual axis; see [`M3_AUTHOR_KEY_MEMBROS`] for the full lift
/// rationale.
pub const M3_AUTHOR_KEY_POLITICAS: &str = ":politicas";
/// Canonical author-facing kebab-case `(defcaixa … :placement (…))`
/// top-level mesh slot label the M3 Aplicacao's cross-cluster
/// distribution strategy ([`crate::aplicacao::Placement`]:
/// `:estrategia` + `:clusters` + `:shard-key` / `:affinity`) surfaces
/// under. Peer of [`M3_AUTHOR_KEY_MEMBROS`] on the sibling M3
/// top-level mesh-slot dual axis; see [`M3_AUTHOR_KEY_MEMBROS`] for
/// the full lift rationale. Byte-identical to the peer
/// [`M3_KEY_PLACEMENT`] renderer-side wire key modulo the leading `:`
/// — the two consts split on the axis every M3 top-level slot carries
/// (author-facing kebab-case label vs. renderer-side camelCase overlay
/// key), the same split the [`M2_AUTHOR_KEY_LIMITS`] / [`M2_KEY_LIMITS`]
/// peer pair established on the sibling M2 axis.
pub const M3_AUTHOR_KEY_PLACEMENT: &str = ":placement";
/// Canonical author-facing kebab-case `(defcaixa … :entrada (…))`
/// top-level mesh slot label the M3 Aplicacao's external-ingress
/// gateway surface ([`crate::aplicacao::Entrada`]: `:host`, `:para`,
/// `:paths`, `:port`) surfaces under. Peer of [`M3_AUTHOR_KEY_MEMBROS`]
/// on the sibling M3 top-level mesh-slot dual axis; see
/// [`M3_AUTHOR_KEY_MEMBROS`] for the full lift rationale.
pub const M3_AUTHOR_KEY_ENTRADA: &str = ":entrada";
/// Canonical author-facing kebab-case `(:de "<caixa>")` per-`:contratos`
/// entry source-endpoint sub-slot label the M3 Aplicacao's WIT-typed
/// inter-Servico edge set surfaces under. Names the "edge tail" —
/// which member `:contratos` entry `n` originates from — per
/// MESH-COMPOSITION §IV table row "`:contratos` | typed inter-Servico
/// edges | each :de + :para must be in :membros; :wit must reference a
/// registered WIT world".
///
/// Peer of [`M3_AUTHOR_KEY_CONTRATOS`] on the `:contratos` sub-slot
/// author-facing-label dual axis: the top-level [`M3_AUTHOR_KEY_CONTRATOS`]
/// const (882f498) names the M3 slot itself, the two
/// `CONTRATO_AUTHOR_KEY_{DE,PARA}` consts name the per-entry endpoint
/// axes the parser reads (`(:de "cart" :para "catalog" …)`).
///
/// Until this lift landed the two kebab-case labels sat once each in
/// [`crate::aplicacao::AplicacaoSpec::validate`]'s per-`:contratos`
/// entry endpoint-shape gate as two two-arm inline `":de"` / `":para"`
/// byte-strings passed as the `slot: &'static str` argument to
/// [`validate_contrato_caixa`], plus a family of test-side probe
/// literals asserting the [`crate::aplicacao::AplicacaoError::ContratoCaixaEmpty`]
/// / [`crate::aplicacao::AplicacaoError::ContratoCaixaInvalid`]
/// diagnostic's `slot:` field carries the expected per-arm value
/// verbatim — with no compile-time link between the validator's arms
/// and the tests' expected values. A future rebrand (a hypothetical
/// `:de` → `:from` for English uniformity matching the OTP `appup`
/// `M2_UPGRADE_FROM_KEY_FROM` (36ffe65) sibling, `:para` → `:to`
/// matching the same, `:de`/`:para` → `:source`/`:target` matching
/// the WIT world's `import`/`export` half-vocabulary, or a per-consumer
/// disambiguation as the `defcaixa` macro stabilizes) would silently
/// desynchronize the production per-entry endpoint-shape gate from the
/// tests until a downstream consumer surfaced the drift at build time
/// as a matches-arm miss far from the rename's commit. This lift closes
/// that gap by routing both halves (production endpoint-shape gate +
/// tests) through two peer consts declared adjacent to the
/// [`M3_AUTHOR_KEY_CONTRATOS`] parent-slot label, so the "one
/// canonical declaration per arm, next to the axis" discipline the
/// peer [`M2_AUTHOR_KEY_LIMITS`] / [`M2_AUTHOR_KEY_BEHAVIOR`] /
/// [`M2_AUTHOR_KEY_UPGRADE_FROM`] (f49c8b0), [`M3_AUTHOR_KEY_MEMBROS`]
/// / [`M3_AUTHOR_KEY_CONTRATOS`] / [`M3_AUTHOR_KEY_POLITICAS`] /
/// [`M3_AUTHOR_KEY_PLACEMENT`] / [`M3_AUTHOR_KEY_ENTRADA`] (882f498),
/// and [`SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] etc. (be40492) top-level
/// slot consts established for the sibling M2 / M3 / Supervisor
/// top-level slot axes extends onto the `:contratos` sub-slot
/// endpoint axis.
pub const CONTRATO_AUTHOR_KEY_DE: &str = ":de";
/// Canonical author-facing kebab-case `(:para "<caixa>")` per-`:contratos`
/// entry target-endpoint sub-slot label the M3 Aplicacao's WIT-typed
/// inter-Servico edge set surfaces under. Names the "edge head" —
/// which member `:contratos` entry `n` terminates at — per
/// MESH-COMPOSITION §IV table row "`:contratos` | typed inter-Servico
/// edges | each :de + :para must be in :membros". Peer of
/// [`CONTRATO_AUTHOR_KEY_DE`] on the sibling `:contratos` per-entry
/// endpoint-shape axis; see [`CONTRATO_AUTHOR_KEY_DE`] for the full
/// lift rationale.
pub const CONTRATO_AUTHOR_KEY_PARA: &str = ":para";
/// Canonical author-facing kebab-case `(defcaixa … :estrategia <s>)`
/// top-level supervisor-tree slot label the OTP `:kind Supervisor`
/// caixa's [`crate::supervisor::RestartStrategy`] discriminator surfaces
/// under. Peer of [`M2_AUTHOR_KEY_LIMITS`] /
/// [`M3_AUTHOR_KEY_MEMBROS`] on the third kind-scoped
/// typed-slot-family axis: the M2 `M2_AUTHOR_KEY_*` consts (f49c8b0)
/// name the Servico-runtime slots, the M3 `M3_AUTHOR_KEY_*` consts
/// (882f498) name the Aplicacao mesh slots, and these
/// `SUPERVISOR_AUTHOR_KEY_*` consts close the last remaining kind ↔
/// slot-family axis — the Supervisor supervision-tree slots
/// (`:estrategia`, `:max-restarts`, `:restart-window`, `:children`) that
/// [`crate::Caixa::declared_supervisor_slots`] tags for the sibling
/// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
/// kind-coherence gate.
///
/// Until this lift landed the four kebab-case labels sat once each in
/// [`crate::Caixa::declared_supervisor_slots`] as four-arm inline
/// `":estrategia"` / `":max-restarts"` / `":restart-window"` /
/// `":children"` byte-strings the tagger pushed onto its return `Vec`,
/// plus a handful of test-side probe literals asserting the diagnostic's
/// `slots:` field carries the expected per-arm value verbatim — with no
/// compile-time link between the tagger's arms and the tests' expected
/// values. A future rebrand (a hypothetical `:estrategia` →
/// `:strategy` for English uniformity, `:max-restarts` →
/// `:max-intensity` matching Erlang/OTP's `MaxIntensity` terminology
/// verbatim, `:restart-window` → `:period` matching OTP's `Period` name,
/// `:children` → `:workers` matching the Elixir `Supervisor.child_spec`
/// idiom, or a per-consumer disambiguation as the `defcaixa` macro
/// stabilizes) would silently desynchronize the production
/// [`crate::Caixa::declared_supervisor_slots`] tagger from the tests
/// until a downstream consumer surfaced the drift at build time as a
/// matches-arm miss far from the rename's commit. This lift closes that
/// gap by routing both halves (production tagger + tests) through four
/// peer consts declared adjacent to the peer M2 / M3 top-level
/// author-key consts, so the "one canonical declaration per arm, next
/// to the axis" discipline the peer [`M2_AUTHOR_KEY_LIMITS`] /
/// [`M2_AUTHOR_KEY_BEHAVIOR`] / [`M2_AUTHOR_KEY_UPGRADE_FROM`] top-level
/// M2 slot consts (f49c8b0) and [`M3_AUTHOR_KEY_MEMBROS`] /
/// [`M3_AUTHOR_KEY_CONTRATOS`] / [`M3_AUTHOR_KEY_POLITICAS`] /
/// [`M3_AUTHOR_KEY_PLACEMENT`] / [`M3_AUTHOR_KEY_ENTRADA`] top-level
/// M3 slot consts (882f498) established for the sibling
/// per-Servico / per-Aplicacao top-level slot axes extends onto the
/// per-Supervisor supervision-tree slot axis, closing the last of the
/// three kind-scoped typed-slot-family author-facing-label axes.
///
/// Same "one canonical byte-string per typed axis" discipline every
/// peer M2 / M3 renderer-wire-key axis carries ([`M2_KEY_LIMITS`] /
/// [`M2_KEY_BEHAVIOR`] / [`M2_KEY_UPGRADE_FROM`],
/// [`M2_LIMITS_KEY_MEMORY`] / [`M2_LIMITS_KEY_FUEL`] /
/// [`M2_LIMITS_KEY_WALL_CLOCK`] / [`M2_LIMITS_KEY_CPU`] (d8b8b4f),
/// [`M2_BEHAVIOR_KEY_ON_INIT`] etc. (21fe462),
/// [`M2_UPGRADE_FROM_KEY_FROM`] / [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`]
/// (36ffe65), [`M3_PLACEMENT_KEY_ESTRATEGIA`] etc.).
pub const SUPERVISOR_AUTHOR_KEY_ESTRATEGIA: &str = ":estrategia";
/// Canonical author-facing kebab-case `(defcaixa … :max-restarts <n>)`
/// top-level supervisor-tree slot label the OTP `:kind Supervisor`
/// caixa's `MaxIntensity` restart-budget counter surfaces under. Peer of
/// [`SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] on the sibling supervision-tree
/// slot axis; see [`SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] for the full lift
/// rationale.
pub const SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS: &str = ":max-restarts";
/// Canonical author-facing kebab-case
/// `(defcaixa … :restart-window "<duration>")` top-level supervisor-tree
/// slot label the OTP `:kind Supervisor` caixa's `Period` rolling-window
/// counter surfaces under. Peer of [`SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`]
/// on the sibling supervision-tree slot axis; see
/// [`SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] for the full lift rationale.
pub const SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW: &str = ":restart-window";
/// Canonical author-facing kebab-case `(defcaixa … :children (…))`
/// top-level supervisor-tree slot label the OTP `:kind Supervisor`
/// caixa's static child-spec list ([`crate::supervisor::ChildSpec`])
/// surfaces under. Peer of [`SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] on the
/// sibling supervision-tree slot axis; see
/// [`SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] for the full lift rationale.
pub const SUPERVISOR_AUTHOR_KEY_CHILDREN: &str = ":children";
/// Canonical author-facing kebab-case `(defcaixa … :deps ((…)))` top-
/// level dep-list slot label the two-list dependency-graph slot family
/// surfaces under. Peer of [`DEP_AUTHOR_KEY_DEPS_DEV`] on the two-list
/// dep-graph slot axis: `:deps` names the runtime-closure dep-list
/// (every `Cargo.toml [dependencies]` equivalent — reached by every
/// build the caixa participates in), the sibling `:deps-dev` names the
/// dev-only dep-list (every `Cargo.toml [dev-dependencies]` equivalent
/// — reached only by test / dev-shim builds).
///
/// Threaded verbatim as the `list: &'static str` field on both
/// [`crate::DepError::DuplicateNome`] (359fba5) and
/// [`crate::DepError::DepIsSelf`] so a `feira lint` diagnostic ("`:deps`
/// entry `caixa-teia` is duplicated" / "`:deps-dev` entry `dev-shim` is
/// a self-reference") self-locates the offending block in the author's
/// `caixa.lisp` without the linter re-deriving the list from context.
///
/// Until this lift landed the two kebab-case labels sat once each on
/// the [`crate::Caixa::validate_deps`] per-list duplicate walk (`list:
/// ":deps"` / `list: ":deps-dev"` in `manifest.rs`) and the paired
/// [`crate::dep::validate_no_self_dep`] per-list self-edge walk (`list:
/// ":deps"` / `list: ":deps-dev"` in `dep.rs`), plus a handful of
/// test-side probe literals asserting the `list:` field of a
/// `DepError::DuplicateNome` / `DepError::DepIsSelf` carries the
/// expected per-list value verbatim — with no compile-time link
/// between the two producers and the tests' expected values. A future
/// rebrand (a hypothetical `:deps` → `:dependencies` matching Cargo's
/// verbatim key, `:deps-dev` → `:dev-dependencies` matching the same,
/// `:deps` / `:deps-dev` → `:runtime-deps` / `:dev-deps` for
/// symmetry, or a per-consumer disambiguation as the `defcaixa` macro
/// stabilizes) would silently desynchronize the two producers from
/// each other and from the tests until a downstream consumer surfaced
/// the drift at build time as a matches-arm miss far from the
/// rename's commit. This lift closes that gap by routing all halves
/// (both production walkers + tests) through two peer consts declared
/// adjacent to the peer M2 / M3 / Supervisor top-level author-key
/// consts, so the "one canonical declaration per arm, next to the
/// axis" discipline the peer [`M2_AUTHOR_KEY_LIMITS`] /
/// [`M2_AUTHOR_KEY_BEHAVIOR`] / [`M2_AUTHOR_KEY_UPGRADE_FROM`]
/// (f49c8b0), [`M3_AUTHOR_KEY_MEMBROS`] / [`M3_AUTHOR_KEY_CONTRATOS`] /
/// [`M3_AUTHOR_KEY_POLITICAS`] / [`M3_AUTHOR_KEY_PLACEMENT`] /
/// [`M3_AUTHOR_KEY_ENTRADA`] (882f498), [`SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`]
/// etc. (be40492), and [`CONTRATO_AUTHOR_KEY_DE`] /
/// [`CONTRATO_AUTHOR_KEY_PARA`] (f50c875) top-level slot / per-entry
/// endpoint consts established for the sibling M2 / M3 / Supervisor
/// slot axes extends onto the two-list dep-graph slot axis.
///
/// Byte-identical to the peer [`CAIXA_KEY_DEPS`] renderer-side wire-key
/// serde-key axis modulo the leading `:` — the two consts split on the
/// axis every dep-graph slot carries (author-facing kebab-case label vs.
/// renderer-side wire key). Same "one canonical byte-string per typed
/// axis" discipline every peer M2 / M3 renderer-wire-key axis carries.
pub const DEP_AUTHOR_KEY_DEPS: &str = ":deps";
/// Canonical author-facing kebab-case `(defcaixa … :deps-dev ((…)))`
/// top-level dep-list slot label the dev-only two-list dependency-graph
/// slot family surfaces under. Peer of [`DEP_AUTHOR_KEY_DEPS`] on the
/// two-list dep-graph slot axis; see [`DEP_AUTHOR_KEY_DEPS`] for the
/// full lift rationale.
pub const DEP_AUTHOR_KEY_DEPS_DEV: &str = ":deps-dev";
/// Canonical camelCase JSON/YAML top-level key for
/// [`crate::supervisor::SupervisorSpec`]'s `estrategia` restart-strategy
/// discriminator — the exact byte-sequence the type's
/// `#[serde(rename_all = "camelCase")]` derive emits, and the scalar every
/// downstream JSON/YAML consumer that reaches into a serialized
/// `SupervisorSpec` (via `Value::get(...)`) must probe on.
///
/// The scalar is derived from the Rust field name `estrategia` by the
/// `rename_all = "camelCase"` derive; `estrategia` has no `_`, so the
/// serde transform is a no-op on this axis and the emitted key equals the
/// source-side field name byte-for-byte. Lifting the byte to one
/// `&'static str` closes the drift footgun structurally: a future
/// refactor renaming the Rust field OR retaining the field name while
/// adding a `#[serde(rename = "…")]` override would silently emit a
/// `SupervisorSpec` whose restart-strategy discriminator lands under one
/// key while every downstream consumer still probes another — the
/// future wasm-operator's supervisor reconcile posture, the M4
/// `caixa.pleme.io/v1alpha1/Supervisor` CR materializer's admission
/// webhook, the future `feira lint` supervisor-tree cross-check. The
/// identity pin (`supervisor_spec_serde_keys_match_lifted_supervisor_key_consts`
/// on the source-side type) catches drift at caixa-core build time
/// rather than at the reconciler's dispatch step, far from the rebrand
/// commit's source.
///
/// Peer of the sibling author-facing
/// [`SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] (`":estrategia"`) on the same
/// per-Supervisor supervision-tree slot axis — that constant names the
/// kebab-case `(defcaixa … :estrategia …)` author surface's top-level
/// slot label, this one names the camelCase JSON/YAML sub-key the
/// serialized `SupervisorSpec` carries the same axis under. Byte-distinct
/// from (though semantically related to) the peer
/// [`M3_PLACEMENT_KEY_ESTRATEGIA`] (also `"estrategia"`) on the M3
/// [`crate::aplicacao::Placement`] axis — that axis carries
/// [`crate::aplicacao::PlacementStrategy`] cross-cluster distribution
/// semantics, this axis carries [`crate::supervisor::RestartStrategy`]
/// OTP supervisor semantics; splitting the two lets each schema's
/// future rebrand land independently on the same
/// "byte-identical-but-semantically-distinct" discipline the peer
/// [`FLEET_PROGRAMS_KEY_NAME`] / [`KUBE_KEY_NAME`] split established.
///
/// Same "one canonical byte-string per typed serialized-key axis"
/// discipline every peer camelCase serde-key lift carries
/// ([`M2_LIMITS_KEY_MEMORY`] / [`M2_LIMITS_KEY_FUEL`] /
/// [`M2_LIMITS_KEY_WALL_CLOCK`] / [`M2_LIMITS_KEY_CPU`] (d8b8b4f),
/// [`M2_BEHAVIOR_KEY_ON_INIT`] etc. (21fe462),
/// [`M2_UPGRADE_FROM_KEY_FROM`] / [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`]
/// (36ffe65), [`M3_PLACEMENT_KEY_ESTRATEGIA`] etc.) — extended here to
/// close the last of the four top-level typed-struct
/// `#[serde(rename_all = "camelCase")]` axes lacking a lifted peer.
pub const SUPERVISOR_KEY_ESTRATEGIA: &str = "estrategia";
/// Canonical camelCase JSON/YAML top-level key for
/// [`crate::supervisor::SupervisorSpec`]'s `max_restarts` axis. Peer of
/// [`SUPERVISOR_KEY_ESTRATEGIA`] on the same sibling
/// supervision-tree serialized-key axis; see [`SUPERVISOR_KEY_ESTRATEGIA`]
/// for the full lift rationale. The Rust field is `snake_case`
/// `max_restarts`; `#[serde(rename_all = "camelCase")]` maps it to the
/// camelCase JSON key `"maxRestarts"` this constant pins.
pub const SUPERVISOR_KEY_MAX_RESTARTS: &str = "maxRestarts";
/// Canonical camelCase JSON/YAML top-level key for
/// [`crate::supervisor::SupervisorSpec`]'s `restart_window` axis. Peer of
/// [`SUPERVISOR_KEY_ESTRATEGIA`] on the same sibling
/// supervision-tree serialized-key axis; see [`SUPERVISOR_KEY_ESTRATEGIA`]
/// for the full lift rationale. The Rust field is `snake_case`
/// `restart_window`; `#[serde(rename_all = "camelCase")]` maps it to the
/// camelCase JSON key `"restartWindow"` this constant pins.
pub const SUPERVISOR_KEY_RESTART_WINDOW: &str = "restartWindow";
/// Canonical camelCase JSON/YAML top-level key for
/// [`crate::supervisor::SupervisorSpec`]'s `children` axis. Peer of
/// [`SUPERVISOR_KEY_ESTRATEGIA`] on the same sibling
/// supervision-tree serialized-key axis; see [`SUPERVISOR_KEY_ESTRATEGIA`]
/// for the full lift rationale. The Rust field is lowercase `children`;
/// `#[serde(rename_all = "camelCase")]` is a no-op on this axis and the
/// emitted key equals the source-side field name byte-for-byte.
pub const SUPERVISOR_KEY_CHILDREN: &str = "children";
/// Canonical camelCase JSON/YAML top-level key for the
/// [`crate::supervisor::ChildSpec`] struct's `caixa` per-entry-name-of-
/// the-child-caixa axis — the `caixa:` field the M2 Supervisor's
/// `#[serde(rename_all = "camelCase")]` derive on
/// [`crate::supervisor::ChildSpec`] emits at each entry of the
/// [`crate::supervisor::SupervisorSpec::children`] list, and the exact
/// scalar every downstream consumer reaching for the child caixa's
/// [`crate::Caixa::nome`] via `Value::get(...)` (the future wasm-operator's
/// per-supervisor-tree child resolver, the M4
/// `caixa.pleme.io/v1alpha1/Supervisor` CR materializer's admission
/// webhook per-child cross-check, the future `feira` supervisor-tree
/// walker's per-child name-lookup, the [`caixa_resolver`] per-child
/// git-clone step) must probe on.
///
/// The scalar is derived from the Rust field name `caixa` by the
/// `rename_all = "camelCase"` derive; `caixa` has no `_`, so the serde
/// transform is a no-op on this axis and the emitted key equals the
/// source-side field name byte-for-byte. Lifting the byte to one
/// `&'static str` closes the drift footgun structurally: a future
/// refactor renaming the Rust field OR retaining the field name while
/// adding a `#[serde(rename = "…")]` override would silently emit a
/// `ChildSpec` whose per-entry child-caixa discriminator lands under
/// one key while every downstream consumer still probes another. The
/// identity pin (`child_spec_serde_keys_match_lifted_supervisor_child_key_consts`
/// on the source-side type) catches drift at caixa-core build time
/// rather than at the reconciler's dispatch step, far from the rebrand
/// commit's source.
///
/// Peer of [`SUPERVISOR_CHILD_KEY_VERSAO`] / [`SUPERVISOR_CHILD_KEY_RESTART`]
/// on the same [`crate::supervisor::ChildSpec`] per-entry serialized-key
/// axis. Peer of the sibling [`SUPERVISOR_KEY_ESTRATEGIA`] /
/// [`SUPERVISOR_KEY_MAX_RESTARTS`] / [`SUPERVISOR_KEY_RESTART_WINDOW`] /
/// [`SUPERVISOR_KEY_CHILDREN`] tetrad (40cc4e5) on the enclosing
/// [`crate::supervisor::SupervisorSpec`] top-level serialized-key axis
/// — that lift pinned the four camelCase JSON keys the M2
/// supervision-tree top-level derive emits, this lift extends the same
/// discipline onto the sibling per-entry `ChildSpec` derive so the last
/// M2 typed-struct sub-block `#[serde(rename_all = "camelCase")]` axis
/// on the Supervisor surface without a lifted serde-key peer joins the
/// substrate's "one canonical byte-string per typed serialized-key axis"
/// discipline.
///
/// Byte-identical to (but semantically distinct from) the peer
/// [`MEMBRO_KEY_CAIXA`] (ce80ca0) on the sibling M3
/// [`crate::aplicacao::Membro`] per-`:membros` entry axis — both axes
/// carry per-entry caixa-name discriminators on typed list slots, but
/// splitting the two lets each schema's future rebrand land
/// independently at its canonical const definition without coupling
/// the M2 Supervisor per-child axis to the M3 Aplicacao per-member axis
/// (or vice versa) — same "byte-identical-but-semantically-distinct"
/// discipline the peer [`FLEET_PROGRAMS_KEY_NAME`] / [`KUBE_KEY_NAME`]
/// split established.
///
/// Same "one canonical byte-string per typed serialized-key axis"
/// discipline every peer camelCase serde-key lift carries
/// ([`M2_LIMITS_KEY_MEMORY`] etc. (d8b8b4f), [`M2_BEHAVIOR_KEY_ON_INIT`]
/// etc. (21fe462), [`M2_UPGRADE_FROM_KEY_FROM`] /
/// [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`] (36ffe65),
/// [`M3_PLACEMENT_KEY_ESTRATEGIA`] etc., [`SUPERVISOR_KEY_ESTRATEGIA`]
/// etc. (40cc4e5), [`MEMBRO_KEY_CAIXA`] / [`MEMBRO_KEY_VERSAO`]
/// (ce80ca0), [`CONTRATO_KEY_DE`] / [`CONTRATO_KEY_PARA`] /
/// [`CONTRATO_KEY_WIT`] (ca463a4), [`ENTRADA_KEY_HOST`] etc. (a3d6162),
/// [`POLITICAS_KEY_TIMEOUT`] etc. (b55cca7), [`CIRCUIT_BREAKER_KEY_MAX_FAILURES`]
/// / [`CIRCUIT_BREAKER_KEY_WINDOW`] (468e959)) — extended here to the
/// last M2 typed-struct sub-block `#[serde(rename_all = "camelCase")]`
/// axis on the Supervisor surface, the per-`:children` entry
/// [`crate::supervisor::ChildSpec`] derive.
pub const SUPERVISOR_CHILD_KEY_CAIXA: &str = "caixa";
/// Canonical camelCase JSON/YAML top-level key for the
/// [`crate::supervisor::ChildSpec`] struct's `versao` per-entry-semver-
/// constraint-of-the-child axis. Peer of [`SUPERVISOR_CHILD_KEY_CAIXA`]
/// on the same [`crate::supervisor::ChildSpec`] per-entry serialized-key
/// axis; see [`SUPERVISOR_CHILD_KEY_CAIXA`] for the full lift rationale.
/// The Rust field is lowercase `versao`; `#[serde(rename_all = "camelCase")]`
/// is a no-op on this axis and the emitted key equals the source-side
/// field name byte-for-byte.
///
/// Byte-identical to (but semantically distinct from) the peer
/// [`MEMBRO_KEY_VERSAO`] (ce80ca0) on the sibling M3
/// [`crate::aplicacao::Membro`] per-`:membros` entry axis and the peer
/// [`FLEET_PROGRAMS_KEY_VERSAO`] on the `lareira-fleet-programs`
/// library-chart values-schema axis — same
/// "byte-identical-but-semantically-distinct" discipline the peer
/// [`FLEET_PROGRAMS_KEY_NAME`] / [`KUBE_KEY_NAME`] /
/// [`MEMBRO_KEY_CAIXA`] / [`SUPERVISOR_CHILD_KEY_CAIXA`] splits
/// established: each schema's future rebrand lands independently at its
/// canonical const definition without coupling one axis to the others.
pub const SUPERVISOR_CHILD_KEY_VERSAO: &str = "versao";
/// Canonical camelCase JSON/YAML top-level key for the
/// [`crate::supervisor::ChildSpec`] struct's `restart` per-entry
/// [`crate::supervisor::RestartPolicy`] discriminator axis. Peer of
/// [`SUPERVISOR_CHILD_KEY_CAIXA`] on the same
/// [`crate::supervisor::ChildSpec`] per-entry serialized-key axis; see
/// [`SUPERVISOR_CHILD_KEY_CAIXA`] for the full lift rationale. The Rust
/// field is lowercase `restart`; `#[serde(rename_all = "camelCase")]`
/// is a no-op on this axis and the emitted key equals the source-side
/// field name byte-for-byte.
pub const SUPERVISOR_CHILD_KEY_RESTART: &str = "restart";
/// Canonical camelCase JSON/YAML top-level key for the
/// [`crate::aplicacao::Membro`] struct's `caixa` per-entry-name-of-the-
/// member-Servico axis — the `caixa:` field the M3 Aplicacao's
/// `#[serde(rename_all = "camelCase")]` derive on [`crate::aplicacao::Membro`]
/// emits at each `:membros` entry, and the exact scalar every downstream
/// `#[serde(rename_all = "camelCase")]` derive on [`crate::aplicacao::Membro`]
/// emits at each `:membros` entry, and the exact scalar every downstream
/// consumer reaching for the member's [`crate::Caixa::nome`] via
/// `Value::get(...)` (the future wasm-operator's per-`:membros` resolver,
/// the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's admission
/// webhook, the `feira app graph` verb's per-member name-lookup, the
/// [`caixa_resolver`] per-`:membros` git-clone step) must probe on.
///
/// The scalar is derived from the Rust field name `caixa` by the
/// `rename_all = "camelCase"` derive; `caixa` has no `_`, so the
/// serde transform is a no-op on this axis and the emitted key equals
/// the source-side field name byte-for-byte. Lifting the byte to one
/// `&'static str` closes the drift footgun structurally: a future
/// refactor renaming the Rust field OR retaining the field name while
/// adding a `#[serde(rename = "…")]` override would silently emit a
/// `Membro` whose per-entry name discriminator lands under one key while
/// every downstream consumer still probes another — the future wasm-
/// operator's per-`:membros` resolver, the M4 CR materializer's admission
/// webhook, the `feira app graph` verb's per-member name-lookup. The
/// identity pin (`membro_serde_keys_match_lifted_membro_key_consts` on
/// the source-side type) catches drift at caixa-core build time rather
/// than at the reconciler's dispatch step, far from the rebrand commit's
/// source.
///
/// Peer of [`MEMBRO_KEY_VERSAO`] on the same [`crate::aplicacao::Membro`]
/// per-entry serialized-key axis. Peer of the sibling
/// [`SUPERVISOR_KEY_ESTRATEGIA`] / [`SUPERVISOR_KEY_MAX_RESTARTS`] /
/// [`SUPERVISOR_KEY_RESTART_WINDOW`] / [`SUPERVISOR_KEY_CHILDREN`] tetrad
/// (40cc4e5) on the sibling `SupervisorSpec` top-level serialized-key
/// axis — that lift pinned the four camelCase JSON keys the M2
/// supervision-tree top-level derive emits, this lift extends the same
/// discipline onto the M3 Aplicacao's per-`:membros` entry derive.
///
/// Same "one canonical byte-string per typed serialized-key axis"
/// discipline every peer camelCase serde-key lift carries
/// ([`M2_LIMITS_KEY_MEMORY`] etc. (d8b8b4f), [`M2_BEHAVIOR_KEY_ON_INIT`]
/// etc. (21fe462), [`M2_UPGRADE_FROM_KEY_FROM`] /
/// [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`] (36ffe65),
/// [`M3_PLACEMENT_KEY_ESTRATEGIA`] etc., [`SUPERVISOR_KEY_ESTRATEGIA`]
/// etc. (40cc4e5)) — extended here to the M3 [`crate::aplicacao::Membro`]
/// per-entry axis, the last top-level typed-struct
/// `#[serde(rename_all = "camelCase")]` axis on the M3 mesh-slot family
/// lacking a lifted peer.
pub const MEMBRO_KEY_CAIXA: &str = "caixa";
/// Canonical camelCase JSON/YAML top-level key for the
/// [`crate::aplicacao::Membro`] struct's `versao` per-entry-semver-
/// constraint-of-the-member axis. Peer of [`MEMBRO_KEY_CAIXA`] on the
/// same [`crate::aplicacao::Membro`] per-entry serialized-key axis; see
/// [`MEMBRO_KEY_CAIXA`] for the full lift rationale. The Rust field is
/// lowercase `versao`; `#[serde(rename_all = "camelCase")]` is a no-op
/// on this axis and the emitted key equals the source-side field name
/// byte-for-byte.
///
/// Byte-identical to [`FLEET_PROGRAMS_KEY_VERSAO`] today — both resolve
/// to the same six-byte `"versao"` literal — but semantically distinct:
/// [`FLEET_PROGRAMS_KEY_VERSAO`] names the `lareira-fleet-programs`
/// library chart's per-entry version-constraint schema-axis (spelled
/// per the chart's `values.schema.json` — the same schema surface
/// [`caixa_mesh::programs_for_aplicacao`] transcribes each `:membros`
/// entry's version constraint into), while this constant names the
/// [`crate::aplicacao::Membro`] typed struct's derive-emitted `versao`
/// field key (spelled per the type's `#[serde(rename_all = "camelCase")]`
/// attribute — a separate schema contract on the upstream typed
/// manifest). Splitting the two lets each schema's future rebrand land
/// independently at its canonical const definition without coupling the
/// Membro typed-struct axis to the fleet-programs values-schema axis
/// (or vice versa) — same "byte-identical-but-semantically-distinct"
/// discipline the peer [`FLEET_PROGRAMS_KEY_NAME`] / [`KUBE_KEY_NAME`]
/// split established on the sibling per-entry name-discriminator axis.
pub const MEMBRO_KEY_VERSAO: &str = "versao";
/// Canonical camelCase JSON/YAML top-level key for the
/// [`crate::aplicacao::WitContract`] struct's `de` per-entry
/// source-endpoint-of-the-contract axis — the `de:` field the M3
/// Aplicacao's `#[serde(rename_all = "camelCase")]` derive on
/// [`crate::aplicacao::WitContract`] emits at each `:contratos` entry,
/// and the exact scalar every downstream consumer reaching for the
/// caller-Servico name via `Value::get(...)` (the future
/// wasm-operator's per-`:contratos` edge resolver, the M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's admission
/// webhook per-edge cross-check, the `feira app graph` verb's per-edge
/// tail-label lookup, the future per-`:contratos` `CiliumNetworkPolicy`
/// emitter's per-edge `fromEndpoints` selector projection) must probe on.
///
/// The scalar is derived from the Rust field name `de` by the
/// `rename_all = "camelCase"` derive; `de` has no `_`, so the serde
/// transform is a no-op on this axis and the emitted key equals the
/// source-side field name byte-for-byte. Lifting the byte to one
/// `&'static str` closes the drift footgun structurally: a future
/// refactor renaming the Rust field OR retaining the field name while
/// adding a `#[serde(rename = "…")]` override would silently emit a
/// `WitContract` whose per-entry caller-Servico discriminator lands
/// under one key while every downstream consumer still probes another —
/// the future wasm-operator's per-`:contratos` edge resolver, the M4 CR
/// materializer's admission webhook per-edge cross-check, the
/// `feira app graph` verb's per-edge tail-label lookup. The identity pin
/// (`wit_contract_serde_keys_match_lifted_contrato_key_consts` on the
/// source-side type) catches drift at caixa-core build time rather than
/// at the reconciler's dispatch step, far from the rebrand commit's
/// source.
///
/// Peer of [`CONTRATO_KEY_PARA`] / [`CONTRATO_KEY_WIT`] on the same
/// [`crate::aplicacao::WitContract`] per-entry serialized-key axis. Peer
/// of the sibling [`MEMBRO_KEY_CAIXA`] / [`MEMBRO_KEY_VERSAO`] pair
/// (ce80ca0) on the sibling M3 [`crate::aplicacao::Membro`] per-entry
/// serialized-key axis — that lift pinned the two camelCase JSON keys
/// the M3 per-`:membros` derive emits, this lift extends the same
/// discipline onto the sibling M3 per-`:contratos` derive so the last
/// M3 mesh-slot atom top-level `#[serde(rename_all = "camelCase")]`
/// axis on the Aplicacao surface without a lifted peer joins the
/// substrate's "one canonical byte-string per typed serialized-key
/// axis" discipline.
///
/// Byte-identical to (but semantically distinct from) the sibling
/// author-facing kebab-case [`CONTRATO_AUTHOR_KEY_DE`] (f50c875) modulo
/// the leading `:` — the two consts split on the axis every M3 mesh-slot
/// atom carries (author-facing kebab-case label vs. renderer-side
/// camelCase overlay key), the same split the [`M2_AUTHOR_KEY_LIMITS`] /
/// [`M2_KEY_LIMITS`] peer pair established on the sibling M2 axis and
/// the [`M3_AUTHOR_KEY_PLACEMENT`] / [`M3_KEY_PLACEMENT`] peer pair
/// established on the sibling M3 top-level slot axis.
pub const CONTRATO_KEY_DE: &str = "de";
/// Canonical camelCase JSON/YAML top-level key for the
/// [`crate::aplicacao::WitContract`] struct's `para` per-entry
/// target-endpoint-of-the-contract axis. Peer of [`CONTRATO_KEY_DE`] on
/// the same [`crate::aplicacao::WitContract`] per-entry serialized-key
/// axis; see [`CONTRATO_KEY_DE`] for the full lift rationale. The Rust
/// field is lowercase `para`; `#[serde(rename_all = "camelCase")]` is a
/// no-op on this axis and the emitted key equals the source-side field
/// name byte-for-byte.
pub const CONTRATO_KEY_PARA: &str = "para";
/// Canonical camelCase JSON/YAML top-level key for the
/// [`crate::aplicacao::WitContract`] struct's `wit` per-entry
/// WIT-world-reference-of-the-contract axis — the discriminator every
/// downstream WIT-shape dispatcher ([`crate::wit_shape_is_http`] /
/// [`crate::wit_shape_is_pubsub`] / [`crate::wit_shape_is_store`], the
/// future M4 per-edge WIT registry resolver, the future
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's admission-time
/// WIT-world classification) keys off. Peer of [`CONTRATO_KEY_DE`] on
/// the same [`crate::aplicacao::WitContract`] per-entry serialized-key
/// axis; see [`CONTRATO_KEY_DE`] for the full lift rationale. The Rust
/// field is lowercase `wit`; `#[serde(rename_all = "camelCase")]` is a
/// no-op on this axis and the emitted key equals the source-side field
/// name byte-for-byte.
pub const CONTRATO_KEY_WIT: &str = "wit";
/// Canonical camelCase YAML sub-key for the [`crate::aplicacao::Placement`]
/// struct's `estrategia` distribution-strategy discriminator — the
/// per-`M3_KEY_PLACEMENT`-block field the M3 [`crate::aplicacao::PlacementStrategy`]
/// enum's `Serialize` derive emits, and the exact scalar every downstream
/// consumer dispatches on:
///
/// - the `lareira-fleet-programs` aggregator's per-entry strategy dispatch
/// (each `programs[].placement.estrategia` reads `"SingleNode"` /
/// `"Replicated"` / `"Sharded"` verbatim to select the takeover
/// semantics per MESH-COMPOSITION.md §II.1),
/// - the future `app-operator` reconciler's per-Aplicacao strategy branch,
/// - the future `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
/// admission-time `spec.placement.estrategia` typed-enum bind,
/// - and every M3 Adaptive weighting the compression pass reads off
/// `placement.estrategia` per MESH-COMPOSITION.md §V.
///
/// The scalar is derived by [`crate::aplicacao::Placement`]'s
/// `#[serde(rename_all = "camelCase")]` from the Rust field name
/// `estrategia`; `estrategia` has no `_`, so the serde transform is a
/// no-op on this axis and the emitted key equals the source-side field
/// name byte-for-byte. Lifting the byte to one `&'static str` closes
/// the drift footgun structurally: a future refactor renaming the Rust
/// field (`estrategia` → `strategy` for English-uniformity, `distribution`
/// for schema-clarity, etc.) OR retaining the field name while adding
/// a `#[serde(rename = "…")]` override would silently emit a
/// `placement:` block whose distribution-strategy discriminator lands
/// under one key while every downstream consumer still probes another —
/// the aggregator's dispatch, the operator's reconcile, the CR
/// materializer's admission bind would each silently no-op, and the
/// workload would silently come up under the strategy's serde-derived
/// default rather than the per-Aplicacao override the typed slot set.
/// The identity pin + serde round-trip pin the sweep introduces catch
/// the drift at caixa-core / caixa-mesh build time rather than at the
/// aggregator's filter step or the operator's reconcile posture, far
/// from the rebrand commit's source.
///
/// Peer of [`M3_KEY_PLACEMENT`] on the same programs.yaml per-entry
/// axis — that constant names the top-level overlay key the entry
/// carries, this one names the per-`placement:` sub-block strategy
/// discriminator every consumer dispatches on. Byte-identical to (but
/// semantically distinct from) [`crate::supervisor::SupervisorSpec`]'s
/// peer `estrategia` field on the M2 supervisor-strategy axis — that
/// axis carries [`crate::supervisor::RestartStrategy`] (`OneForOne` /
/// `OneForAll` / `RestForOne` / `SimpleOneForOne`, OTP supervisor
/// semantics) while this axis carries [`crate::aplicacao::PlacementStrategy`]
/// (`SingleNode` / `Replicated` / `Sharded`, cross-cluster distribution
/// semantics); splitting the two lets each schema's future rebrand
/// land independently on the same byte-identical-but-semantically-
/// distinct discipline the [`FLEET_PROGRAMS_KEY_NAME`] / [`KUBE_KEY_NAME`]
/// split established.
pub const M3_PLACEMENT_KEY_ESTRATEGIA: &str = "estrategia";
/// Canonical camelCase YAML sub-key for the [`crate::aplicacao::Placement`]
/// struct's `clusters` cluster-pool axis — the per-`M3_KEY_PLACEMENT`-block
/// field carrying the validated cluster-list (non-empty + duplicate-free
/// per [`crate::aplicacao::AplicacaoSpec::validate_placement`]) that every
/// downstream cross-cluster consumer filters off:
///
/// - the `lareira-fleet-programs` aggregator's per-cluster fanout filter
/// (each cluster's aggregator scopes `.Values.programs` by
/// `.placement.clusters | contains .Values.cluster`, so a workload's
/// `clusters: [rio, mar]` list ends up landing on rio + mar and no other
/// cluster per MESH-COMPOSITION.md §III.4),
/// - the future `app-operator` reconciler's per-Aplicacao cluster-set
/// dispatch,
/// - the future `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
/// admission-time `spec.placement.clusters` typed-list bind, and
/// - the M3 Adaptive compression pass's per-cluster weight lookup per
/// MESH-COMPOSITION.md §V.
///
/// The scalar is derived by [`crate::aplicacao::Placement`]'s
/// `#[serde(rename_all = "camelCase")]` from the Rust field name
/// `clusters`; `clusters` has no `_`, so the serde transform is a no-op
/// on this axis and the emitted key equals the source-side field name
/// byte-for-byte. Lifting the byte to one `&'static str` closes the same
/// drift footgun the peer [`M3_PLACEMENT_KEY_ESTRATEGIA`] lift closed on
/// the sibling distribution-strategy discriminator: a future refactor
/// renaming the Rust field (`clusters` → `clusterPool` for schema-clarity,
/// `sites` for eventual multi-substrate reach, etc.) OR retaining the
/// field name while adding a `#[serde(rename = "…")]` override would
/// silently emit a `placement:` block whose cluster-list lands under one
/// key while every downstream consumer still probes another — the
/// aggregator's per-cluster fanout filter would then see an empty
/// `clusters` list on every entry and silently drop every workload from
/// every cluster (the failure surfacing as "the newly-deployed Aplicacao
/// never spins up anywhere" far from the rebrand commit's source). The
/// identity pin + serde-derive round-trip pin the sweep introduces catch
/// the drift at caixa-core / caixa-mesh build time rather than at the
/// aggregator's fanout step or the operator's reconcile posture.
///
/// Peer of [`M3_KEY_PLACEMENT`] / [`M3_PLACEMENT_KEY_ESTRATEGIA`] on the
/// same programs.yaml per-entry axis — `M3_KEY_PLACEMENT` names the
/// top-level overlay key each entry carries, `M3_PLACEMENT_KEY_ESTRATEGIA`
/// names the per-sub-block distribution-strategy discriminator every
/// dispatch consumer branches on, this constant names the per-sub-block
/// cluster-pool list every per-cluster fanout consumer scopes by.
pub const M3_PLACEMENT_KEY_CLUSTERS: &str = "clusters";
/// Canonical camelCase YAML sub-key for the [`crate::aplicacao::Placement`]
/// struct's `affinity` placement-engine-hint axis — the per-`M3_KEY_PLACEMENT`-
/// block optional field carrying the validated non-empty affinity hint
/// (per [`crate::aplicacao::AplicacaoSpec::validate_placement`]) that every
/// downstream placement-hint consumer weights off:
///
/// - the `lareira-fleet-programs` aggregator's per-entry M3 Adaptive
/// compression pass reading `placement.affinity` to weight the emitted
/// `ComputeUnit`'s replica-distribution overlay per MESH-COMPOSITION.md §V,
/// - the future `app-operator` reconciler's per-Aplicacao pod-affinity /
/// node-affinity K8s-primitive materializer keying off the same value as
/// an `app.pleme.io/affinity-hint=<value>` label selector,
/// - the future `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
/// admission-time `spec.placement.affinity` typed-string bind, and
/// - the M4 cross-cluster placement engine's per-hint takeover-priority
/// dispatch on the same value (`data-locality` / `low-latency` /
/// `anti-affinity` per the [`crate::aplicacao::validate_placement_affinity`]
/// value-shape gate's documented canonical hint set).
///
/// The scalar is derived by [`crate::aplicacao::Placement`]'s
/// `#[serde(rename_all = "camelCase")]` from the Rust field name
/// `affinity`; `affinity` has no `_`, so the serde transform is a no-op on
/// this axis and the emitted key equals the source-side field name
/// byte-for-byte. Unlike the always-emitted [`M3_PLACEMENT_KEY_ESTRATEGIA`]
/// / [`M3_PLACEMENT_KEY_CLUSTERS`] axes, the `affinity` field carries a
/// `#[serde(skip_serializing_if = "Option::is_none")]` attribute so the
/// key appears in the rendered `placement:` block iff the typed slot
/// resolves to `Some(_)` — the omit-when-unset contract the peer typed
/// slots ([`crate::aplicacao::MeshPolicy::timeout`],
/// [`crate::aplicacao::MeshPolicy::retries`],
/// [`crate::aplicacao::MeshPolicy::mtls_required`]) each carry to keep an
/// unset typed slot from bloating every rendered programs.yaml entry with
/// a nominal-only `affinity: null` value the downstream weighting passes
/// would then need to unwrap defensively.
///
/// Lifting the byte to one `&'static str` closes the same drift footgun
/// the peer [`M3_PLACEMENT_KEY_ESTRATEGIA`] / [`M3_PLACEMENT_KEY_CLUSTERS`]
/// lifts closed on the sibling always-emitted axes: a future refactor
/// renaming the Rust field (`affinity` → `affinityHint` for schema-clarity,
/// `placementHint` for symmetry with the future per-cluster affinity
/// hierarchy, etc.) OR retaining the field name while adding a
/// `#[serde(rename = "…")]` override would silently emit a `placement:`
/// block whose affinity hint lands under one key while every downstream
/// weighting consumer still probes another — the M3 Adaptive compression
/// pass would then see a `None` affinity on every entry and silently fall
/// back to the uniform-weight baseline (the workload's typed
/// `:affinity "data-locality"` hint would be silently discarded, and the
/// failure surfaces as "the newly-deployed Aplicacao's replicas don't
/// cluster where the typed slot said they should" far from the rebrand
/// commit's source). The identity pin + serde-derive round-trip pin the
/// sweep introduces catch the drift at caixa-core / caixa-mesh build time
/// rather than at the aggregator's weighting step or the operator's
/// reconcile posture.
///
/// Peer of [`M3_KEY_PLACEMENT`] / [`M3_PLACEMENT_KEY_ESTRATEGIA`] /
/// [`M3_PLACEMENT_KEY_CLUSTERS`] on the same programs.yaml per-entry
/// axis — `M3_KEY_PLACEMENT` names the top-level overlay key each entry
/// carries, `M3_PLACEMENT_KEY_ESTRATEGIA` names the per-sub-block
/// distribution-strategy discriminator every dispatch consumer branches
/// on, `M3_PLACEMENT_KEY_CLUSTERS` names the per-sub-block cluster-pool
/// list every per-cluster fanout consumer scopes by, this constant names
/// the per-sub-block optional placement-engine hint every weighting
/// consumer reads off.
pub const M3_PLACEMENT_KEY_AFFINITY: &str = "affinity";
/// Canonical camelCase YAML sub-key for the [`crate::aplicacao::Placement`]
/// struct's `shard_key` shard-selection-template axis — the per-`M3_KEY_PLACEMENT`-
/// block optional field carrying the validated non-empty shard-key
/// template (per [`crate::aplicacao::AplicacaoSpec::validate_placement`]'s
/// `ShardedKeyEmpty` arm — the build rejects any `:placement Sharded`
/// that omits the slot, and rejects any non-Sharded strategy that
/// carries the slot as `ShardKeyOnNonSharded`) that every downstream
/// shard-dispatch consumer materializes off:
///
/// - the `lareira-fleet-programs` aggregator's per-entry M3 shard-pool
/// dispatch materializer keying off `placement.shardKey` to hash each
/// incoming entity into the per-cluster shard pool the Akka-style
/// cluster-sharding reconciler owns (per MESH-COMPOSITION.md §II.4);
/// - the future `app-operator` reconciler's per-Aplicacao
/// `ShardedResource` CR emitter binding the typed template to the
/// K8s-primitive shard-assignment controller's `spec.hashKey`;
/// - the future `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
/// admission-time `spec.placement.shardKey` typed-string bind, and
/// - the M4 Orleans-style virtual-actor runtime's per-grain
/// placement dispatch reading the same value as the grain-identity
/// hash source (per RUNTIME-PATTERNS.md's virtual-actor pattern
/// entry).
///
/// The scalar is derived by [`crate::aplicacao::Placement`]'s
/// `#[serde(rename_all = "camelCase")]` from the Rust field name
/// `shard_key`; unlike the peer `affinity` / `clusters` / `estrategia`
/// axes (whose field names carry no `_`, so the serde transform is a
/// no-op), the `shard_key` field's `snake_case` name is actively
/// transformed by the derive to `shardKey` — the emitted key differs
/// from the source-side field name and the drift-footgun surface is
/// therefore correspondingly larger. Unlike the always-emitted
/// [`M3_PLACEMENT_KEY_ESTRATEGIA`] / [`M3_PLACEMENT_KEY_CLUSTERS`]
/// axes, the `shard_key` field carries a
/// `#[serde(skip_serializing_if = "Option::is_none")]` attribute so the
/// key appears in the rendered `placement:` block iff the typed slot
/// resolves to `Some(_)` — the omit-when-unset contract the peer typed
/// slots ([`M3_PLACEMENT_KEY_AFFINITY`],
/// [`crate::aplicacao::MeshPolicy::timeout`],
/// [`crate::aplicacao::MeshPolicy::retries`],
/// [`crate::aplicacao::MeshPolicy::mtls_required`]) each carry to keep
/// an unset typed slot from bloating every rendered programs.yaml
/// entry with a nominal-only `shardKey: null` value the downstream
/// shard-dispatch passes would then need to unwrap defensively.
///
/// Lifting the byte to one `&'static str` closes the same drift footgun
/// the peer [`M3_PLACEMENT_KEY_ESTRATEGIA`] / [`M3_PLACEMENT_KEY_CLUSTERS`]
/// / [`M3_PLACEMENT_KEY_AFFINITY`] lifts closed on the sibling axes:
/// a future refactor renaming the Rust field (`shard_key` →
/// `partition_key` for Kafka-symmetric naming, `entity_key` for
/// Akka/Orleans-symmetric naming, `hash_key` for schema-clarity, etc.)
/// OR retaining the field name while adding a `#[serde(rename = "…")]`
/// override OR dropping the struct-level `rename_all = "camelCase"`
/// attribute would silently emit a `placement:` block whose shard-
/// selection template lands under one key while every downstream shard-
/// dispatch consumer still probes another — the M3 shard-pool
/// dispatch materializer would then see a `None` shard-key on every
/// entry and silently fall back to the per-entry random-placement
/// baseline (the workload's typed `:shard-key "$tenantId"` template
/// would be silently discarded, and per-tenant entities would scatter
/// across every cluster in the pool instead of consistently landing on
/// one — the failure surfaces as "the newly-deployed sharded Aplicacao
/// mysteriously loses its per-tenant locality" far from the rebrand
/// commit's source, and Cilium's per-entity trace surfaces the
/// symptom only in hubble traces of the actual data-plane skew, not in
/// `kubectl describe`). The identity pin + serde-derive round-trip
/// pin the sweep introduces catch the drift at caixa-core / caixa-mesh
/// build time rather than at the aggregator's shard-dispatch step or
/// the operator's reconcile posture. The serde-derive pin is
/// particularly load-bearing on this axis (relative to the peer
/// `affinity` / `clusters` / `estrategia` pins) because the underlying
/// derive transform is *not* a no-op — the emitted `shardKey` key
/// differs from the source-side `shard_key` field by construction,
/// so any rebrand that touches either endpoint of the transform (the
/// field name OR the `rename_all` attribute OR a per-field `rename`
/// override) reaches this pin's assertion by construction.
///
/// Peer of [`M3_KEY_PLACEMENT`] / [`M3_PLACEMENT_KEY_ESTRATEGIA`] /
/// [`M3_PLACEMENT_KEY_CLUSTERS`] / [`M3_PLACEMENT_KEY_AFFINITY`] on the
/// same programs.yaml per-entry axis — `M3_KEY_PLACEMENT` names the
/// top-level overlay key each entry carries, `M3_PLACEMENT_KEY_ESTRATEGIA`
/// names the per-sub-block distribution-strategy discriminator every
/// dispatch consumer branches on, `M3_PLACEMENT_KEY_CLUSTERS` names the
/// per-sub-block cluster-pool list every per-cluster fanout consumer
/// scopes by, `M3_PLACEMENT_KEY_AFFINITY` names the per-sub-block
/// optional placement-engine hint every weighting consumer reads off,
/// this constant names the per-sub-block optional shard-selection
/// template every shard-dispatch consumer materializes off. Completes
/// the M3 `Placement` sub-key quartet's canonical-key lift alongside
/// the sibling always-emitted axes.
pub const M3_PLACEMENT_KEY_SHARD_KEY: &str = "shardKey";
/// Canonical M3 [`crate::aplicacao::PlacementStrategy::SingleNode`]
/// variant discriminator scalar-value — the exact byte-string the
/// `Serialize` derive on the un-`rename`d enum emits under
/// [`M3_PLACEMENT_KEY_ESTRATEGIA`] whenever the typed slot's
/// distribution strategy is the single-cluster-active-at-a-time arm
/// (OTP distributed-application takeover, MESH-COMPOSITION.md §II.1).
///
/// The scalar every downstream cluster-side dispatcher probes verbatim
/// to pick the takeover semantics:
///
/// - the `lareira-fleet-programs` aggregator's per-entry
/// `placement.estrategia` strategy dispatch (`if $strat ==
/// "SingleNode" { ... }`),
/// - the future `app-operator` reconciler's per-Aplicacao
/// strategy-branch (`match placement.estrategia { "SingleNode" =>
/// … }`),
/// - the future `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
/// admission-time enum-arm bind, and
/// - the M3 Adaptive compression pass's per-strategy weighting per
/// MESH-COMPOSITION.md §V.
///
/// The scalar is derived by `#[derive(Serialize)]` on the
/// [`crate::aplicacao::PlacementStrategy`] enum with no
/// `#[serde(rename_all = …)]` attribute, so the emitted string is
/// byte-for-byte the source-side variant name. Lifting the byte to
/// one `&'static str` closes the drift footgun structurally: a future
/// refactor renaming the variant (`SingleNode` → `Singleton` for OTP-
/// vocabulary parity, `Active` for shorter-form-clarity, etc.) OR
/// adding a `#[serde(rename_all = "kebab-case")]` attribute would
/// silently emit a `placement.estrategia:` scalar whose distribution
/// strategy lands under one spelling while every downstream consumer
/// still dispatches on another — the aggregator's strategy branch,
/// the operator's reconcile posture, the CR materializer's
/// admission-time enum-arm bind would each silently no-op onto the
/// enum's `default()` (`Replicated`) and the workload would come up
/// on every declared cluster active-active rather than the
/// single-cluster-takeover the typed slot named. The serde
/// round-trip pin the sweep introduces
/// ([`crate::aplicacao::tests::placement_strategy_variants_serialize_to_lifted_scalar_values`])
/// catches the drift at caixa-core build time rather than at the
/// aggregator's dispatch step or the operator's reconcile posture.
///
/// Peer of [`M3_PLACEMENT_ESTRATEGIA_REPLICATED`] /
/// [`M3_PLACEMENT_ESTRATEGIA_SHARDED`] on the same closed
/// PlacementStrategy enum surface — together the three constants
/// name every author-reachable arm of the M3 distribution-strategy
/// discriminator, mirroring the closed-enum-scalar-value trajectory
/// [`CILIUM_AUTH_MODE_REQUIRED`] / [`CILIUM_AUTH_MODE_DISABLED`]
/// (8ab119f) established on the sibling Cilium
/// `MutualAuthenticationMode` OpenAPI schema enum.
pub const M3_PLACEMENT_ESTRATEGIA_SINGLE_NODE: &str = "SingleNode";
/// Canonical M3 [`crate::aplicacao::PlacementStrategy::Replicated`]
/// variant discriminator scalar-value — the exact byte-string the
/// `Serialize` derive on the un-`rename`d enum emits under
/// [`M3_PLACEMENT_KEY_ESTRATEGIA`] whenever the typed slot's
/// distribution strategy is the every-cluster-active-active arm (the
/// enum's `default()` and the canonical happy-path per
/// MESH-COMPOSITION.md §II.1).
///
/// Peer of the sibling [`M3_PLACEMENT_ESTRATEGIA_SINGLE_NODE`] /
/// [`M3_PLACEMENT_ESTRATEGIA_SHARDED`] scalars on the same closed
/// enum surface — see the sibling doc for the full drift-mode
/// analysis. This is the arm the un-`:placement` (`Placement::default()`)
/// path serializes as, so drift here silently rebrands the substrate's
/// default distribution posture across every Aplicacao that never
/// declares the slot explicitly.
pub const M3_PLACEMENT_ESTRATEGIA_REPLICATED: &str = "Replicated";
/// Canonical M3 [`crate::aplicacao::PlacementStrategy::Sharded`]
/// variant discriminator scalar-value — the exact byte-string the
/// `Serialize` derive on the un-`rename`d enum emits under
/// [`M3_PLACEMENT_KEY_ESTRATEGIA`] whenever the typed slot's
/// distribution strategy is the hash-keyed-across-clusters arm (Akka
/// cluster sharding, MESH-COMPOSITION.md §II.4). The one arm on which
/// the typed [`M3_PLACEMENT_KEY_SHARD_KEY`] sub-block is required —
/// `AplicacaoSpec::validate_placement` gates `shard_key.is_some() ==
/// matches!(estrategia, Sharded)` as a structural partition of every
/// validated [`crate::aplicacao::Placement`].
///
/// Peer of the sibling [`M3_PLACEMENT_ESTRATEGIA_SINGLE_NODE`] /
/// [`M3_PLACEMENT_ESTRATEGIA_REPLICATED`] scalars on the same closed
/// enum surface — see the [`M3_PLACEMENT_ESTRATEGIA_SINGLE_NODE`] doc
/// for the full drift-mode analysis. This is the arm the future Akka-
/// style cluster-sharding reconciler dispatches on before hashing
/// `placement.shardKey` across `placement.clusters`, so drift here
/// silently collapses the hash-keyed distribution back onto the
/// aggregator's default (Replicated) and every sharded workload's
/// per-entity routing invariant vanishes at the data plane.
pub const M3_PLACEMENT_ESTRATEGIA_SHARDED: &str = "Sharded";
/// Canonical M2 [`crate::supervisor::RestartStrategy::OneForOne`] variant
/// discriminator scalar-value — the exact byte-string the `Serialize`
/// derive on the un-`rename`d enum emits under
/// [`SUPERVISOR_KEY_ESTRATEGIA`] whenever the typed `:supervisor
/// :estrategia` slot's strategy is the restart-only-the-failed-child arm
/// (the enum's `default()` and the canonical happy-path per
/// theory/INSPIRATIONS.md §II.2 — Erlang/OTP `one_for_one`).
///
/// The scalar is the un-`rename`d Rust variant name verbatim; a future
/// `#[serde(rename_all = "kebab-case")]` attribute on the enum, or a
/// per-variant `#[serde(rename = "…")]` override, or a variant rename in
/// the source, would silently emit a `:supervisor :estrategia` scalar
/// whose per-failure sibling-restart discipline lands under one spelling
/// while every downstream consumer still dispatches on another — the
/// future wasm-operator's per-supervisor sibling-restart branch, the
/// future M4 `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's
/// admission-time enum-arm bind, the `caixa-operator`'s hierarchical
/// reconciliation scheduler's per-strategy fan-out would each silently
/// no-op onto the enum's `default()` (`OneForOne`) and the tree would
/// come up with the wrong sibling-restart posture on every non-default
/// arm. The serde round-trip pin the sweep introduces
/// ([`crate::supervisor::tests::restart_strategy_variants_serialize_to_lifted_scalar_values`])
/// catches the drift at caixa-core build time rather than at the
/// operator's reconcile posture.
///
/// Peer of [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ALL`] /
/// [`SUPERVISOR_ESTRATEGIA_REST_FOR_ONE`] /
/// [`SUPERVISOR_ESTRATEGIA_SIMPLE_ONE_FOR_ONE`] on the same closed
/// [`crate::supervisor::RestartStrategy`] enum surface — together the
/// four constants name every author-reachable arm of the OTP-shaped
/// per-supervisor sibling-restart discriminator, mirroring the
/// closed-enum-scalar-value trajectory [`M3_PLACEMENT_ESTRATEGIA_SINGLE_NODE`]
/// / [`M3_PLACEMENT_ESTRATEGIA_REPLICATED`] /
/// [`M3_PLACEMENT_ESTRATEGIA_SHARDED`] (3f0e21c) established on the
/// sibling M3 `PlacementStrategy` enum on the peer per-Aplicacao
/// distribution-strategy axis.
pub const SUPERVISOR_ESTRATEGIA_ONE_FOR_ONE: &str = "OneForOne";
/// Canonical M2 [`crate::supervisor::RestartStrategy::OneForAll`] variant
/// discriminator scalar-value — the exact byte-string the `Serialize`
/// derive on the un-`rename`d enum emits under
/// [`SUPERVISOR_KEY_ESTRATEGIA`] whenever the typed `:supervisor
/// :estrategia` slot's strategy is the restart-every-sibling-on-any-
/// failure arm (Erlang/OTP `one_for_all`, used when children share state
/// and must be in sync).
///
/// Peer of the sibling [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ONE`] /
/// [`SUPERVISOR_ESTRATEGIA_REST_FOR_ONE`] /
/// [`SUPERVISOR_ESTRATEGIA_SIMPLE_ONE_FOR_ONE`] scalars on the same
/// closed enum surface — see the sibling
/// [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ONE`] doc for the full drift-mode
/// analysis.
pub const SUPERVISOR_ESTRATEGIA_ONE_FOR_ALL: &str = "OneForAll";
/// Canonical M2 [`crate::supervisor::RestartStrategy::RestForOne`]
/// variant discriminator scalar-value — the exact byte-string the
/// `Serialize` derive on the un-`rename`d enum emits under
/// [`SUPERVISOR_KEY_ESTRATEGIA`] whenever the typed `:supervisor
/// :estrategia` slot's strategy is the restart-failed-and-later-started-
/// siblings arm (Erlang/OTP `rest_for_one`, used when later children
/// depend on earlier ones so the startup-order suffix must be
/// re-established).
///
/// Peer of the sibling [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ONE`] /
/// [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ALL`] /
/// [`SUPERVISOR_ESTRATEGIA_SIMPLE_ONE_FOR_ONE`] scalars on the same
/// closed enum surface — see the sibling
/// [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ONE`] doc for the full drift-mode
/// analysis.
pub const SUPERVISOR_ESTRATEGIA_REST_FOR_ONE: &str = "RestForOne";
/// Canonical M2 [`crate::supervisor::RestartStrategy::SimpleOneForOne`]
/// variant discriminator scalar-value — the exact byte-string the
/// `Serialize` derive on the un-`rename`d enum emits under
/// [`SUPERVISOR_KEY_ESTRATEGIA`] whenever the typed `:supervisor
/// :estrategia` slot's strategy is the dynamic-children-of-one-shape arm
/// (Erlang/OTP `simple_one_for_one`, the one arm on which
/// [`crate::supervisor::SupervisorSpec::validate`] gates
/// `children.is_empty()` as a structural partition — static `:children`
/// on a `SimpleOneForOne` supervisor is a build-time rejection).
///
/// Peer of the sibling [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ONE`] /
/// [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ALL`] /
/// [`SUPERVISOR_ESTRATEGIA_REST_FOR_ONE`] scalars on the same closed
/// enum surface — see the sibling
/// [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ONE`] doc for the full drift-mode
/// analysis.
pub const SUPERVISOR_ESTRATEGIA_SIMPLE_ONE_FOR_ONE: &str = "SimpleOneForOne";
/// Canonical M2 [`crate::supervisor::RestartPolicy::Permanent`] variant
/// discriminator scalar-value — the exact byte-string the `Serialize`
/// derive on the un-`rename`d enum emits under
/// [`SUPERVISOR_CHILD_KEY_RESTART`] whenever the typed `:children :restart`
/// per-child restart-policy slot is the always-restart-regardless-of-exit
/// arm (the enum's `default()` and the canonical happy-path per
/// theory/INSPIRATIONS.md §II.2 — Erlang/OTP `permanent`, the
/// long-running-service posture where the supervisor must bring the
/// child back on every failure mode).
///
/// The scalar is the un-`rename`d Rust variant name verbatim; a future
/// `#[serde(rename_all = "kebab-case")]` attribute on the enum, or a
/// per-variant `#[serde(rename = "…")]` override, or a variant rename in
/// the source, would silently emit a `:children :restart` scalar
/// whose per-exit restart-decision discipline lands under one spelling
/// while every downstream consumer still dispatches on another — the
/// future wasm-operator's per-child restart-decision branch, the future
/// M4 `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's
/// admission-time enum-arm bind, the `caixa-operator`'s hierarchical
/// reconciliation scheduler's per-child-policy fan-out would each silently
/// no-op onto the enum's `default()` (`Permanent`) and children would
/// come up with the wrong per-exit restart posture on every non-default
/// arm — a `:temporary` `oneShot` child would be restarted on clean
/// exit (the successful-completion signal treated as failure), a
/// `:transient` child that clean-exited would be restarted (masking the
/// clean-completion contract), and the operator's post-exit dispatch
/// would silently degrade to the always-restart posture. The serde
/// round-trip pin the sweep introduces
/// ([`crate::supervisor::tests::restart_policy_variants_serialize_to_lifted_scalar_values`])
/// catches the drift at caixa-core build time rather than at the
/// operator's reconcile posture.
///
/// Peer of [`SUPERVISOR_CHILD_RESTART_TEMPORARY`] /
/// [`SUPERVISOR_CHILD_RESTART_TRANSIENT`] on the same closed
/// [`crate::supervisor::RestartPolicy`] enum surface — together the
/// three constants name every author-reachable arm of the OTP-shaped
/// per-child restart-decision discriminator, mirroring the
/// closed-enum-scalar-value trajectory
/// [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ONE`] /
/// [`SUPERVISOR_ESTRATEGIA_ONE_FOR_ALL`] /
/// [`SUPERVISOR_ESTRATEGIA_REST_FOR_ONE`] /
/// [`SUPERVISOR_ESTRATEGIA_SIMPLE_ONE_FOR_ONE`] (09ffb2d) established on
/// the sibling `RestartStrategy` enum on the peer per-supervisor
/// sibling-restart-strategy axis and [`M3_PLACEMENT_ESTRATEGIA_SINGLE_NODE`]
/// / [`M3_PLACEMENT_ESTRATEGIA_REPLICATED`] /
/// [`M3_PLACEMENT_ESTRATEGIA_SHARDED`] (3f0e21c) established on the M3
/// `PlacementStrategy` enum on the peer per-Aplicacao distribution-strategy
/// axis. The three OTP-shaped closed-enum discriminator axes on the
/// caixa typed surface (supervisor sibling-restart strategy, per-child
/// restart policy, per-Aplicacao placement strategy) now each carry the
/// same three-path-convergence (`Serialize` derive → `as_str` helper →
/// lifted constant) drift-detection posture.
pub const SUPERVISOR_CHILD_RESTART_PERMANENT: &str = "Permanent";
/// Canonical M2 [`crate::supervisor::RestartPolicy::Temporary`] variant
/// discriminator scalar-value — the exact byte-string the `Serialize`
/// derive on the un-`rename`d enum emits under
/// [`SUPERVISOR_CHILD_KEY_RESTART`] whenever the typed `:children :restart`
/// per-child restart-policy slot is the never-restart arm (Erlang/OTP
/// `temporary`, the one-shot posture where the child's completion — clean
/// or not — is itself the success signal; the `oneShot`
/// [`crate::render::COMPUTEUNIT_SPEC_KEY_TRIGGER`] arm maps here).
///
/// Peer of the sibling [`SUPERVISOR_CHILD_RESTART_PERMANENT`] /
/// [`SUPERVISOR_CHILD_RESTART_TRANSIENT`] scalars on the same
/// closed enum surface — see the sibling
/// [`SUPERVISOR_CHILD_RESTART_PERMANENT`] doc for the full drift-mode
/// analysis.
pub const SUPERVISOR_CHILD_RESTART_TEMPORARY: &str = "Temporary";
/// Canonical M2 [`crate::supervisor::RestartPolicy::Transient`] variant
/// discriminator scalar-value — the exact byte-string the `Serialize`
/// derive on the un-`rename`d enum emits under
/// [`SUPERVISOR_CHILD_KEY_RESTART`] whenever the typed `:children :restart`
/// per-child restart-policy slot is the restart-only-on-abnormal-exit arm
/// (Erlang/OTP `transient`, the "restart on non-zero exit or unhandled
/// exception; a clean exit completes the child" posture — the third
/// canonical OTP per-child restart-decision arm alongside `permanent`
/// and `temporary`).
///
/// Peer of the sibling [`SUPERVISOR_CHILD_RESTART_PERMANENT`] /
/// [`SUPERVISOR_CHILD_RESTART_TEMPORARY`] scalars on the same
/// closed enum surface — see the sibling
/// [`SUPERVISOR_CHILD_RESTART_PERMANENT`] doc for the full drift-mode
/// analysis.
pub const SUPERVISOR_CHILD_RESTART_TRANSIENT: &str = "Transient";
/// Canonical camelCase JSON/YAML top-level key for the
/// [`crate::aplicacao::Entrada`] struct's `host` external-hostname axis —
/// the `host:` field the M3 Aplicacao's `#[serde(rename_all = "camelCase")]`
/// derive on [`crate::aplicacao::Entrada`] emits at the singleton
/// `:entrada` block, and the exact scalar every downstream consumer
/// reaching for the external hostname via `Value::get(...)` (the
/// [`caixa_mesh`] Gateway/HTTPRoute emitter's per-Aplicacao
/// `spec.hostnames` projection under [`GATEWAY_API_KEY_HOSTNAME`] /
/// [`GATEWAY_API_KEY_HOSTNAMES`], the future `app-operator`
/// reconciler's per-Aplicacao ingress-hostname bind, the future
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's admission-time
/// hostname cross-check against the cluster's declared
/// [`GATEWAY_API_HOSTNAME_MAX_LEN`] discipline) must probe on.
///
/// The scalar is derived from the Rust field name `host` by the
/// `rename_all = "camelCase"` derive; `host` has no `_`, so the serde
/// transform is a no-op on this axis and the emitted key equals the
/// source-side field name byte-for-byte. Lifting the byte to one
/// `&'static str` closes the drift footgun structurally: a future
/// refactor renaming the Rust field OR retaining the field name while
/// adding a `#[serde(rename = "…")]` override would silently emit an
/// `Entrada` whose external-hostname discriminator lands under one key
/// while every downstream consumer still probes another — the Gateway
/// emitter's per-Aplicacao hostname projection, the operator's ingress
/// bind, the CR materializer's admission-time cross-check would each
/// silently fall back to no-hostname and the Gateway API would either
/// admit an all-hostname listener (breaking the per-Aplicacao
/// host-isolation contract MESH-COMPOSITION.md §III.5 promises) or
/// reject the resource outright at admission. The identity pin
/// (`entrada_serde_keys_match_lifted_entrada_key_consts` on the
/// source-side type) catches drift at caixa-core build time rather than
/// at the Gateway controller's admission step, far from the rebrand
/// commit's source.
///
/// Peer of [`ENTRADA_KEY_PARA`] / [`ENTRADA_KEY_PATHS`] /
/// [`ENTRADA_KEY_PORT`] on the same [`crate::aplicacao::Entrada`]
/// singleton serialized-key axis. Peer of the sibling
/// [`MEMBRO_KEY_CAIXA`] / [`MEMBRO_KEY_VERSAO`] pair (ce80ca0) and
/// [`CONTRATO_KEY_DE`] / [`CONTRATO_KEY_PARA`] / [`CONTRATO_KEY_WIT`]
/// triad (ca463a4) on the sibling M3 per-`:membros` and per-`:contratos`
/// entry axes — those lifts pinned the M3 collection-slot atom
/// camelCase JSON keys, this lift extends the same discipline onto the
/// singleton `:entrada` mesh slot so the last M3 typed-struct
/// `#[serde(rename_all = "camelCase")]` axis on the Aplicacao surface
/// joins the substrate's "one canonical byte-string per typed
/// serialized-key axis" discipline. Same discipline every peer
/// camelCase serde-key lift carries ([`M2_LIMITS_KEY_MEMORY`] etc.
/// (d8b8b4f), [`M2_BEHAVIOR_KEY_ON_INIT`] etc. (21fe462),
/// [`M2_UPGRADE_FROM_KEY_FROM`] / [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`]
/// (36ffe65), [`M3_PLACEMENT_KEY_ESTRATEGIA`] etc.,
/// [`SUPERVISOR_KEY_ESTRATEGIA`] etc. (40cc4e5)).
pub const ENTRADA_KEY_HOST: &str = "host";
/// Canonical camelCase JSON/YAML top-level key for the
/// [`crate::aplicacao::Entrada`] struct's `para` destination-member axis
/// — the `para:` field naming which `:membros` entry the external
/// Gateway routes to. Peer of [`ENTRADA_KEY_HOST`] on the same
/// [`crate::aplicacao::Entrada`] singleton serialized-key axis; see
/// [`ENTRADA_KEY_HOST`] for the full lift rationale. The Rust field is
/// lowercase `para`; `#[serde(rename_all = "camelCase")]` is a no-op on
/// this axis and the emitted key equals the source-side field name
/// byte-for-byte.
///
/// Byte-identical to [`CONTRATO_KEY_PARA`] today — both resolve to the
/// same four-byte `"para"` literal — but semantically distinct:
/// [`CONTRATO_KEY_PARA`] names the per-`:contratos` edge's callee-Servico
/// discriminator on the [`crate::aplicacao::WitContract`] surface, while
/// this constant names the singleton `:entrada` block's Gateway-route
/// destination-Servico discriminator on the sibling
/// [`crate::aplicacao::Entrada`] surface. Splitting the two lets each
/// schema's future rebrand land independently on the same
/// "byte-identical-but-semantically-distinct" discipline the peer
/// [`FLEET_PROGRAMS_KEY_NAME`] / [`KUBE_KEY_NAME`] split established
/// (935979a) on the sibling per-entry name-discriminator axis and the
/// [`FLEET_PROGRAMS_KEY_VERSAO`] / [`MEMBRO_KEY_VERSAO`] split
/// established (ce80ca0) on the sibling per-entry version-constraint
/// axis.
pub const ENTRADA_KEY_PARA: &str = "para";
/// Canonical camelCase JSON/YAML top-level key for the
/// [`crate::aplicacao::Entrada`] struct's `paths` per-Aplicacao
/// path-filter axis — the `paths:` sequence the M3 Aplicacao's
/// `#[serde(rename_all = "camelCase")]` derive emits at the singleton
/// `:entrada` block, and the exact scalar every downstream
/// per-`:entrada :paths` HTTPRoute-match-projection consumer must probe
/// on (the [`caixa_mesh`] HTTPRoute emitter's per-Aplicacao `matches[]`
/// projection under [`GATEWAY_API_KEY_MATCHES`], defaulting to
/// [`GATEWAY_API_DEFAULT_HTTP_ROUTE_PATH`] when the slot is empty per
/// 48e2083). Peer of [`ENTRADA_KEY_HOST`] on the same
/// [`crate::aplicacao::Entrada`] singleton serialized-key axis; see
/// [`ENTRADA_KEY_HOST`] for the full lift rationale. The Rust field is
/// lowercase `paths`; `#[serde(rename_all = "camelCase")]` is a no-op
/// on this axis and the emitted key equals the source-side field name
/// byte-for-byte.
pub const ENTRADA_KEY_PATHS: &str = "paths";
/// Canonical camelCase JSON/YAML top-level key for the
/// [`crate::aplicacao::Entrada`] struct's `port` destination-Servico
/// port axis — the `port:` field the M3 Aplicacao's
/// `#[serde(rename_all = "camelCase")]` derive emits at the singleton
/// `:entrada` block, defaulting via [`crate::aplicacao::default_port`]
/// to [`crate::DEFAULT_SERVICO_PORT`] when the author omits the slot.
/// Peer of [`ENTRADA_KEY_HOST`] on the same
/// [`crate::aplicacao::Entrada`] singleton serialized-key axis; see
/// [`ENTRADA_KEY_HOST`] for the full lift rationale. The Rust field is
/// lowercase `port`; `#[serde(rename_all = "camelCase")]` is a no-op on
/// this axis and the emitted key equals the source-side field name
/// byte-for-byte.
///
/// Byte-identical to [`KUBE_KEY_PORT`] today — both resolve to the same
/// four-byte `"port"` literal — but semantically distinct:
/// [`KUBE_KEY_PORT`] names the K8s Service/ContainerPort per-resource
/// port-discriminator axis, while this constant names the typed
/// [`crate::aplicacao::Entrada`] singleton block's Gateway-route
/// destination-Servico port axis on the M3 Aplicacao surface.
/// Splitting the two lets each schema's future rebrand land
/// independently.
pub const ENTRADA_KEY_PORT: &str = "port";
/// Canonical camelCase JSON/YAML top-level key for the
/// [`crate::aplicacao::MeshPolicy`] struct's `timeout` per-call
/// wall-clock cap axis — the `timeout:` field the M3 Aplicacao's
/// `#[serde(rename_all = "camelCase")]` derive on
/// [`crate::aplicacao::MeshPolicy`] emits at the singleton `:politicas`
/// block, and the exact scalar every downstream mesh-timeout consumer
/// must probe on (the future M4 per-edge `:politicas` overlay
/// projection onto Cilium `L7Rules` / Gateway API `HTTPRoute`
/// per-backend `timeouts.backendRequest` axis per
/// MESH-COMPOSITION.md §III.3, the future
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's admission-time
/// mesh-timeout cross-check, the future `feira lint` per-`:politicas`
/// authored-duration bound-check against
/// [`crate::POLICY_TIMEOUT_MAX`]).
///
/// The scalar is derived from the Rust field name `timeout` by the
/// `rename_all = "camelCase"` derive; `timeout` has no `_`, so the
/// serde transform is a no-op on this axis and the emitted key equals
/// the source-side field name byte-for-byte. Lifting the byte to one
/// `&'static str` closes the drift footgun structurally: a future
/// refactor renaming the Rust field OR retaining the field name while
/// adding a `#[serde(rename = "…")]` override would silently emit a
/// [`MeshPolicy`][mp] whose per-call timeout discriminator lands under
/// one key while every downstream consumer still probes another — the
/// M4 per-edge overlay projection, the CR materializer's cross-check,
/// the linter's bound-check would each silently fall back to
/// no-timeout and every `:contratos`-edge request would silently
/// bypass the per-call cap the typed slot set, with the failure
/// surfacing as "the mesh no longer enforces the timeout the
/// Aplicacao authored" far from the rebrand commit's source. The
/// identity pin (`mesh_policy_serde_keys_match_lifted_politicas_key_consts`
/// on the source-side type) catches drift at caixa-core build time
/// rather than at the mesh controller's reconcile step.
///
/// [mp]: crate::aplicacao::MeshPolicy
///
/// Peer of [`POLITICAS_KEY_RETRIES`] / [`POLITICAS_KEY_CIRCUIT_BREAKER`] /
/// [`POLITICAS_KEY_MTLS_REQUIRED`] / [`POLITICAS_KEY_RATE_LIMIT`] on the
/// same [`crate::aplicacao::MeshPolicy`] singleton serialized-key
/// axis. Peer of the sibling [`ENTRADA_KEY_HOST`] etc. tetrad
/// (a3d6162), [`M3_PLACEMENT_KEY_ESTRATEGIA`] etc. tetrad,
/// [`MEMBRO_KEY_CAIXA`] / [`MEMBRO_KEY_VERSAO`] pair (ce80ca0), and
/// [`CONTRATO_KEY_DE`] / [`CONTRATO_KEY_PARA`] / [`CONTRATO_KEY_WIT`]
/// triad (ca463a4) on the sibling M3 typed-struct axes — those lifts
/// pinned every peer M3 mesh-slot atom, this lift closes the last M3
/// typed-struct top-level `#[serde(rename_all = "camelCase")]` axis on
/// the Aplicacao surface without a lifted serde-key peer (the
/// [`crate::aplicacao::MeshPolicy`] singleton `:politicas` block) so
/// the entire M3 typed-struct surface joins the substrate's "one
/// canonical byte-string per typed serialized-key axis" discipline.
/// Same discipline every peer camelCase serde-key lift carries
/// ([`M2_LIMITS_KEY_MEMORY`] etc. (d8b8b4f),
/// [`M2_BEHAVIOR_KEY_ON_INIT`] etc. (21fe462),
/// [`M2_UPGRADE_FROM_KEY_FROM`] / [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`]
/// (36ffe65), [`SUPERVISOR_KEY_ESTRATEGIA`] etc. (40cc4e5)).
pub const POLITICAS_KEY_TIMEOUT: &str = "timeout";
/// Canonical camelCase JSON/YAML top-level key for the
/// [`crate::aplicacao::MeshPolicy`] struct's `retries` transient-failure
/// retry-count axis — the `retries:` field the M3 Aplicacao's
/// `#[serde(rename_all = "camelCase")]` derive on
/// [`crate::aplicacao::MeshPolicy`] emits at the singleton `:politicas`
/// block. Peer of [`POLITICAS_KEY_TIMEOUT`] on the same
/// [`crate::aplicacao::MeshPolicy`] singleton serialized-key axis; see
/// [`POLITICAS_KEY_TIMEOUT`] for the full lift rationale. The Rust
/// field is lowercase `retries`; `#[serde(rename_all = "camelCase")]`
/// is a no-op on this axis and the emitted key equals the source-side
/// field name byte-for-byte.
pub const POLITICAS_KEY_RETRIES: &str = "retries";
/// Canonical camelCase JSON/YAML top-level key for the
/// [`crate::aplicacao::MeshPolicy`] struct's `circuit_breaker`
/// circuit-breaker sub-block axis — the `circuitBreaker:` field the M3
/// Aplicacao's `#[serde(rename_all = "camelCase")]` derive on
/// [`crate::aplicacao::MeshPolicy`] emits at the singleton `:politicas`
/// block, and the exact camelCase scalar (Rust field
/// `circuit_breaker` → serde-emitted `circuitBreaker`, one of the two
/// `MeshPolicy` axes the derive-attribute non-trivially transforms
/// alongside [`POLITICAS_KEY_MTLS_REQUIRED`] and
/// [`POLITICAS_KEY_RATE_LIMIT`]) every downstream circuit-breaker
/// consumer must probe on (the future M4 per-edge `:politicas` overlay
/// projection onto the mesh's per-backend failure-counter reset
/// window per MESH-COMPOSITION.md §III.3 breaker semantics, the future
/// `feira lint` per-`:politicas` breaker-window bound-check against
/// [`crate::POLICY_BREAKER_WINDOW_MAX`] and
/// [`crate::POLICY_BREAKER_MAX_FAILURES_MAX`]). Peer of
/// [`POLITICAS_KEY_TIMEOUT`] on the same
/// [`crate::aplicacao::MeshPolicy`] singleton serialized-key axis; see
/// [`POLITICAS_KEY_TIMEOUT`] for the full lift rationale.
///
/// This axis is one of the three non-trivial camelCase transforms
/// [`crate::aplicacao::MeshPolicy`]'s derive emits (`circuit_breaker`
/// → `circuitBreaker`, `mtls_required` → `mtlsRequired`, `rate_limit`
/// → `rateLimit`); a future accidental `rename_all = "snake_case"` /
/// `"kebab-case"` / verbatim-field-name flip at the derive would
/// silently rebrand the emitted key to `circuit_breaker` /
/// `circuit-breaker` / `circuit_breaker` respectively, breaking every
/// downstream `Value::get(POLITICAS_KEY_CIRCUIT_BREAKER)` consumer.
/// The identity pin (`mesh_policy_serde_keys_match_lifted_politicas_key_consts`
/// on the source-side type) catches drift on all three non-trivial
/// axes simultaneously.
pub const POLITICAS_KEY_CIRCUIT_BREAKER: &str = "circuitBreaker";
/// Canonical camelCase JSON/YAML top-level key for the
/// [`crate::aplicacao::MeshPolicy`] struct's `mtls_required`
/// mTLS-enforcement-toggle axis — the `mtlsRequired:` field the M3
/// Aplicacao's `#[serde(rename_all = "camelCase")]` derive on
/// [`crate::aplicacao::MeshPolicy`] emits at the singleton `:politicas`
/// block, and the exact camelCase scalar (Rust field `mtls_required`
/// → serde-emitted `mtlsRequired`) every downstream mesh-identity
/// consumer must probe on (the future M4 per-edge `:politicas` overlay
/// projection onto Cilium `CiliumNetworkPolicy` per-rule
/// [`CILIUM_KEY_AUTHENTICATION`] mode dispatch under the
/// [`cilium_auth_mode`] bijection projection (a4dc43c) — the mesh's
/// sandboxing-by-default posture MESH-COMPOSITION.md §III.3 promises
/// keys off this exact byte-sequence to opt out of mTLS enforcement
/// per-edge, so drift here silently reopens the every-edge-mTLS
/// invariant the substrate defaults to). Peer of
/// [`POLITICAS_KEY_TIMEOUT`] on the same
/// [`crate::aplicacao::MeshPolicy`] singleton serialized-key axis; see
/// [`POLITICAS_KEY_TIMEOUT`] for the full lift rationale.
pub const POLITICAS_KEY_MTLS_REQUIRED: &str = "mtlsRequired";
/// Canonical camelCase JSON/YAML top-level key for the
/// [`crate::aplicacao::MeshPolicy`] struct's `rate_limit`
/// token-bucket-rate-limit axis — the `rateLimit:` field the M3
/// Aplicacao's `#[serde(rename_all = "camelCase")]` derive on
/// [`crate::aplicacao::MeshPolicy`] emits at the singleton `:politicas`
/// block, and the exact camelCase scalar (Rust field `rate_limit` →
/// serde-emitted `rateLimit`) every downstream rate-limit consumer
/// must probe on (the future M4 per-edge `:politicas` overlay
/// projection onto the mesh's per-backend token-bucket `(rate,
/// window)` decoder driven by the canonical
/// [`crate::aplicacao::rate_limit_codec`] unit-suffix bijection). Peer
/// of [`POLITICAS_KEY_TIMEOUT`] on the same
/// [`crate::aplicacao::MeshPolicy`] singleton serialized-key axis; see
/// [`POLITICAS_KEY_TIMEOUT`] for the full lift rationale.
pub const POLITICAS_KEY_RATE_LIMIT: &str = "rateLimit";
/// Canonical camelCase JSON/YAML sub-key for the
/// [`crate::aplicacao::CircuitBreaker`] struct's `max_failures`
/// consecutive-failure-count axis — the `maxFailures:` field the M3
/// Aplicacao's `#[serde(rename_all = "camelCase")]` derive on
/// [`crate::aplicacao::CircuitBreaker`] emits inside the
/// [`POLITICAS_KEY_CIRCUIT_BREAKER`] sub-block, and the exact camelCase
/// scalar (Rust field `max_failures` → serde-emitted `maxFailures`,
/// the load-bearing non-trivial camelCase transform on this
/// [`CircuitBreaker`][cb] axis alongside the no-op
/// [`CIRCUIT_BREAKER_KEY_WINDOW`] sibling) every downstream breaker-
/// tuning consumer must probe on (the future M4 per-edge `:politicas`
/// overlay projection onto the mesh's per-backend
/// consecutive-failure-counter tripping threshold per
/// MESH-COMPOSITION.md §III.3 breaker semantics, the future
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's admission-time
/// breaker cross-check against
/// [`crate::POLICY_BREAKER_MAX_FAILURES_MAX`], the future
/// `feira lint` per-`:politicas :circuit-breaker` bound-check gate).
///
/// [cb]: crate::aplicacao::CircuitBreaker
///
/// Peer of [`CIRCUIT_BREAKER_KEY_WINDOW`] on the same
/// [`crate::aplicacao::CircuitBreaker`] serialized-key axis; the two
/// consts together close the sub-block's typed-struct axis. Extends
/// the [`POLITICAS_KEY_CIRCUIT_BREAKER`] parent-axis lift (b55cca7)
/// one level deeper — the parent const names the outer sub-block key
/// the derive on [`crate::aplicacao::MeshPolicy`] emits, this pair
/// names the inner keys the derive on the payload type emits, so a
/// consumer walking `Value::get(POLITICAS_KEY_CIRCUIT_BREAKER)
/// .and_then(|v| v.get(CIRCUIT_BREAKER_KEY_MAX_FAILURES))` navigates
/// the whole [`crate::aplicacao::MeshPolicy`] breaker-tuning shape
/// entirely through lifted canonical byte-sequences with no inline
/// string literal at either level.
///
/// A future accidental `rename_all = "snake_case"` /
/// `"kebab-case"` / verbatim-field-name flip at the derive on
/// [`crate::aplicacao::CircuitBreaker`] would silently rebrand the
/// emitted key to `max_failures` / `max-failures` / `max_failures`
/// respectively, breaking every downstream
/// `Value::get(CIRCUIT_BREAKER_KEY_MAX_FAILURES)` consumer — with the
/// drift surfacing at apply time far from the derive-attr commit. The
/// identity pin
/// (`circuit_breaker_serde_keys_match_lifted_circuit_breaker_key_consts`
/// on the source-side type) catches drift at caixa-core build time.
///
/// Same discipline every peer camelCase serde-key lift carries
/// ([`M2_LIMITS_KEY_MEMORY`] etc. (d8b8b4f),
/// [`M2_BEHAVIOR_KEY_ON_INIT`] etc. (21fe462),
/// [`M2_UPGRADE_FROM_KEY_FROM`] / [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`]
/// (36ffe65), [`SUPERVISOR_KEY_ESTRATEGIA`] etc. (40cc4e5),
/// [`POLITICAS_KEY_TIMEOUT`] etc. (b55cca7)).
pub const CIRCUIT_BREAKER_KEY_MAX_FAILURES: &str = "maxFailures";
/// Canonical camelCase JSON/YAML sub-key for the
/// [`crate::aplicacao::CircuitBreaker`] struct's `window`
/// failure-counter reset-window axis — the `window:` field the M3
/// Aplicacao's `#[serde(rename_all = "camelCase")]` derive on
/// [`crate::aplicacao::CircuitBreaker`] emits inside the
/// [`POLITICAS_KEY_CIRCUIT_BREAKER`] sub-block. The Rust field is
/// lowercase `window`; `#[serde(rename_all = "camelCase")]` is a
/// no-op on this axis and the emitted key equals the source-side
/// field name byte-for-byte. Peer of
/// [`CIRCUIT_BREAKER_KEY_MAX_FAILURES`] on the same
/// [`crate::aplicacao::CircuitBreaker`] serialized-key axis; see
/// [`CIRCUIT_BREAKER_KEY_MAX_FAILURES`] for the full lift rationale.
pub const CIRCUIT_BREAKER_KEY_WINDOW: &str = "window";
/// Canonical `lareira-fleet-programs` values-schema key naming the
/// per-caixa entry sequence — the exact YAML key the fleet-programs
/// library chart's `values.yaml` reads as `programs:` (a sequence of
/// per-Servico entries the chart's `range` iterates over to emit one
/// `ComputeUnit` CR per entry). Two production consumers in
/// [`caixa_flux`] carry this key on the same fleet-programs schema
/// axis:
///
/// 1. [`caixa_flux::upsert_into_helmrelease_programs`] — the writer-
/// side upsert path on the aggregator-HelmRelease shape. Walks
/// `HelmRelease.spec.values.programs[]` under this exact key to
/// match by `metadata.name` and either replace-in-place or append.
///
/// 2. [`caixa_flux::upsert_into_programs_yaml`] — the writer-side
/// upsert path on the bare-values.yaml shape. Walks the
/// top-level `programs[]` sequence under the same key.
///
/// Until this lift landed both consumers carried the bare `"programs"`
/// byte inline — `upsert_into_helmrelease_programs`'s
/// `values_map.entry(Value::String("programs".into()))` at
/// `caixa-flux/src/lib.rs:539` and `upsert_into_programs_yaml`'s
/// `let programs_key = Value::String("programs".into());` at
/// `caixa-flux/src/lib.rs:591`. A future fleet-programs schema-key
/// rebrand (the library chart moving to plural `programas` for
/// Brazilian-Portuguese uniformity with the rest of the substrate's
/// surface, to a namespaced `pleme.pleme.io/programs` for multi-tenant
/// aggregator-values isolation, or to per-kind `servicos` / `aplicacaos`
/// splits once the schema grows past the flat sequence — the
/// ABSORPTION-ROADMAP.md M4 trajectory) without a coordinated edit
/// on both writer-side sites would silently emit an entry under one
/// key (e.g. `programas:`) while the peer-side upsert still probes
/// the prior key — the aggregator's `range .Values.programs` would
/// then iterate an empty sequence and every `ComputeUnit` CR would
/// silently vanish from the cluster's fleet, with the failure
/// surfacing as "the newly-deployed Servico's pods never spin up" far
/// from the rebrand commit's source. Lifting the literal to one
/// `&'static str` closes the drift footgun structurally — both
/// consumers read from the same memory, so any future rebrand reaches
/// both writer sites by construction and a CI build that re-introduces
/// a sibling inline `"programs"` literal trips the peer pinning tests
/// at the build-time fail-before-deploy posture every prior
/// load-bearing-string lift on this surface
/// ([`M3_KEY_PLACEMENT`] under the same `programs.yaml` per-entry
/// axis, [`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] / [`M2_KEY_UPGRADE_FROM`]
/// on the peer M2 overlay-key surfaces, [`DEFAULT_NAMESPACE`]
/// / [`DEFAULT_LIBRARY_NAME`] / [`DEFAULT_SERVICO_PORT`] on the peer
/// shared-string / port surfaces) establishes.
///
/// Peer of [`M3_KEY_PLACEMENT`] on the same fleet-programs values
/// schema — that constant names the per-entry overlay key, this one
/// names the top-level array key both writer verbs upsert into.
pub const FLEET_PROGRAMS_KEY_PROGRAMS: &str = "programs";
/// Canonical `lareira-fleet-programs` values-schema key naming the
/// per-entry name discriminator — the `name:` field the library
/// chart's `range .Values.programs` step reads to key each rendered
/// `ComputeUnit` CR's `metadata.name` off, and the exact key both
/// writer-side upsert paths in [`caixa_flux`] match against to
/// replace-in-place-vs-append. Peer of [`FLEET_PROGRAMS_KEY_PROGRAMS`]
/// on the same fleet-programs values schema — that constant names
/// the top-level array key, this one names the per-entry name-axis
/// both writer verbs walk the array by.
///
/// Two production consumers write this key:
///
/// 1. [`caixa_flux::programs_yaml_entry`] — the emit-side per-Servico
/// entry-builder writes the per-entry name-axis at this exact key
/// (seeded from the Caixa's `nome`), at
/// `caixa-flux/src/lib.rs`'s `entry.insert("name".into(), …)` call.
/// 2. [`caixa_mesh::programs_for_aplicacao`] — the Aplicacao-side
/// per-`:membros` entry-builder writes the peer per-entry name-axis
/// at the same key (seeded from each `:membros` entry's `:caixa`
/// binding), at `caixa-mesh/src/lib.rs`'s per-member
/// `entry.insert("name".into(), …)` call.
///
/// Two production consumers read this key:
///
/// 3. [`caixa_flux::upsert_into_helmrelease_programs`] — the writer-
/// side upsert path on the aggregator-HelmRelease shape reads the
/// per-entry key twice (new-entry's `.get("name")` extract +
/// per-slot `.get("name")` match-vs-new_name inside
/// `HelmRelease.spec.values.programs[]`), plus a
/// `Error::MissingField("name")` diagnostic naming the same axis.
/// 4. [`caixa_flux::upsert_into_programs_yaml`] — the writer-side
/// upsert path on the bare-values.yaml shape reads the same per-
/// entry key over the top-level `programs[]` sequence via the
/// same three-site (extract + match + `MissingField`) shape.
///
/// Until this lift landed both writers carried the bare `"name"`
/// byte inline at every read + `Error::MissingField("name")`
/// diagnostic site, and both emitters carried the same bare byte at
/// their `entry.insert("name".into(), …)` call. A future fleet-
/// programs schema-key rebrand on the per-entry name-discriminator
/// axis (per the same trajectory [`FLEET_PROGRAMS_KEY_PROGRAMS`]'s
/// doc-comment names — the `lareira-fleet-programs` library chart
/// moving its per-entry name-axis to `nome:` for Brazilian-Portuguese
/// uniformity with the rest of the substrate's surface, or to a
/// namespaced `pleme.pleme.io/name` for multi-tenant aggregator
/// values isolation, or to per-kind `servico-name` / `aplicacao-name`
/// splits once the schema grows past the flat sequence — the
/// ABSORPTION-ROADMAP.md M4 trajectory) without a coordinated edit
/// across all four sites would silently split the schema: one
/// emitter would write under `nome:` while the peer-side upsert
/// still probed `name:` — the aggregator's `range .Values.programs`
/// would then iterate entries whose per-entry name-axis the library
/// chart's `metadata.name` templating reads as empty (or match
/// against the wrong entry on upsert), and every rendered
/// `ComputeUnit` CR would silently collide on empty
/// `metadata.name` or vanish at the aggregator's per-entry name-
/// keyed reduce step, with the failure surfacing as "the Servico's
/// pods never spin up under the expected name" far from the rebrand
/// commit's source. Lifting the literal to one `&'static str` closes
/// the drift footgun structurally — every consumer reads the same
/// memory, so any future rebrand reaches all four sites by
/// construction and a CI build that re-introduces a sibling inline
/// `"name"` literal trips the peer pinning tests at the build-time
/// fail-before-deploy posture every prior load-bearing-string lift
/// on this surface ([`FLEET_PROGRAMS_KEY_PROGRAMS`] on the sibling
/// fleet-programs top-level array-key axis, [`M3_KEY_PLACEMENT`] /
/// [`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] / [`M2_KEY_UPGRADE_FROM`]
/// on the peer per-entry overlay-key surfaces) establishes.
///
/// Byte-identical to [`KUBE_KEY_NAME`] today — both resolve to the
/// same three-byte `"name"` literal — but semantically distinct:
/// [`KUBE_KEY_NAME`] names the K8s CR canonical `metadata.name` axis
/// (every rendered CR's identity discriminator, spelled per the K8s
/// apiserver's OpenAPI v3 schema), while this constant names the
/// `lareira-fleet-programs` library chart's per-entry name-axis
/// (spelled per the chart's `values.schema.json` — a separate schema
/// contract). Splitting the two lets each schema's future rebrand
/// land independently at its canonical const definition without
/// coupling the K8s CR canonical-key axis to the fleet-programs
/// values-schema axis (or vice versa).
pub const FLEET_PROGRAMS_KEY_NAME: &str = "name";
/// Canonical `lareira-fleet-programs` values-schema key naming the
/// per-entry parent-Aplicacao-graph discriminator — the `aplicacao:`
/// annotation the substrate operator's fleet-aggregator reads to
/// group each rendered `programs[]` entry back onto the parent
/// Aplicacao its M3 `:membros` list contributed it, and the exact
/// key downstream fleet consumers (per-graph observability filters,
/// per-Aplicacao Cilium-policy reconciliation, per-graph Gateway/
/// `HTTPRoute` attachment) walk to project the flat `programs[]`
/// sequence back onto its typed Aplicacao graph.
///
/// Peer of [`FLEET_PROGRAMS_KEY_NAME`] and [`M3_KEY_PLACEMENT`] on
/// the same fleet-programs values schema — `FLEET_PROGRAMS_KEY_NAME`
/// carries the per-entry Servico-name discriminator (the `:membros`
/// row's own `:caixa` binding), `M3_KEY_PLACEMENT` carries the M3
/// placement overlay cloned per entry, and this constant carries the
/// per-entry parent-Aplicacao-nome annotation the aggregator uses to
/// group entries back into their Aplicacao graph. Together the three
/// per-entry keys (plus the top-level [`FLEET_PROGRAMS_KEY_PROGRAMS`]
/// array key) name every axis one `programs[]` entry the caixa-mesh
/// fan-out emits contributes to the substrate operator's read shape.
///
/// One production consumer writes this key:
/// [`caixa_mesh::programs_for_aplicacao`] — the Aplicacao-side
/// per-`:membros` entry-builder writes the parent-Aplicacao-nome
/// annotation at this exact key (seeded from the enclosing Caixa's
/// `:nome`), at `caixa-mesh/src/lib.rs`'s per-member
/// `entry.insert("aplicacao".into(), …)` call. Unlike the peer
/// [`FLEET_PROGRAMS_KEY_NAME`] axis (written by both caixa-flux's
/// per-Servico entry builder and caixa-mesh's per-`:membros` builder
/// — a Servico rendered standalone has no parent-Aplicacao annotation
/// to carry), the parent-Aplicacao-nome annotation is emitted only
/// by the caixa-mesh Aplicacao-side fan-out — Servicos rendered
/// standalone through the caixa-flux path leave the annotation
/// absent, which is exactly the discriminator the operator's
/// aggregator uses to distinguish Aplicacao-graph-scoped entries
/// from stand-alone Servico entries.
///
/// Until this lift landed the caixa-mesh emitter carried the bare
/// `"aplicacao"` byte inline at its `entry.insert("aplicacao".into(),
/// …)` call, and the peer in-file test probe (the
/// `programs_for_aplicacao_annotates_with_parent_nome` fixture's
/// `e.get("aplicacao").and_then(|v| v.as_str())` navigation) carried
/// the same bare byte at its readback site. A future fleet-programs
/// schema-key rebrand on the per-entry parent-Aplicacao-annotation
/// axis (per the same trajectory the sibling [`FLEET_PROGRAMS_KEY_NAME`]
/// doc-comment names — the `lareira-fleet-programs` library chart
/// moving its per-entry parent-graph-annotation to a namespaced
/// `pleme.pleme.io/aplicacao` for multi-tenant aggregator isolation
/// once the M4 flat-`programs[]`-per-cluster shape splits into
/// per-graph sequences, or to `graph:` for parity with the M3
/// `:contratos` graph nomenclature, or to typed `parent:` on the
/// ABSORPTION-ROADMAP.md M4 hierarchical-fleet trajectory) without
/// a coordinated edit across both sites would silently split the
/// schema: the emitter would write under the drifted key while the
/// aggregator's per-Aplicacao filter would still read `aplicacao:`
/// — every fan-out entry would silently vanish from its parent
/// graph's projected view at the aggregator's per-Aplicacao reduce
/// step, with the failure surfacing as "the Aplicacao's Servicos
/// never appear in per-graph observability filters" far from the
/// rebrand commit's source. Lifting the literal to one `&'static
/// str` closes the drift footgun structurally — every consumer
/// reads the same memory, so any future rebrand reaches both sites
/// by construction and a CI build that re-introduces a sibling
/// inline `"aplicacao"` literal trips the peer pinning tests at the
/// build-time fail-before-deploy posture every prior load-bearing-
/// string lift on this surface ([`FLEET_PROGRAMS_KEY_PROGRAMS`] on
/// the sibling fleet-programs top-level array-key axis,
/// [`FLEET_PROGRAMS_KEY_NAME`] on the peer per-entry name-
/// discriminator axis, [`M3_KEY_PLACEMENT`] / [`M2_KEY_LIMITS`] /
/// [`M2_KEY_BEHAVIOR`] / [`M2_KEY_UPGRADE_FROM`] on the peer per-
/// entry overlay-key surfaces) establishes.
///
/// Byte-identical to the string form of the
/// [`caixa_core::CaixaKind::Aplicacao`] enum variant today — both
/// resolve to the same nine-byte `"aplicacao"` literal — but
/// semantically distinct: `CaixaKind`'s `Aplicacao` variant names
/// the `:kind` enum arm (the typed kind-tag every `defcaixa` selects
/// among), while this constant names the `lareira-fleet-programs`
/// library chart's per-entry parent-graph-annotation axis (spelled
/// per the chart's `values.schema.json` — a separate schema
/// contract, one whose future rebrand can land independently of the
/// kind-tag axis). Splitting the two lets each schema's future
/// rebrand land at its canonical const/variant definition without
/// coupling the `:kind` enum-tag axis to the fleet-programs values-
/// schema axis (or vice versa) — the same discipline the sibling
/// [`FLEET_PROGRAMS_KEY_NAME`] doc-comment establishes vs.
/// [`KUBE_KEY_NAME`] on the K8s CR canonical name-axis.
pub const FLEET_PROGRAMS_KEY_APLICACAO: &str = "aplicacao";
/// Canonical `lareira-fleet-programs` values-schema key naming the
/// per-entry version-constraint discriminator — the `versao:` field
/// each rendered `programs[]` entry carries so the substrate operator's
/// per-`:membros` resolver can resolve each member's caixa.lisp against
/// its Aplicacao-declared version-constraint. Every `:membros` row's
/// `:versao` (the semver / range constraint the M3 Aplicacao names on
/// its `:membros` list) flows through this exact key on the emitted
/// per-entry programs.yaml row.
///
/// Peer of [`FLEET_PROGRAMS_KEY_NAME`], [`FLEET_PROGRAMS_KEY_APLICACAO`],
/// and [`M3_KEY_PLACEMENT`] on the same fleet-programs values schema —
/// `FLEET_PROGRAMS_KEY_NAME` carries the per-entry Servico-name
/// discriminator (each `:membros` row's `:caixa` binding),
/// `FLEET_PROGRAMS_KEY_APLICACAO` carries the per-entry parent-graph
/// annotation, `M3_KEY_PLACEMENT` carries the M3 placement overlay
/// cloned per entry, and this constant carries the per-entry version-
/// constraint the operator's resolver reads to fetch the correct
/// caixa.lisp release. Together the four per-entry keys (plus the
/// top-level [`FLEET_PROGRAMS_KEY_PROGRAMS`] array key) name every axis
/// one `programs[]` entry the caixa-mesh fan-out emits contributes to
/// the substrate operator's read shape.
///
/// One production consumer writes this key:
/// [`caixa_mesh::programs_for_aplicacao`] — the Aplicacao-side
/// per-`:membros` entry-builder writes the per-entry version-
/// constraint at this exact key (seeded from each `:membros` row's
/// `:versao` binding), at `caixa-mesh/src/lib.rs`'s per-member
/// `entry.insert("versao".into(), …)` call. Unlike the peer
/// [`FLEET_PROGRAMS_KEY_NAME`] axis (written by both caixa-flux's
/// per-Servico entry builder and caixa-mesh's per-`:membros` builder
/// — a Servico rendered standalone through the caixa-flux path resolves
/// its own `:versao` from its `caixa.lisp` root and hands it to the
/// resolver via a distinct path), the per-`:membros` version-constraint
/// annotation is emitted only by the caixa-mesh Aplicacao-side fan-out.
///
/// Until this lift landed the caixa-mesh emitter carried the bare
/// `"versao"` byte inline at its `entry.insert("versao".into(), …)`
/// call — a partial single-source where three of four per-entry
/// fleet-programs axis keys were canonical
/// ([`FLEET_PROGRAMS_KEY_NAME`] via 030a63f,
/// [`FLEET_PROGRAMS_KEY_APLICACAO`] via cc69ac2, [`M3_KEY_PLACEMENT`])
/// and the fourth was scattered. Lifting the fourth key completes the
/// fleet-programs values-schema single-sourcing across every per-entry
/// axis; every future per-graph aggregator, per-`:membros` resolver,
/// per-entry version-constraint consumer inherits the same `&'static
/// str` by construction. A future schema-key rebrand on the per-entry
/// version-constraint axis (a namespaced `pleme.pleme.io/versao` for
/// multi-tenant aggregator isolation, or `version:` for parity with
/// upstream conventions, or typed `constraint:` on the ABSORPTION-
/// ROADMAP.md M4 typed-resolver trajectory) lands at the one const
/// rather than scattered across every future per-emitter/per-resolver
/// site.
///
/// Byte-identical to the `Membro::versao` field name on the M3
/// [`AplicacaoSpec`](aplicacao::AplicacaoSpec) today — both resolve to the same six-byte `"versao"`
/// literal — but semantically distinct: `Membro::versao` names the
/// author-side `:versao` slot on each `:membros` row (the typed
/// version-constraint slot every `defcaixa` populates on its
/// `:membros` list), while this constant names the
/// `lareira-fleet-programs` library chart's per-entry version-
/// constraint axis (spelled per the chart's `values.schema.json` — a
/// separate schema contract, one whose future rebrand can land
/// independently of the author-side slot-name axis). Splitting the two
/// lets each schema's future rebrand land at its canonical const /
/// field definition without coupling the author-side slot-name axis to
/// the fleet-programs values-schema axis (or vice versa) — the same
/// discipline the sibling [`FLEET_PROGRAMS_KEY_APLICACAO`] doc-comment
/// establishes vs. the [`CaixaKind::Aplicacao`] enum-variant tag.
pub const FLEET_PROGRAMS_KEY_VERSAO: &str = "versao";
/// Canonical pleme-io label namespace prefix. Every cluster object
/// emitted by any caixa-side renderer that needs to carry the
/// pleme-io workload identity uses this prefix; runtime label
/// injectors (`lareira-fleet-programs` chart's pod template,
/// `pleme-computeunit` library chart's identity sidecar, the
/// caixa-operator's pod-mutating webhook) and runtime label
/// consumers (Cilium identity-based policy, Hubble flow attribution,
/// `caixa-mesh`'s policy / Gateway emission, future
/// observability/tracing renderers) all spell the same prefix
/// exactly the same way — drift between *any* of those = a
/// CiliumNetworkPolicy that matches no pods, a Hubble flow that
/// can't be correlated to its workload, an OpenTelemetry resource
/// attribute that doesn't join to its caixa lacre.
///
/// Lifted to a const so a future top-level rebrand or multi-tenant
/// label-namespace migration is a one-line edit, not a search-and-
/// replace across every renderer crate.
pub const PLEME_LABEL_PREFIX: &str = "pleme.pleme.io";
/// Canonical pleme-io label key naming the **Aplicacao** the workload
/// belongs to. Together with [`LABEL_PROGRAM`] this is the load-bearing
/// identity tuple every per-Aplicacao mesh renderer (Cilium, Gateway,
/// future caixa-otel) keys off — `(LABEL_APLICACAO, LABEL_PROGRAM)` =
/// the unique workload selector inside one cluster.
pub const LABEL_APLICACAO: &str = "pleme.pleme.io/aplicacao";
/// Canonical pleme-io label key naming the **program** (i.e. the
/// caixa Servico's `:nome`) a pod runs. `LABEL_APLICACAO` +
/// `LABEL_PROGRAM` together pick exactly one workload identity in one
/// cluster. Used as the `matchLabels` axis on every Cilium
/// `endpointSelector` / `fromEndpoints` rule and on Gateway API
/// `backendRefs` selectors emitted by [`crate`]'s downstream
/// renderers.
pub const LABEL_PROGRAM: &str = "pleme.pleme.io/program";
/// Canonical pleme-io label key naming the **contrato** (the M3
/// `:contratos` edge: `<de>-to-<para>`) a CiliumNetworkPolicy enforces.
/// Carried on the policy's *own* labels (not on workload pods) so
/// Hubble + cluster operators can group flows by typed contrato edge,
/// not just by source/destination pod identity.
pub const LABEL_CONTRATO: &str = "pleme.pleme.io/contrato";
/// Canonical M3 `:contratos` edge-direction separator byte-string every
/// caixa-mesh emitter that encodes a typed edge as a K8s-name-shaped
/// scalar (the [`LABEL_CONTRATO`] label value carried on every
/// per-`(:de, :para)` `CiliumNetworkPolicy`'s `metadata.labels`, and
/// the per-`(:de, :para)` `CiliumNetworkPolicy`'s `metadata.name`
/// itself) inserts between the `:de` and `:para` halves of the typed
/// edge tuple. Load-bearing on both the writer half (the CNP renderer)
/// and the reader half (Hubble flow grouping by contrato label,
/// per-CNP operator filters, `kubectl get cnp -l pleme.pleme.io/contrato=<de>-to-<para>`
/// grep-by-label). Until this lift landed the `-to-` byte-string sat
/// in two verbatim inline-`format!` sites at the caixa-mesh
/// `cilium_network_policies` emitter — one at the
/// [`LABEL_CONTRATO`] `labels.insert(...)` call and one at the
/// [`kube_resource_skeleton`] `name:` argument — with no compile-time
/// link between them. A future edge-encoding rebrand (`-to-` → `->`
/// for compactness, `-to-` → `_to_` to reserve `-` for embedded
/// DNS-1123-label boundaries, an edge-direction-arrow migration to
/// UTF-8 shapes) would have had to be threaded through both sites in
/// lockstep or the two would silently split: one CNP's `metadata.name`
/// keys off the drifted encoding, its own `metadata.labels.pleme.pleme.io/contrato`
/// value keys off the original, and every operator-side grep-by-label
/// query (`kubectl get cnp -l pleme.pleme.io/contrato=cart-to-catalog`)
/// finds the label but the resulting CNP's `metadata.name` no longer
/// matches the queried edge encoding. Every downstream consumer that
/// joins the two axes (the M4 mesh-graph audit, the future Hubble-side
/// contrato-flow renderer, the operator's per-edge policy inspector)
/// silently loses the join. Lifted onto one `&'static str` so a future
/// edge-encoding rebrand lands at one const, and every downstream
/// consumer picks up the new encoding by construction.
pub const CONTRATO_EDGE_LABEL_SEPARATOR: &str = "-to-";
/// Canonical M3 `:contratos` edge label value — the `<de>-to-<para>`
/// K8s-name-shaped scalar every per-`(:de, :para)` `CiliumNetworkPolicy`
/// document carries at its `metadata.labels.pleme.pleme.io/contrato`
/// axis (the [`LABEL_CONTRATO`] label key). Composes on the lifted
/// [`CONTRATO_EDGE_LABEL_SEPARATOR`] byte-string so a future
/// edge-encoding rebrand lands at one canonical composition, and every
/// downstream consumer that grep-by-label picks up the new encoding by
/// construction.
///
/// Peer of [`cilium_network_policy_name`] on the sibling per-`(:de,
/// :para)` CNP `metadata.name` encoding axis — the CNP name composes
/// on this helper's output (the CNP `metadata.name` is
/// `format!("{aplicacao}-{contrato_edge_label(de, para)}")`), so a
/// future rebrand on either axis reaches both consumers through one
/// canonical composition instead of a coordinated two-site rewrite of
/// caixa-mesh's `cilium_network_policies` per-`(:de, :para)` group's
/// [`LABEL_CONTRATO`] `labels.insert(...)` call and the
/// [`kube_resource_skeleton`] `name:` argument.
#[must_use]
pub fn contrato_edge_label(de: &str, para: &str) -> String {
format!("{de}{CONTRATO_EDGE_LABEL_SEPARATOR}{para}")
}
/// Canonical per-`(:de, :para)` `CiliumNetworkPolicy` `metadata.name`
/// K8s-name-shaped scalar every caixa-mesh `cilium_network_policies`
/// emitter mounts its per-edge CNP under. Composes on the lifted
/// [`contrato_edge_label`] helper (the CNP name is the parent
/// Aplicacao's `:nome` joined to the contrato-edge-label by a
/// canonical `-` separator: `format!("{aplicacao}-{edge}")`), so the
/// two axes — the CNP `metadata.labels.pleme.pleme.io/contrato` value
/// and the CNP `metadata.name` — share one canonical
/// edge-encoding source of truth ([`CONTRATO_EDGE_LABEL_SEPARATOR`]).
///
/// Peer of [`contrato_edge_label`] on the parent-composition axis —
/// the two writer-side helpers close the canonical
/// `(LABEL_CONTRATO-value, metadata.name)` per-CNP identity pair so a
/// future edge-encoding rebrand or a per-emitter typo can't silently
/// split the two axes at emit time and orphan every operator-side
/// grep-by-label query at apply time far from the source caixa.lisp.
///
/// The `aplicacao` prefix scopes the emitted CNP to its owning
/// Aplicacao (so two Aplicacaos hosting a same-named `(de, para)`
/// contrato edge — `checkout-cart-to-catalog` vs
/// `orders-cart-to-catalog` — land at distinct CNP `metadata.name`s
/// with no `kubectl apply` collision at the shared namespace).
#[must_use]
pub fn cilium_network_policy_name(aplicacao: &str, de: &str, para: &str) -> String {
let edge = contrato_edge_label(de, para);
format!("{aplicacao}-{edge}")
}
/// Canonical per-`:entrada` `HTTPRoute` `metadata.name` K8s-name-shaped
/// scalar every caixa-mesh `gateway_routes` emitter mounts its
/// per-`:entrada` HTTPRoute under. Composes the parent Aplicacao's
/// `:nome` and the `:entrada :para` destination Servico's `:nome` on a
/// canonical `-` separator (`format!("{aplicacao}-{para}")`), so the
/// per-`(:aplicacao, :entrada.para)` HTTPRoute identity axis lives at
/// one composer instead of a verbatim inline `format!("{}-{}",
/// caixa.nome, entrada.para)` at the [`caixa_mesh::gateway_routes`]
/// [`kube_resource_skeleton`] `name:` argument.
///
/// Peer of [`cilium_network_policy_name`] on the sibling per-Aplicacao
/// per-CR K8s-name-shaped-identity-scalar axis: the CNP name composer
/// carries the per-`(:de, :para)` L4/L7 policy CR name and this
/// composer carries the per-`:entrada` L7 route CR name; both share
/// the same "aplicacao-prefixed sub-identity" discipline (a per-CR
/// identity scalar keyed off the parent Aplicacao's `:nome` joined to
/// the per-CR sub-axis by a canonical `-` separator) so a future
/// substrate-side per-Aplicacao Gateway API axis extension
/// (`GRPCRoute` on grpc-shaped `:contratos` payloads once the sibling
/// [`WitTarget`] variant lands, `TCPRoute` on the sibling l4-only
/// tcp-shaped payload axis, per-`:entrada` `HTTPRouteFilter` /
/// `BackendTLSPolicy` overlays the Gateway API v1.x per-route policy
/// extension surface acknowledges) reaches the shared "aplicacao-prefix
/// + sub-axis + canonical `-` separator" naming discipline through
/// this composer's peer-shape by construction. Until this lift landed
/// the HTTPRoute `metadata.name` axis sat as a verbatim inline
/// `format!("{}-{}", caixa.nome, entrada.para)` at the
/// [`caixa_mesh::gateway_routes`] emitter (with an in-file test-side
/// probe pinning the expected `checkout-cart` shape by verbatim
/// literal), and any future name-encoding rebrand on this axis
/// (`<aplicacao>-<para>` → `<aplicacao>-httproute-<para>` for
/// operator-side per-CR-kind disambiguation once the sibling
/// GRPCRoute / TCPRoute lands and their names would otherwise collide,
/// `<aplicacao>-<para>` → `<aplicacao>.<para>` on a DNS-1123-subdomain-
/// safe axis migration, a per-namespace scoping prefix for
/// multi-tenant Aplicacao hosting) would have had to be threaded
/// through both sites in lockstep or the HTTPRoute `metadata.name`
/// silently split from the operator-side grep-by-name / `kubectl get
/// httproute -n tatara-system <aplicacao>-<para>` lookup encoding at
/// apply time far from the source caixa.lisp.
///
/// The `aplicacao` prefix scopes the emitted HTTPRoute to its owning
/// Aplicacao (so two Aplicacaos hosting a same-named `:entrada :para`
/// destination — `checkout-cart` vs `orders-cart` — land at distinct
/// HTTPRoute `metadata.name`s with no `kubectl apply` collision at the
/// shared namespace, mirroring the peer CNP `metadata.name` collision
/// posture the sibling [`cilium_network_policy_name`] composer's
/// docstring names).
#[must_use]
pub fn gateway_api_http_route_name(aplicacao: &str, para: &str) -> String {
format!("{aplicacao}-{para}")
}
/// Canonical K8s API key naming the resource's API-version selector
/// (e.g. `cilium.io/v2`, `gateway.networking.k8s.io/v1`,
/// `wasm.pleme.io/v1alpha1`). Lifted to a const so a future API-server
/// rename or a multi-version-skew migration is a one-line edit, not a
/// search-and-replace across every per-target renderer.
pub const KUBE_KEY_API_VERSION: &str = "apiVersion";
/// Canonical K8s API key naming the resource's kind discriminator
/// (e.g. `CiliumNetworkPolicy`, `Gateway`, `HTTPRoute`, `ComputeUnit`).
pub const KUBE_KEY_KIND: &str = "kind";
/// Canonical K8s API key naming the resource's metadata block.
pub const KUBE_KEY_METADATA: &str = "metadata";
/// Canonical K8s API key naming the resource's name (under metadata).
pub const KUBE_KEY_NAME: &str = "name";
/// Canonical K8s API key naming the resource's namespace (under metadata).
pub const KUBE_KEY_NAMESPACE: &str = "namespace";
/// Canonical K8s API key naming the resource's labels (under metadata).
pub const KUBE_KEY_LABELS: &str = "labels";
/// Canonical K8s API key naming the resource's per-kind body (sibling
/// to [`KUBE_KEY_METADATA`] at the K8s CR top level). Every typed
/// substrate renderer that materializes a CR populates `spec.*` from
/// the source caixa.lisp — caixa-mesh's `cilium_network_policies`
/// per-`(:de, :para)` `CiliumNetworkPolicy` emitter (the policy's
/// `endpointSelector` / `ingress` block lives under spec),
/// caixa-mesh's `gateway_routes` `Gateway` + `HTTPRoute` emitter (the
/// listeners / rules / parentRefs block lives under spec),
/// caixa-flux's `programs_yaml_entry` + `upsert_into_helmrelease_programs`
/// (the fleet `HelmRelease`'s `spec.values.programs[]` axis),
/// caixa-helm's `values.yaml` builder (the upstream ComputeUnit YAML's
/// `spec.*` axis the rendered `lareira-<nome>` chart re-routes through
/// the library alias). Spelled exactly as the K8s apiserver expects
/// (the canonical OpenAPI v3 schema property name K8s machinery
/// validates against on every CR registration), so the rendered YAML
/// round-trips through every K8s schema parser without per-renderer
/// string drift. Lifted on the trajectory the peer
/// [`KUBE_KEY_API_VERSION`] / [`KUBE_KEY_KIND`] /
/// [`KUBE_KEY_METADATA`] / [`KUBE_KEY_NAME`] / [`KUBE_KEY_NAMESPACE`]
/// / [`KUBE_KEY_LABELS`] / [`KUBE_KEY_MATCH_LABELS`] canonical-K8s-
/// API-key constants establish.
pub const KUBE_KEY_SPEC: &str = "spec";
/// Canonical K8s API key naming the `matchLabels` axis of a
/// [`LabelSelector`][k8s-ls] — the equality-based projection of the
/// selector schema (the other axis, `matchExpressions`, is set-based
/// and intentionally out-of-scope for the V0 [`label_selector`]
/// helper). Spelled exactly as the K8s apiserver expects (camelCase
/// `matchLabels`, not `match_labels` / `MatchLabels` / `match-labels`)
/// so the rendered YAML round-trips through every K8s schema parser
/// (Cilium CRDs, Gateway API, `ComputeUnit`, future
/// `mesh.pleme.io/v1alpha1/Aplicacao`) without per-renderer string
/// drift.
///
/// [k8s-ls]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#labelselector-v1-meta
pub const KUBE_KEY_MATCH_LABELS: &str = "matchLabels";
/// Canonical K8s API key naming the per-CR **`rules` collection** axis —
/// the container the apiserver-side OpenAPI schema for every rule-shaped
/// CR (Cilium L7 `spec.ingress[].toPorts[].rules`, Gateway API
/// `HTTPRoute.spec.rules[]`, RBAC `Role.rules[]` /
/// `ClusterRole.rules[]`, and every future rule-list-shaped CR the M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` materializer + the per-edge
/// `CiliumClusterwideEnvoyConfig` emitter will land on) mounts the
/// per-CR list of match/action rules under. Spelled exactly as the K8s
/// apiserver expects (lowercase `rules`, not `Rules` / `rule` /
/// `ruleset`) so the rendered YAML round-trips through every K8s schema
/// parser without per-renderer string drift.
///
/// Two production-code call sites in this crate's downstream
/// [`caixa-mesh`][cm] renderer carry this key on the same
/// K8s-rule-list-axis surface (both landing sites lived at inline
/// `"rules".into()` before this lift):
///
/// 1. `cilium_network_policies` — the per-`(:de, :para)`
/// `CiliumNetworkPolicy` emitter's per-`toPorts[]` `rules:` mapping
/// (the Cilium L7 rule-list container that carries the `http:` /
/// `kafka:` / `dns:` per-protocol L7 rules the Cilium data plane
/// dispatches on).
/// 2. `gateway_routes` — the `HTTPRoute` emitter's top-level
/// `spec.rules[]` sequence (the Gateway API rule-list container that
/// carries the per-rule `matches[]` + `backendRefs[]` + timeouts /
/// retries overlay the gateway-class-controller dispatches on).
///
/// Five test-side traversal sites in the same renderer navigate the
/// rendered mesh bundle's per-CR `rules:` axis to pin per-CR L7-rule /
/// Gateway-API-rule presence, absence, and content invariants (the
/// `.get("rules")` retrievals under `toPorts[]` on the L7 policy pins
/// and under `spec` on the HTTPRoute pins). All seven sites now route
/// through this const so a future K8s CRD schema rebrand on the shared
/// axis (or the canonical typo footgun `"Rules"` / `"rule"` /
/// `"ruleset"`) surfaces at this one const rather than as an admission-
/// time silent drop across two distinct CR emitters.
///
/// Lifted on the trajectory the peer [`KUBE_KEY_API_VERSION`] /
/// [`KUBE_KEY_KIND`] / [`KUBE_KEY_METADATA`] / [`KUBE_KEY_NAME`] /
/// [`KUBE_KEY_NAMESPACE`] / [`KUBE_KEY_LABELS`] / [`KUBE_KEY_SPEC`] /
/// [`KUBE_KEY_MATCH_LABELS`] canonical-K8s-API-key constants establish
/// — extends the K8s-CR top-level `(apiVersion, kind, metadata, spec)`
/// axis quartet + the nested `metadata.{name, namespace, labels}`
/// triplet + the `LabelSelector.matchLabels` selector-projection axis
/// onto the load-bearing nested `spec.rules[]` / `toPorts[].rules`
/// rule-list container axis every downstream L7-policy /
/// HTTPRoute-rule-dispatch consumer of the rendered mesh bundle keys
/// off.
///
/// [cm]: ../../caixa_mesh/index.html
pub const KUBE_KEY_RULES: &str = "rules";
/// Canonical K8s API key naming the per-CR **L4 port** scalar axis —
/// the field the apiserver-side OpenAPI schema for every port-carrying
/// CR body-position (Cilium L7 `spec.ingress[].toPorts[].ports[].port`
/// per-port-tuple L4 port number, Gateway API
/// `Gateway.spec.listeners[].port` per-listener L4 port number,
/// Gateway API `HTTPRoute.spec.rules[].backendRefs[].port` per-rule
/// per-backend L4 port number, and every future port-shaped CR body-
/// position the M4 `mesh.pleme.io/v1alpha1/Aplicacao` materializer +
/// the per-edge `CiliumClusterwideEnvoyConfig` emitter will land on)
/// mounts the L4 port value under. Spelled exactly as the K8s
/// apiserver expects (lowercase `port`, not `Port` / `portNumber` /
/// `portValue` / `targetPort` — the L4-port-number axis, distinct
/// from the `targetPort` L4-forwarding-destination axis on the K8s
/// Service CRD that lives on a sibling field name the port-value
/// axis is not) so the rendered YAML round-trips through every K8s
/// schema parser without per-renderer string drift.
///
/// Three production-code call sites in this crate's downstream
/// [`caixa-mesh`][cm] renderer carry this key on the same
/// K8s-L4-port-scalar-axis surface (all three landing sites lived at
/// inline `"port".into()` before this lift):
///
/// 1. `cilium_network_policies` — the per-`(:de, :para)`
/// `CiliumNetworkPolicy` emitter's per-`toPorts[].ports[]` port-
/// tuple entry's `port:` scalar (the L4 port number the Cilium
/// data plane's per-tuple bpf policy dispatch loop compares
/// against the observed TCP/UDP L4 header port value).
/// 2. `gateway_routes` — the `Gateway` emitter's per-listener
/// `spec.listeners[].port` scalar (the L4 port number the
/// gateway-class-controller's per-listener bind loop opens the
/// listener socket on).
/// 3. `gateway_routes` — the `HTTPRoute` emitter's per-rule
/// `spec.rules[].backendRefs[].port` scalar (the L4 port number
/// the gateway-class-controller's per-rule backend-dispatch loop
/// forwards the matched request to on the resolved Service /
/// ExternalName backend).
///
/// Two test-side traversal sites in the same renderer navigate the
/// rendered mesh bundle's per-CR L4-port scalar axis to pin per-CR
/// port-value content invariants (the `.get("port")` retrievals under
/// `toPorts[].ports[]` on the L7 policy pin threading through
/// [`DEFAULT_SERVICO_PORT`] and under `backendRefs[]` on the
/// HTTPRoute-backend-port pin). All five sites now route through this
/// const so a future K8s CRD schema rebrand on the shared axis (or
/// the canonical typo footgun `"Port"` / `"portNumber"` /
/// `"portValue"`) surfaces at this one const rather than as an
/// admission-time silent drop across three distinct CR emitters.
///
/// Lifted on the trajectory the peer [`KUBE_KEY_API_VERSION`] /
/// [`KUBE_KEY_KIND`] / [`KUBE_KEY_METADATA`] / [`KUBE_KEY_NAME`] /
/// [`KUBE_KEY_NAMESPACE`] / [`KUBE_KEY_LABELS`] / [`KUBE_KEY_SPEC`] /
/// [`KUBE_KEY_MATCH_LABELS`] / [`KUBE_KEY_RULES`] canonical-K8s-API-
/// key constants establish — extends the K8s-CR top-level
/// `(apiVersion, kind, metadata, spec)` axis quartet + the nested
/// `metadata.{name, namespace, labels}` triplet + the
/// `LabelSelector.matchLabels` selector-projection axis + the
/// `spec.rules[]` / `toPorts[].rules` rule-list container axis onto
/// the load-bearing nested L4-port-scalar axis every downstream
/// bpf-policy-dispatch / gateway-listener-bind / gateway-backend-
/// dispatch consumer of the rendered mesh bundle keys off.
///
/// [cm]: ../../caixa_mesh/index.html
pub const KUBE_KEY_PORT: &str = "port";
/// Canonical K8s API key naming the per-CR **L4/L7 protocol**
/// scalar-discriminator axis — the field the apiserver-side `OpenAPI`
/// schema for every protocol-carrying CR body-position (Cilium L7
/// `spec.ingress[].toPorts[].ports[].protocol` per-port-tuple L4
/// transport protocol discriminator picking between `TCP` / `UDP` /
/// `SCTP` / `ANY`, Gateway API `Gateway.spec.listeners[].protocol`
/// per-listener L7 listener-protocol discriminator picking between
/// `HTTP` / `HTTPS` / `TCP` / `TLS` / `UDP`, and every future
/// protocol-shaped CR body-position the M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` materializer + the per-edge
/// `CiliumClusterwideEnvoyConfig` emitter will land on) mounts the
/// protocol-value discriminator under. Spelled exactly as the K8s
/// apiserver expects (lowercase `protocol`, not `Protocol` /
/// `proto` / `transportProtocol` — the singular scalar-key
/// convention K8s uses across every protocol-carrying CR family,
/// distinct from the `protocols[]` plural-container axis used on a
/// few application-layer-protocol CRDs which is not this axis) so
/// the rendered YAML round-trips through every K8s schema parser
/// without per-renderer string drift.
///
/// Two production-code call sites in this crate's downstream
/// [`caixa-mesh`][cm] renderer carry this key on the same
/// K8s-protocol-scalar-axis surface (both landing sites lived at
/// inline `"protocol".into()` before this lift):
///
/// 1. `cilium_network_policies` — the per-`(:de, :para)`
/// `CiliumNetworkPolicy` emitter's per-`toPorts[].ports[]` port-
/// tuple entry's `protocol:` scalar (the L4 transport protocol
/// discriminator the Cilium data plane's per-tuple bpf policy
/// dispatch loop compares against the observed L4 header
/// protocol before applying the port match — a drifted key here
/// makes the per-tuple bpf policy fall back to the CRD default
/// `ANY`, silently admitting UDP traffic through a TCP-only
/// rule).
/// 2. `gateway_routes` — the `Gateway` emitter's per-listener
/// `spec.listeners[].protocol` scalar (the L7 listener protocol
/// discriminator the gateway-class-controller's per-listener
/// bind loop selects the L7 parser + TLS termination strategy
/// from — a drifted key here silently fails the listener
/// validation, the gateway-class-controller rejects the entire
/// `Gateway` object at admission time, no L7 traffic admitted).
///
/// One test-side traversal site in the same renderer navigates the
/// rendered mesh bundle's per-CR protocol scalar axis to pin per-CR
/// listener-protocol content invariants (the
/// `gateway_emits_gateway_plus_httproute_pair` `.get("protocol")`
/// retrieval on the emitted `Gateway`'s first listener pinning the
/// canonical `HTTP` listener-protocol value). All three sites now
/// route through this const so a future K8s CRD schema rebrand on
/// the shared axis (or the canonical typo footgun `"Protocol"` /
/// `"proto"` / `"transportProtocol"`) surfaces at this one const
/// rather than as an admission-time silent drop across two distinct
/// CR emitters.
///
/// Lifted on the trajectory the peer [`KUBE_KEY_API_VERSION`] /
/// [`KUBE_KEY_KIND`] / [`KUBE_KEY_METADATA`] / [`KUBE_KEY_NAME`] /
/// [`KUBE_KEY_NAMESPACE`] / [`KUBE_KEY_LABELS`] / [`KUBE_KEY_SPEC`] /
/// [`KUBE_KEY_MATCH_LABELS`] / [`KUBE_KEY_RULES`] /
/// [`KUBE_KEY_PORT`] canonical-K8s-API-key constants establish —
/// extends the K8s-CR top-level `(apiVersion, kind, metadata, spec)`
/// axis quartet + the nested `metadata.{name, namespace, labels}`
/// triplet + the `LabelSelector.matchLabels` selector-projection
/// axis + the `spec.rules[]` / `toPorts[].rules` rule-list container
/// axis + the L4-port-scalar axis onto the load-bearing nested
/// L4/L7-protocol-scalar-discriminator axis every downstream bpf-
/// policy-dispatch / gateway-listener-bind consumer of the rendered
/// mesh bundle keys off before it can commit to a port match or a
/// listener parser.
///
/// [cm]: ../../caixa_mesh/index.html
pub const KUBE_KEY_PROTOCOL: &str = "protocol";
/// Canonical K8s API key naming the per-CR **discriminated-union type**
/// scalar-discriminator axis — the field the apiserver-side OpenAPI schema
/// for every discriminated-union CR body-position (Gateway API v1
/// `HTTPRouteMatch.path.type` per-`HTTPRouteMatch` path-selection-predicate
/// discriminator picking between `Exact` / `PathPrefix` /
/// `RegularExpression`, K8s core `Condition.type` per-condition kind
/// discriminator, K8s core `Volume.<projection>.type` per-projection
/// content-source discriminator, and every future discriminated-union CR
/// body-position the M4 `mesh.pleme.io/v1alpha1/Aplicacao` materializer
/// + the per-edge `CiliumClusterwideEnvoyConfig` emitter's per-listener
/// filter-chain type-discriminator + a future per-`:entrada :paths`
/// typed slot admitting a per-path `(:predicate <Exact|Prefix|Regex>)`
/// axis will land on) mounts the discriminated-union type-value under.
/// Spelled exactly as the K8s apiserver expects (lowercase `type`, not
/// `Type` / `kind` / `discriminator` — the singular scalar-key
/// convention K8s uses across every discriminated-union CR family,
/// distinct from the top-level [`KUBE_KEY_KIND`] CRD-registration
/// discriminator on the K8s CR top-level which is the CRD-lookup half
/// of the `(apiVersion, kind)` tuple the K8s apiserver's `RESTMapper`
/// consults and is not this axis) so the rendered YAML round-trips
/// through every K8s schema parser without per-renderer string drift.
///
/// One production-code call site in this crate's downstream
/// [`caixa-mesh`][cm] renderer carries this key on the same
/// K8s-discriminated-union-type-scalar-axis surface (the landing site
/// lived at an inline `"type".into()` before this lift):
///
/// 1. `gateway_routes` — the `HTTPRoute` emitter's per-rule per-match
/// `spec.rules[].matches[].path.type` scalar (the path-selection-
/// predicate discriminator the gateway-class-controller's per-rule
/// L7 dispatch pass selects the path-match strategy from — a drifted
/// key here silently fails the per-match path-selection-predicate
/// validation, the Gateway API v1 `PathMatchType` OpenAPI schema
/// validator drops the entire `HTTPRoute` object at admission with
/// no per-rule L7 URL-path filtering applied, and every external
/// `:entrada` path-filtered flow the route was authored to accept
/// drops at the gateway-class-controller's admission gate with no
/// field naming the discriminator-drift root cause).
///
/// Pairs with the sibling [`GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX`]
/// (530705d) per-`HTTPRouteMatch` path-selection-predicate discriminator
/// scalar-VALUE the discriminator scalar-KEY here holds under, closing
/// the per-`HTTPRouteMatch` path-selection-predicate `(type key →
/// PathPrefix value)` scalar-key/scalar-value discriminator axis pair
/// the M3 Aplicacao mesh renderer's external `:entrada` per-path
/// L7-filtering ingress contract rests on — the same shape the sibling
/// [`KUBE_KEY_PROTOCOL`] (0307950) key + [`KUBE_PROTOCOL_TCP`] (2123047)
/// / [`GATEWAY_API_PROTOCOL_HTTP`] (1b57473) value pair already carries
/// on the L4/L7-protocol scalar-discriminator surface. A `"Type"` /
/// `"kind"` / `"discriminator"` / `"predicate"` typo at the production-
/// code call site lands outside the Gateway API v1 `HTTPPathMatch`
/// OpenAPI schema's admitted property set, surfacing apply-side as a
/// non-self-locating "spec.rules[0].matches[0].path: Unknown field
/// \"Type\"" apiserver admission-rejection far from the source
/// `caixa.lisp` / the renderer's `path_match.insert(…)` call site.
///
/// Lifted on the trajectory the peer [`KUBE_KEY_API_VERSION`] /
/// [`KUBE_KEY_KIND`] / [`KUBE_KEY_METADATA`] / [`KUBE_KEY_NAME`] /
/// [`KUBE_KEY_NAMESPACE`] / [`KUBE_KEY_LABELS`] / [`KUBE_KEY_SPEC`] /
/// [`KUBE_KEY_MATCH_LABELS`] / [`KUBE_KEY_RULES`] / [`KUBE_KEY_PORT`] /
/// [`KUBE_KEY_PROTOCOL`] canonical-K8s-API-key constants establish —
/// extends the K8s-CR top-level `(apiVersion, kind, metadata, spec)`
/// axis quartet + the nested `metadata.{name, namespace, labels}`
/// triplet + the `LabelSelector.matchLabels` selector-projection axis
/// + the `spec.rules[]` / `toPorts[].rules` rule-list container axis +
/// the L4-port-scalar axis + the L4/L7-protocol-scalar-discriminator
/// axis onto the load-bearing nested discriminated-union-type-scalar-
/// discriminator axis every downstream gateway-class-controller /
/// apiserver-side OpenAPI-schema-validator consumer of the rendered
/// mesh bundle keys off before it can commit to a per-match path-
/// selection predicate.
///
/// [cm]: ../../caixa_mesh/index.html
pub const KUBE_KEY_TYPE: &str = "type";
/// Default cluster-wide K8s namespace every caixa renderer emits
/// objects into when the source caixa doesn't pin its own. The single
/// source of truth both [`caixa-flux`][cf]'s programs.yaml /
/// GitRepository / HelmRelease / Kustomization emitters and
/// [`caixa-mesh`][cm]'s programs fan-out / CiliumNetworkPolicy /
/// Gateway / HTTPRoute emitters consult — re-exported by each
/// renderer's lib as `pub use caixa_core::DEFAULT_NAMESPACE`, so a
/// future per-cluster-namespace rebrand (e.g. moving to `pleme-system`
/// once `tatara-system` outlives its scoping intent) is a one-line
/// edit here, not a coordinated rewrite across every renderer
/// crate's `metadata.namespace` slot.
///
/// Until this lift landed both renderers carried their own `pub const
/// DEFAULT_NAMESPACE: &str = "tatara-system"` declarations
/// (caixa-flux/src/lib.rs:77, caixa-mesh/src/lib.rs:172), with the
/// `caixa-mesh` site's doc-comment explicitly acknowledging the
/// duplication ("Mirrors `caixa_flux::DEFAULT_NAMESPACE`"); a future
/// rebrand on either side without a coordinated edit on the other
/// would have silently emitted into two distinct namespaces on the
/// same cluster's apply — Servicos at programs.yaml's namespace,
/// their Aplicacao's NetworkPolicies / Gateways / HTTPRoutes at a
/// drifted one — and the CiliumNetworkPolicy's `endpointSelector`
/// would match no pods (different namespace), silently dropping every
/// L7 contrato flow at apply time with no diagnostic naming the
/// namespace-drift root cause.
///
/// Lifting it to caixa-core's render-constants block alongside the
/// peer [`LABEL_APLICACAO`] / [`LABEL_PROGRAM`] / [`LABEL_CONTRATO`]
/// label-namespace constants and the canonical [`KUBE_KEY_NAMESPACE`]
/// API-key constant makes the namespace-axis discipline structural:
/// every renderer that reaches for the default namespace consults the
/// same `&'static str`, and every future renderer (the M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer, the future
/// per-edge `CiliumClusterwideEnvoyConfig` emitter, the future
/// caixa-otel collector-pipeline emitter) inherits the same value by
/// construction, with no opportunity for per-renderer drift. Same
/// "the typed constant lives in one place" discipline the
/// [`PLEME_LABEL_PREFIX`] (a8d4d57) and [`KUBE_KEY_API_VERSION`] /
/// [`KUBE_KEY_KIND`] / [`KUBE_KEY_METADATA`] lifts apply on the peer
/// shared-string axes.
///
/// [cf]: ../../caixa_flux/index.html
/// [cm]: ../../caixa_mesh/index.html
pub const DEFAULT_NAMESPACE: &str = "tatara-system";
/// Canonical FluxCD installation namespace every `caixa-flux` `Kustomization`
/// document apply-targets. The single source of truth both axes of the
/// rendered `kustomization.yaml` document reach for:
///
/// - `metadata.namespace` — the namespace the `Kustomization` resource
/// itself lives in (the `FluxCD` `kustomize-controller` watches this
/// namespace by default; a drifted value sits outside the controller's
/// watch window and is never reconciled);
/// - `spec.sourceRef.name` — the `GitRepository` the bootstrap pipeline
/// created at `flux bootstrap` time and the per-Servico `Kustomization`
/// transitively threads its `path: ./clusters/<cluster>/services/<name>`
/// reference through. The canonical FluxCD bootstrap convention names
/// this `GitRepository` after the installation namespace (the
/// `flux-system` namespace contains a `GitRepository/flux-system`
/// pointing at the operator's source-of-truth repo); both axes are the
/// same conceptual "Flux installation namespace" load-bearing string
/// and must move together on any future rebrand.
///
/// Until this lift landed both axes carried inline `flux-system` literals
/// inside [`cluster_bundle`]'s `kustomization.yaml` format-string template
/// (caixa-flux/src/lib.rs:477, 483) — two production-code consumers of the
/// same load-bearing FluxCD-installation-namespace convention, drift-prone
/// by construction. A future per-cluster Flux installation rebrand (the
/// operator moving the bootstrap controllers to a different installation
/// namespace, e.g. `flux-pleme` to match the per-tenant scoping convention
/// once `flux-system` outlives its scoping intent; or any per-edition
/// rebrand the FluxCD upgrade docs name) on one axis without a coordinated
/// edit on the other would have silently emitted a `Kustomization` whose
/// `metadata.namespace` sat outside the `kustomize-controller` watch
/// window (controller-side: never reconciled, every `HelmRelease` /
/// `GitRepository` it gates frozen at last-applied state) or whose
/// `spec.sourceRef.name` pointed at a `GitRepository` that doesn't exist
/// in the rebranded namespace (apply-side: the reference dangles, the
/// dependent chart never pulls). The apply-time symptom (the Servico's
/// `HelmRelease` is created but never reconciled, or never reaches its
/// chart source) is invisible at admission and surfaces only as
/// "the cluster says the resources are applied but nothing changed",
/// typically far from the rebrand commit's source.
///
/// Lifting it to caixa-core's render-constants block alongside the peer
/// [`DEFAULT_NAMESPACE`] (a085b26, the workload-side
/// `tatara-system` namespace every emitted resource lives in) makes the
/// installation-namespace axis discipline structural: both kustomization
/// axes consult the same `&'static str`, and every future renderer that
/// reaches for the canonical Flux installation namespace (the future M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
/// `Kustomization`, the future per-edge `Kustomization` the operator
/// emits for the `CiliumClusterwideEnvoyConfig` pipeline, the future
/// `caixa-otel` collector-pipeline `Kustomization`) inherits the same
/// value by construction with no opportunity for per-renderer drift.
/// Same "the typed constant lives in one place" discipline the
/// [`DEFAULT_NAMESPACE`] (a085b26) / [`DEFAULT_LIBRARY_NAME`] (41438dc) /
/// [`crate::DEFAULT_SERVICO_PORT`] (1e22add) /
/// [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] (0a6a602) lifts apply on the
/// peer canonical-load-bearing-string surface.
///
/// The value is a valid DNS-1123 label (the K8s apiserver-side floor every
/// `metadata.namespace` rule enforces): lowercase ASCII alphanumeric with
/// `-` separators, no leading / trailing hyphen, length within the
/// [`DNS_1123_LABEL_MAX_LEN`] (63-byte) cap. A future rebrand on this lift
/// cannot silently land a value the apiserver refuses, by construction:
/// the [`default_flux_system_namespace_is_a_valid_dns_1123_label`] pin
/// trips at caixa-core build time on any drift past the typed floor.
///
/// [cf]: ../../caixa_flux/index.html
pub const DEFAULT_FLUX_SYSTEM_NAMESPACE: &str = "flux-system";
/// Canonical FluxCD `HelmRelease` CRD `apiVersion` every `caixa-flux`
/// `helmrelease.yaml` document emits. The Flux v2 `helm-controller` watches
/// resources at this exact group/version (`helm.toolkit.fluxcd.io/v2`);
/// drift to a stale `v2beta1` / `v2beta2` (the pre-GA Flux v2 betas every
/// upstream Flux GA-migration doc names) silently routes the rendered
/// `HelmRelease` outside the controller's `Watches` and breaks at apply
/// time with a non-self-locating "no kind 'HelmRelease' is registered for
/// version 'helm.toolkit.fluxcd.io/v2beta2'" error far from the source
/// caixa.lisp / the renderer's format-string template.
///
/// The single source of truth both axes of the rendered Flux bundle reach
/// for:
///
/// - `helmrelease.yaml` `apiVersion` — the top-level CRD-group/version
/// the rendered document declares (caixa-flux/src/lib.rs:455 — the
/// `helmrelease` format-string template);
/// - `kustomization.yaml` `spec.healthChecks[]` per-entry `apiVersion`
/// — the same Flux-v2 `HelmRelease` reference the parent Kustomization
/// gates its health-check on (caixa-flux/src/lib.rs:504 — the
/// `kustomization` format-string template). The Flux v2 contract pairs
/// a `HelmRelease` document with its sibling `Kustomization`'s
/// `healthChecks[].apiVersion` axis: both must name the same Flux v2
/// `HelmRelease` CRD group/version for the Kustomization's per-resource
/// health-gate to bind to the rendered HelmRelease; a future Flux v3
/// promotion (the upstream Flux roadmap names a per-CRD-group / per-
/// v3 version migration once the Flux v2 LTS branch closes) on one
/// axis without a coordinated edit on the other would have silently
/// emitted a `Kustomization` whose `healthChecks[].apiVersion` pointed
/// at an obsolete CRD group/version (apply-side: the health check
/// never resolves, the parent Kustomization sits perpetually in
/// `Reconciling`).
///
/// Until this lift landed both axes carried inline
/// `helm.toolkit.fluxcd.io/v2` literals inside [`cluster_bundle`]'s
/// `helmrelease.yaml` + `kustomization.yaml` format-string templates and a
/// matching pair inside the in-file `upsert_into_helmrelease_programs`
/// test fixtures (caixa-flux/src/lib.rs:928, 970) — four occurrences of
/// the same load-bearing FluxCD-CRD-group/version convention, drift-prone
/// by construction. The PRIME DIRECTIVE duplication-budget rule
/// (THEORY.md §I.3.5: "every recurring shape becomes a generator before
/// it becomes a pattern; every pattern becomes a library before it
/// becomes duplicated code. The duplication budget is zero.") promotes
/// the constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) lift
/// established on the sibling Flux-installation-namespace axis. The two
/// render-side consumers now thread the same `&'static str` through their
/// format-string templates so a future Flux v3 promotion lands in one
/// place; the test fixtures keep the value as a literal because they
/// exercise `serde_yaml::from_str` on a static YAML document — the
/// build-time pin [`default_flux_helmrelease_api_version_matches_caixa_flux_test_fixtures`]
/// trips if the literals ever drift past the typed const.
///
/// Same "the typed constant lives in one place" discipline the
/// [`DEFAULT_NAMESPACE`] (a085b26) / [`DEFAULT_LIBRARY_NAME`] (41438dc) /
/// [`crate::DEFAULT_SERVICO_PORT`] (1e22add) /
/// [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] (0a6a602) /
/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) lifts apply on the peer
/// canonical-load-bearing-string surface.
///
/// [cf]: ../../caixa_flux/index.html
pub const FLUX_HELMRELEASE_API_VERSION: &str = "helm.toolkit.fluxcd.io/v2";
/// Canonical FluxCD `GitRepository` CRD `apiVersion` every `caixa-flux`
/// `gitrepository.yaml` document emits. The Flux v2 `source-controller`
/// watches resources at this exact group/version
/// (`source.toolkit.fluxcd.io/v1`); drift to a stale `v1beta1` / `v1beta2`
/// (the pre-GA Flux v2 source-controller betas every upstream Flux GA-
/// migration doc names) silently routes the rendered `GitRepository`
/// outside the controller's `Watches` and breaks at apply time with a
/// non-self-locating "no kind 'GitRepository' is registered for version
/// 'source.toolkit.fluxcd.io/v1beta2'" error far from the source
/// caixa.lisp / the renderer's format-string template.
///
/// The single source of truth the `gitrepository.yaml` `apiVersion` axis
/// reaches for (caixa-flux/src/lib.rs:436 — the `gitrepo` format-string
/// template). The Flux v2 source/helm/kustomize controller triple pairs
/// each CRD-group/version against its sibling controller's `Watches`
/// registration: the rendered `GitRepository` is the chart-source the
/// sibling `HelmRelease` document's `spec.chart.spec.sourceRef.kind:
/// GitRepository` references, and the parent `Kustomization`'s
/// `spec.sourceRef.kind: GitRepository` also points at this same CRD
/// group/version. A future Flux v3 promotion on this axis without a
/// coordinated edit on the sibling [`FLUX_HELMRELEASE_API_VERSION`] /
/// future-`FLUX_KUSTOMIZATION_API_VERSION` axes would silently land the
/// rendered `GitRepository` outside the source-controller's `Watches`
/// (controller-side: never reconciled, the dependent HelmRelease's
/// `chart: sourceRef` dangles, every per-Servico apply silently comes
/// up with the prior reconciled state).
///
/// Until this lift landed the axis carried an inline
/// `source.toolkit.fluxcd.io/v1` literal inside [`cluster_bundle`]'s
/// `gitrepository.yaml` format-string template — one occurrence today,
/// promoted to a typed substrate-side `&'static str` on the same
/// trajectory the [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) lifts established on the
/// sibling Flux-v2-load-bearing-string surface. The render-side consumer
/// now threads the same `&'static str` through its format-string
/// template so a future Flux v3 promotion lands in one place; every
/// future renderer that reaches for the canonical Flux v2 `GitRepository`
/// apiVersion (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-Aplicacao `GitRepository`, a future per-edge
/// `GitRepository` the operator emits for the
/// `CiliumClusterwideEnvoyConfig` pipeline, a future `caixa-otel`
/// collector-pipeline `GitRepository`) inherits the same value by
/// construction with no opportunity for per-renderer drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`DEFAULT_NAMESPACE`] (a085b26) / [`DEFAULT_LIBRARY_NAME`] (41438dc) /
/// [`crate::DEFAULT_SERVICO_PORT`] (1e22add) /
/// [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] (0a6a602) /
/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) /
/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) lifts apply on the peer
/// canonical-load-bearing-string surface.
///
/// [cf]: ../../caixa_flux/index.html
pub const FLUX_GITREPOSITORY_API_VERSION: &str = "source.toolkit.fluxcd.io/v1";
/// Canonical FluxCD `GitRepository` CRD `kind` discriminator every
/// `caixa-flux`-emitted document that names a Flux v2 `GitRepository`
/// at a [`KUBE_KEY_KIND`]-rooted axis declares. Paired peer to the
/// sibling [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) — the K8s
/// apiserver-side CRD resolution contract is the `(apiVersion, kind)`
/// tuple keyed against the registered `CustomResourceDefinition`, so
/// drift on the kind axis is exactly as load-bearing as drift on the
/// apiVersion axis it accompanies (the apiserver's `RESTMapper` consults
/// both together; a `("source.toolkit.fluxcd.io/v1", "GitRepostiory")`
/// typo at any one of the three production-code call sites lands
/// outside the registered Flux v2 source-controller CRD's
/// `RESTKind` lookup, surfacing apply-side as a non-self-locating
/// "no kind 'GitRepostiory' is registered for version
/// 'source.toolkit.fluxcd.io/v1'" error far from the source
/// caixa.lisp / the renderer's format-string template).
///
/// The single source of truth the rendered Flux bundle's three
/// `GitRepository`-naming axes reach for:
///
/// - the rendered `gitrepository.yaml` document's top-level
/// [`KUBE_KEY_KIND`] axis (caixa-flux/src/lib.rs:505 — the
/// `gitrepo` format-string template);
/// - the rendered `helmrelease.yaml` document's
/// `spec.chart.spec.sourceRef.kind` axis (caixa-flux/src/lib.rs:556 —
/// the `helmrelease` format-string template), pointing back at the
/// sibling `GitRepository` the chart sources from;
/// - the rendered `kustomization.yaml` document's `spec.sourceRef.kind`
/// axis (caixa-flux/src/lib.rs:591 — the `kustomization` format-
/// string template), pointing back at the cluster's bootstrap
/// `GitRepository` (paired with [`DEFAULT_FLUX_SYSTEM_NAMESPACE`]
/// on the namespace axis).
///
/// All three axes name the same K8s CRD discriminator and must move
/// together on any future Flux v3 rebrand (e.g. an upstream Flux v3
/// rename like `GitSource`). Until this lift landed the three axes
/// carried inline `GitRepository` literals across the three production-
/// code occurrences in caixa-flux/src/lib.rs:505, 556, 591 (the
/// `cluster_bundle` `gitrepo` + `helmrelease` + `kustomization` format-
/// string templates) plus a matching set inside the in-file
/// `cluster_bundle_*` test fixtures — six occurrences of the same load-
/// bearing FluxCD-CRD-`kind`-discriminator convention, drift-prone by
/// construction. A drift on the `helmrelease.yaml`
/// `spec.chart.spec.sourceRef.kind` site alone — the one apply-side
/// failure mode the apiserver can't self-locate — would have silently
/// dangled the HelmRelease's chart sourceRef (controller-side: the
/// `helm-controller` never resolves a chart for the HelmRelease, the
/// rendered Servico chart never reconciles, every per-Servico apply
/// silently comes up with the prior reconciled state) with no diagnostic
/// naming the kind-drift root cause far from the source caixa.lisp.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) lifts established on
/// the sibling Flux-v2-load-bearing-string axes — extends the
/// discipline from the apiVersion half of the `(apiVersion, kind)`
/// CRD-lookup tuple onto the kind half on the same Flux v2
/// source-controller CRD. The three render-side consumers now thread
/// the same `&'static str` through their format-string templates so a
/// future Flux v3 rebrand lands in one place; every future renderer
/// that reaches for the canonical Flux v2 `GitRepository` kind (the
/// future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
/// per-Aplicacao `GitRepository`, a future per-edge `GitRepository`
/// the operator emits for the `CiliumClusterwideEnvoyConfig` pipeline,
/// a future `caixa-otel` collector-pipeline `GitRepository`) inherits
/// the same value by construction with no opportunity for per-renderer
/// drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) /
/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) lifts apply on the peer
/// canonical-Flux-v2-load-bearing-string surface.
///
/// [cf]: ../../caixa_flux/index.html
pub const FLUX_KIND_GIT_REPOSITORY: &str = "GitRepository";
/// Canonical FluxCD `HelmRelease` CRD `kind` discriminator every
/// `caixa-flux`-emitted document that names a Flux v2 `HelmRelease`
/// at a [`KUBE_KEY_KIND`]-rooted axis declares. Paired peer to the
/// sibling [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) — the K8s
/// apiserver-side CRD resolution contract is the `(apiVersion, kind)`
/// tuple keyed against the registered `CustomResourceDefinition`, so
/// drift on the kind axis is exactly as load-bearing as drift on the
/// apiVersion axis it accompanies (the apiserver's `RESTMapper`
/// consults both together; a `("helm.toolkit.fluxcd.io/v2",
/// "HelmRelase")` typo at any one of the two production-code call
/// sites lands outside the registered Flux v2 helm-controller CRD's
/// `RESTKind` lookup, surfacing apply-side as a non-self-locating
/// "no kind 'HelmRelase' is registered for version
/// 'helm.toolkit.fluxcd.io/v2'" error far from the source
/// caixa.lisp / the renderer's format-string template).
///
/// The single source of truth the rendered Flux bundle's two
/// `HelmRelease`-naming axes reach for:
///
/// - the rendered `helmrelease.yaml` document's top-level
/// [`KUBE_KEY_KIND`] axis (caixa-flux/src/lib.rs:580 — the
/// `helmrelease` format-string template);
/// - the rendered `kustomization.yaml` document's
/// `spec.healthChecks[].kind` axis (caixa-flux/src/lib.rs:631 —
/// the `kustomization` format-string template), pointing back at
/// the sibling `HelmRelease` the Kustomization pins as a
/// health-gate before declaring its own reconcile complete.
///
/// Both axes name the same K8s CRD discriminator and must move
/// together on any future Flux v3 rebrand (e.g. an upstream Flux v3
/// rename like `ChartRelease`). Until this lift landed the two axes
/// carried inline `HelmRelease` literals across the two production-
/// code occurrences in caixa-flux/src/lib.rs:580 (the
/// `cluster_bundle` `helmrelease` format-string template) and 631
/// (the `kustomization` `spec.healthChecks[]` element). A drift on
/// the `kustomization.yaml` `spec.healthChecks[].kind` site alone —
/// the one apply-side failure mode the apiserver can't self-locate
/// (a healthCheck kind typo doesn't fail apply-parse the way a
/// top-level kind typo does; it sits as a dangling unmatched health
/// gate the `kustomize-controller` perpetually re-evaluates) —
/// would have silently pinned the parent Kustomization at
/// `Reconciling` forever with no diagnostic naming the kind-drift
/// root cause far from the source caixa.lisp.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) lifts established on
/// the sibling Flux-v2-load-bearing-string axes — extends the
/// discipline from the kind axis of the Flux v2 source-controller
/// CRD (the [`FLUX_KIND_GIT_REPOSITORY`] lift) onto the kind axis of
/// the sibling Flux v2 helm-controller CRD. The two render-side
/// consumers now thread the same `&'static str` through their
/// format-string templates so a future Flux v3 rebrand lands in one
/// place; every future renderer that reaches for the canonical Flux
/// v2 `HelmRelease` kind (the future M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-
/// Aplicacao `HelmRelease`, a future per-edge `HelmRelease` the
/// operator emits for the `CiliumClusterwideEnvoyConfig` pipeline,
/// a future `caixa-otel` collector-pipeline `HelmRelease`) inherits
/// the same value by construction with no opportunity for per-
/// renderer drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) /
/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) lifts apply on the
/// peer canonical-Flux-v2-load-bearing-string surface.
///
/// [cf]: ../../caixa_flux/index.html
pub const FLUX_KIND_HELM_RELEASE: &str = "HelmRelease";
/// Canonical FluxCD `Kustomization` CRD `apiVersion` every `caixa-flux`
/// `kustomization.yaml` document emits. The Flux v2 `kustomize-controller`
/// watches resources at this exact group/version
/// (`kustomize.toolkit.fluxcd.io/v1`); drift to a stale `v1beta1` /
/// `v1beta2` (the pre-GA Flux v2 kustomize-controller betas every
/// upstream Flux GA-migration doc names) silently routes the rendered
/// `Kustomization` outside the controller's `Watches` and breaks at
/// apply time with a non-self-locating "no kind 'Kustomization' is
/// registered for version 'kustomize.toolkit.fluxcd.io/v1beta2'" error
/// far from the source caixa.lisp / the renderer's format-string
/// template.
///
/// The single source of truth the `kustomization.yaml` `apiVersion`
/// axis reaches for (caixa-flux/src/lib.rs:531 — the `kustomization`
/// format-string template). Completes the Flux v2 controller triplet
/// (source-controller + helm-controller + kustomize-controller) lift
/// alongside the sibling [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3)
/// and [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) — every per-
/// controller CRD-group/version is now a typed substrate-side
/// `&'static str` consumed through one `pub use caixa_core::FLUX_*`
/// re-export at the renderer site. The three controllers share the
/// canonical `.toolkit.fluxcd.io` root (asserted by
/// [`tests::flux_controller_triplet_api_versions_share_toolkit_fluxcd_io_root`]),
/// so a future Flux v3 promotion that forks any controller out of the
/// toolkit group surfaces here as a coordinated cross-axis edit-point
/// across all three constants.
///
/// The rendered `Kustomization`'s `metadata.namespace` (the Flux
/// installation namespace, [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] —
/// 7197d38) and `spec.sourceRef.kind: GitRepository`
/// (referenced through [`FLUX_GITREPOSITORY_API_VERSION`]) and
/// `spec.healthChecks[].apiVersion` (the rendered `HelmRelease`'s
/// CRD-group/version, [`FLUX_HELMRELEASE_API_VERSION`]) all share
/// the cluster-side contract with the upstream Flux v2 controller
/// triplet: a coordinated edit on any one of these four constants
/// must move alongside the sibling axes, and the lift makes that
/// movement a typed substrate-side edit-point rather than a
/// distributed-across-format-string-template-literals refactor.
///
/// Until this lift landed the axis carried an inline
/// `kustomize.toolkit.fluxcd.io/v1` literal inside [`cluster_bundle`]'s
/// `kustomization.yaml` format-string template — one occurrence today,
/// promoted to a typed substrate-side `&'static str` on the same
/// trajectory the [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) lifts established on
/// the sibling Flux-v2-load-bearing-string surface. The render-side
/// consumer now threads the same `&'static str` through its
/// format-string template so a future Flux v3 promotion lands in one
/// place; every future renderer that reaches for the canonical Flux
/// v2 `Kustomization` apiVersion (the future M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
/// `Kustomization`, a future per-edge `Kustomization` the operator
/// emits for the `CiliumClusterwideEnvoyConfig` pipeline, a future
/// `caixa-otel` collector-pipeline `Kustomization`) inherits the
/// same value by construction with no opportunity for per-renderer
/// drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`DEFAULT_NAMESPACE`] (a085b26) / [`DEFAULT_LIBRARY_NAME`] (41438dc) /
/// [`crate::DEFAULT_SERVICO_PORT`] (1e22add) /
/// [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] (0a6a602) /
/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) /
/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) lifts apply on the
/// peer canonical-load-bearing-string surface.
///
/// [cf]: ../../caixa_flux/index.html
pub const FLUX_KUSTOMIZATION_API_VERSION: &str = "kustomize.toolkit.fluxcd.io/v1";
/// Canonical FluxCD `Kustomization` CRD `kind` discriminator every
/// `caixa-flux`-emitted document that names a Flux v2 `Kustomization`
/// at a [`KUBE_KEY_KIND`]-rooted axis declares. Paired peer to the
/// sibling [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) — the K8s
/// apiserver-side CRD resolution contract is the `(apiVersion, kind)`
/// tuple keyed against the registered `CustomResourceDefinition`, so
/// drift on the kind axis is exactly as load-bearing as drift on the
/// apiVersion axis it accompanies (the apiserver's `RESTMapper` consults
/// both together; a `("kustomize.toolkit.fluxcd.io/v1", "Kustomizaton")`
/// typo at the production-code call site lands outside the registered
/// Flux v2 kustomize-controller CRD's `RESTKind` lookup, surfacing
/// apply-side as a non-self-locating "no kind 'Kustomizaton' is
/// registered for version 'kustomize.toolkit.fluxcd.io/v1'" error far
/// from the source caixa.lisp / the renderer's format-string template).
///
/// The single source of truth the rendered Flux bundle's
/// `Kustomization`-naming axis reaches for:
///
/// - the rendered `kustomization.yaml` document's top-level
/// [`KUBE_KEY_KIND`] axis (caixa-flux/src/lib.rs:651 — the
/// `kustomization` format-string template).
///
/// The kind axis names the same K8s CRD discriminator as the sibling
/// [`FLUX_KUSTOMIZATION_API_VERSION`] apiVersion axis and must move
/// together on any future Flux v3 rebrand. Until this lift landed the
/// axis carried an inline `Kustomization` literal across the one
/// production-code occurrence in caixa-flux/src/lib.rs:651 (the
/// `cluster_bundle` `kustomization` format-string template) plus a
/// matching set inside the in-file `cluster_bundle_*` test fixtures —
/// occurrences of the same load-bearing FluxCD-CRD-`kind`-discriminator
/// convention, drift-prone by construction. A drift on the top-level
/// `kustomization.yaml` `kind` axis would have surfaced as a
/// non-self-locating "no kind 'Kustomizaton' is registered for version
/// 'kustomize.toolkit.fluxcd.io/v1'" error far from the source
/// caixa.lisp at apply parse time, with the rendered parent Kustomization
/// never reconciling and every downstream per-Servico `dependsOn` chain
/// freezing at the kustomize-controller's CRD-lookup boundary.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
/// [`FLUX_KIND_HELM_RELEASE`] (e24ea3c) /
/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) /
/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) lifts established on
/// the sibling Flux-v2-load-bearing-string axes — extends the
/// discipline from the apiVersion half of the `(apiVersion, kind)`
/// CRD-lookup tuple onto the kind half on the same Flux v2
/// kustomize-controller CRD. Completes the Flux v2 controller triplet
/// kind-axis lift (source-controller + helm-controller +
/// kustomize-controller) alongside the sibling
/// [`FLUX_KIND_GIT_REPOSITORY`] and [`FLUX_KIND_HELM_RELEASE`] — every
/// per-controller CRD `kind` discriminator is now a typed substrate-side
/// `&'static str` consumed through one `pub use caixa_core::FLUX_KIND_*`
/// re-export at the renderer site. The render-side consumer now threads
/// the same `&'static str` through its format-string template so a
/// future Flux v3 rebrand lands in one place; every future renderer
/// that reaches for the canonical Flux v2 `Kustomization` kind (the
/// future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
/// per-Aplicacao `Kustomization`, a future per-edge `Kustomization`
/// the operator emits for the `CiliumClusterwideEnvoyConfig` pipeline,
/// a future `caixa-otel` collector-pipeline `Kustomization`) inherits
/// the same value by construction with no opportunity for per-renderer
/// drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
/// [`FLUX_KIND_HELM_RELEASE`] (e24ea3c) /
/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) /
/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) lifts apply on the peer
/// canonical-Flux-v2-load-bearing-string surface.
///
/// [cf]: ../../caixa_flux/index.html
pub const FLUX_KIND_KUSTOMIZATION: &str = "Kustomization";
/// Canonical Flux v2 per-`HelmRelease`/`Kustomization` source-reference
/// container-axis key every `caixa-flux`-emitted bundle document mounts its
/// per-CR source-of-truth pointer under (`spec.chart.spec.sourceRef` on
/// `HelmRelease`, `spec.sourceRef` on `Kustomization`) — the Flux v2 CRD
/// schema places the `(kind, name, namespace)` reference triple under this
/// single container key, so drift on the container axis is exactly as
/// load-bearing as drift on the sibling [`FLUX_KIND_GIT_REPOSITORY`]
/// (dbbcf29) kind-discriminator + [`DEFAULT_FLUX_SYSTEM_NAMESPACE`]
/// (7197d38) namespace axes the block nests (a `"source_ref"` / `"source"`
/// / `"sourceReference"` / `"gitSourceRef"` typo at either the emit-side
/// format-string template or a downstream test-fixture probe silently
/// dangles the `HelmRelease.spec.chart.spec.sourceRef` chart resolution +
/// the `Kustomization.spec.sourceRef` source resolution at the Flux v2
/// source-controller's CRD registration; the source-controller's per-CR
/// reconcile loop keys off this exact container axis to source the
/// `(kind, name, namespace)` reference triple, and a drift silently freezes
/// the dependent per-Servico `dependsOn` chain at apply time with no
/// field naming the sourceRef-container-drift root cause).
///
/// The single source of truth the rendered Flux bundle's per-CR
/// source-reference-container-axis-naming reaches for:
///
/// - the rendered `helmrelease.yaml` document's per-`HelmRelease`
/// `spec.chart.spec.sourceRef` block (caixa-flux/src/lib.rs — the
/// `cluster_bundle` `helmrelease` format-string template's
/// `{source_ref_key}:\n` sub-block header, now threaded through
/// the lifted const via a `{source_ref_key}` named-arg
/// interpolation);
/// - the rendered `kustomization.yaml` document's per-`Kustomization`
/// `spec.sourceRef` block (caixa-flux/src/lib.rs — the sibling
/// `cluster_bundle` `kustomization` format-string template's
/// `{source_ref_key}:\n` sub-block header, now threaded through
/// the lifted const via the sibling `{source_ref_key}` named-arg
/// interpolation);
/// - five test-side navigation sites in `mod tests` that probe the
/// rendered documents' `.get("sourceRef")` container axis to pin
/// the emitted `(kind, name, namespace)` reference triple against
/// the sibling lifted [`FLUX_KIND_GIT_REPOSITORY`] +
/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] axes.
///
/// The container-axis key names the same Flux-v2-source-controller-side
/// per-CR source-of-truth reference-triple container as the sibling
/// per-CRD `kind` discriminator [`FLUX_KIND_GIT_REPOSITORY`] nests inside,
/// and must move together on any future Flux v3 rebrand (a hypothetical
/// upstream Flux v3 rename of the source-reference container axis from
/// `sourceRef` to `source` / `sourceReference` / `sourceOf`, coordinated
/// with the upstream fluxcd/flux2 project's per-version deprecation
/// cycle, would land at this one const rather than scattered across the
/// two per-CR format-string templates + five per-test-fixture probe
/// sites).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
/// [`FLUX_KIND_HELM_RELEASE`] (e24ea3c) /
/// [`FLUX_KIND_KUSTOMIZATION`] (2d61a6f) /
/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) /
/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) lifts established on the
/// sibling canonical-Flux-v2-load-bearing-string surfaces — extends the
/// per-CRD kind-discriminator + apiVersion + install-namespace lift
/// trajectory onto the sibling per-CR source-reference container-axis
/// key the `cluster_bundle` `HelmRelease` + `Kustomization` renderers
/// both consume under their nested `(kind, name, namespace)` reference
/// triple.
///
/// [cf]: ../../caixa_flux/index.html
pub const FLUX_KEY_SOURCE_REF: &str = "sourceRef";
/// Canonical Flux v2 per-`HelmRelease` inline-chart-template container-axis
/// key every `caixa-flux`-emitted `HelmRelease` document nests its per-CR
/// chart-template block under (`spec.chart` on `HelmRelease`) — the Flux v2
/// CRD schema places the `HelmChartTemplate` sub-document (whose nested
/// `spec.chart` string names the referenced chart, `spec.sourceRef` names
/// the source-of-truth `(kind, name, namespace)` triple, and
/// `spec.interval` names the per-CR reconcile cadence) under this single
/// container key, so drift on the container axis silently dangles the
/// whole chart-template block the Flux v2 `helm-controller`'s per-CR
/// reconcile loop reads to source the referenced chart at Helm-render time
/// (a `"Chart"` / `"chartTemplate"` / `"helmChart"` / `"chartRef"` typo at
/// either the emit-side format-string template or a downstream test-
/// fixture probe silently dangles the `HelmRelease.spec.chart` chart-
/// template resolution at the Flux v2 helm-controller's CRD registration;
/// the referenced chart never resolves, and the per-Servico workload
/// freezes at apply time with no field naming the container-axis-drift
/// root cause).
///
/// The single source of truth the rendered Flux bundle's per-CR
/// chart-template-container-axis-naming reaches for:
///
/// - the rendered `helmrelease.yaml` document's per-`HelmRelease`
/// `spec.chart` block (caixa-flux/src/lib.rs — the `cluster_bundle`
/// `helmrelease` format-string template's baked `chart:\n` container
/// axis at line 914, sibling to the peer lifted [`FLUX_KEY_SOURCE_REF`]
/// source-reference container axis nested inside the same block +
/// [`FLUX_KEY_VALUES`] per-cluster-override block-body axis at the
/// sibling `spec.values` position);
/// - two test-side navigation sites in `mod tests` that probe the
/// rendered `helmrelease.yaml` document's `.get("chart")` container
/// axis to reach the nested `spec.chart.spec.sourceRef.kind` pin
/// against the sibling lifted [`FLUX_KIND_GIT_REPOSITORY`] axis
/// (caixa-flux/src/lib.rs:2680, 2774).
///
/// The container-axis key names the same Flux-v2-helm-controller-side
/// per-`HelmRelease` chart-template container as the peer sibling per-CR
/// source-reference container-axis [`FLUX_KEY_SOURCE_REF`] nests under,
/// and must move together on any future Flux v3 rebrand (a hypothetical
/// upstream Flux v3 rename of the per-`HelmRelease` chart-template
/// container axis from `chart` to `Chart` / `chartTemplate` / `helmChart`
/// / `chartRef`, coordinated with the upstream fluxcd/flux2 project's
/// per-version deprecation cycle, would land at this one const rather
/// than scattered across the one per-CR format-string template + two
/// per-test-fixture probe sites).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`FLUX_KEY_SOURCE_REF`] (e985089) /
/// [`FLUX_KEY_VALUES`] (b54dc87) lifts established on the sibling
/// canonical-Flux-v2-per-`HelmRelease`-body-key surfaces — completes the
/// triplet of Flux v2 per-`HelmRelease` `spec.*` body-key constants
/// (`spec.chart` + `spec.chart.spec.sourceRef` + `spec.values`) the
/// `cluster_bundle` renderer's `helmrelease.yaml` format-string template
/// threads through its per-CR block-body layout.
///
/// The inner scalar-value axis `spec.chart.spec.chart` (the chart-name
/// leaf the `HelmChartTemplate.spec` sub-document mounts under; the same
/// spelling `"chart"` at a distinct schema position) is a schematically
/// separate leaf-scalar-key axis (the chart-NAME field the helm-controller
/// resolves through the sibling [`FLUX_KEY_SOURCE_REF`] triple's source),
/// and is not covered by this lift — a rebrand of the container axis
/// (`spec.chart` in this const) does not necessarily coincide with a
/// rebrand of the leaf-scalar `spec.chart.spec.chart` chart-name field
/// key, so the two axes stay decoupled at the substrate.
///
/// [cf]: ../../caixa_flux/index.html
pub const FLUX_KEY_CHART: &str = "chart";
/// Canonical Flux v2 `HelmChartTemplate.spec.chart` per-CR chart-NAME-
/// reference leaf-scalar-key every `caixa-flux`-emitted `HelmRelease`
/// document nests inside the parent `spec.chart.spec` sub-document (the
/// `HelmChartTemplate.spec` block the parent [`FLUX_KEY_CHART`] (8467748)
/// container-axis key opens; a nested [`KUBE_KEY_SPEC`] axis inside that
/// container hosts this leaf plus its sibling [`FLUX_KEY_SOURCE_REF`]
/// per-CR source-reference triple).
///
/// The parent [`FLUX_KEY_CHART`] docstring explicitly names this leaf-
/// scalar axis as *not* covered by that container-axis lift ("The inner
/// scalar-value axis `spec.chart.spec.chart` … is a schematically
/// separate leaf-scalar-key axis (the chart-NAME field the helm-controller
/// resolves through the sibling [`FLUX_KEY_SOURCE_REF`] triple's
/// source), and is not covered by this lift — a rebrand of the
/// container axis … does not necessarily coincide with a rebrand of
/// the leaf-scalar `spec.chart.spec.chart` chart-name field key, so
/// the two axes stay decoupled at the substrate."). This const closes
/// the substrate-side declaration of the sibling leaf-scalar axis the
/// parent container-axis lift explicitly left as future work.
///
/// The Flux v2 `helm-controller`'s reconcile pipeline reads the chart-
/// NAME reference from this exact leaf-scalar-axis key on every
/// reconcile: the value at `HelmChartTemplate.spec.chart` names the
/// chart-artifact the sibling `HelmChartTemplate.spec.sourceRef`
/// triple's source-artifact publishes (an OCIRepository's remote OCI
/// chart archive by chart-name, a GitRepository's sub-tree path by
/// directory-name, a HelmRepository's chart index entry by chart-name).
/// A drifted `spec.chart.spec.Chart` / `spec.chart.spec.chartRef` /
/// `spec.chart.spec.chartName` at the emission-side key would silently
/// land a well-formed but ignored `HelmChartTemplate.spec.*` extra
/// property the apiserver's CRD OpenAPI schema permits (arbitrary
/// `spec.*` extras) and the helm-controller would fail to resolve any
/// chart-artifact through the sibling `sourceRef` triple's source at
/// reconcile time (the sibling `sourceRef` still resolves the *source*
/// artifact, but the chart-NAME lookup inside the source
/// short-circuits at the missing chart-NAME field with a
/// non-self-locating "chart 'unknown' not found in <source>" error far
/// from the source `caixa.lisp` / the renderer's format-string
/// template).
///
/// The single source of truth the rendered Flux bundle's per-CR
/// `HelmChartTemplate.spec.chart` chart-NAME reference leaf-scalar-
/// axis key reaches for:
///
/// - the rendered `helmrelease.yaml` document's per-`HelmChartTemplate`
/// `spec.chart` chart-NAME leaf scalar (caixa-flux/src/lib.rs:1814
/// — the `cluster_bundle` `helmrelease` format-string template's
/// lifted `chart: {chart_path}` interpolation the peer sibling
/// [`FLUX_KEY_SOURCE_REF`] source-reference triple's per-CR source-
/// artifact publishes).
///
/// The leaf-scalar-axis key names the same Flux-v2-helm-controller-
/// side per-`HelmChartTemplate` chart-NAME field every
/// `caixa-flux`-emitted `HelmRelease` document threads the chart
/// artifact name through, and must move together on any future Flux
/// v3 rebrand (a hypothetical upstream Flux v3 rename of the per-
/// `HelmChartTemplate.spec.chart` chart-NAME reference leaf-scalar-
/// axis from `chart` to `Chart` / `chartRef` / `chartName`,
/// coordinated with the upstream fluxcd/flux2 project's per-version
/// deprecation cycle, would land at this one const rather than
/// scattered across the one per-CR format-string template site).
///
/// Deliberate axis-independence discipline with the parent
/// [`FLUX_KEY_CHART`] container-axis re-export: both consts spell the
/// same underlying `"chart"` string but name distinct schema axes on
/// the same CRD group (Flux v2 `HelmRelease.spec.chart` container-
/// axis parent vs `HelmRelease.spec.chart.spec.chart` chart-NAME leaf
/// grandchild), so the two `pub const` declarations stay sibling
/// constants at the rustc symbol-name axis rather than coalescing onto
/// one canonical declaration — a future Flux v3 rebrand on the leaf-
/// scalar-axis lands independently of the sibling container-axis
/// rebrand. Peer to the deliberate [`CILIUM_KEY_PATH`] (ef6114f) /
/// [`GATEWAY_API_KEY_PATH`] (9f45aa4) axis-independence discipline the
/// two-CRD-groups-sharing-a-string sibling `"path"` re-exports
/// established on the peer canonical-axis-independence surface.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`FLUX_KEY_CHART`] (8467748) /
/// [`FLUX_KEY_SOURCE_REF`] (e985089) / [`FLUX_KEY_VALUES`] (b54dc87) /
/// [`FLUX_KEY_HEALTH_CHECKS`] (6dbff58) lifts established on the
/// sibling canonical-Flux-v2-per-`HelmRelease`-body-key surfaces —
/// completes the per-`HelmRelease` chart-template `(spec.chart →
/// spec.chart.spec.chart + spec.chart.spec.sourceRef)` axis chain by
/// declaring the leaf-scalar sibling of the container-axis parent
/// the `FLUX_KEY_CHART` lift already anchors.
///
/// [cf]: ../../caixa_flux/index.html
pub const FLUX_HELMCHART_TEMPLATE_KEY_CHART: &str = "chart";
/// Canonical Flux v2 per-`HelmRelease` values-override block-body-axis key
/// every `caixa-flux`-emitted `HelmRelease` document nests its per-cluster
/// value overrides under (`spec.values` on `HelmRelease`) — the Flux v2
/// CRD schema places the arbitrary per-cluster-override YAML body under
/// this single key, so drift on the block-body-axis silently dangles the
/// per-cluster override the `helm-controller`'s per-CR reconcile loop
/// merges into the referenced chart's `values.yaml` at Helm-render time
/// (a `"Values"` / `"vals"` / `"chartValues"` / `"overrides"` typo at
/// either the emit-side format-string template, the `upsert_into_helmrelease_programs`
/// upsert-path's `spec.values.programs[]` write, or a downstream
/// test-fixture probe silently routes the per-cluster overrides nowhere;
/// the workload silently comes up with the referenced chart's admission-
/// time defaults, far from the source `caixa.lisp` / the renderer's
/// format-string template).
///
/// The single source of truth every Flux-v2-per-`HelmRelease` values-
/// override-block-axis navigation reaches for:
///
/// - the rendered `helmrelease.yaml` document's per-`HelmRelease`
/// `spec.values` block (caixa-flux/src/lib.rs:900 — the
/// `cluster_bundle` `helmrelease` format-string template's baked
/// `values:\n` key beside the peer sibling lifted
/// [`DEFAULT_LIBRARY_NAME`] wrap key + [`HELM_VALUES_KEY_ENABLED`]
/// enable-toggle);
/// - the `upsert_into_helmrelease_programs` upsert path's
/// `spec.values.programs[]` write-side navigation
/// (caixa-flux/src/lib.rs:649 — the `lareira-fleet-programs`-
/// targeted `HelmRelease` CR's per-Servico entry-list mount);
/// - three test-side navigation sites in `mod tests` that probe the
/// rendered documents' `.get("values")` block-body axis to pin the
/// emitted per-cluster overrides against the sibling lifted
/// [`DEFAULT_LIBRARY_NAME`] wrap key + [`HELM_VALUES_KEY_ENABLED`]
/// enable-toggle + [`FLEET_PROGRAMS_KEY_PROGRAMS`] entry-list axis.
///
/// The block-body-axis key names the same Flux-v2-helm-controller-side
/// per-`HelmRelease` per-cluster-override block-body every
/// `caixa-flux`-emitted `HelmRelease` document threads its per-cluster
/// overlays through, and must move together on any future Flux v3
/// rebrand (a hypothetical upstream Flux v3 rename of the values-
/// override block-body-axis from `values` to `Values` / `chartValues`
/// / `overrides`, coordinated with the upstream fluxcd/flux2 project's
/// per-version deprecation cycle, would land at this one const rather
/// than scattered across the one emit-side format-string template + one
/// upsert-side write-side navigation + three per-test-fixture probe
/// sites).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
/// [`FLUX_KIND_HELM_RELEASE`] (e24ea3c) /
/// [`FLUX_KIND_KUSTOMIZATION`] (2d61a6f) /
/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) /
/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) /
/// [`FLUX_KEY_SOURCE_REF`] (e985089) lifts established on the sibling
/// canonical-Flux-v2-load-bearing-string surfaces — extends the per-CRD
/// kind-discriminator + apiVersion + install-namespace + source-
/// reference-container lift trajectory onto the sibling per-CR values-
/// override-block-body-axis key both `cluster_bundle` +
/// `upsert_into_helmrelease_programs` renderers consume under the
/// per-cluster override + per-Servico entry-list nesting.
///
/// [cf]: ../../caixa_flux/index.html
pub const FLUX_KEY_VALUES: &str = "values";
/// Canonical Flux v2 per-`Kustomization` health-gate reference-list
/// container-axis key every `caixa-flux`-emitted `kustomization.yaml`
/// document mounts its per-sibling-`HelmRelease` health-probe list under
/// (`spec.healthChecks` on `Kustomization`) — the Flux v2 CRD schema places
/// the `[]NamespacedObjectKindReference` list under this single container
/// key, so drift on the container axis silently dangles the whole per-
/// Kustomization health-gate the Flux v2 `kustomize-controller`'s per-CR
/// reconcile loop reads to gate `Ready=True` on the referenced sibling
/// `HelmRelease` reaching its `HelmReleaseReady=True` condition (a
/// `"HealthChecks"` / `"healthchecks"` / `"healthcheck"` /
/// `"health_checks"` / `"probes"` typo at either the emit-side format-
/// string template or a downstream test-fixture probe silently
/// dangles the parent `Kustomization` at `Reconciling` forever at the Flux
/// v2 kustomize-controller's health-gate evaluation; the dependent per-
/// cluster fleet-programs upsert chain never sees `Ready=True` at apply
/// time with no field naming the container-axis-drift root cause).
///
/// The single source of truth every Flux-v2-per-`Kustomization` health-
/// gate-reference-list-container-axis-naming reaches for:
///
/// - the rendered `kustomization.yaml` document's per-`Kustomization`
/// `spec.healthChecks` block (caixa-flux/src/lib.rs — the
/// `cluster_bundle` `kustomization` format-string template's baked
/// `healthChecks:\n` container-axis key at line 990, threaded together
/// with the sibling lifted [`FLUX_HELMRELEASE_API_VERSION`] per-entry
/// `apiVersion` axis + [`FLUX_KIND_HELM_RELEASE`] per-entry `kind`
/// axis the health-gate references);
/// - three test-side navigation sites in `mod tests` that probe the
/// rendered `kustomization.yaml` document's
/// `.get("healthChecks")` container axis to pin the emitted per-entry
/// `apiVersion` + `kind` against the sibling lifted
/// [`FLUX_HELMRELEASE_API_VERSION`] + [`FLUX_KIND_HELM_RELEASE`] axes
/// (caixa-flux/src/lib.rs:2266, 2952, 3016).
///
/// The container-axis key names the same Flux-v2-kustomize-controller-side
/// per-`Kustomization` health-gate-reference-list the sibling per-entry
/// `apiVersion` [`FLUX_HELMRELEASE_API_VERSION`] + per-entry `kind`
/// [`FLUX_KIND_HELM_RELEASE`] axes nest under, and must move together on
/// any future Flux v3 rebrand (a hypothetical upstream Flux v3 rename of
/// the per-`Kustomization` health-gate reference-list container axis from
/// `healthChecks` to `HealthChecks` / `healthchecks` / `healthcheck` /
/// `health_checks` / `probes`, coordinated with the upstream fluxcd/flux2
/// project's per-version deprecation cycle, would land at this one const
/// rather than scattered across the one emit-side format-string template +
/// three per-test-fixture probe sites).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
/// [`FLUX_KIND_HELM_RELEASE`] (e24ea3c) /
/// [`FLUX_KIND_KUSTOMIZATION`] (2d61a6f) /
/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) /
/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) /
/// [`FLUX_KEY_SOURCE_REF`] (e985089) /
/// [`FLUX_KEY_CHART`] (8467748) /
/// [`FLUX_KEY_VALUES`] (b54dc87) lifts established on the sibling
/// canonical-Flux-v2-load-bearing-string surfaces — extends the per-CRD
/// kind-discriminator + apiVersion + install-namespace + source-
/// reference-container + chart-template-container + values-override-block
/// lift trajectory onto the sibling per-`Kustomization` health-gate-
/// reference-list container-axis key the `cluster_bundle` renderer
/// consumes under its `kustomization.yaml` format-string template.
///
/// [cf]: ../../caixa_flux/index.html
pub const FLUX_KEY_HEALTH_CHECKS: &str = "healthChecks";
/// Canonical Flux v2 per-CR reconcile-poll cadence scalar-axis key every
/// `caixa-flux`-emitted Flux document (`GitRepository`, `HelmRelease`,
/// `Kustomization`) declares its per-CR `spec.interval` reconcile cadence
/// under. Unlike the sibling per-CR body-key axes ([`FLUX_KEY_SOURCE_REF`],
/// [`FLUX_KEY_CHART`], [`FLUX_KEY_VALUES`], [`FLUX_KEY_HEALTH_CHECKS`])
/// which each land on exactly one of the three Flux v2 controller CRDs,
/// the reconcile-poll cadence scalar-axis is the *shared* Flux v2 per-CR
/// contract every controller (the `source-controller`, the
/// `helm-controller`, the `kustomize-controller`) reads to schedule its
/// per-CR reconcile loop off the sibling per-CR CRD registration. Drift on
/// the scalar-axis key silently drops the per-CR reconcile schedule from
/// the Flux v2 controllers' per-CR watch registrations — a `"Interval"` /
/// `"period"` / `"cadence"` / `"pollInterval"` / `"reconcileInterval"`
/// typo at any of the three emit-side format-string template sites
/// silently drops the per-CR reconcile schedule from the affected Flux v2
/// controller's per-CR watch registration; the referenced Git source
/// never re-polls / the referenced chart never re-templates / the parent
/// Kustomization never re-applies at upstream drift, freezing the whole
/// cluster's per-`caixa` per-cluster bundle at the last-applied snapshot
/// with no field naming the scalar-axis-drift root cause.
///
/// The single source of truth every Flux-v2-per-CR-reconcile-poll-cadence-
/// scalar-axis-naming reaches for — the three per-CR emit sites the
/// [`cluster_bundle`][cf] renderer threads through are all named through
/// this one const:
///
/// - the rendered `gitrepository.yaml` document's per-`GitRepository`
/// `spec.interval` scalar (caixa-flux/src/lib.rs — the `cluster_bundle`
/// `gitrepo` format-string template's baked `interval:` scalar-axis
/// key, nested alongside the sibling lifted
/// [`FLUX_GITREPOSITORY_API_VERSION`] top-level `apiVersion` +
/// [`FLUX_KIND_GIT_REPOSITORY`] top-level `kind` axes the source-
/// controller reads to bind the per-CR poll cycle);
/// - the rendered `helmrelease.yaml` document's per-`HelmRelease`
/// `spec.interval` scalar (caixa-flux/src/lib.rs — the `cluster_bundle`
/// `helmrelease` format-string template's baked `interval:` scalar-
/// axis key, nested alongside the sibling lifted
/// [`FLUX_HELMRELEASE_API_VERSION`] top-level `apiVersion` +
/// [`FLUX_KIND_HELM_RELEASE`] top-level `kind` axes the helm-controller
/// reads to bind the per-CR poll cycle);
/// - the rendered `kustomization.yaml` document's per-`Kustomization`
/// `spec.interval` scalar (caixa-flux/src/lib.rs — the `cluster_bundle`
/// `kustomization` format-string template's baked `interval:` scalar-
/// axis key, nested alongside the sibling lifted
/// [`FLUX_KUSTOMIZATION_API_VERSION`] top-level `apiVersion` +
/// [`FLUX_KIND_KUSTOMIZATION`] top-level `kind` axes the kustomize-
/// controller reads to bind the per-CR poll cycle).
///
/// The three sites must move together on any future Flux v3 rebrand (a
/// hypothetical upstream fluxcd/flux2 rename from `interval` to `Interval`
/// / `period` / `cadence` / `pollInterval` / `reconcileInterval`,
/// coordinated with the upstream project's per-version deprecation cycle,
/// would land at this one const rather than scattered across the three
/// per-CR emit-side format-string template sites). This is a distinct
/// duplication shape from the sibling [`FLUX_KEY_SOURCE_REF`] /
/// [`FLUX_KEY_CHART`] / [`FLUX_KEY_VALUES`] / [`FLUX_KEY_HEALTH_CHECKS`]
/// lifts: those closed *one-CR-body-key* duplication trios (one emit-site
/// per CR + several test-side probes); this one closes the sibling
/// *three-CR-shared-body-key* triplet the Flux v2 reconcile-poll cadence
/// contract shares across all three per-cluster-bundle CRDs.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`FLUX_KEY_SOURCE_REF`] (e985089) /
/// [`FLUX_KEY_CHART`] (8467748) /
/// [`FLUX_KEY_VALUES`] (b54dc87) /
/// [`FLUX_KEY_HEALTH_CHECKS`] (6dbff58) lifts established on the sibling
/// canonical-Flux-v2-per-CR-body-key surfaces — extends the per-CR
/// body-key lift trajectory onto the sibling *cross-CR-shared* reconcile-
/// poll cadence scalar-axis every Flux v2 controller reads to bind its
/// per-CR poll cycle.
///
/// [cf]: ../../caixa_flux/fn.cluster_bundle.html
pub const FLUX_KEY_INTERVAL: &str = "interval";
/// Canonical Flux v2 per-`GitRepository` `spec.ref.tag` git-tag-selector
/// scalar-axis key every `caixa-flux`-emitted `gitrepository.yaml`
/// document declares when the per-Servico bundle's `git_ref` is a
/// tag-shaped selector. Peer of [`FLUX_GITREPOSITORY_REF_KEY_BRANCH`]
/// / [`FLUX_GITREPOSITORY_REF_KEY_COMMIT`] on the sibling per-shape
/// arms of the `FluxCD` source-controller `GitRepository.spec.ref`
/// ref-selection discriminated-union axis — the three-way sub-selector
/// key set the Flux v2 `source-controller` reads to bind the per-CR
/// git-source clone `refspec` from the (tag | branch | commit) input
/// triple. A drifted value at any of the three keys (`"Tag"` /
/// `"gitTag"` / `"tagName"` at this arm, `"Branch"` / `"gitBranch"`
/// at the sibling arm, `"Commit"` / `"sha"` / `"revision"` at the
/// third arm) silently dangles the whole `spec.ref` sub-block at the
/// `FluxCD` `source-controller`'s CRD registration; the per-Servico
/// clone never resolves at reconcile time and the sibling
/// `HelmRelease.spec.chart.spec.sourceRef` reference dangles at
/// admission with no field naming the sub-selector-key-drift root
/// cause. Changing this value is a coordinated Flux v3 migration
/// alongside the upstream `fluxcd/flux2` deprecation cycle, not an
/// incidental edit.
///
/// The single source of truth every Flux-v2-per-`GitRepository`-
/// `spec.ref`-tag-arm-axis-naming reaches for — the two per-render
/// consumer sites the [`crate::render`]-side lift closes on the
/// [`caixa_flux::GitRefSpec::Tag`] variant are both named through this
/// one const via the [`caixa_flux::GitRefSpec::ref_field_name`]
/// dispatch:
///
/// - the rendered `gitrepository.yaml` document's per-`GitRepository`
/// `spec.ref.tag` YAML sub-field (caixa-flux's `cluster_bundle`
/// `gitref_field` composer, the sole in-tree emission site);
/// - the sibling per-render human-readable narrator's `tag <value>`
/// prefix (caixa-flux's `cluster_bundle` `tag_human` composer's
/// tag-arm branch), the operator-facing per-arm narrator prose
/// `feira app graph` / `feira deploy` diagnostics quote.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5) —
/// promotes the sub-selector-key byte-string to a typed substrate-side
/// `&'static str` on the same trajectory the peer per-CR body-key
/// [`FLUX_KEY_SOURCE_REF`] (e985089) / [`FLUX_KEY_CHART`] (8467748) /
/// [`FLUX_KEY_VALUES`] (b54dc87) / [`FLUX_KEY_HEALTH_CHECKS`] (6dbff58)
/// / [`FLUX_KEY_INTERVAL`] (48db6e2) lifts established on the sibling
/// canonical-Flux-v2-per-CR-body-key surfaces — pivots the discipline
/// from the per-CR body-key axis onto the sibling per-`GitRepository`-
/// `spec.ref`-sub-selector-key axis every `cluster_bundle`-rendered
/// bundle threads its per-shape ref-selection through, and closes the
/// coordinated 2-site duplication (`gitref_field` YAML emit +
/// `tag_human` narrator prose) the prior inline `format!(" tag:
/// {t:?}")` + `format!("tag {t}")` literals in
/// caixa-flux/src/lib.rs carried on the tag-arm of the discriminated-
/// union.
///
/// [cf]: ../../caixa_flux/index.html
pub const FLUX_GITREPOSITORY_REF_KEY_TAG: &str = "tag";
/// Canonical Flux v2 per-`GitRepository` `spec.ref.branch`
/// git-branch-selector scalar-axis key every `caixa-flux`-emitted
/// `gitrepository.yaml` document declares when the per-Servico
/// bundle's `git_ref` is a branch-shaped selector. Peer of
/// [`FLUX_GITREPOSITORY_REF_KEY_TAG`] /
/// [`FLUX_GITREPOSITORY_REF_KEY_COMMIT`] on the sibling per-shape arms
/// of the `FluxCD` source-controller `GitRepository.spec.ref`
/// ref-selection discriminated-union axis; see
/// [`FLUX_GITREPOSITORY_REF_KEY_TAG`] for the full lift rationale.
///
/// [cf]: ../../caixa_flux/index.html
pub const FLUX_GITREPOSITORY_REF_KEY_BRANCH: &str = "branch";
/// Canonical Flux v2 per-`GitRepository` `spec.ref.commit`
/// git-commit-selector scalar-axis key every `caixa-flux`-emitted
/// `gitrepository.yaml` document declares when the per-Servico
/// bundle's `git_ref` is a commit-shaped selector. Peer of
/// [`FLUX_GITREPOSITORY_REF_KEY_TAG`] /
/// [`FLUX_GITREPOSITORY_REF_KEY_BRANCH`] on the sibling per-shape arms
/// of the `FluxCD` source-controller `GitRepository.spec.ref`
/// ref-selection discriminated-union axis; see
/// [`FLUX_GITREPOSITORY_REF_KEY_TAG`] for the full lift rationale.
///
/// [cf]: ../../caixa_flux/index.html
pub const FLUX_GITREPOSITORY_REF_KEY_COMMIT: &str = "commit";
/// Canonical Flux v2 per-`GitRepository` `spec.ref` ref-selection
/// discriminated-union parent container-axis key every `caixa-flux`-
/// emitted `gitrepository.yaml` document mounts its per-shape
/// `{tag, branch, commit}` sub-selector arm under. Nests one level
/// above the sibling [`FLUX_GITREPOSITORY_REF_KEY_TAG`] /
/// [`FLUX_GITREPOSITORY_REF_KEY_BRANCH`] /
/// [`FLUX_GITREPOSITORY_REF_KEY_COMMIT`] triple it wraps — the K8s
/// Flux v2 `source.toolkit.fluxcd.io/v1` `GitRepository` CRD schema
/// pins the per-CR ref-selection through this `spec.ref` container-
/// axis, and every rendered `spec.ref.{tag,branch,commit}` arm the
/// [`caixa_flux::GitRefSpec`] discriminated-union emits nests
/// beneath this exact key.
///
/// The FluxCD `source-controller`'s per-CR `RESTMapper` reads
/// `spec.ref` to source the per-Servico git clone refspec (the
/// container-axis carrying the three-way `{tag, branch, commit}`
/// arm the controller dispatches on), so drift on the container-
/// axis KEY is exactly as load-bearing as drift on the sibling per-
/// shape sub-selector KEY the arms decode through: a `"Ref"` /
/// `"gitRef"` / `"revision"` / `"source"` typo at the writer site
/// silently emits a `GitRepository` whose ref-selection container-
/// axis the CRD schema validator drops as unknown, and the sibling
/// `HelmRelease.spec.chart.spec.sourceRef` reference dangles at
/// admission with the per-Servico clone never resolving at reconcile
/// time — apply-side: the Flux v2 `source-controller`'s per-CR
/// reconcile loop no-ops entirely (no clone, no artifact, no
/// checksum), the sibling `HelmRelease`'s per-chart resolve step
/// finds the empty artifact, and every rendered `HelmRelease` /
/// `Kustomization` bundle document downstream of this `GitRepository`
/// silently no-ops at the FluxCD apply chain with no field naming
/// the container-axis-drift root cause.
///
/// The single source of truth every Flux-v2-per-`GitRepository`-
/// `spec.ref`-container-axis-naming reaches for — the two per-render
/// consumer sites the [`crate::render`]-side lift closes:
///
/// - the rendered `gitrepository.yaml` document's per-`GitRepository`
/// `spec.ref` YAML block-body axis (caixa-flux's `cluster_bundle`
/// `gitrepo` template composer's `ref:` sub-block header — the
/// sole production emission site the prior inline `"ref:"`
/// literal sat at);
/// - the peer test-fixture navigation site
/// (caixa-flux's `cluster_bundle_gitrepository_ref_*` per-arm
/// round-trip pin's `.get("ref")` sub-selector traversal step —
/// the sole test-side reader site the prior inline `"ref"`
/// literal sat at).
///
/// Changing this value is a coordinated Flux v3 migration alongside
/// the upstream `fluxcd/flux2` deprecation cycle, not an incidental
/// edit — pinning it here means the migration lands as one edit at
/// the const plus a re-run of the pin tests rather than a per-
/// renderer sweep with no single source of truth to consult.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5)
/// promotes the parent-container-axis byte-string to a typed
/// substrate-side `&'static str` on the same trajectory the sibling
/// per-shape arm sub-selector-key
/// [`FLUX_GITREPOSITORY_REF_KEY_TAG`] (7d40380) /
/// [`FLUX_GITREPOSITORY_REF_KEY_BRANCH`] (7d40380) /
/// [`FLUX_GITREPOSITORY_REF_KEY_COMMIT`] (7d40380) triple lifts
/// established on the sibling per-shape arm surface — nests the
/// parent container-axis KEY above the already-lifted per-shape arm
/// sub-selector-KEY triple, so the whole per-`GitRepository`
/// `spec.ref` sub-schema (parent container-axis KEY + per-shape arm
/// sub-selector-KEY triple + per-arm value) now navigates through
/// four caixa-core `&'static str`s in coordination, and any future
/// Flux v2 sub-schema rebrand (an upstream `fluxcd/flux2` v3
/// rename of the ref-selection container-axis from `spec.ref` to
/// `spec.gitRef` / `spec.source.ref`) lands at one const edit
/// coordinated with the sibling per-shape arm lifts.
///
/// [cf]: ../../caixa_flux/index.html
pub const FLUX_GITREPOSITORY_KEY_REF: &str = "ref";
/// Canonical Flux v2 `GitRepository.spec.url` per-CR remote-repo-URL
/// leaf-scalar-axis key every [`caixa-flux`][cf]-rendered
/// `gitrepository.yaml` document declares. The FluxCD `source-controller`
/// reads `spec.url` as the git remote URL it clones per-reconcile — the
/// authoritative remote the per-Servico artifact archive is sourced from
/// at every reconcile cycle. A drifted key (e.g. `"URL"`, `"gitUrl"`,
/// `"repo"`, `"repository"`) at the writer site would silently emit a
/// `GitRepository` whose CRD schema validator drops the URL field as
/// unknown, and the per-Servico artifact would never populate — the
/// downstream `HelmRelease.spec.chart.spec.sourceRef` reference dangles
/// with an empty artifact at admission, every rendered `HelmRelease` /
/// `Kustomization` bundle document downstream silently no-ops at
/// reconcile time with no field naming the URL-key-drift root cause.
///
/// Sibling to the already-lifted per-`GitRepository`-CR `spec` sub-
/// block keys [`FLUX_GITREPOSITORY_KEY_REF`] (84a3c20, the parent
/// container-axis for the `spec.ref.{tag,branch,commit}` per-shape arm
/// discriminated union) — this constant names the peer per-CR leaf-
/// scalar remote-URL axis on the same top-level `spec` position. Both
/// axes together completely enumerate the `GitRepository.spec.*` per-
/// CR sub-block keys `caixa-flux`'s current `cluster_bundle` gitrepo
/// template writes (`spec.interval` reaches through the lifted
/// `FLUX_KEY_INTERVAL`, `spec.url` through this constant, `spec.ref`
/// through [`FLUX_GITREPOSITORY_KEY_REF`]), so any future Flux v3
/// `GitRepository` schema promotion lands as one caixa-core edit
/// coordinated across the sibling sub-block key axes.
///
/// The single source of truth every Flux-v2-per-`GitRepository`-
/// `spec.url`-leaf-scalar-axis-naming reaches for — one production
/// consumer today:
///
/// - the rendered `gitrepository.yaml` document's per-`GitRepository`
/// `spec.url` leaf-scalar remote-URL axis (caixa-flux's
/// `cluster_bundle` `gitrepo` template composer's `url:` sub-key
/// — the sole production emission site the prior inline `"url:"`
/// literal sat at).
///
/// Every future per-`GitRepository` renderer (the M4 typed-Aplicacao
/// materializer's per-Aplicacao `GitRepository` synthesis for
/// per-aggregator-manifest sources, any future `caixa-otel`
/// collector-pipeline `GitRepository`, any future per-cluster snapshot
/// `GitRepository` the operator emits) inherits the canonical URL
/// leaf-scalar key by construction with no opportunity for
/// per-renderer drift.
///
/// [cf]: ../../caixa_flux/index.html
pub const FLUX_GITREPOSITORY_KEY_URL: &str = "url";
/// Canonical Flux v2 per-cluster-bundle `HelmRelease` document
/// filename every [`caixa-flux`][cf]-rendered `cluster_bundle` carries
/// at the per-Servico bundle's rendered file collection — the fixed
/// filename the sibling `gitrepository.yaml` + `kustomization.yaml`
/// bundle documents key against when the cluster-side `FluxCD`
/// controllers reconcile the per-Servico release cycle, and the
/// exact filename every downstream consumer that reaches into the
/// rendered bundle by document name looks up.
///
/// Two production consumers reach for this filename:
///
/// - [`caixa-flux`][cf]'s [`cluster_bundle`][cb] `BundleFile`
/// assembly's per-file `path` axis for the `HelmRelease`
/// document — the sole caixa-flux production emit site the prior
/// inline `PathBuf::from("helmrelease.yaml")` literal sat at,
/// one of the three canonical per-Servico Flux bundle files the
/// renderer emits alongside the sibling `gitrepository.yaml` +
/// `kustomization.yaml` documents;
/// - the peer test-fixture navigators in this crate reach into the
/// rendered `BundleFile` collection by the same filename to
/// round-trip-pin each emitted `HelmRelease` axis — a dozen
/// `.find(|f| f.path == PathBuf::from("helmrelease.yaml"))` +
/// `names.contains(&"helmrelease.yaml".to_string())` fixture
/// navigators across every per-CR body-axis sweep, `apiVersion`
/// round-trip, `spec.chart` / `spec.values` / `spec.sourceRef`
/// nested block existence pin.
///
/// Until this lift landed the filename `"helmrelease.yaml"` lived as
/// thirteen verbatim inline literals (one production
/// `PathBuf::from("helmrelease.yaml")` at the `cluster_bundle`
/// `BundleFile`-vec construction site + twelve test-side
/// `PathBuf::from("helmrelease.yaml")` /
/// `names.contains(&"helmrelease.yaml".to_string())` /
/// `.expect("helmrelease.yaml present")` fixture navigators). A drift
/// on the emit side (a `"HelmRelease.yaml"` / `"helm-release.yaml"` /
/// `"helmrelease.yml"` / `"helm_release.yaml"` typo, or an accidental
/// per-fork rebrand onto a stale filename any per-edition Flux
/// substrate might introduce) at any one site would surface as one of
/// two silent failure modes at cluster-side reconcile time:
///
/// - the `FluxCD` `kustomize-controller` refuses to apply the
/// rendered bundle at all — the per-Servico
/// `Kustomization.spec.path` opens the bundle directory and its
/// `HelmRelease` navigator returns `None`, with the reconcile
/// dropping at "no `HelmRelease` document found under this
/// bundle" far from the emit-drift commit's source, and the
/// per-Servico release cycle drops with no field naming the
/// bundle-filename-drift root cause (the operator sees "the
/// release never picks up its Helm chart" with no canonical
/// anchor to compare the rendered filename against);
/// - the sibling `Kustomization` document's per-CR
/// `spec.healthChecks[]` references the drifted filename via
/// `namespace/name` — the healthCheck stays perpetually `Unknown`
/// because the referenced `HelmRelease` never materializes at the
/// expected bundle path, and the peer `GitRepository` document's
/// every-poll reconcile ticks the bundle-tree hash over the
/// drifted filename with the per-Servico release cycle silently
/// frozen at "waiting on healthCheck".
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// filename to a typed substrate-side `&'static str` on the same
/// trajectory the peer [`HELM_CHART_YAML_FILENAME`] (c2c99b0) /
/// [`HELM_VALUES_YAML_FILENAME`] (9a980ba) lifts established on the
/// sibling Helm-chart-directory filename axes — pivots the
/// canonical-filename single-sourcing discipline from the per-Helm-
/// chart-directory metadata / values file surfaces onto the sibling
/// per-Flux-v2-bundle `HelmRelease` document filename axis every
/// rendered per-Servico bundle declares at its cluster-side reconcile
/// tree. Peer of a future sibling lift on the other two per-Servico
/// Flux bundle document filenames (`gitrepository.yaml` +
/// `kustomization.yaml`) — this const anchors the first coordinate
/// of the per-bundle
/// `(gitrepository, helmrelease, kustomization)` filename axis triple
/// every rendered cluster bundle carries.
///
/// [cf]: ../../caixa_flux/index.html
/// [cb]: ../../caixa_flux/fn.cluster_bundle.html
pub const FLUX_HELMRELEASE_YAML_FILENAME: &str = "helmrelease.yaml";
/// Canonical Flux v2 per-cluster-bundle `GitRepository` document
/// filename every [`caixa-flux`][cf]-rendered [`cluster_bundle`][cb]
/// carries at the per-Servico bundle's rendered file collection — the
/// fixed filename the sibling `helmrelease.yaml` +
/// `kustomization.yaml` documents key against when the cluster-side
/// `FluxCD` `source-controller` reconciles the per-Servico Git-source
/// poll cycle, and the exact filename every downstream consumer that
/// reaches into the rendered bundle by document name looks up.
///
/// Two production consumers reach for this filename:
///
/// - [`caixa-flux`][cf]'s [`cluster_bundle`][cb] `BundleFile`
/// assembly's per-file `path` axis for the `GitRepository`
/// document — the sole caixa-flux production emit site the prior
/// inline `PathBuf::from("gitrepository.yaml")` literal sat at,
/// one of the three canonical per-Servico Flux bundle files the
/// renderer emits alongside the sibling `helmrelease.yaml` +
/// `kustomization.yaml` documents (the second coordinate of the
/// per-bundle `(gitrepository, helmrelease, kustomization)`
/// filename axis triple this const closes);
/// - the peer test-fixture navigators in this crate reach into the
/// rendered `BundleFile` collection by the same filename to
/// round-trip-pin each emitted `GitRepository` axis — every
/// `.find(|f| f.path == PathBuf::from("gitrepository.yaml"))` +
/// `names.contains(&"gitrepository.yaml".to_string())` fixture
/// navigator across the per-CR body-axis sweeps that pin the
/// Git-source apiVersion / kind / `spec.url` / `spec.ref`
/// round-trips.
///
/// Until this lift landed the filename `"gitrepository.yaml"` lived
/// as nine verbatim inline literals across [`caixa-flux`][cf] (one
/// production `PathBuf::from("gitrepository.yaml")` at the
/// `cluster_bundle` `BundleFile`-vec construction site + eight
/// test-side fixture navigators). A drift on the emit side (a
/// `"GitRepository.yaml"` / `"git-repository.yaml"` /
/// `"gitrepository.yml"` typo, or an accidental per-fork rebrand)
/// would surface at cluster-side reconcile time far from the source:
/// the `FluxCD` `source-controller` never registers a `GitRepository`
/// document under the expected bundle path, the sibling
/// `HelmRelease.spec.chart.spec.sourceRef` reference dangles at
/// admission, and the per-Servico release cycle silently freezes at
/// last-applied state with no field naming the filename-drift root
/// cause.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// filename to a typed substrate-side `&'static str` on the same
/// trajectory the peer [`FLUX_HELMRELEASE_YAML_FILENAME`] (ba7b0b2) /
/// [`HELM_CHART_YAML_FILENAME`] (c2c99b0) /
/// [`HELM_VALUES_YAML_FILENAME`] (9a980ba) lifts established on the
/// sibling per-Flux-v2-bundle / per-Helm-chart-directory filename
/// axes — pairs with the sibling
/// [`FLUX_KUSTOMIZATION_YAML_FILENAME`] on the third coordinate to
/// close the per-bundle `(gitrepository, helmrelease, kustomization)`
/// filename axis triple every rendered cluster bundle carries.
///
/// [cf]: ../../caixa_flux/index.html
/// [cb]: ../../caixa_flux/fn.cluster_bundle.html
pub const FLUX_GITREPOSITORY_YAML_FILENAME: &str = "gitrepository.yaml";
/// Canonical Flux v2 per-cluster-bundle `Kustomization` document
/// filename every [`caixa-flux`][cf]-rendered [`cluster_bundle`][cb]
/// carries at the per-Servico bundle's rendered file collection — the
/// fixed filename the sibling `gitrepository.yaml` +
/// `helmrelease.yaml` documents key against when the cluster-side
/// `FluxCD` `kustomize-controller` reconciles the per-Servico apply
/// cycle, and the exact filename every downstream consumer that
/// reaches into the rendered bundle by document name looks up.
///
/// Two production consumers reach for this filename:
///
/// - [`caixa-flux`][cf]'s [`cluster_bundle`][cb] `BundleFile`
/// assembly's per-file `path` axis for the `Kustomization`
/// document — the sole caixa-flux production emit site the prior
/// inline `PathBuf::from("kustomization.yaml")` literal sat at,
/// one of the three canonical per-Servico Flux bundle files the
/// renderer emits alongside the sibling `gitrepository.yaml` +
/// `helmrelease.yaml` documents (the third coordinate of the
/// per-bundle `(gitrepository, helmrelease, kustomization)`
/// filename axis triple this const closes);
/// - the peer test-fixture navigators in this crate reach into the
/// rendered `BundleFile` collection by the same filename to
/// round-trip-pin each emitted `Kustomization` axis — every
/// `.find(|f| f.path == PathBuf::from("kustomization.yaml"))` +
/// `names.contains(&"kustomization.yaml".to_string())` fixture
/// navigator across the per-CR body-axis sweeps that pin the
/// Kustomization apiVersion / kind / `spec.sourceRef` /
/// `spec.healthChecks` round-trips.
///
/// Until this lift landed the filename `"kustomization.yaml"` lived
/// as sixteen verbatim inline literals across [`caixa-flux`][cf]
/// (one production `PathBuf::from("kustomization.yaml")` at the
/// `cluster_bundle` `BundleFile`-vec construction site + fifteen
/// test-side fixture navigators). A drift on the emit side (a
/// `"Kustomization.yaml"` / `"kustomize.yaml"` / `"kustomization.yml"`
/// typo, or an accidental per-fork rebrand) would surface at
/// cluster-side reconcile time far from the source: the `FluxCD`
/// `kustomize-controller` never picks up the parent `Kustomization`
/// under the expected bundle path, every per-Servico apply silently
/// stops advancing at last-applied state, and the sibling
/// `HelmRelease` / `GitRepository` reconciles register with no
/// parent Kustomization gating their health, with no field naming
/// the filename-drift root cause.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// filename to a typed substrate-side `&'static str` on the same
/// trajectory the peer [`FLUX_HELMRELEASE_YAML_FILENAME`] (ba7b0b2) /
/// [`FLUX_GITREPOSITORY_YAML_FILENAME`] (this commit) /
/// [`HELM_CHART_YAML_FILENAME`] (c2c99b0) /
/// [`HELM_VALUES_YAML_FILENAME`] (9a980ba) lifts established on the
/// sibling per-Flux-v2-bundle / per-Helm-chart-directory filename
/// axes — closes the per-bundle `(gitrepository, helmrelease,
/// kustomization)` filename axis triple every rendered cluster
/// bundle carries.
///
/// [cf]: ../../caixa_flux/index.html
/// [cb]: ../../caixa_flux/fn.cluster_bundle.html
pub const FLUX_KUSTOMIZATION_YAML_FILENAME: &str = "kustomization.yaml";
/// Canonical K8s Gateway API CRD `apiVersion` every `caixa-mesh`-emitted
/// `Gateway` / `HTTPRoute` document declares. The K8s apiserver-side
/// SIG-Network Gateway API conformance registers the `Gateway` /
/// `HTTPRoute` / `GatewayClass` / `TCPRoute` / `TLSRoute` / `GRPCRoute`
/// CRDs at this exact group/version (`gateway.networking.k8s.io/v1`);
/// drift to a stale `v1beta1` / `v1alpha2` (the pre-GA Gateway API betas
/// every upstream conformance doc names) silently routes the rendered
/// `Gateway` / `HTTPRoute` outside the apiserver's CRD-version
/// registration and breaks at apply time with a non-self-locating "no
/// kind 'Gateway' is registered for version
/// 'gateway.networking.k8s.io/v1beta1'" error far from the source
/// caixa.lisp / the renderer's [`kube_resource_skeleton`] call site.
///
/// The single source of truth both Gateway-API CRD axes of the rendered
/// Aplicacao mesh bundle reach for:
///
/// - `Gateway` `apiVersion` — the top-level CRD-group/version the
/// rendered Gateway document declares (caixa-mesh/src/lib.rs:455 —
/// the `gateway_routes` per-Aplicacao Gateway skeleton call);
/// - `HTTPRoute` `apiVersion` — the same Gateway API CRD
/// group/version every per-`:entrada :paths` HTTPRoute declares
/// (caixa-mesh/src/lib.rs:496 — the `gateway_routes` HTTPRoute
/// skeleton call). The K8s SIG-Network Gateway API contract bumps
/// `Gateway`, `HTTPRoute`, `GatewayClass`, and the rest of the
/// per-conformance CRD set as a unit; a future Gateway-API GA
/// promotion (the upstream Gateway API SIG roadmap names per-CRD-
/// group / per-version migration once the v1 GA branch matures) on
/// one axis without a coordinated edit on the other would have
/// silently emitted a `Gateway` / `HTTPRoute` pair pointing at
/// distinct CRD versions — apply-side: the `Gateway` and
/// `HTTPRoute` land in two distinct apiserver-side CRD
/// registrations, the per-route attached-policy resolution
/// pipeline never binds, every external `:entrada` flow drops at
/// the gateway with no field naming the version-drift root cause.
///
/// Until this lift landed both axes carried inline
/// `gateway.networking.k8s.io/v1` literals across two production-code
/// occurrences in caixa-mesh/src/lib.rs:455, 496 (the `gateway_routes`
/// `Gateway` + `HTTPRoute` skeleton calls) plus a matching pair inside
/// the in-file `gateway_carries_canonical_kube_skeleton_without_labels`
/// + `httproute_carries_canonical_kube_skeleton_without_labels` test
/// fixtures — four occurrences of the same load-bearing Gateway API
/// CRD-group/version convention, drift-prone by construction.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) /
/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) lifts established on
/// the peer Flux-v2-controller-triplet canonical-load-bearing-string
/// axis — extends the discipline from the cluster-side Flux v2
/// reconcile contract (the source/helm/kustomize controllers) onto
/// the cluster-side K8s Gateway API ingress contract (the
/// Gateway-API-conformant gateway implementation: Cilium, Istio,
/// Envoy Gateway, NGINX, et al.). The two render-side consumers now
/// thread the same `&'static str` through their `kube_resource_skeleton`
/// calls so a future Gateway API CRD-group/version promotion lands in
/// one place; every future renderer that reaches for the canonical
/// Gateway API CRD apiVersion (the future M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
/// Gateway + HTTPRoute, a future per-edge `TCPRoute` / `TLSRoute` /
/// `GRPCRoute` the caixa-mesh emits for non-HTTP `:entrada` edges,
/// a future `GatewayClass` the operator emits for per-cluster
/// gateway-class scoping) inherits the same value by construction
/// with no opportunity for per-renderer drift.
///
/// [cm]: ../../caixa_mesh/index.html
pub const GATEWAY_API_API_VERSION: &str = "gateway.networking.k8s.io/v1";
/// Canonical Cilium CRD `apiVersion` every `caixa-mesh`-emitted
/// `CiliumNetworkPolicy` document declares. The Cilium control plane's
/// upstream-shipped CRD bundle registers `CiliumNetworkPolicy`,
/// `CiliumClusterwideNetworkPolicy`, `CiliumEndpoint`, `CiliumIdentity`,
/// `CiliumNode`, `CiliumLocalRedirectPolicy`, and the rest of the
/// per-conformance Cilium CRD set at this exact group/version
/// (`cilium.io/v2`); drift to a stale `v2alpha1` (the historical
/// pre-stable Cilium-CRD-group/version label upstream Cilium-CRD docs
/// reference for in-flight per-CRD-version migration) silently routes
/// the rendered `CiliumNetworkPolicy` outside the cluster's
/// Cilium-operator-side CRD-version registration and breaks at apply
/// time with a non-self-locating "no kind 'CiliumNetworkPolicy' is
/// registered for version 'cilium.io/v2alpha1'" error far from the
/// source caixa.lisp / the renderer's [`kube_resource_skeleton`] call
/// site.
///
/// The single source of truth the rendered Aplicacao Cilium-side
/// mesh bundle's CRD-group/version axis reaches for:
///
/// - `CiliumNetworkPolicy` `apiVersion` — the top-level CRD-group/
/// version every emitted CNP document declares
/// (caixa-mesh/src/lib.rs:326 — the `cilium_network_policies`
/// per-`(:de, :para)` policy skeleton call). Until this lift
/// landed both the production-code emit at the per-policy
/// skeleton call site and the matching in-file
/// `cilium_policy_carries_canonical_kube_skeleton` test fixture
/// pin (caixa-mesh/src/lib.rs:1560) carried inline `"cilium.io/v2"`
/// string literals — two occurrences of the same load-bearing
/// Cilium-CRD-group/version convention, drift-prone by
/// construction. The Cilium project bumps the per-conformance
/// Cilium-CRD set as a unit; a future Cilium-CRD-group/version
/// promotion (the upstream Cilium roadmap names per-CRD-group /
/// per-version migration once the `cilium.io/v3` branch lands) on
/// one axis without a coordinated edit on the other would have
/// silently emitted a `CiliumNetworkPolicy` document whose
/// top-level apiVersion drifts off the lifted-test-fixture pin —
/// apply-side: the policy lands in a stale CRD-version
/// registration the Cilium operator no longer watches, every
/// `(:de, :para)` intra-mesh L4 contract drops at the eBPF data
/// plane with no field naming the version-drift root cause.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`GATEWAY_API_API_VERSION`] (3c6cfc3) /
/// [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
/// [`FLUX_GITREPOSITORY_API_VERSION`] (8a6c8a3) /
/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) /
/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) lifts established on
/// the peer K8s Gateway API ingress / Flux v2 reconcile canonical-
/// load-bearing-string axes — extends the discipline from the
/// cluster-side K8s Gateway API ingress + Flux v2 reconcile contracts
/// onto the cluster-side Cilium identity-based mesh contract (the
/// eBPF-anchored Cilium control plane that materializes every
/// per-`(:de, :para)` L4 / L7 contrato as an identity-keyed eBPF
/// allow rule). The render-side consumer now threads the same
/// `&'static str` through its `kube_resource_skeleton` call so a
/// future Cilium-CRD-group/version promotion lands in one place;
/// every future renderer that reaches for the canonical
/// Cilium-CRD apiVersion (the future M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
/// per-Aplicacao CiliumNetworkPolicy fan-out, a future
/// `CiliumClusterwideNetworkPolicy` the caixa-mesh emits for
/// cluster-scoped baseline-allow / baseline-deny rules, a future
/// `CiliumLocalRedirectPolicy` the operator emits for per-Servico
/// local-redirect coordination) inherits the same value by
/// construction with no opportunity for per-renderer drift.
///
/// [cm]: ../../caixa_mesh/index.html
pub const CILIUM_API_VERSION: &str = "cilium.io/v2";
/// Canonical Cilium CRD `kind` discriminator the rendered
/// `CiliumNetworkPolicy` document declares at its top-level
/// [`KUBE_KEY_KIND`] axis. Pairs with the sibling [`CILIUM_API_VERSION`]
/// (279d611) — the K8s apiserver-side CRD resolution contract is the
/// `(apiVersion, kind)` tuple keyed against the registered
/// `CustomResourceDefinition`, so drift on the kind axis is exactly as
/// load-bearing as drift on the apiVersion axis it accompanies (the
/// apiserver's `RESTMapper` consults both together; a
/// `("cilium.io/v2", "CilumNetworkPolicy")` typo at the production-code
/// call site lands outside the registered Cilium-operator-side
/// `CiliumNetworkPolicy` CRD's `RESTKind` lookup, surfacing apply-side as
/// a non-self-locating "no kind 'CilumNetworkPolicy' is registered for
/// version 'cilium.io/v2'" error far from the source caixa.lisp / the
/// renderer's [`kube_resource_skeleton`] call site).
///
/// The single source of truth the rendered Aplicacao Cilium-side mesh
/// bundle's `CiliumNetworkPolicy`-naming axis reaches for:
///
/// - the rendered `CiliumNetworkPolicy` document's top-level
/// [`KUBE_KEY_KIND`] axis (caixa-mesh/src/lib.rs:382 — the
/// `cilium_network_policies` per-`(:de, :para)` policy
/// [`kube_resource_skeleton`] call).
///
/// The kind axis names the same Cilium-operator-side CRD discriminator
/// as the sibling [`CILIUM_API_VERSION`] apiVersion axis and must move
/// together on any future `cilium.io/v3` rebrand. Until this lift
/// landed the axis carried an inline `CiliumNetworkPolicy` literal at
/// the one production-code occurrence in caixa-mesh/src/lib.rs:382 (the
/// `cilium_network_policies` [`kube_resource_skeleton`] kind argument)
/// plus a matching set inside the in-file
/// `cilium_policy_carries_canonical_kube_skeleton` /
/// `render_all_includes_every_artifact_kind` /
/// `cilium_policy_metadata_block_iterates_alphabetically` test fixtures
/// — occurrences of the same load-bearing Cilium-CRD-`kind`-discriminator
/// convention, drift-prone by construction. A drift on the top-level
/// `CiliumNetworkPolicy` `kind` axis would have surfaced as a
/// non-self-locating "no kind 'CilumNetworkPolicy' is registered for
/// version 'cilium.io/v2'" error far from the source caixa.lisp at
/// apply parse time, with the rendered per-`(:de, :para)` CNP never
/// landing in the Cilium-operator-side CRD registration and every
/// intra-mesh L4/L7 contrato flow dropping at the eBPF data plane with
/// no field naming the kind-discriminator-drift root cause.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`FLUX_KIND_KUSTOMIZATION`] (4114773) /
/// [`FLUX_KIND_HELM_RELEASE`] (e24ea3c) /
/// [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
/// [`CILIUM_API_VERSION`] (279d611) /
/// [`GATEWAY_API_API_VERSION`] (3c6cfc3) lifts established on the
/// sibling cluster-side-CRD-`kind`-discriminator + canonical-CRD-
/// group/version axes — extends the discipline from the apiVersion
/// half of the `(apiVersion, kind)` CRD-lookup tuple onto the kind
/// half on the same Cilium-CRD-axis, completing the per-Cilium-CRD
/// kind+apiVersion lift pair the M3 Aplicacao mesh renderer's eBPF
/// data-plane contract rests on. The render-side consumer now threads
/// the same `&'static str` through its [`kube_resource_skeleton`] call
/// so a future `cilium.io/v3` rebrand lands in one place; every future
/// renderer that reaches for the canonical Cilium `CiliumNetworkPolicy`
/// kind (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-Aplicacao CiliumNetworkPolicy fan-out, a future
/// per-cluster baseline-allow / baseline-deny renderer that emits the
/// peer `CiliumClusterwideNetworkPolicy`, a future per-Servico
/// local-redirect renderer that emits the peer
/// `CiliumLocalRedirectPolicy`) inherits the same value by construction
/// with no opportunity for per-renderer drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`FLUX_KIND_KUSTOMIZATION`] (4114773) /
/// [`FLUX_KIND_HELM_RELEASE`] (e24ea3c) /
/// [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
/// [`CILIUM_API_VERSION`] (279d611) /
/// [`GATEWAY_API_API_VERSION`] (3c6cfc3) /
/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) lifts apply on the peer
/// canonical-cluster-side-CRD-discriminator surface.
///
/// [cm]: ../../caixa_mesh/index.html
pub const CILIUM_KIND_NETWORK_POLICY: &str = "CiliumNetworkPolicy";
/// Canonical Cilium `CiliumNetworkPolicy` L4/L7 per-ingress-rule port-set
/// container-axis key every `cilium_network_policies`-emitted CNP
/// document mounts its per-ingress-rule `[{ports: […], rules: {…}}]`
/// list under (`spec.ingress[].toPorts[]`). Pairs with the sibling
/// [`KUBE_KEY_RULES`] (a205eb3) — the Cilium L7-dispatch schema nests
/// `spec.ingress[].toPorts[].rules.http[]` under the shared
/// (`toPorts`, `rules`) container-key pair, so drift on the `toPorts`
/// axis is exactly as load-bearing as drift on the `rules` axis it
/// wraps (the Cilium-operator-side CRD schema validator drops any
/// `spec.ingress[]` entry whose port-set container carries an
/// unrecognized key — a `"toports"` / `"toPort"` / `"targetPorts"` typo
/// silently emits an ingress rule whose per-port set the Cilium
/// operator's per-CNP L4/L7 dispatch pass no-ops entirely: every
/// intra-mesh `:contratos` flow the CNP was authored to allow now
/// drops at the eBPF data plane's default-deny gate with no field
/// naming the port-set-container-drift root cause).
///
/// The single source of truth the rendered Aplicacao Cilium-side mesh
/// bundle's per-CNP port-set-container-naming axis reaches for:
///
/// - the rendered `CiliumNetworkPolicy` document's
/// `spec.ingress[].toPorts[]` axis (caixa-mesh/src/lib.rs:939 —
/// the `cilium_network_policies` per-`(:de, :para)` policy's
/// `ingress_rule.insert("toPorts", …)` call).
///
/// The port-set-container axis names the same Cilium-operator-side
/// per-ingress-rule dispatch container as the sibling [`KUBE_KEY_RULES`]
/// nested L7-dispatch container axis and must move together on any
/// future Cilium CRD schema rebrand (an upstream `cilium.io/v3` rename
/// of the port-set container from `toPorts` to `ports` / `portSet` /
/// `endpoints`, coordinated with the Cilium project's periodic CRD
/// schema-migration passes). Until this lift landed the axis carried
/// an inline `toPorts` literal at the one production-code occurrence
/// in caixa-mesh/src/lib.rs:939 (the `cilium_network_policies`
/// `ingress_rule.insert("toPorts", …)` call) plus a matching set
/// inside the in-file `cilium_http_contracts_emit_l7_rules` /
/// `cilium_pubsub_contracts_skip_l7_rules` /
/// `cilium_multiple_edges_same_pair_fold_into_one_policy` /
/// `cnp_authentication_carries_mtls_overlay_at_ingress_rule_level` /
/// `cnp_l4_fallback_port_routes_through_lifted_default_servico_port`
/// test-fixture navigations — six occurrences of the same load-bearing
/// Cilium-CRD-`toPorts`-container-key convention, drift-prone by
/// construction. A drift on any one production or test-fixture site
/// to `"toports"` / `"toPort"` / `"targetPorts"` would have surfaced
/// as a Cilium-operator-side schema validator drop at apply time (the
/// affected `spec.ingress[]` entry's port-set container the CRD
/// schema validator recognizes as unknown), with every intra-mesh
/// `:contratos` flow the CNP was authored to allow dropping at the
/// eBPF data plane's default-deny gate with no field naming the
/// container-drift root cause. A drift on the test-fixture side
/// silently masks the emission-side pin (`.get("toPorts")` returns
/// `None` under both the drifted-key emitter and the drifted-key
/// probe — the `cilium_pubsub_contracts_skip_l7_rules` absence pin's
/// downstream `to_ports.get("rules").is_none()` assertion succeeds
/// vacuously because `to_ports` is itself `None`).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`KUBE_KEY_RULES`] (a205eb3) /
/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
/// [`CILIUM_API_VERSION`] (279d611) lifts established on the sibling
/// canonical-K8s-CR-rule-list-axis / canonical-Cilium-CRD-`kind` /
/// canonical-Cilium-CRD-`apiVersion` surfaces — extends the discipline
/// from the outer `(apiVersion, kind, spec)` shell of the Cilium CNP
/// down through the load-bearing `spec.ingress[].toPorts[].rules`
/// dispatch axis onto the port-set container half of the
/// `(toPorts, rules)` L4/L7-dispatch container-key pair, completing
/// the per-CNP L4/L7-dispatch-axis lift pair the M3 Aplicacao mesh
/// renderer's eBPF data-plane contract rests on. The render-side
/// consumer now threads the same `&'static str` through its
/// `ingress_rule.insert(…)` call so a future Cilium-CRD rebrand
/// on the port-set-container axis (or an upstream Cilium project
/// rename to a per-CRD sibling name — unlikely but the same
/// coordination point the prior lifts anchor for) lands in one place;
/// every future renderer that reaches for the canonical
/// per-CNP port-set-container-axis (the future M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
/// CiliumNetworkPolicy fan-out, a future
/// `CiliumClusterwideNetworkPolicy` renderer that emits cluster-scoped
/// baseline-allow rules with the same `spec.ingress[].toPorts[]`
/// shape, a future `CiliumClusterwideEnvoyConfig` renderer whose
/// per-edge Envoy configuration nests under the same port-set
/// container-key convention) inherits the same value by construction
/// with no opportunity for per-renderer drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`KUBE_KEY_RULES`] (a205eb3) /
/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
/// [`CILIUM_API_VERSION`] (279d611) lifts apply on the peer
/// canonical-Cilium-CNP-dispatch-axis surface.
///
/// [cm]: ../../caixa_mesh/index.html
pub const CILIUM_KEY_TO_PORTS: &str = "toPorts";
/// Canonical Cilium `CiliumNetworkPolicy` destination-identity selector-
/// axis key every `cilium_network_policies`-emitted CNP document mounts
/// its L3-target `LabelSelector` under (`spec.endpointSelector`). Pairs
/// with the sibling [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) — the Cilium CNP
/// schema pins the destination workload through the `endpointSelector`
/// axis and the admitted L4 port set through the `toPorts` axis, so
/// drift on the destination-identity axis is exactly as load-bearing as
/// drift on the port-set-container axis it accompanies (the Cilium-
/// operator-side CRD schema validator drops any `spec` block whose
/// destination-identity axis carries an unrecognized key — an
/// `"endpointselector"` / `"endpointSelectors"` / `"endpoints"` typo
/// silently emits a CNP whose L3-target selector the Cilium operator's
/// per-CNP identity-resolution pass no-ops entirely: the policy binds
/// against no destination pods and every intra-mesh `:contratos` flow
/// the CNP was authored to allow drops at the eBPF data plane's
/// default-deny gate with no field naming the destination-identity-
/// axis-drift root cause).
///
/// The single source of truth the rendered Aplicacao Cilium-side mesh
/// bundle's per-CNP destination-identity-axis-naming reaches for:
///
/// - the rendered `CiliumNetworkPolicy` document's
/// `spec.endpointSelector` axis (caixa-mesh/src/lib.rs:990 —
/// the `cilium_network_policies` per-`(:de, :para)` policy's
/// `policy_spec.insert("endpointSelector", …)` call).
///
/// The destination-identity axis names the same Cilium-operator-side
/// per-CNP L3-target selector as the sibling [`CILIUM_KEY_TO_PORTS`]
/// per-ingress-rule port-set-container axis and must move together on
/// any future Cilium CRD schema rebrand (an upstream `cilium.io/v3`
/// rename of the destination-identity axis from `endpointSelector` to
/// `endpoints` / `targetSelector` / `destinationSelector`, coordinated
/// with the Cilium project's periodic CRD schema-migration passes).
/// Until this lift landed the axis carried an inline `endpointSelector`
/// literal at the one production-code occurrence in
/// caixa-mesh/src/lib.rs:990 (the `cilium_network_policies`
/// `policy_spec.insert("endpointSelector", …)` call) plus a matching
/// set inside the in-file
/// `cilium_policy_endpoint_selector_targets_destination_program` /
/// `cnp_endpoint_selector_carries_program_only_single_axis_shape` test-
/// fixture navigations — three occurrences of the same load-bearing
/// Cilium-CRD-`endpointSelector`-axis-key convention, drift-prone by
/// construction. A drift on any one production or test-fixture site
/// to `"endpointselector"` / `"endpointSelectors"` / `"endpoints"` would
/// have surfaced as a Cilium-operator-side schema validator drop at
/// apply time (the affected `spec` block's destination-identity axis
/// the CRD schema validator recognizes as unknown), with every intra-
/// mesh `:contratos` flow the CNP was authored to allow dropping at the
/// eBPF data plane's default-deny gate with no field naming the
/// destination-identity-drift root cause. A drift on the test-fixture
/// side silently masks the emission-side pin (`.get("endpointSelector")`
/// returns `None` under both the drifted-key emitter and the drifted-key
/// probe — the downstream `.and_then(|s| s.get("matchLabels"))` chain
/// short-circuits vacuously because the outer selector-lookup is itself
/// `None`).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) /
/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
/// [`CILIUM_API_VERSION`] (279d611) lifts established on the sibling
/// canonical-Cilium-CNP-dispatch-axis / canonical-Cilium-CRD-`kind` /
/// canonical-Cilium-CRD-`apiVersion` surfaces — extends the discipline
/// from the outer `(apiVersion, kind, spec)` shell of the Cilium CNP
/// and the per-ingress-rule `toPorts.rules` L4/L7-dispatch axis onto
/// the destination-identity half of the `(endpointSelector, ingress)`
/// per-CNP-body key pair, completing the per-CNP L3/L4/L7-triad lift
/// set the M3 Aplicacao mesh renderer's eBPF data-plane contract rests
/// on. The render-side consumer now threads the same `&'static str`
/// through its `policy_spec.insert(…)` call so a future Cilium-CRD
/// rebrand on the destination-identity axis (or an upstream Cilium
/// project rename to a per-CRD sibling name — unlikely but the same
/// coordination point the prior lifts anchor for) lands in one place;
/// every future renderer that reaches for the canonical per-CNP
/// destination-identity-axis (the future M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
/// `CiliumNetworkPolicy` fan-out, a future
/// `CiliumClusterwideNetworkPolicy` renderer that emits cluster-scoped
/// baseline-allow rules with the same `spec.endpointSelector` shape, a
/// future `CiliumLocalRedirectPolicy` renderer whose per-Servico local-
/// redirect selector nests under the same destination-identity axis
/// convention) inherits the same value by construction with no
/// opportunity for per-renderer drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) /
/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
/// [`CILIUM_API_VERSION`] (279d611) lifts apply on the peer
/// canonical-Cilium-CNP-body-axis surface.
///
/// [cm]: ../../caixa_mesh/index.html
pub const CILIUM_KEY_ENDPOINT_SELECTOR: &str = "endpointSelector";
/// Canonical Cilium `CiliumNetworkPolicy` traffic-direction container-
/// axis key every `cilium_network_policies`-emitted CNP document mounts
/// its inbound-per-`(:de, :para)` ingress-rule list under (`spec.ingress[]`).
/// Pairs with the sibling [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) +
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) — the per-CNP `spec` schema mounts
/// the destination workload identity under `endpointSelector`, the
/// permitted inbound-per-`(:de, :para)` ingress-rule list under
/// `ingress[]`, and each per-ingress-rule port-set under
/// `ingress[].toPorts[]`, so drift on the traffic-direction axis is
/// exactly as load-bearing as drift on the destination-identity /
/// port-set-container axes it accompanies (the Cilium-operator-side CRD
/// schema validator drops any `spec` block whose traffic-direction axis
/// carries an unrecognized key — an `"Ingress"` / `"ingressRules"` /
/// `"inbound"` typo silently emits a CNP whose ingress-rule list the
/// Cilium operator's per-CNP L4/L7-dispatch pass no-ops entirely: the
/// policy binds against the destination workload but admits no ingress
/// traffic, and every intra-mesh `:contratos` flow the CNP was authored
/// to allow drops at the eBPF data plane's default-deny gate with no
/// field naming the traffic-direction-axis-drift root cause).
///
/// The single source of truth the rendered Aplicacao Cilium-side mesh
/// bundle's per-CNP traffic-direction-axis-naming reaches for:
///
/// - the rendered `CiliumNetworkPolicy` document's `spec.ingress[]`
/// axis (caixa-mesh/src/lib.rs:1036 — the `cilium_network_policies`
/// per-`(:de, :para)` policy's `policy_spec.insert("ingress", …)`
/// call).
///
/// The traffic-direction axis names the same Cilium-operator-side per-
/// CNP inbound-traffic dispatch container as the sibling
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] destination-identity axis and
/// [`CILIUM_KEY_TO_PORTS`] per-ingress-rule port-set container-axis and
/// must move together on any future Cilium CRD schema rebrand (an
/// upstream `cilium.io/v3` rename of the traffic-direction axis from
/// `ingress` to `inbound` / `ingressRules` / `incoming`, coordinated
/// with the Cilium project's periodic CRD schema-migration passes, or
/// the introduction of a sibling `egress` axis for outbound-traffic
/// dispatch under the same per-CNP-body schema). Until this lift landed
/// the axis carried an inline `ingress` literal at the one production-
/// code occurrence in caixa-mesh/src/lib.rs:1036 (the
/// `cilium_network_policies` `policy_spec.insert("ingress", …)` call)
/// plus a matching set inside the in-file
/// `cilium_http_contracts_emit_l7_rules` /
/// `cilium_policies_are_identity_based` /
/// `cnp_from_endpoints_carries_program_plus_aplicacao_labels_two_axis_shape`
/// / `cilium_multiple_edges_same_pair_fold_into_one_policy` /
/// `cilium_pubsub_contracts_skip_l7_rules` /
/// `render_multi_doc_contains_expected_kinds` /
/// `cnp_authentication_carries_mtls_overlay_at_ingress_rule_level` /
/// `cnp_l4_fallback_port_routes_through_lifted_default_servico_port`
/// test-fixture navigations — nine occurrences of the same load-bearing
/// Cilium-CRD-`ingress`-axis-key convention, drift-prone by
/// construction. A drift on any one production or test-fixture site
/// to `"Ingress"` / `"ingressRules"` / `"inbound"` would have surfaced
/// as a Cilium-operator-side schema validator drop at apply time (the
/// affected `spec` block's traffic-direction axis the CRD schema
/// validator recognizes as unknown), with every intra-mesh `:contratos`
/// flow the CNP was authored to allow dropping at the eBPF data plane's
/// default-deny gate with no field naming the traffic-direction-drift
/// root cause. A drift on the test-fixture side silently masks the
/// emission-side pin (`.get("ingress")` returns `None` under both the
/// drifted-key emitter and the drifted-key probe — the downstream
/// `.and_then(|i| i.as_sequence())` chain short-circuits vacuously
/// because the outer traffic-direction-lookup is itself `None`, and
/// every per-CNP downstream navigation — `fromEndpoints`, `toPorts`,
/// `authentication` — rides through the same short-circuited outer
/// axis-lookup with no field naming the drift root cause).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) /
/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
/// [`CILIUM_API_VERSION`] (279d611) lifts established on the sibling
/// canonical-Cilium-CNP-destination-identity /
/// canonical-Cilium-CNP-port-set-container /
/// canonical-K8s-CR-rule-list / canonical-Cilium-CRD-`kind` /
/// canonical-Cilium-CRD-`apiVersion` surfaces — completes the per-CNP
/// L3/L4/L7-triad lift set `(endpointSelector, ingress → toPorts →
/// rules)` the M3 Aplicacao mesh renderer's eBPF data-plane contract
/// rests on by lifting the traffic-direction axis that structurally
/// separates the destination-identity axis from the port-set-container
/// axis nested beneath it. The render-side consumer now threads the
/// same `&'static str` through its `policy_spec.insert(…)` call so a
/// future Cilium-CRD rebrand on the traffic-direction axis (or an
/// upstream Cilium project rename to a per-CRD sibling name — unlikely
/// on the CRD's stable `cilium.io/v2` slot, but the coordination point
/// the prior lifts anchor for) lands in one place; every future
/// renderer that reaches for the canonical per-CNP traffic-direction-
/// axis (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-Aplicacao `CiliumNetworkPolicy` fan-out, a future
/// `CiliumClusterwideNetworkPolicy` renderer that emits cluster-scoped
/// baseline-allow rules with the same `spec.ingress[]` shape, a future
/// `CiliumLocalRedirectPolicy` renderer whose per-Servico local-
/// redirect ingress-rule list nests under the same traffic-direction
/// axis convention) inherits the same value by construction with no
/// opportunity for per-renderer drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) /
/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
/// [`CILIUM_API_VERSION`] (279d611) lifts apply on the peer
/// canonical-Cilium-CNP-body-axis surface.
///
/// [cm]: ../../caixa_mesh/index.html
pub const CILIUM_KEY_INGRESS: &str = "ingress";
/// Canonical Cilium `CiliumNetworkPolicy` per-ingress-rule identity-
/// source selector-list axis key every `cilium_network_policies`-emitted
/// CNP document mounts its permitted-source `LabelSelector` list under
/// (`spec.ingress[].fromEndpoints[]`). Pairs with the sibling
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) — the Cilium CNP schema
/// pins the destination workload identity through the per-CNP-body
/// `endpointSelector` axis and the admitted source workload identities
/// through the per-ingress-rule `fromEndpoints[]` axis, so drift on the
/// identity-source axis is exactly as load-bearing as drift on the
/// destination-identity axis it accompanies (the Cilium-operator-side
/// CRD schema validator drops any per-ingress-rule block whose
/// identity-source axis carries an unrecognized key — a
/// `"fromendpoints"` / `"fromEndPoint"` / `"sourceEndpoints"` typo
/// silently emits a CNP whose per-`(:de, :para)` ingress-rule identity-
/// source list the Cilium operator's per-CNP identity-resolution pass
/// no-ops entirely: the ingress rule admits no source pods and every
/// intra-mesh `:contratos` flow the CNP was authored to allow drops at
/// the eBPF data plane's default-deny gate with no field naming the
/// identity-source-axis-drift root cause).
///
/// The single source of truth the rendered Aplicacao Cilium-side mesh
/// bundle's per-ingress-rule identity-source-axis-naming reaches for:
///
/// - the rendered `CiliumNetworkPolicy` document's per-ingress-rule
/// `fromEndpoints[]` axis (caixa-mesh/src/lib.rs:991 — the
/// `cilium_network_policies` per-`(:de, :para)` policy's
/// `ingress_rule.insert("fromEndpoints", …)` call).
///
/// The identity-source axis names the same Cilium-operator-side per-
/// ingress-rule source-workload selector list as the sibling
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] destination-identity axis and must
/// move together on any future Cilium CRD schema rebrand (an upstream
/// `cilium.io/v3` rename of the identity-source axis from
/// `fromEndpoints` to `sourceEndpoints` / `fromWorkloads` /
/// `sourceSelectors`, coordinated with the Cilium project's periodic
/// CRD schema-migration passes). Until this lift landed the axis
/// carried an inline `fromEndpoints` literal at the one production-code
/// occurrence in caixa-mesh/src/lib.rs:991 (the `cilium_network_policies`
/// `ingress_rule.insert("fromEndpoints", …)` call) plus a matching set
/// inside the in-file
/// `cnp_from_endpoints_carries_program_plus_aplicacao_labels_two_axis_shape`
/// / `cilium_policies_are_identity_based`
/// / `cnp_authentication_carries_mtls_overlay_at_ingress_rule_level`
/// test-fixture navigations — five occurrences of the same load-bearing
/// Cilium-CRD-`fromEndpoints`-axis-key convention, drift-prone by
/// construction. A drift on any one production or test-fixture site
/// to `"fromendpoints"` / `"fromEndPoint"` / `"sourceEndpoints"` would
/// have surfaced as a Cilium-operator-side schema validator drop at
/// apply time (the affected per-ingress-rule block's identity-source
/// axis the CRD schema validator recognizes as unknown), with every
/// intra-mesh `:contratos` flow the CNP was authored to allow dropping
/// at the eBPF data plane's default-deny gate with no field naming the
/// identity-source-drift root cause. A drift on the test-fixture side
/// silently masks the emission-side pin
/// (`.get("fromEndpoints")` returns `None` under both the drifted-key
/// emitter and the drifted-key probe — the downstream `.and_then(|e|
/// e.as_sequence())` / `.and_then(|e| e.get(KUBE_KEY_MATCH_LABELS))`
/// chain short-circuits vacuously because the outer identity-source-
/// lookup is itself `None`).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) /
/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
/// [`CILIUM_API_VERSION`] (279d611) lifts established on the sibling
/// canonical-Cilium-CNP-destination-identity /
/// canonical-Cilium-CNP-traffic-direction-container /
/// canonical-Cilium-CNP-port-set-container /
/// canonical-K8s-CR-rule-list / canonical-Cilium-CRD-`kind` /
/// canonical-Cilium-CRD-`apiVersion` surfaces — completes the per-CNP
/// identity-pair lift set `(endpointSelector, fromEndpoints)` the M3
/// Aplicacao mesh renderer's eBPF data-plane contract rests on by
/// lifting the identity-source axis structurally paired with the
/// destination-identity axis under the Cilium-operator-side per-CNP
/// SPIFFE-identity-bound access-control contract. The render-side
/// consumer now threads the same `&'static str` through its
/// `ingress_rule.insert(…)` call so a future Cilium-CRD rebrand on the
/// identity-source axis (or an upstream Cilium project rename to a
/// per-CRD sibling name — unlikely on the CRD's stable `cilium.io/v2`
/// slot, but the coordination point the prior lifts anchor for) lands
/// in one place; every future renderer that reaches for the canonical
/// per-ingress-rule identity-source-axis (the future M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
/// `CiliumNetworkPolicy` fan-out, a future
/// `CiliumClusterwideNetworkPolicy` renderer that emits cluster-scoped
/// baseline-allow rules with the same `spec.ingress[].fromEndpoints[]`
/// shape, a future `CiliumLocalRedirectPolicy` renderer whose per-
/// Servico local-redirect source-workload selector list nests under
/// the same identity-source axis convention) inherits the same value
/// by construction with no opportunity for per-renderer drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) /
/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
/// [`CILIUM_API_VERSION`] (279d611) lifts apply on the peer
/// canonical-Cilium-CNP-body-axis surface.
///
/// [cm]: ../../caixa_mesh/index.html
pub const CILIUM_KEY_FROM_ENDPOINTS: &str = "fromEndpoints";
/// Canonical Cilium `CiliumNetworkPolicy` per-`toPorts[]`-entry L4
/// port-tuple-list-container axis key every `cilium_network_policies`-
/// emitted CNP document mounts its per-port-set `[{port, protocol}]` list
/// under (`spec.ingress[].toPorts[].ports[]`). Nests inside the sibling
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) — the Cilium CNP schema pins the
/// per-ingress-rule port-set-container axis through the `toPorts[]` list
/// and the per-port-set L4 port-tuple list through the `ports[]` axis
/// beneath each entry, so drift on the L4 port-tuple-list-container axis
/// is exactly as load-bearing as drift on the port-set container axis it
/// nests inside (the Cilium-operator-side CRD schema validator drops any
/// per-`toPorts[]` entry whose port-tuple-list-container axis carries an
/// unrecognized key — a `"port"` / `"portList"` / `"L4Ports"` typo
/// silently emits a CNP whose per-`(:de, :para)` per-port-set L4
/// port-tuple list the Cilium operator's per-CNP L4-allow eBPF-program
/// generation pass no-ops entirely: the port-set admits no `(port,
/// protocol)` tuple and every intra-mesh `:contratos` flow the CNP was
/// authored to allow drops at the eBPF data plane's default-deny gate
/// with no field naming the L4-port-tuple-list-container-axis-drift root
/// cause).
///
/// The single source of truth the rendered Aplicacao Cilium-side mesh
/// bundle's per-`toPorts[]`-entry L4-port-tuple-list-container-axis-
/// naming reaches for:
///
/// - the rendered `CiliumNetworkPolicy` document's per-`toPorts[]`-
/// entry `ports[]` axis (caixa-mesh/src/lib.rs:1081 — the
/// `cilium_network_policies` per-`(:de, :para)` policy's
/// `to_port.insert("ports", …)` call).
///
/// The L4 port-tuple-list-container axis names the same Cilium-operator-
/// side per-port-set L4-allow eBPF-program-generation source-list as the
/// sibling [`CILIUM_KEY_TO_PORTS`] port-set container axis it nests
/// inside and must move together on any future Cilium CRD schema rebrand
/// (an upstream `cilium.io/v3` rename of the L4 port-tuple-list axis
/// from `ports` to `portList` / `l4Ports` / `tuples`, coordinated with
/// the Cilium project's periodic CRD schema-migration passes). Until this
/// lift landed the axis carried an inline `ports` literal at the one
/// production-code occurrence in caixa-mesh/src/lib.rs:1081 (the
/// `cilium_network_policies` `to_port.insert("ports", …)` call) plus a
/// matching set inside the in-file
/// `cilium_pubsub_contracts_skip_l7_rules`
/// / `cnp_l4_fallback_port_reflects_default_servico_port`
/// test-fixture navigations — three occurrences of the same load-bearing
/// Cilium-CRD-`ports`-axis-key convention, drift-prone by construction. A
/// drift on any one production or test-fixture site to `"port"` /
/// `"portList"` / `"L4Ports"` would have surfaced as a Cilium-operator-
/// side schema validator drop at apply time (the affected per-`toPorts[]`
/// entry's port-tuple-list-container axis the CRD schema validator
/// recognizes as unknown), with every intra-mesh `:contratos` flow the
/// CNP was authored to allow dropping at the eBPF data plane's default-
/// deny gate with no field naming the L4-port-tuple-list-container-drift
/// root cause. A drift on the test-fixture side silently masks the
/// emission-side pin (`.get("ports")` returns `None` under both the
/// drifted-key emitter and the drifted-key probe — the downstream
/// `.and_then(|p| p.as_sequence())` / `.and_then(|s| s.first())` /
/// `.and_then(|p| p.get("port"))` chain short-circuits vacuously because
/// the outer L4-port-tuple-list-container lookup is itself `None`).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) /
/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
/// [`CILIUM_API_VERSION`] (279d611) lifts established on the sibling
/// canonical-Cilium-CNP-identity-source /
/// canonical-Cilium-CNP-destination-identity /
/// canonical-Cilium-CNP-traffic-direction-container /
/// canonical-Cilium-CNP-port-set-container /
/// canonical-K8s-CR-rule-list / canonical-Cilium-CRD-`kind` /
/// canonical-Cilium-CRD-`apiVersion` surfaces — nests the per-port-set
/// L4 port-tuple-list-container axis structurally beneath the sibling
/// [`CILIUM_KEY_TO_PORTS`] port-set-container axis, extending the per-CNP
/// L3/L4/L7-triad `(endpointSelector, ingress → toPorts → ports / rules)`
/// lift set with the L4-half's port-tuple-list-container axis the M3
/// Aplicacao mesh renderer's eBPF data-plane L4-allow contract rests on.
/// The render-side consumer now threads the same `&'static str` through
/// its `to_port.insert(…)` call so a future Cilium-CRD rebrand on the
/// L4 port-tuple-list-container axis (or an upstream Cilium project
/// rename to a per-CRD sibling name — unlikely on the CRD's stable
/// `cilium.io/v2` slot, but the coordination point the prior lifts
/// anchor for) lands in one place; every future renderer that reaches
/// for the canonical per-`toPorts[]`-entry L4-port-tuple-list-container
/// axis (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-Aplicacao `CiliumNetworkPolicy` fan-out, a future
/// `CiliumClusterwideNetworkPolicy` renderer that emits cluster-scoped
/// baseline-allow rules with the same
/// `spec.ingress[].toPorts[].ports[]` shape, a future
/// `CiliumLocalRedirectPolicy` renderer whose per-Servico local-redirect
/// L4 port-tuple list nests under the same L4-port-tuple-list-container
/// axis convention) inherits the same value by construction with no
/// opportunity for per-renderer drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) /
/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
/// [`CILIUM_API_VERSION`] (279d611) lifts apply on the peer
/// canonical-Cilium-CNP-body-axis surface.
///
/// [cm]: ../../caixa_mesh/index.html
pub const CILIUM_KEY_PORTS: &str = "ports";
/// Canonical Cilium `CiliumNetworkPolicy` per-ingress-rule mutual-auth
/// policy body-axis key every `cilium_network_policies`-emitted CNP
/// document mounts its per-rule mTLS enforcement block under
/// (`spec.ingress[].authentication`). Sibling to
/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) +
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) at the per-ingress-rule body
/// level — the Cilium CNP schema places the per-rule mutual-auth mode
/// (`{mode: required | disabled}`) at the ingress-rule axis alongside
/// the identity-source (`fromEndpoints`) and port-set (`toPorts`)
/// axes, so drift on the authentication axis is exactly as
/// load-bearing as drift on the sibling per-ingress-rule-body axes it
/// pairs with (the Cilium-operator-side CRD schema validator drops
/// any per-`ingress[]` entry whose mutual-auth axis carries an
/// unrecognized key — a `"auth"` / `"mutualAuth"` / `"mtls"` typo
/// silently emits a CNP whose per-`(:de, :para)` per-rule mTLS block
/// the Cilium operator's per-CNP mutual-auth SPIFFE-handshake
/// pipeline no-ops entirely: the ingress rule falls back to the
/// cluster-default authentication mode (typically `"disabled"` — no
/// mutual-auth enforcement), and every intra-mesh `:contratos` flow
/// the CNP was authored to protect with per-edge mTLS silently
/// bypasses the SPIFFE-identity-bound mutual-auth handshake with no
/// field naming the mutual-auth-axis-drift root cause).
///
/// The single source of truth the rendered Aplicacao Cilium-side
/// mesh bundle's per-ingress-rule mutual-auth-axis naming reaches for:
///
/// - the rendered `CiliumNetworkPolicy` document's per-`ingress[]`
/// entry `authentication` axis (caixa-mesh/src/lib.rs — the
/// `cilium_network_policies` per-`(:de, :para)` policy's
/// `ingress_rule.insert("authentication", …)` call in the
/// `:politicas :mtls-required` overlay emit gate).
///
/// The mutual-auth axis names the same Cilium-operator-side per-rule
/// SPIFFE-identity-handshake enforcement policy as the sibling per-
/// ingress-rule identity-source (`fromEndpoints`) and port-set
/// (`toPorts`) axes it pairs with, and must move together on any
/// future Cilium CRD schema rebrand (an upstream `cilium.io/v3`
/// rename of the mutual-auth axis from `authentication` to
/// `mutualAuth` / `mtls` / `authPolicy`, coordinated with the Cilium
/// project's periodic CRD schema-migration passes). Until this lift
/// landed the axis carried an inline `authentication` literal at the
/// one production-code emitter site (the `cilium_network_policies`
/// per-rule `ingress_rule.insert("authentication", …)` call in the
/// `:mtls-required` overlay emit gate) plus a matching set inside
/// the in-file `cnp_authentication_renders_every_policy_independently`
/// / `cnp_authentication_position_is_rule_level_not_nested` /
/// `cnp_authentication_pubsub_contracts_carry_overlay_too` /
/// `cnp_authentication_mode_is_a_yaml_string_scalar` /
/// `cnp_omits_authentication_when_mtls_required_unset` /
/// `cnp_explicit_mtls_required_false_emits_disabled_mode` /
/// `cnp_authentication_overlay_when_mtls_required_set` (name approximate)
/// test-fixture navigations — ten occurrences of the same
/// load-bearing Cilium-CRD-mutual-auth-axis-key convention, drift-
/// prone by construction. A drift on any one production or test-
/// fixture site to `"auth"` / `"mutualAuth"` / `"mtls"` would surface
/// as a Cilium-operator-side schema-validator drop at apply time
/// (the affected per-`ingress[]` entry's mutual-auth-axis key the
/// CRD schema validator recognizes as unknown), with every intra-
/// mesh `:contratos` flow the CNP was authored to protect with per-
/// edge SPIFFE-identity-bound mutual-auth silently bypassing the
/// mTLS handshake at the Cilium data-plane's default-authentication
/// mode with no field naming the mutual-auth-axis-drift root cause.
/// A drift on the test-fixture side silently masks the emission-
/// side pin (`.get("authentication")` returns `None` under both the
/// drifted-key emitter and the drifted-key probe — every downstream
/// `.and_then(|a| a.get("mode"))` chain short-circuits vacuously
/// because the outer mutual-auth-body-lookup is itself `None`).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`CILIUM_KEY_PORTS`] (1087693) /
/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) /
/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
/// [`CILIUM_API_VERSION`] (279d611) lifts established on the
/// sibling canonical-Cilium-CNP-body-axis surfaces — nests the
/// per-ingress-rule mutual-auth axis structurally beside the sibling
/// [`CILIUM_KEY_FROM_ENDPOINTS`] identity-source and
/// [`CILIUM_KEY_TO_PORTS`] port-set-container axes at the per-rule
/// body triple `(fromEndpoints, toPorts, authentication)` the M3
/// Aplicacao mesh renderer's SPIFFE-identity-bound per-edge mTLS
/// contract rests on.
///
/// [cm]: ../../caixa_mesh/index.html
pub const CILIUM_KEY_AUTHENTICATION: &str = "authentication";
/// Canonical Cilium `CiliumNetworkPolicy` per-`ingress[].authentication`
/// block mTLS-mode-discriminator leaf-scalar-axis key every
/// `cilium_network_policies`-emitted CNP document mounts its per-rule
/// mutual-auth mode leaf under (`spec.ingress[].authentication.mode`).
/// Nests exactly one level beneath the sibling
/// [`CILIUM_KEY_AUTHENTICATION`] (db31108) per-ingress-rule mutual-auth
/// body-axis it sits inside: the Cilium CNP schema places the mTLS
/// enforcement mode discriminator (`"required"` / `"disabled"`) as the
/// single leaf-scalar axis of the per-rule authentication block, so
/// drift on the mode-discriminator leaf axis is exactly as load-bearing
/// as drift on the sibling per-ingress-rule mutual-auth body-axis key
/// (`authentication`) it nests inside (the Cilium-operator-side CNP
/// schema validator drops any per-`ingress[]` entry whose per-rule
/// mutual-auth block carries an unrecognized leaf axis — a `"policy"` /
/// `"authMode"` / `"handshakeMode"` typo at either the emit-side single-
/// field-overlay call site or a downstream renderer's per-rule authn
/// leaf upsert silently emits a per-`ingress[]` mutual-auth block whose
/// mode-discriminator leaf the Cilium CRD schema validator rejects as
/// unknown; the ingress rule falls back to the cluster-default
/// authentication mode (typically `"disabled"` — no mutual-auth
/// enforcement) silently bypassing the SPIFFE-identity-bound mTLS
/// handshake every intra-mesh `:contratos` flow the CNP was authored to
/// protect with per-edge mTLS, and the emit-side/probe-side split
/// silently masks the per-rule mutual-auth pin (`.get("mode")` returns
/// `None` under both the drifted-key emitter and the drifted-key probe
/// — every downstream `.and_then(|v| v.as_str())` chain short-circuits
/// vacuously because the outer mode-leaf-lookup is itself `None`).
///
/// The single source of truth the rendered Aplicacao Cilium-side mesh
/// bundle's per-ingress-rule mutual-auth-mode-leaf-axis naming reaches
/// for:
///
/// - the rendered `CiliumNetworkPolicy` document's per-`ingress[]`
/// entry `authentication.mode` leaf axis (caixa-mesh/src/lib.rs —
/// the `cilium_network_policies` per-`(:de, :para)` policy's
/// `single_field_overlay(spec.politicas.mtls_required, "mode", …)`
/// call site in the `:politicas :mtls-required` overlay emit gate,
/// the exact field the `single_field_overlay` helper writes the
/// single leaf under when the tristate `:mtls-required` slot is
/// set).
///
/// The mode-discriminator leaf-axis names the same Cilium-operator-side
/// per-rule SPIFFE-identity-handshake enforcement policy as the sibling
/// per-ingress-rule mutual-auth-body-axis key (`authentication`) it nests
/// inside, and must move together on any future Cilium CRD schema
/// rebrand (an upstream `cilium.io/v3` rename of the mutual-auth mode-
/// discriminator leaf from `mode` to `policy` / `authMode` /
/// `handshakeMode`, coordinated with the Cilium project's periodic CRD
/// schema-migration passes). Until this lift landed the axis carried an
/// inline `mode` literal at the one production-code emitter site (the
/// `cilium_network_policies` per-rule `single_field_overlay(...,
/// "mode", ...)` call in the `:mtls-required` overlay emit gate) plus a
/// matching set inside the in-file `cnp_carries_politicas_mtls_required_
/// on_every_rule` / `cnp_explicit_mtls_required_false_emits_disabled_
/// mode` / `cnp_authentication_renders_every_policy_independently` /
/// `cnp_authentication_pubsub_contracts_carry_overlay_too` /
/// `cnp_authentication_mode_is_a_yaml_string_scalar` test-fixture
/// navigations — six occurrences of the same load-bearing Cilium-CRD-
/// mutual-auth-mode-discriminator-leaf-axis-key convention, drift-prone
/// by construction. A drift on any one production or test-fixture site
/// to `"policy"` / `"authMode"` / `"handshakeMode"` would surface as a
/// Cilium-operator-side schema-validator drop at apply time (the
/// affected per-`ingress[]` entry's per-rule mutual-auth-mode-
/// discriminator-leaf-axis key the CRD schema validator recognizes as
/// unknown), with every intra-mesh `:contratos` flow the CNP was
/// authored to protect with per-edge SPIFFE-identity-bound mutual-auth
/// silently bypassing the mTLS handshake at the Cilium data-plane's
/// default-authentication mode with no field naming the mutual-auth-
/// mode-discriminator-leaf-axis-drift root cause.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`CILIUM_KEY_AUTHENTICATION`] (db31108) /
/// [`CILIUM_KEY_PORTS`] (1087693) /
/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) /
/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
/// [`CILIUM_API_VERSION`] (279d611) lifts established on the
/// sibling canonical-Cilium-CNP-body-axis surfaces — descends the
/// per-ingress-rule mutual-auth mode-discriminator leaf axis one level
/// beneath the parent [`CILIUM_KEY_AUTHENTICATION`] body-axis key it
/// pairs with, completing the per-rule mutual-auth
/// `(authentication → mode)` body/leaf axis pair the M3 Aplicacao mesh
/// renderer's SPIFFE-identity-bound per-edge mTLS enforcement contract
/// rests on.
///
/// [cm]: ../../caixa_mesh/index.html
pub const CILIUM_KEY_MODE: &str = "mode";
/// Canonical Cilium `CiliumNetworkPolicy` per-`ingress[].toPorts[].rules`
/// L7-HTTP-rule-list-discriminator container-axis key every
/// `cilium_network_policies`-emitted CNP document mounts its per-`toPorts[]`
/// entry L7 HTTP-rule list under (`spec.ingress[].toPorts[].rules.http`).
/// Nests exactly one level beneath the sibling [`KUBE_KEY_RULES`] (a205eb3)
/// per-`toPorts[]` rule-list-container axis it sits inside: the Cilium CNP
/// schema places the L7-protocol-selection discriminator (`http` / future
/// `kafka` / future `dns`) as the single per-protocol keyed axis of the
/// per-`toPorts[]` rules block, so drift on the L7-HTTP-rule-list-
/// discriminator axis is exactly as load-bearing as drift on the sibling
/// [`KUBE_KEY_RULES`] per-`toPorts[]` rule-list-container axis-key it nests
/// inside (the Cilium-operator-side CNP schema validator drops any per-
/// `toPorts[]` entry whose per-protocol L7-rule-list-discriminator key it
/// recognizes as unknown — a `"HTTP"` / `"Http"` / `"http/1.1"` /
/// `"httpRules"` typo at either the emit-side `rules.insert(…)` call site
/// or a downstream renderer's per-`toPorts[]` L7-rule-list upsert silently
/// emits a per-`toPorts[]` entry whose L7-HTTP-rule-list-discriminator key
/// the Cilium CRD schema validator rejects as unknown; the per-`toPorts[]`
/// entry falls back to L4-only enforcement — no L7 URL-path predicate is
/// applied — silently admitting every HTTP-method / URL-path combination
/// the ingress rule was authored to filter to the exact path prefix set
/// the typed `:contratos` graph names at the L7 introspection axis, and
/// the emit-side/probe-side split silently masks the per-`toPorts[]` L7-
/// rule-list pin (`.get("http")` returns `None` under both the drifted-
/// key emitter and the drifted-key probe — every downstream
/// `.and_then(|h| h.as_sequence())` chain short-circuits vacuously because
/// the outer L7-HTTP-rule-list-lookup is itself `None`).
///
/// The single source of truth the rendered Aplicacao Cilium-CNP-side
/// intra-mesh L7-tuple-gating bundle's per-`toPorts[]` L7-HTTP-rule-list-
/// discriminator-axis naming reaches for:
///
/// - the rendered `CiliumNetworkPolicy` document's per-`toPorts[]` entry
/// `rules.http` L7-HTTP-rule-list-discriminator axis (caixa-mesh/src/lib.rs —
/// the `cilium_network_policies` per-`(:de, :para)` policy's
/// `rules.insert("http", …)` call in the `WitTarget::Http` L7-
/// introspection emit branch, the exact per-protocol keyed axis of
/// the per-`toPorts[]` rules block the L7 URL-path predicate lands
/// under).
///
/// The L7-HTTP-rule-list-discriminator axis names the same Cilium-operator-
/// side per-`toPorts[]` L7 URL-path predicate selection as the sibling
/// [`KUBE_KEY_RULES`] per-`toPorts[]` rule-list-container axis-key it nests
/// inside, and must move together on any future Cilium CRD schema rebrand
/// (an upstream `cilium.io/v3` rename of the L7-HTTP-rule-list-
/// discriminator from `http` to `httpRules` / `l7Http` / `httpMatch`,
/// coordinated with the Cilium project's periodic CRD schema-migration
/// passes). Until this lift landed the axis carried an inline `http`
/// literal at the one production-code emitter site (the
/// `cilium_network_policies` per-`(:de, :para)` `rules.insert("http", …)`
/// call in the `WitTarget::Http` L7 introspection emit branch) plus a
/// matching set inside the in-file `cilium_l7_rules_fan_in_captures_every_
/// http_edge` / `cilium_http_contracts_carry_l7_path` test-fixture
/// navigations — three occurrences of the same load-bearing Cilium-CRD-
/// L7-HTTP-rule-list-discriminator convention, drift-prone by
/// construction. A drift on any one production or test-fixture site to
/// `"HTTP"` / `"Http"` / `"httpRules"` would surface as a Cilium-operator-
/// side schema-validator drop at apply time (the affected per-
/// `toPorts[]` entry's L7-rule-list-discriminator key the CRD schema
/// validator recognizes as unknown), with every intra-mesh HTTP-shaped
/// `:contratos` flow the CNP was authored to filter to a URL-path prefix
/// silently bypassing the L7 path predicate at the Cilium data-plane's
/// L4-only fallback dispatch with no field naming the L7-HTTP-rule-list-
/// discriminator-drift root cause.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`CILIUM_KEY_MODE`] (4289dfb) /
/// [`CILIUM_KEY_AUTHENTICATION`] (db31108) /
/// [`CILIUM_KEY_PORTS`] (1087693) /
/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) /
/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
/// [`CILIUM_API_VERSION`] (279d611) lifts established on the
/// sibling canonical-Cilium-CNP-body-axis surfaces — descends the per-
/// `toPorts[]` L7-HTTP-rule-list-discriminator axis one level beneath the
/// parent [`KUBE_KEY_RULES`] per-`toPorts[]` rule-list-container axis-key
/// it nests inside, completing the per-`toPorts[]` L7-introspection
/// `(rules → http)` container/protocol-discriminator axis pair the M3
/// Aplicacao mesh renderer's HTTP-shaped-`:contratos` URL-path-prefix-
/// filtering L7-enforcement contract rests on.
///
/// [cm]: ../../caixa_mesh/index.html
pub const CILIUM_KEY_HTTP: &str = "http";
/// Canonical Cilium `CiliumNetworkPolicy` per-`ingress[].toPorts[].rules.http[]`
/// per-HTTP-rule URL-path-predicate leaf-scalar-axis key every
/// `cilium_network_policies`-emitted CNP document mounts its per-HTTP-rule
/// URL-path-prefix predicate scalar under
/// (`spec.ingress[].toPorts[].rules.http[].path`). Nests exactly one level
/// beneath the sibling [`CILIUM_KEY_HTTP`] (ccd81e8) per-`toPorts[]`
/// L7-HTTP-rule-list-discriminator container-axis it sits inside: the Cilium
/// CNP schema places the per-HTTP-rule URL-path predicate scalar (the exact
/// URL-path regex the Cilium L7 dispatch pass matches the observed HTTP
/// request line's path segment against) as the single load-bearing leaf-
/// scalar axis of the per-`rules.http[]` entry — so drift on the per-HTTP-
/// rule URL-path-predicate leaf axis is exactly as load-bearing as drift on
/// the sibling [`CILIUM_KEY_HTTP`] per-`toPorts[]` L7-HTTP-rule-list-
/// discriminator container-axis key it nests inside (the Cilium-operator-
/// side CNP schema validator drops any per-`rules.http[]` entry whose per-
/// HTTP-rule URL-path-predicate leaf key it recognizes as unknown — a
/// `"Path"` / `"pathPrefix"` / `"regex"` / `"urlPath"` / `"pathMatch"` typo
/// at either the emit-side `http_rule.insert(…)` call site or a downstream
/// renderer's per-`rules.http[]` URL-path leaf upsert silently emits a per-
/// `rules.http[]` entry whose URL-path-predicate leaf-axis key the Cilium
/// CRD schema validator rejects as unknown; the per-`rules.http[]` entry
/// falls back to a match-any-URL-path predicate — the per-`toPorts[]` L7
/// rule admits every URL path on the destination port silently, bypassing
/// the URL-path-prefix predicate the typed `:contratos` HTTP-shaped edge's
/// `:endpoint` slot names at the L7 introspection axis, and the emit-
/// side/probe-side split silently masks the per-`rules.http[]` URL-path
/// pin (`.get("path")` returns `None` under both the drifted-key emitter
/// and the drifted-key probe — every downstream `.and_then(|v| v.as_str())`
/// chain short-circuits vacuously because the outer per-HTTP-rule URL-
/// path-lookup is itself `None`).
///
/// The single source of truth the rendered Aplicacao Cilium-CNP-side
/// intra-mesh per-`toPorts[]` L7-URL-path-predicate-gating bundle's per-
/// `rules.http[]` URL-path-predicate-leaf-axis naming reaches for:
///
/// - the rendered `CiliumNetworkPolicy` document's per-`toPorts[]`
/// `rules.http[]` entry's `path` URL-path-predicate leaf axis
/// (caixa-mesh/src/lib.rs — the `cilium_network_policies` per-`(:de,
/// :para)` policy's `http_rule.insert("path", …)` call in the
/// `WitTarget::Http` L7 introspection emit branch, the exact per-
/// `rules.http[]` leaf axis the per-HTTP-rule URL-path predicate scalar
/// lands under, seeded from the typed HTTP-shaped `:contratos` edge's
/// `:endpoint` slot).
///
/// The per-HTTP-rule URL-path-predicate-leaf-axis names the same Cilium-
/// operator-side per-`rules.http[]` URL-path predicate selection as the
/// sibling [`CILIUM_KEY_HTTP`] per-`toPorts[]` L7-HTTP-rule-list-
/// discriminator container-axis key it nests inside, and must move together
/// on any future Cilium CRD schema rebrand (an upstream `cilium.io/v3`
/// rename of the per-HTTP-rule URL-path-predicate leaf from `path` to
/// `urlPath` / `pathPrefix` / `pathMatch`, coordinated with the Cilium
/// project's periodic CRD schema-migration passes). Until this lift landed
/// the axis carried an inline `path` literal at the one production-code
/// emitter site (the `cilium_network_policies` per-`(:de, :para)`
/// `http_rule.insert("path", …)` call in the `WitTarget::Http` L7
/// introspection emit branch) plus a matching set inside the in-file
/// `cilium_http_contracts_emit_l7_rules` test-fixture per-HTTP-rule URL-
/// path-predicate presence-and-value pin — two occurrences of the same
/// load-bearing Cilium-CRD per-HTTP-rule URL-path-predicate-leaf-axis
/// convention, drift-prone by construction. A drift on any one production
/// or test-fixture site to `"Path"` / `"pathPrefix"` / `"regex"` /
/// `"urlPath"` / `"pathMatch"` would surface as a Cilium-operator-side
/// schema-validator drop at apply time (the affected per-`rules.http[]`
/// entry's URL-path-predicate leaf-axis key the CRD schema validator
/// recognizes as unknown), with every intra-mesh HTTP-shaped `:contratos`
/// flow the CNP was authored to filter to a URL-path prefix silently
/// bypassing the L7 URL-path predicate at the Cilium data-plane's match-
/// any-URL-path fallback with no field naming the URL-path-predicate-
/// leaf-axis-drift root cause.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`CILIUM_KEY_HTTP`] (ccd81e8) /
/// [`CILIUM_KEY_MODE`] (4289dfb) /
/// [`CILIUM_KEY_AUTHENTICATION`] (db31108) /
/// [`CILIUM_KEY_PORTS`] (1087693) /
/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) /
/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
/// [`CILIUM_API_VERSION`] (279d611) lifts established on the
/// sibling canonical-Cilium-CNP-body-axis surfaces — descends the per-
/// `toPorts[]` L7-introspection `(rules → http → path)` container /
/// protocol-discriminator / URL-path-predicate axis chain one leaf level
/// beneath the parent [`CILIUM_KEY_HTTP`] per-`toPorts[]` L7-HTTP-rule-
/// list-discriminator axis-key it nests inside, completing the per-
/// `toPorts[]` L7-introspection `(rules → http → path)` container /
/// protocol-discriminator / URL-path-predicate axis triple the M3
/// Aplicacao mesh renderer's HTTP-shaped-`:contratos` URL-path-prefix-
/// filtering L7-enforcement contract rests on.
///
/// Distinct from the sibling K8s-Gateway-API-side
/// [`GATEWAY_API_KEY_PATH`] (9f45aa4) per-`HTTPRouteMatch` path-matcher
/// container-axis key: both keys spell the same underlying `"path"`
/// string but name distinct schema axes on distinct CRD groups — the
/// Cilium-side axis is a per-HTTP-rule URL-path predicate leaf scalar
/// on the Cilium `cilium.io/v2` `CiliumNetworkPolicy` CRD's per-
/// `toPorts[].rules.http[]` entry, the Gateway-API-side axis is a per-
/// `HTTPRouteMatch` path-matcher two-leaf container (`{type, value}`)
/// on the K8s Gateway API v1 `HTTPRoute` CRD's `spec.rules[].matches[]`
/// entry. Keeping them as sibling `pub const` declarations (rather than
/// coalescing onto a single shared constant that happens to carry the
/// same string) mirrors the deliberate axis-independence discipline the
/// [`CILIUM_KIND_NETWORK_POLICY`] / [`GATEWAY_API_KIND_GATEWAY`] /
/// [`GATEWAY_API_KIND_HTTP_ROUTE`] kind-discriminator lifts already
/// codified on the sibling per-CRD-kind axes, so a future Cilium-side
/// per-HTTP-rule URL-path-predicate rebrand (Cilium `cilium.io/v3` renames
/// `path` → `urlPath`) can land independently of the Gateway-API-side
/// per-`HTTPRouteMatch` path-matcher container-axis rebrand without any
/// cross-CRD coordination footgun where a shared constant would force a
/// coupled edit against schema evolutions the two CRD projects run on
/// independent cadences. Note: Rust's `&'static str` interner coalesces
/// identical byte-sequences onto one storage allocation at codegen time,
/// so at runtime a `.as_ptr()` comparison between the two constants can't
/// distinguish "sibling `pub const` declarations carrying identical
/// bytes" from "coalesced canonical declaration" — the axis-independence
/// discipline lives at the rustc symbol-name axis (the two `pub const
/// CILIUM_KEY_PATH` / `pub const GATEWAY_API_KEY_PATH` symbols a future
/// rebrand of one leaves the other structurally untouched under) rather
/// than the runtime-address axis, and the per-axis re-export identity
/// pins in the consuming renderer crates (each pinning the local re-
/// export against its own canonical declaration on its own axis) remain
/// the load-bearing "no sibling local `pub const` drift" gate for the
/// pair.
///
/// [cm]: ../../caixa_mesh/index.html
pub const CILIUM_KEY_PATH: &str = "path";
/// Canonical K8s Gateway API CRD `kind` discriminator the rendered
/// `Gateway` document declares at its top-level [`KUBE_KEY_KIND`] axis.
/// Pairs with the sibling [`GATEWAY_API_API_VERSION`] (3c6cfc3) — the
/// K8s apiserver-side CRD resolution contract is the
/// `(apiVersion, kind)` tuple keyed against the registered
/// `CustomResourceDefinition`, so drift on the kind axis is exactly as
/// load-bearing as drift on the apiVersion axis it accompanies (the
/// apiserver's `RESTMapper` consults both together; a
/// `("gateway.networking.k8s.io/v1", "Gatway")` typo at the production-
/// code call site lands outside the registered Gateway-API-conformant
/// `Gateway` CRD's `RESTKind` lookup, surfacing apply-side as a
/// non-self-locating "no kind 'Gatway' is registered for version
/// 'gateway.networking.k8s.io/v1'" error far from the source caixa.lisp
/// / the renderer's [`kube_resource_skeleton`] call site).
///
/// The single source of truth the rendered Aplicacao Gateway-API-side
/// ingress bundle's `Gateway`-naming axis reaches for:
///
/// - the rendered `Gateway` document's top-level [`KUBE_KEY_KIND`]
/// axis (caixa-mesh/src/lib.rs:578 — the `gateway_routes` per-
/// Aplicacao `Gateway` [`kube_resource_skeleton`] kind argument).
///
/// The kind axis names the same Gateway-API-conformant CRD discriminator
/// as the sibling [`GATEWAY_API_API_VERSION`] apiVersion axis and must
/// move together on any future Gateway-API rebrand. Until this lift
/// landed the axis carried an inline `Gateway` literal at the one
/// production-code occurrence in caixa-mesh/src/lib.rs:578 (the
/// `gateway_routes` `Gateway` [`kube_resource_skeleton`] kind argument)
/// plus a matching set inside the in-file
/// `gateway_carries_canonical_kube_skeleton_without_labels` /
/// `render_all_includes_every_artifact_kind` test fixtures plus the
/// `find()` predicate of every per-Gateway-kind test that picks the
/// `Gateway` document out of the rendered Aplicacao mesh bundle — five
/// occurrences of the same load-bearing Gateway-API-CRD-`kind`-
/// discriminator convention, drift-prone by construction. A drift on
/// the top-level `Gateway` `kind` axis would have surfaced as a
/// non-self-locating "no kind 'Gatway' is registered for version
/// 'gateway.networking.k8s.io/v1'" error far from the source caixa.lisp
/// at apply parse time, with the rendered per-Aplicacao Gateway never
/// landing in the apiserver-side CRD registration and every external
/// `:entrada` flow dropping at the gateway-class-controller's reconcile
/// loop with no field naming the kind-discriminator-drift root cause.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
/// [`FLUX_KIND_KUSTOMIZATION`] (4114773) /
/// [`FLUX_KIND_HELM_RELEASE`] (e24ea3c) /
/// [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
/// [`GATEWAY_API_API_VERSION`] (3c6cfc3) lifts established on the
/// sibling cluster-side-CRD-`kind`-discriminator + canonical-CRD-
/// group/version axes — extends the discipline from the apiVersion
/// half of the `(apiVersion, kind)` CRD-lookup tuple onto the kind
/// half on the same Gateway-API-CRD-axis, beginning the per-Gateway-
/// API-CRD kind+apiVersion lift pair the M3 Aplicacao mesh renderer's
/// external `:entrada` ingress contract rests on. The render-side
/// consumer now threads the same `&'static str` through its
/// [`kube_resource_skeleton`] call so a future Gateway-API rebrand
/// lands in one place; every future renderer that reaches for the
/// canonical Gateway-API `Gateway` kind (the future M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
/// Gateway fan-out, a future per-cluster `GatewayClass` renderer the
/// operator emits for per-cluster gateway-class scoping, a future
/// per-edge `TCPRoute` / `TLSRoute` / `GRPCRoute` renderer for non-HTTP
/// `:entrada` edges that pair against this same `Gateway` parent)
/// inherits the same value by construction with no opportunity for
/// per-renderer drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
/// [`FLUX_KIND_KUSTOMIZATION`] (4114773) /
/// [`FLUX_KIND_HELM_RELEASE`] (e24ea3c) /
/// [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
/// [`GATEWAY_API_API_VERSION`] (3c6cfc3) /
/// [`CILIUM_API_VERSION`] (279d611) lifts apply on the peer
/// canonical-cluster-side-CRD-discriminator surface.
///
/// [cm]: ../../caixa_mesh/index.html
pub const GATEWAY_API_KIND_GATEWAY: &str = "Gateway";
/// Canonical K8s Gateway API CRD `kind` discriminator the rendered
/// `HTTPRoute` document declares at its top-level [`KUBE_KEY_KIND`] axis.
/// Pairs with the sibling [`GATEWAY_API_API_VERSION`] (3c6cfc3) and the
/// peer [`GATEWAY_API_KIND_GATEWAY`] (fb4639c) — the K8s apiserver-side
/// CRD resolution contract is the `(apiVersion, kind)` tuple keyed
/// against the registered `CustomResourceDefinition`, so drift on the
/// kind axis is exactly as load-bearing as drift on the apiVersion axis
/// it accompanies (the apiserver's `RESTMapper` consults both together;
/// a `("gateway.networking.k8s.io/v1", "HTTPRout")` typo at the
/// production-code call site lands outside the registered Gateway-API-
/// conformant `HTTPRoute` CRD's `RESTKind` lookup, surfacing apply-side
/// as a non-self-locating "no kind 'HTTPRout' is registered for version
/// 'gateway.networking.k8s.io/v1'" error far from the source caixa.lisp /
/// the renderer's [`kube_resource_skeleton`] call site).
///
/// The single source of truth the rendered Aplicacao Gateway-API-side
/// ingress bundle's `HTTPRoute`-naming axis reaches for:
///
/// - the rendered `HTTPRoute` document's top-level [`KUBE_KEY_KIND`]
/// axis (caixa-mesh/src/lib.rs:663 — the `gateway_routes` per-
/// Aplicacao `HTTPRoute` [`kube_resource_skeleton`] kind argument).
///
/// The kind axis names the same Gateway-API-conformant CRD discriminator
/// as the sibling [`GATEWAY_API_API_VERSION`] apiVersion axis and the
/// peer [`GATEWAY_API_KIND_GATEWAY`] parent-Gateway axis, and must move
/// together with both on any future Gateway-API rebrand. Until this lift
/// landed the axis carried an inline `HTTPRoute` literal at the one
/// production-code occurrence in caixa-mesh/src/lib.rs:663 (the
/// `gateway_routes` `HTTPRoute` [`kube_resource_skeleton`] kind argument)
/// plus a matching set inside the in-file
/// `httproute_carries_canonical_kube_skeleton_without_labels` /
/// `render_all_includes_every_artifact_kind` test fixtures plus the
/// `find()` predicate of every per-HTTPRoute-kind test that picks the
/// `HTTPRoute` document out of the rendered Aplicacao mesh bundle —
/// multiple occurrences of the same load-bearing Gateway-API-CRD-`kind`-
/// discriminator convention, drift-prone by construction.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`GATEWAY_API_KIND_GATEWAY`] (fb4639c) /
/// [`CILIUM_KIND_NETWORK_POLICY`] (eac85cb) /
/// [`FLUX_KIND_KUSTOMIZATION`] (4114773) /
/// [`FLUX_KIND_HELM_RELEASE`] (e24ea3c) /
/// [`FLUX_KIND_GIT_REPOSITORY`] (dbbcf29) /
/// [`GATEWAY_API_API_VERSION`] (3c6cfc3) lifts established on the
/// sibling cluster-side-CRD-`kind`-discriminator + canonical-CRD-
/// group/version axes — completes the per-Gateway-API-CRD `kind`-axis
/// lift trajectory across the `(Gateway, HTTPRoute)` pair that the
/// renderer's `gateway_routes` external `:entrada` ingress contract
/// emits together. Every guarantee in [MESH-COMPOSITION.md §V][mc] —
/// "every Aplicacao with `:entrada` emits one `Gateway` + one
/// `HTTPRoute` per `:paths` entry pointing at the same
/// `gateway.networking.k8s.io/v1` group/version — now threads through
/// one lifted `&'static str` apiece for both halves of the pair, so a
/// future Gateway-API rebrand lands at one substrate-side edit-point
/// per axis and no per-renderer drift surface remains across the pair.
///
/// A future Gateway-API-side renderer the M3.x absorption roadmap
/// names — `TCPRoute`, `TLSRoute`, `GRPCRoute` for non-HTTP `:entrada`
/// edges, the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-Aplicacao `HTTPRoute` fan-out, a future per-edge
/// route-attached-policy renderer (`BackendTLSPolicy`,
/// `BackendLBPolicy`) — inherits the canonical `HTTPRoute` kind
/// discriminator by construction with no opportunity for per-renderer
/// drift.
///
/// [mc]: https://github.com/pleme-io/theory/blob/main/MESH-COMPOSITION.md
/// [cm]: ../../caixa_mesh/index.html
pub const GATEWAY_API_KIND_HTTP_ROUTE: &str = "HTTPRoute";
/// Canonical K8s Gateway API `Gateway.spec.listeners[].protocol` HTTP
/// listener-protocol scalar value the rendered `Gateway` document's
/// first (and V0-only) listener declares under its
/// [`KUBE_KEY_PROTOCOL`] axis. Pairs with the sibling
/// [`GATEWAY_API_KIND_GATEWAY`] (fb4639c) +
/// [`GATEWAY_API_KIND_HTTP_ROUTE`] (1adccc0) — the K8s Gateway API v1
/// CRD schema pins the per-listener L7 parser + TLS-termination
/// strategy through the `spec.listeners[].protocol` scalar value (the
/// gateway-class-controller's per-listener bind loop selects the L7
/// parser + TLS termination strategy from this exact byte-sequence;
/// the Gateway API v1 `ProtocolType` OpenAPI schema enum admits the
/// closed set `{"HTTP", "HTTPS", "TCP", "TLS", "UDP"}` verbatim), so
/// drift on the listener-protocol value is exactly as load-bearing as
/// drift on the sibling [`GATEWAY_API_KIND_GATEWAY`] +
/// [`GATEWAY_API_KIND_HTTP_ROUTE`] CRD `kind` discriminators the pair
/// declares together (a `("Gateway", "http")` /
/// `("Gateway", "Http")` / `("Gateway", "http/1.1")` typo at the
/// production-code call site lands outside the Gateway API v1
/// `ProtocolType` OpenAPI schema enum, surfacing apply-side as a
/// non-self-locating "spec.listeners[0].protocol: Unsupported value:
/// \"http\": supported values: \"HTTP\", \"HTTPS\", \"TCP\", \"TLS\",
/// \"UDP\"" apiserver admission-rejection far from the source
/// `caixa.lisp` / the renderer's `listener.insert(…)` call site — the
/// rendered per-Aplicacao `Gateway` object never reconciles at the
/// gateway-class-controller's per-listener bind loop and every
/// external `:entrada` HTTP flow drops at the gateway-class-
/// controller's admission gate with no field naming the
/// listener-protocol-drift root cause).
///
/// The single source of truth the rendered Aplicacao Gateway-API-side
/// ingress bundle's per-listener L7-parser-selection axis reaches for:
///
/// - the rendered `Gateway` document's `spec.listeners[0].protocol`
/// axis (the `gateway_routes` per-`:entrada` `Gateway` emitter's
/// `listener.insert(KUBE_KEY_PROTOCOL, "HTTP")` call — the sole
/// production-code call site the prior inline `"HTTP".into()`
/// literal sat at, caixa-mesh/src/lib.rs:2123).
///
/// The listener-protocol value names the same Gateway-API-
/// implementation-side per-listener L7-parser-selection scalar as the
/// sibling [`KUBE_KEY_PROTOCOL`] key-axis discriminator carries the
/// value under, and must move together with the sibling K8s Gateway
/// API `ProtocolType` OpenAPI schema enum on any future Gateway API
/// rebrand (an upstream Gateway API v2 rename of the HTTP listener
/// protocol from `HTTP` to `HTTP/1.1` / `HTTP/2` / `http`, coordinated
/// with the upstream SIG-Network Gateway API `ProtocolType` enum
/// deprecation cycle, would land at this one const rather than
/// scattered across every per-emitter listener-block-insertion site).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`GATEWAY_API_KIND_GATEWAY`] (fb4639c) /
/// [`GATEWAY_API_KIND_HTTP_ROUTE`] (1adccc0) /
/// [`DEFAULT_GATEWAY_CLASS_NAME`] (d9b0743) lifts established on the
/// sibling Gateway-API-CRD-`kind`-discriminator + Gateway-controller-
/// binding-scalar-value axes — extends the per-Gateway-API-CRD-`kind`-
/// discriminator lift pair across the `(Gateway, HTTPRoute)` pair
/// onto the sibling per-Gateway `spec.listeners[].protocol`
/// listener-protocol-scalar-value axis the same `gateway_routes`
/// external `:entrada` ingress emitter carries.
///
/// A future Gateway-API-side renderer the M3.x absorption roadmap
/// names — an HTTPS listener with TLS termination (a sibling
/// `GATEWAY_API_PROTOCOL_HTTPS` const value the same enum admits),
/// the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` materializer's
/// per-Aplicacao multi-listener fan-out over `{HTTP, HTTPS, TLS}`,
/// a future per-listener route-attached-policy renderer that binds
/// distinct policy chains per listener-protocol — inherits the
/// canonical `HTTP` listener-protocol value by construction with no
/// opportunity for per-renderer drift.
///
/// [cm]: ../../caixa_mesh/index.html
pub const GATEWAY_API_PROTOCOL_HTTP: &str = "HTTP";
/// Canonical K8s Gateway API v1 `PathMatchType` OpenAPI schema enum's
/// `PathPrefix` per-`HTTPRouteMatch` path-selection-predicate discriminator
/// value every `gateway_routes`-emitted `HTTPRoute` per-rule `matches[]`
/// entry declares under its per-match `spec.rules[].matches[].path.type`
/// scalar axis. Pairs with the sibling [`GATEWAY_API_KEY_PATH`] (9f45aa4)
/// per-`HTTPRouteMatch` path-matcher container-axis key it nests one level
/// beneath — the Gateway API v1 CRD schema pins per-`HTTPRouteMatch`
/// request-path selection through the `spec.rules[].matches[].path`
/// container axis (each match entry names one path-selection predicate the
/// request line's `:path` pseudo-header must satisfy under a `type`
/// discriminator scalar value; the Gateway API v1 `PathMatchType` OpenAPI
/// schema enum admits the closed set `{"Exact", "PathPrefix",
/// "RegularExpression"}` verbatim), so drift on the path-match-type value
/// is exactly as load-bearing as drift on the sibling
/// [`GATEWAY_API_PROTOCOL_HTTP`] (1b57473) per-listener L7-parser-selection
/// scalar value the peer `spec.listeners[].protocol` axis carries (a
/// `"pathPrefix"` / `"path_prefix"` / `"Prefix"` / `"path-prefix"` typo at
/// the production-code call site lands outside the Gateway API v1
/// `PathMatchType` OpenAPI schema enum's admitted set, surfacing apply-side
/// as a non-self-locating "spec.rules[0].matches[0].path.type: Unsupported
/// value: \"pathPrefix\": supported values: \"Exact\", \"PathPrefix\",
/// \"RegularExpression\"" apiserver admission-rejection far from the
/// source `caixa.lisp` / the renderer's `path_match.insert(…)` call site —
/// the rendered per-Aplicacao `HTTPRoute` object never reconciles at the
/// gateway-class-controller's per-rule L7 dispatch loop and every external
/// `:entrada` path-filtered flow drops at the gateway-class-controller's
/// admission gate with no field naming the path-match-type-drift root
/// cause).
///
/// The single source of truth the rendered Aplicacao Gateway-API-side
/// ingress bundle's per-`HTTPRouteMatch` path-selection-predicate-
/// discriminator-value-naming reaches for:
///
/// - the rendered `HTTPRoute` document's per-match
/// `spec.rules[].matches[].path.type` axis (caixa-mesh/src/lib.rs —
/// the `gateway_routes` per-match `path_match.insert("type",
/// "PathPrefix")` call the prior inline `"PathPrefix".into()` literal
/// sat at).
///
/// The path-match-type value names the same Gateway-API-implementation-
/// side per-`HTTPRouteMatch` request-path-selection-predicate discriminator
/// as the sibling [`GATEWAY_API_KEY_PATH`] path-matcher container-axis key
/// carries the value under, and must move together with the sibling K8s
/// Gateway API v1 `PathMatchType` OpenAPI schema enum on any future
/// Gateway API rebrand (an upstream Gateway API v2 rename of the prefix-
/// path-selection discriminator from `PathPrefix` to `Prefix` / `path-
/// prefix` / `PathPrefixMatch`, coordinated with the upstream SIG-Network
/// Gateway API `PathMatchType` enum deprecation cycle, would land at this
/// one const rather than scattered across every per-emitter per-match
/// path-block-insertion site).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`GATEWAY_API_PROTOCOL_HTTP`] (1b57473) /
/// [`GATEWAY_API_KIND_GATEWAY`] (fb4639c) /
/// [`GATEWAY_API_KIND_HTTP_ROUTE`] (1adccc0) /
/// [`DEFAULT_GATEWAY_CLASS_NAME`] (d9b0743) lifts established on the
/// sibling per-listener L7-parser-selection scalar-value +
/// Gateway-API-CRD-`kind`-discriminator + Gateway-controller-binding
/// scalar-value axes — extends the canonical-Gateway-API-v1-OpenAPI-
/// schema-enum-value single-sourcing discipline the `ProtocolType.HTTP`
/// lift established onto the sibling `PathMatchType.PathPrefix`
/// per-`HTTPRouteMatch` path-selection-predicate discriminator the same
/// `gateway_routes` external `:entrada` ingress emitter carries under
/// the shared `HTTPRoute` body.
///
/// A future Gateway-API-side renderer the M3.x absorption roadmap
/// names — a sibling `GATEWAY_API_PATH_MATCH_TYPE_EXACT` /
/// `GATEWAY_API_PATH_MATCH_TYPE_REGULAR_EXPRESSION` const value the same
/// `PathMatchType` enum admits, the future M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` materializer's per-Aplicacao
/// multi-predicate fan-out over `{Exact, PathPrefix, RegularExpression}`,
/// a future per-match `:entrada :paths` typed slot admitting a per-path
/// `(:predicate <Exact|Prefix|Regex>)` axis — inherits the canonical
/// `PathPrefix` path-match-type value by construction with no opportunity
/// for per-renderer drift.
///
/// [cm]: ../../caixa_mesh/index.html
pub const GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX: &str = "PathPrefix";
/// Canonical K8s core `Protocol` OpenAPI schema enum's `TCP` L4-transport-
/// protocol scalar value every `cilium_network_policies`-emitted
/// `CiliumNetworkPolicy` document's per-`spec.ingress[].toPorts[].ports[]`
/// port-tuple declares under its per-tuple [`KUBE_KEY_PROTOCOL`] axis.
/// Pairs with the sibling [`KUBE_KEY_PROTOCOL`] (0307950) per-CR L4/L7
/// protocol-scalar-discriminator container-axis key the value nests
/// directly under — the K8s core `Protocol` schema pins per-`ContainerPort`
/// / `ServicePort` / `EndpointPort` / `NetworkPolicyPort` L4-transport
/// selection through the `protocol` scalar (each port entry names one
/// L4-transport-protocol discriminator the CNI / kube-proxy / eBPF-data-
/// plane bpf policy dispatch loop keys off before applying the port match;
/// the K8s core `Protocol` OpenAPI schema enum admits the closed set
/// `{"TCP", "UDP", "SCTP"}` verbatim — see
/// https://kubernetes.io/docs/reference/generated/kubernetes-api/v1/#protocol-v1-core),
/// so drift on the L4-transport-protocol value is exactly as load-bearing
/// as drift on the sibling [`GATEWAY_API_PROTOCOL_HTTP`] (1b57473) per-
/// listener L7-parser-selection scalar value the peer Gateway-API v1
/// `ProtocolType` OpenAPI schema enum admits under the same
/// [`KUBE_KEY_PROTOCOL`] container-axis key (a `"tcp"` / `"Tcp"` /
/// `"TCP/IP"` / `"transport-tcp"` typo at the production-code call site
/// lands outside the K8s core `Protocol` OpenAPI schema enum's admitted
/// set, surfacing apply-side as a non-self-locating
/// "spec.ingress[0].toPorts[0].ports[0].protocol: Unsupported value:
/// \"tcp\": supported values: \"SCTP\", \"TCP\", \"UDP\"" apiserver
/// admission-rejection far from the source `caixa.lisp` / the renderer's
/// `port_entry.insert(…)` call site — the rendered per-`(:de, :para)`
/// `CiliumNetworkPolicy` object never reconciles at the Cilium operator's
/// per-CNP L4 dispatch pass and every intra-mesh `:contratos` L4-tuple-
/// gated flow drops at the Cilium operator's admission gate with no field
/// naming the L4-transport-protocol-drift root cause; worse — because the
/// `protocol` scalar carries a schema-side default of `TCP` on the K8s
/// core `Protocol` enum, a silently-elided drift on the emit lands a
/// `CiliumNetworkPolicy` whose ingress rule falls back to the default L4-
/// transport-protocol and every port-match on a non-default transport
/// silently misses at the eBPF data plane's per-tuple dispatch).
///
/// The single source of truth the rendered Aplicacao Cilium-CNP-side
/// intra-mesh L4-tuple-gating bundle's per-`toPorts[].ports[]` port-tuple
/// L4-transport-protocol-discriminator-value-naming reaches for:
///
/// - the rendered `CiliumNetworkPolicy` document's per-tuple
/// `spec.ingress[].toPorts[].ports[].protocol` axis (caixa-mesh/src/lib.rs —
/// the `cilium_network_policies` per-`(:de, :para)`
/// `port_entry.insert(KUBE_KEY_PROTOCOL, "TCP")` call the prior
/// inline `"TCP".into()` literal sat at).
///
/// The L4-transport-protocol value names the same K8s-core-`Protocol`-
/// enum-side per-port-tuple L4-transport-selection discriminator as the
/// sibling [`KUBE_KEY_PROTOCOL`] key-axis discriminator carries the value
/// under, and must move together with the sibling K8s core `Protocol`
/// OpenAPI schema enum on any future K8s core `Protocol` rebrand (an
/// upstream K8s core `Protocol` rename or extension — e.g. the
/// `KEP-3675 QUIC transport` proposal's `"QUIC"` addition to the enum,
/// coordinated with the upstream SIG-Network per-version deprecation
/// cycle — would land at this one const rather than scattered across
/// every per-emitter L4-port-block-insertion site).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`GATEWAY_API_PROTOCOL_HTTP`] (1b57473) /
/// [`GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX`] (530705d) /
/// [`GATEWAY_API_KIND_GATEWAY`] (fb4639c) /
/// [`GATEWAY_API_KIND_HTTP_ROUTE`] (1adccc0) /
/// [`DEFAULT_GATEWAY_CLASS_NAME`] (d9b0743) lifts established on the
/// sibling per-listener L7-parser-selection scalar-value + per-match
/// path-selection-predicate discriminator-value + Gateway-API-CRD-
/// `kind`-discriminator + Gateway-controller-binding scalar-value axes —
/// extends the canonical-cluster-side-OpenAPI-schema-enum-value single-
/// sourcing discipline the Gateway-API v1 `ProtocolType.HTTP` /
/// `PathMatchType.PathPrefix` lifts established onto the sibling
/// K8s-core `Protocol.TCP` per-port-tuple L4-transport-protocol-
/// discriminator the `cilium_network_policies` intra-mesh L4-tuple-gating
/// emitter carries under the shared `CiliumNetworkPolicy` body.
///
/// A future Cilium-CNP-side / K8s-core-`Protocol`-side renderer the M3.x
/// absorption roadmap names — a sibling `KUBE_PROTOCOL_UDP` /
/// `KUBE_PROTOCOL_SCTP` const value the same `Protocol` enum admits, the
/// future M4 `mesh.pleme.io/v1alpha1/Aplicacao` materializer's per-
/// Aplicacao multi-transport fan-out over `{TCP, UDP, SCTP}` for
/// `nats:pub-sub` / `wasi:sockets/udp` contratos, a future per-contrato
/// `:transport <TCP|UDP|SCTP>` typed slot admitting a per-edge transport-
/// protocol axis — inherits the canonical `TCP` L4-transport-protocol
/// value by construction with no opportunity for per-renderer drift.
///
/// [cm]: ../../caixa_mesh/index.html
pub const KUBE_PROTOCOL_TCP: &str = "TCP";
/// Canonical Cilium `CiliumNetworkPolicy` `MutualAuthenticationMode` OpenAPI
/// schema enum's `required` per-`ingress[].authentication.mode` mTLS-mandatory
/// scalar-value every `cilium_network_policies`-emitted CNP document declares
/// under its per-rule mutual-auth-mode-discriminator leaf axis when the typed
/// `:politicas :mtls-required` tristate is `Some(true)`. Pairs with the sibling
/// [`CILIUM_KEY_MODE`] (4289dfb) per-authn-block mode-discriminator leaf-axis
/// key the value nests directly under, and the sibling
/// [`CILIUM_AUTH_MODE_DISABLED`] scalar-value the `Some(false)` opt-out arm of
/// the same tristate emits — the Cilium CNP `MutualAuthenticationMode` OpenAPI
/// schema enum admits the closed set `{"required", "disabled", "test-always-
/// fail"}` verbatim (the `test-always-fail` arm is an infrastructure-side
/// debugging surface, not an author-reachable slot), so drift on the mTLS-
/// mandatory scalar-value is exactly as load-bearing as drift on the sibling
/// per-authn-block mode-discriminator leaf axis it nests under (a `"Required"`
/// / `"REQUIRED"` / `"mandatory"` / `"mtls-required"` typo at either the
/// production-code call site or a downstream probe lands outside the Cilium
/// CNP `MutualAuthenticationMode` OpenAPI schema enum's admitted set,
/// surfacing apply-side as a Cilium-agent per-rule mutual-auth-block schema-
/// validator drop far from the source `caixa.lisp` / the renderer's
/// `single_field_overlay(mtls_required, CILIUM_KEY_MODE, …)` call site — the
/// rendered per-`(:de, :para)` `CiliumNetworkPolicy` object never enforces
/// per-edge SPIFFE-identity-bound mutual-auth at the Cilium data-plane's per-
/// rule handshake gate and every intra-mesh `:contratos` flow the CNP was
/// authored to protect with per-edge mTLS silently bypasses the handshake at
/// the Cilium data-plane's default-authentication mode with no field naming
/// the mTLS-mandatory-scalar-value-drift root cause).
///
/// The single source of truth the rendered Aplicacao Cilium-CNP-side per-edge
/// mutual-auth-mode-discriminator affirmative-value-naming reaches for:
///
/// - the rendered `CiliumNetworkPolicy` document's per-rule
/// `spec.ingress[].authentication.mode` leaf value (caixa-mesh/src/lib.rs
/// — the `cilium_network_policies` per-`(:de, :para)`
/// `single_field_overlay(spec.politicas.mtls_required, CILIUM_KEY_MODE,
/// |required| …)` closure's `if required { … }` arm the prior inline
/// `"required".into()` literal sat at, plus every test-fixture navigation
/// that pins the emitted value under the `:mtls-required t` presence,
/// fan-out, and pubsub-carry-overlay-too shapes).
///
/// The mTLS-mandatory scalar-value names the same Cilium-agent-side per-rule
/// SPIFFE-identity-handshake-mandatory enforcement policy as the sibling
/// [`CILIUM_KEY_MODE`] leaf-axis key carries the value under, and must move
/// together with the sibling Cilium CNP `MutualAuthenticationMode` OpenAPI
/// schema enum on any future Cilium CRD schema rebrand (an upstream
/// `cilium.io/v3` rename of the mTLS-mandatory scalar-value from `required`
/// to `enforce` / `mandatory` / `strict`, coordinated with the Cilium
/// project's periodic CRD schema-migration passes, would land at this one
/// const rather than scattered across every per-emitter per-rule authn-block-
/// insertion site).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5, "every
/// recurring shape becomes a generator before it becomes a pattern; every
/// pattern becomes a library before it becomes duplicated code. The
/// duplication budget is zero.") promotes the constant to a typed substrate-
/// side `&'static str` on the same trajectory the
/// [`GATEWAY_API_PROTOCOL_HTTP`] (1b57473) /
/// [`GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX`] (530705d) /
/// [`KUBE_PROTOCOL_TCP`] (2123047) scalar-value lifts established on the
/// sibling canonical-cluster-side-OpenAPI-schema-enum-value surfaces —
/// extends the canonical-cluster-side-OpenAPI-schema-enum-value single-
/// sourcing discipline the Gateway-API v1 `ProtocolType.HTTP` /
/// `PathMatchType.PathPrefix` / K8s-core `Protocol.TCP` lifts established
/// onto the sibling Cilium-CNP-side `MutualAuthenticationMode.required`
/// per-rule mTLS-mandatory scalar-value the `cilium_network_policies` per-
/// edge SPIFFE-identity-bound mutual-auth emitter carries under the shared
/// `CiliumNetworkPolicy` body.
///
/// [cm]: ../../caixa_mesh/index.html
pub const CILIUM_AUTH_MODE_REQUIRED: &str = "required";
/// Canonical Cilium `CiliumNetworkPolicy` `MutualAuthenticationMode` OpenAPI
/// schema enum's `disabled` per-`ingress[].authentication.mode` mTLS-skipped
/// scalar-value every `cilium_network_policies`-emitted CNP document declares
/// under its per-rule mutual-auth-mode-discriminator leaf axis when the typed
/// `:politicas :mtls-required` tristate is the explicit `Some(false)` opt-out
/// arm (an author who *named* the axis and asked for the mTLS handshake to be
/// skipped on this Aplicacao's edges — e.g. a debug or legacy-bridge
/// Aplicacao that needs to talk to non-mesh peers, distinct from the `None`
/// slot-absent arm the renderer maps to omit-the-block-entirely). Peer to
/// the sibling [`CILIUM_AUTH_MODE_REQUIRED`] mTLS-mandatory scalar-value the
/// `Some(true)` affirmative arm emits under the same tristate branch — the
/// Cilium CNP `MutualAuthenticationMode` OpenAPI schema enum admits the two
/// arms as a matched author-reachable pair.
///
/// The single source of truth the rendered Aplicacao Cilium-CNP-side per-edge
/// mutual-auth-mode-discriminator negative-value-naming reaches for:
///
/// - the rendered `CiliumNetworkPolicy` document's per-rule
/// `spec.ingress[].authentication.mode` leaf value (caixa-mesh/src/lib.rs
/// — the `cilium_network_policies` per-`(:de, :para)`
/// `single_field_overlay(spec.politicas.mtls_required, CILIUM_KEY_MODE,
/// |required| …)` closure's `else { … }` arm the prior inline
/// `"disabled".into()` literal sat at, plus the
/// `cnp_explicit_mtls_required_false_emits_disabled_mode` test-fixture
/// probe that pins the explicit-opt-out arm's rendered value).
///
/// Same drift-mode risk as the sibling [`CILIUM_AUTH_MODE_REQUIRED`] pin: a
/// `"Disabled"` / `"DISABLED"` / `"off"` / `"skip"` typo lands outside the
/// Cilium CNP `MutualAuthenticationMode` OpenAPI schema enum's admitted set;
/// the rendered per-`(:de, :para)` `CiliumNetworkPolicy` object never reaches
/// the Cilium agent's per-rule mutual-auth-block schema validator's admitted
/// set and the author's explicit-opt-out intent silently collapses onto the
/// cluster-default authentication mode (typically also "disabled" today, but
/// environment-divergent — take effect) with no field naming the mTLS-
/// skipped-scalar-value-drift root cause.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5, "every
/// recurring shape becomes a generator before it becomes a pattern; every
/// pattern becomes a library before it becomes duplicated code. The
/// duplication budget is zero.") promotes the constant to a typed substrate-
/// side `&'static str` on the same trajectory the sibling
/// [`CILIUM_AUTH_MODE_REQUIRED`] mTLS-mandatory scalar-value lift establishes
/// on the affirmative arm of the same `MutualAuthenticationMode` enum —
/// completes the per-authn-block `(mode → {required, disabled})` leaf-axis /
/// author-reachable-scalar-value-pair single-sourcing the M3 Aplicacao mesh
/// renderer's SPIFFE-identity-bound per-edge mTLS enforcement + explicit-
/// opt-out contract rests on across the two arms of the `:politicas
/// :mtls-required` tristate.
///
/// [cm]: ../../caixa_mesh/index.html
pub const CILIUM_AUTH_MODE_DISABLED: &str = "disabled";
/// Canonical `bool → &'static str` bijection projection every consumer of the
/// Cilium `CiliumNetworkPolicy` `MutualAuthenticationMode` OpenAPI schema
/// enum's closed-set author-reachable scalar-value pair
/// ([`CILIUM_AUTH_MODE_REQUIRED`] / [`CILIUM_AUTH_MODE_DISABLED`]) consults
/// so the per-tristate-arm dispatch — `Some(true)` (mTLS handshake
/// mandatory) → [`CILIUM_AUTH_MODE_REQUIRED`], `Some(false)` (mTLS
/// handshake skipped, explicit opt-out) → [`CILIUM_AUTH_MODE_DISABLED`] —
/// lives in exactly one place. The two arms of the `:politicas
/// :mtls-required` tristate's non-`None` value-space each land on a
/// distinct `MutualAuthenticationMode` scalar; the `None` slot-absent arm
/// is the caller's [`single_field_overlay`] emission-gate concern (the
/// helper returns `None` and the outer `authentication:` block is omitted
/// entirely), not this projection's — see the per-emit-site
/// `if let Some(overlay) = mtls_overlay { rule.insert(CILIUM_KEY_AUTHENTICATION,
/// overlay.clone()) }` guard.
///
/// The single source of truth the rendered Aplicacao Cilium-CNP-side
/// per-edge mutual-auth-mode-discriminator scalar-value dispatch reaches
/// for:
///
/// - the rendered `CiliumNetworkPolicy` document's per-rule
/// `spec.ingress[].authentication.mode` leaf value (caixa-mesh/src/lib.rs
/// — the `cilium_network_policies` per-`(:de, :para)`
/// `single_field_overlay(spec.politicas.mtls_required, CILIUM_KEY_MODE,
/// |required| serde_yaml::Value::String(cilium_auth_mode(required).into()))`
/// closure body).
/// - the generic-helper pin in this crate's
/// `single_field_overlay_threads_typed_value_through_closure` test
/// that mirrors the production overlay's shape letter-for-letter and
/// now threads through the same shared projection.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5, "every
/// recurring shape becomes a generator before it becomes a pattern; every
/// pattern becomes a library before it becomes duplicated code. The
/// duplication budget is zero.") promotes the per-tristate-arm dispatch
/// body onto a shared projection on the same trajectory the sibling
/// [`CILIUM_AUTH_MODE_REQUIRED`] / [`CILIUM_AUTH_MODE_DISABLED`]
/// closed-set-scalar-value lifts established for the two arms of the
/// same `MutualAuthenticationMode` enum — closes the pair of related
/// lift trajectories the `(value-space, arm-dispatch)` per-authn-block
/// leaf's canonical decomposition rests on. The prior inline `if required
/// { CILIUM_AUTH_MODE_REQUIRED } else { CILIUM_AUTH_MODE_DISABLED }` body
/// split across the two occurrences — the caixa-mesh production emitter's
/// closure and the caixa-core generic-helper pin's closure — would have
/// let a per-arm reassignment (e.g. an upstream Cilium v3 schema rename
/// swap of the `required` ↔ `disabled` scalars, or the addition of a
/// third `MutualAuthenticationMode` variant that reshapes the closed set)
/// drift on one closure body but not the peer, silently letting a Cilium
/// data-plane pod either enforce mTLS where the author asked for skip or
/// skip it where the author asked for enforce.
///
/// Pairs with the [`CILIUM_KEY_MODE`] per-authentication-block mode-
/// discriminator leaf-axis key at the caller's
/// `single_field_overlay(spec.politicas.mtls_required, CILIUM_KEY_MODE,
/// |required| serde_yaml::Value::String(cilium_auth_mode(required).into()))`
/// call: the key is the field name the leaf mounts under, this projection
/// is the scalar the leaf carries. Same-shape peer to the K8s core
/// `Protocol` closed-set enum's future `bool → {"TCP", "UDP"}` /
/// K8s Gateway API v1 `PathMatchType` closed-set enum's future variant-
/// pick projections the M3.x absorption roadmap acknowledges — the M3
/// mesh renderer's `MutualAuthenticationMode` bijection surface is the
/// first landed instance of the canonical `(closed-set-CRD-schema-enum-
/// value pair, per-typed-arm dispatch projection)` compound.
///
/// [cm]: ../../caixa_mesh/index.html
#[must_use]
pub fn cilium_auth_mode(required: bool) -> &'static str {
if required {
CILIUM_AUTH_MODE_REQUIRED
} else {
CILIUM_AUTH_MODE_DISABLED
}
}
/// Canonical K8s Gateway API `HTTPRoute` parent-Gateway-binding container-
/// axis key every `gateway_routes`-emitted `HTTPRoute` document mounts its
/// per-route parent-Gateway `[{name}]` list under (`spec.parentRefs[]`).
/// Pairs with the sibling [`GATEWAY_API_KIND_HTTP_ROUTE`] (1adccc0) +
/// [`GATEWAY_API_KIND_GATEWAY`] (fb4639c) — the Gateway API v1 CRD schema
/// pins the per-HTTPRoute parent-Gateway identity through the
/// `spec.parentRefs[]` container axis (each entry names the parent
/// Gateway the route attaches to; the sibling `hostnames` + `rules`
/// container axes carry the per-route host-match + per-rule L7-dispatch
/// halves under the same `spec` block), so drift on the parent-Gateway-
/// binding axis is exactly as load-bearing as drift on the per-HTTPRoute
/// `kind` discriminator axis it accompanies (the K8s apiserver-side
/// Gateway API CRD schema validator drops any `spec` block whose parent-
/// binding container axis carries an unrecognized key — a `"parentRef"`
/// / `"parents"` / `"parentGateways"` typo silently emits an `HTTPRoute`
/// whose parent-Gateway attachment the Gateway API implementation's
/// per-HTTPRoute reconcile loop no-ops entirely: the route lands
/// unattached to any Gateway, and every external `:entrada` flow the
/// `HTTPRoute` was authored to accept drops at the Gateway API
/// implementation's per-Gateway HTTP-listener fan-in with no field
/// naming the parent-Gateway-binding-axis-drift root cause).
///
/// The single source of truth the rendered Aplicacao Gateway-API-side
/// ingress bundle's per-HTTPRoute parent-Gateway-binding-axis-naming
/// reaches for:
///
/// - the rendered `HTTPRoute` document's `spec.parentRefs[]` axis
/// (caixa-mesh/src/lib.rs:1389 — the `gateway_routes` per-Aplicacao
/// `HTTPRoute`'s `r_spec.insert("parentRefs", …)` call).
///
/// The parent-Gateway-binding axis names the same Gateway-API-
/// implementation-side per-HTTPRoute route→Gateway attachment container
/// as the sibling [`GATEWAY_API_KIND_HTTP_ROUTE`] +
/// [`GATEWAY_API_KIND_GATEWAY`] CRD `kind` discriminators the pair
/// declares together, and must move together on any future Gateway API
/// rebrand (an upstream Gateway API v2 rename of the parent-binding
/// axis from `parentRefs` to `parents` / `parentGateways` /
/// `attachedTo`, coordinated with the upstream SIG-Network Gateway API
/// deprecation cycle). Until this lift landed the axis carried an
/// inline `parentRefs` literal at the one production-code occurrence in
/// caixa-mesh/src/lib.rs:1389 (the `gateway_routes`
/// `r_spec.insert("parentRefs", …)` call) — the single load-bearing
/// Gateway-API-CRD-`parentRefs`-axis-key occurrence, drift-prone by
/// construction. A drift on the production site to `"parentRef"` /
/// `"parents"` / `"parentGateways"` would have surfaced as a Gateway-
/// API-implementation-side schema validator drop at apply time (the
/// affected `HTTPRoute`'s parent-Gateway-binding axis the CRD schema
/// validator recognizes as unknown), with every external `:entrada`
/// flow the `HTTPRoute` was authored to accept dropping at the Gateway
/// API implementation's per-Gateway HTTP-listener fan-in with no field
/// naming the parent-Gateway-binding-drift root cause.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`CILIUM_KEY_PORTS`] (1087693) /
/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) /
/// [`GATEWAY_API_KIND_HTTP_ROUTE`] (1adccc0) /
/// [`GATEWAY_API_KIND_GATEWAY`] (fb4639c) lifts established on the
/// sibling canonical-Cilium-CNP-body-axis /
/// canonical-Gateway-API-CRD-`kind`-discriminator surfaces — pivots the
/// per-CNP-body-axis lift discipline onto the sibling per-HTTPRoute-
/// body-axis surface, beginning the per-Gateway-API-HTTPRoute-body-axis
/// canonical-string-pin set (`parentRefs`, `hostnames`) the M3
/// Aplicacao mesh renderer's external `:entrada` ingress contract rests
/// on across the Gateway API HTTPRoute-side per-route body-shape. The
/// render-side consumer now threads the same `&'static str` through
/// its `r_spec.insert(…)` call so a future Gateway API rebrand on the
/// parent-Gateway-binding axis (or an upstream SIG-Network Gateway API
/// v2 rename to a per-CRD sibling name) lands in one place; every
/// future renderer that reaches for the canonical per-HTTPRoute parent-
/// Gateway-binding axis (the future M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
/// `HTTPRoute` fan-out, a future per-edge `TCPRoute` / `TLSRoute` /
/// `GRPCRoute` renderer for non-HTTP `:entrada` edges whose per-route
/// parent-Gateway-binding nests under the same axis convention, a
/// future per-Aplicacao `ReferenceGrant` renderer whose cross-namespace
/// parent-Gateway attachment binds against this same axis) inherits the
/// same value by construction with no opportunity for per-renderer
/// drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`CILIUM_KEY_PORTS`] (1087693) /
/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) /
/// [`GATEWAY_API_KIND_HTTP_ROUTE`] (1adccc0) /
/// [`GATEWAY_API_KIND_GATEWAY`] (fb4639c) lifts apply on the peer
/// canonical-Gateway-API-HTTPRoute-body-axis surface.
///
/// [cm]: ../../caixa_mesh/index.html
pub const GATEWAY_API_KEY_PARENT_REFS: &str = "parentRefs";
/// Canonical K8s Gateway API `HTTPRoute` per-`spec.parentRefs[]` entry
/// listener-selector sub-axis key every `gateway_routes`-emitted
/// `HTTPRoute` document mounts under each parent-Gateway attachment to
/// pin the route to one specific listener out of the parent Gateway's
/// `spec.listeners[]` list (`spec.parentRefs[].sectionName`). Pairs
/// with the sibling [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) — the
/// Gateway API v1 CRD schema pins per-HTTPRoute route→Gateway
/// attachment through the `spec.parentRefs[]` container axis and the
/// per-entry listener-selection sub-axis through `sectionName` beneath
/// each entry (each `SectionName`-typed scalar binds to a
/// `Gateway.spec.listeners[].name` byte-string). Omitting the
/// selector attaches the route to *every* listener on the parent
/// Gateway — the Gateway API v1 default fan-out that silently doubles
/// route emission once the substrate ships a second listener under
/// the HTTPS-by-default trajectory the peer
/// [`GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME`] (cd60fde) docstring
/// forecasts (`"http"` → `"http-v1"` alongside a sibling `"https"`
/// listener once cert-manager-issued per-`:entrada :host` certificates
/// land). Pinning the selector by construction binds each substrate-
/// emitted route to exactly one listener on the parent Gateway, so a
/// future multi-listener migration lands as one const-edit on the
/// paired [`GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME`] declaration
/// instead of a silent per-route dispatch flip.
///
/// The single source of truth the rendered Aplicacao Gateway-API-side
/// ingress bundle's per-HTTPRoute per-parentRef listener-selector-axis-
/// naming reaches for:
///
/// - the rendered `HTTPRoute` document's per-parentRef
/// `spec.parentRefs[].sectionName` axis (the `gateway_routes` per-
/// Aplicacao HTTPRoute's `parent_ref.insert(<KEY>, …)` call the
/// paired [`GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME`] `&'static str`
/// — the same byte-string the parent Gateway's sole
/// `listener.insert(GATEWAY_API_KEY_NAME, …)` call emits at
/// `spec.listeners[].name` — flows through, so a substrate-side
/// rebrand of the canonical listener-name identifier reaches both
/// the listener-name emitter and the sectionName selector by
/// construction).
///
/// The per-parentRef listener-selector sub-axis names the same
/// Gateway-API-implementation-side per-HTTPRoute route→listener
/// attachment sub-container as the sibling
/// [`GATEWAY_API_KEY_PARENT_REFS`] per-HTTPRoute parent-Gateway-binding
/// container axis it accompanies, and must move together on any future
/// Gateway API rebrand (an upstream SIG-Network Gateway API v2 rename
/// of the per-entry listener-selection sub-axis from `sectionName` to
/// `listenerName` / `listener` / `attachTo`, coordinated with the
/// Gateway API deprecation cycle). Until this lift landed the axis had
/// zero production-code call sites — the substrate emitted an
/// `HTTPRoute` whose `spec.parentRefs[]` entries omitted the selector
/// entirely, silently accepting the Gateway API v1 attach-to-every-
/// listener default fan-out. A future substrate-side second listener
/// under the same parent Gateway (the HTTPS-by-default trajectory the
/// [`GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME`] docstring forecasts)
/// would have silently doubled every route's emitted per-request
/// dispatch surface — every external `:entrada` request the route was
/// authored to accept on `:80` would have accepted a matching request
/// on `:443` too, with the second-listener leak surfacing only in per-
/// request access logs (never in `kubectl describe httproute` — the
/// implicit fan-out reads as intended per the Gateway API v1 spec).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
/// [`GATEWAY_API_KEY_MATCHES`] (8f9ed08) /
/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
/// [`GATEWAY_API_KEY_HOSTNAMES`] (bd7ea31) lifts established on the
/// sibling canonical-Gateway-API-HTTPRoute-body-axis surface — extends
/// the per-Gateway-API-HTTPRoute-body-axis canonical-string-pin set
/// onto the per-parentRef listener-selector sub-axis the M3 Aplicacao
/// mesh renderer's external `:entrada` ingress contract now rests on.
/// The render-side consumer threads the same `&'static str` through
/// its `parent_ref.insert(…)` call so a future Gateway API rebrand on
/// the per-parentRef listener-selector sub-axis (or an upstream SIG-
/// Network Gateway API v2 rename to a per-CRD sibling name) lands in
/// one place; every future renderer that reaches for the canonical
/// per-HTTPRoute per-parentRef listener-selector sub-axis (the future
/// M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-
/// Aplicacao `HTTPRoute` fan-out, a future per-edge `TCPRoute` /
/// `TLSRoute` / `GRPCRoute` renderer whose per-parentRef listener-
/// selection nests under the same axis convention) inherits the same
/// value by construction with no opportunity for per-renderer drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
/// [`GATEWAY_API_KEY_MATCHES`] (8f9ed08) lifts apply on the peer
/// canonical-Gateway-API-HTTPRoute-body-axis surface.
///
/// [cm]: ../../caixa_mesh/index.html
pub const GATEWAY_API_KEY_SECTION_NAME: &str = "sectionName";
/// Canonical K8s Gateway API `HTTPRoute` per-rule backend-destination
/// container-axis key every `gateway_routes`-emitted `HTTPRoute`
/// document mounts its per-rule `[{name, port}]` backend list under
/// (`spec.rules[].backendRefs[]`). Pairs with the sibling
/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) — the Gateway API v1 CRD
/// schema pins the per-HTTPRoute route→Gateway attachment through the
/// `spec.parentRefs[]` container axis and the per-rule route→Servico
/// backend fan-out through the `spec.rules[].backendRefs[]` axis
/// beneath each rule entry, so drift on the per-rule backend-destination
/// axis is exactly as load-bearing as drift on the per-HTTPRoute
/// parent-Gateway-binding axis it accompanies (the K8s apiserver-side
/// Gateway API CRD schema validator drops any per-rule block whose
/// backend-destination container axis carries an unrecognized key — a
/// `"backendRef"` / `"backends"` / `"forwardTo"` typo silently emits an
/// `HTTPRoute` whose per-rule backend fan-out the Gateway API
/// implementation's per-rule L7 dispatch loop no-ops entirely: no
/// backend is picked, and every external `:entrada` request the rule
/// was authored to route drops at the gateway-class-controller's
/// per-rule reconcile with no field naming the backend-destination-
/// axis-drift root cause).
///
/// The single source of truth the rendered Aplicacao Gateway-API-side
/// ingress bundle's per-HTTPRoute per-rule backend-destination-axis-
/// naming reaches for:
///
/// - the rendered `HTTPRoute` document's per-rule
/// `spec.rules[].backendRefs[]` axis (caixa-mesh/src/lib.rs:1414 —
/// the `gateway_routes` per-Aplicacao HTTPRoute's per-rule
/// `rule.insert("backendRefs", …)` call).
///
/// The per-rule backend-destination container axis names the same
/// Gateway-API-implementation-side per-rule route→Servico backend fan-
/// out container as the sibling [`GATEWAY_API_KEY_PARENT_REFS`] per-
/// HTTPRoute parent-Gateway-binding container axis it accompanies, and
/// must move together on any future Gateway API rebrand (an upstream
/// SIG-Network Gateway API v2 rename of the backend-destination axis
/// from `backendRefs` to `backends` / `forwardTo` / `to`, coordinated
/// with the Gateway API deprecation cycle). Until this lift landed the
/// axis carried an inline `backendRefs` literal at the one production-
/// code occurrence in caixa-mesh/src/lib.rs:1414 (the `gateway_routes`
/// per-rule `rule.insert("backendRefs", …)` call) plus a matching set
/// inside the in-file `httproute_routes_to_entrada_para` /
/// `httproute_rule_keys_pin_overlay_position` test-fixture navigations —
/// three occurrences of the same load-bearing Gateway-API-CRD-
/// `backendRefs`-axis-key convention, drift-prone by construction. A
/// drift on any one production or test-fixture site to `"backendRef"` /
/// `"backends"` / `"forwardTo"` would have surfaced as a Gateway API
/// implementation-side schema validator drop at apply time (the
/// affected per-rule backend-destination axis the CRD schema validator
/// recognizes as unknown), with every external `:entrada` request the
/// rule was authored to route dropping at the gateway-class-
/// controller's per-rule reconcile with no field naming the backend-
/// destination-drift root cause. A drift on the test-fixture side
/// silently masks the emission-side pin (`.get("backendRefs")` returns
/// `None` under both the drifted-key emitter and the drifted-key probe
/// — the downstream `.and_then(|b| b.as_sequence())` /
/// `.and_then(|s| s.first())` chain short-circuits vacuously because
/// the outer per-rule backend-destination lookup is itself `None`).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
/// [`CILIUM_KEY_PORTS`] (1087693) /
/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) lifts established on the sibling
/// canonical-Gateway-API-HTTPRoute-body-axis /
/// canonical-Cilium-CNP-body-axis surfaces — extends the per-Gateway-
/// API-HTTPRoute-body-axis canonical-string-pin set the sibling
/// `parentRefs` lift began (`parentRefs`, `backendRefs`, future
/// `hostnames`) the M3 Aplicacao mesh renderer's external `:entrada`
/// ingress contract rests on across the Gateway API HTTPRoute-side per-
/// route body-shape. The render-side consumer now threads the same
/// `&'static str` through its `rule.insert(…)` call so a future Gateway
/// API rebrand on the per-rule backend-destination axis (or an upstream
/// SIG-Network Gateway API v2 rename to a per-CRD sibling name) lands
/// in one place; every future renderer that reaches for the canonical
/// per-HTTPRoute per-rule backend-destination axis (the future M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
/// `HTTPRoute` fan-out, a future per-edge `TCPRoute` / `TLSRoute` /
/// `GRPCRoute` renderer for non-HTTP `:entrada` edges whose per-rule
/// backend-destination nests under the same axis convention, a future
/// per-route mirroring / traffic-split renderer whose per-weight
/// backend list binds against this same axis) inherits the same value
/// by construction with no opportunity for per-renderer drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
/// [`CILIUM_KEY_PORTS`] (1087693) /
/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) lifts apply on the peer canonical-
/// Gateway-API-HTTPRoute-body-axis surface.
///
/// [cm]: ../../caixa_mesh/index.html
pub const GATEWAY_API_KEY_BACKEND_REFS: &str = "backendRefs";
/// Canonical K8s Gateway API `HTTPRoute` per-rule route-match
/// container-axis key every `gateway_routes`-emitted `HTTPRoute`
/// per-rule block mounts its per-rule `[{path: {type, value}}]`
/// route-match fan-out list under (`spec.rules[].matches[]`). Pairs
/// with the sibling [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) — the
/// Gateway API v1 CRD schema pins per-rule request-selection through
/// the `spec.rules[].matches[]` container axis (each entry names one
/// `HTTPRouteMatch` predicate the request line + headers + query must
/// satisfy for the rule's backend fan-out to apply) alongside the
/// per-rule route→Servico backend fan-out under
/// `spec.rules[].backendRefs[]`, so drift on the per-rule route-match
/// axis is exactly as load-bearing as drift on the sibling per-rule
/// backend-destination axis it accompanies (the K8s apiserver-side
/// Gateway API CRD schema validator drops any per-rule block whose
/// route-match container axis carries an unrecognized key — a
/// `"match"` / `"routeMatches"` / `"predicates"` typo silently emits
/// an `HTTPRoute` whose per-rule request-selection axis the Gateway
/// API implementation's per-rule L7 dispatch loop no-ops entirely: no
/// request predicate is evaluated, the rule matches every request
/// unconditionally at the wildcard predicate, and every external
/// `:entrada` path filter the rule was authored to enforce drops at
/// the gateway-class-controller's per-rule reconcile with no field
/// naming the route-match-axis-drift root cause).
///
/// The single source of truth the rendered Aplicacao Gateway-API-side
/// ingress bundle's per-HTTPRoute per-rule route-match-axis-naming
/// reaches for:
///
/// - the rendered `HTTPRoute` document's per-rule
/// `spec.rules[].matches[]` axis (caixa-mesh/src/lib.rs — the
/// `gateway_routes` per-Aplicacao HTTPRoute's per-rule
/// `rule.insert("matches", …)` call seeded from the Aplicacao's
/// `:entrada :paths` slot).
///
/// The per-rule route-match container axis names the same Gateway-
/// API-implementation-side per-rule request-selection predicate fan-
/// out container as the sibling [`GATEWAY_API_KEY_BACKEND_REFS`]
/// per-rule backend-destination container axis it accompanies, and
/// must move together on any future Gateway API rebrand (an upstream
/// SIG-Network Gateway API v2 rename of the route-match axis from
/// `matches` to `match` / `routeMatches` / `predicates`, coordinated
/// with the Gateway API deprecation cycle). Until this lift landed
/// the axis carried an inline `matches` literal at the one
/// production-code occurrence in caixa-mesh/src/lib.rs (the
/// `gateway_routes` per-rule `rule.insert("matches", …)` call) plus
/// a matching test-fixture navigation inside the in-file
/// `httproute_rule_keys_pin_overlay_position` pin's
/// `contains_key("matches")` presence assertion — two occurrences of
/// the same load-bearing Gateway-API-CRD-`matches`-axis-key
/// convention, drift-prone by construction. A drift on the
/// production site to `"match"` / `"routeMatches"` / `"predicates"`
/// would have surfaced as a Gateway API implementation-side schema
/// validator drop at apply time (the affected per-rule route-match
/// axis the CRD schema validator recognizes as unknown), with the
/// per-rule request predicate degrading to the wildcard match at the
/// gateway-class-controller's per-rule reconcile with no field
/// naming the route-match-drift root cause. A drift on the test-
/// fixture side silently masks the emission-side pin
/// (`contains_key("matches")` returns `false` under both the
/// drifted-key emitter and the drifted-key probe).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
/// [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) /
/// [`GATEWAY_API_KEY_RETRY`] (231bbf5) lifts established on the
/// sibling canonical-Gateway-API-HTTPRoute-body-axis surface —
/// completes the per-rule top-level-axis lifted-string set
/// (`matches`, `backendRefs`, `timeouts`, `retry`) the
/// `httproute_rule_keys_pin_overlay_position` pin binds against, so
/// every one of the four per-rule top-level axes now threads a
/// lifted `&'static str` apiece. The render-side consumer now
/// threads the same `&'static str` through its `rule.insert(…)`
/// call so a future Gateway API rebrand on the per-rule route-match
/// axis (or an upstream SIG-Network Gateway API v2 rename to a
/// per-CRD sibling name) lands in one place; every future renderer
/// that reaches for the canonical per-HTTPRoute per-rule route-match
/// axis (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-Aplicacao `HTTPRoute` fan-out, a future
/// per-edge `GRPCRoute` renderer whose per-rule request-match
/// predicate nests under the same axis convention, a future
/// per-route header-match / query-match renderer whose per-predicate
/// list binds against this same axis) inherits the same value by
/// construction with no opportunity for per-renderer drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
/// [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) /
/// [`GATEWAY_API_KEY_RETRY`] (231bbf5) lifts apply on the peer
/// canonical-Gateway-API-HTTPRoute-per-rule-body-axis surface.
///
/// [cm]: ../../caixa_mesh/index.html
pub const GATEWAY_API_KEY_MATCHES: &str = "matches";
/// Canonical K8s Gateway API `Gateway` per-listener-set container-axis
/// key every `gateway_routes`-emitted `Gateway` document mounts its
/// per-Gateway `[{name, port, protocol, hostname}]` L7-listener fan-out
/// list under (`spec.listeners[]`). Pairs with the sibling
/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) +
/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) — the Gateway API v1 CRD
/// schema pins the per-Gateway L7-listener fan-out through the
/// `spec.listeners[]` container axis (each entry names one listener the
/// Gateway accepts external traffic on; the sibling
/// `spec.parentRefs[]` + `spec.rules[].backendRefs[]` container axes
/// carry the per-HTTPRoute parent-Gateway attachment + per-rule
/// backend-destination fan-out halves under the paired `HTTPRoute`
/// `spec` block), so drift on the per-Gateway L7-listener-set axis is
/// exactly as load-bearing as drift on the per-HTTPRoute parent-Gateway-
/// binding + per-rule backend-destination axes it accompanies (the K8s
/// apiserver-side Gateway API CRD schema validator drops any `spec`
/// block whose L7-listener-set container axis carries an unrecognized
/// key — a `"listener"` / `"listen"` / `"servers"` typo silently emits
/// a `Gateway` whose L7-listener fan-out the Gateway API
/// implementation's per-Gateway reconcile loop no-ops entirely: no
/// listener is opened, and every external `:entrada` flow the Gateway
/// was authored to accept drops at the gateway-class-controller's per-
/// Gateway HTTP-listener fan-in with no field naming the L7-listener-
/// set-axis-drift root cause).
///
/// The single source of truth the rendered Aplicacao Gateway-API-side
/// ingress bundle's per-Gateway L7-listener-set-axis-naming reaches
/// for:
///
/// - the rendered `Gateway` document's `spec.listeners[]` axis
/// (caixa-mesh/src/lib.rs — the `gateway_routes` per-Aplicacao
/// `Gateway`'s `g_spec.insert("listeners", …)` call).
///
/// The per-Gateway L7-listener-set container axis names the same
/// Gateway-API-implementation-side per-Gateway inbound-traffic-
/// acceptance-vector fan-out container as the sibling
/// [`GATEWAY_API_KEY_PARENT_REFS`] per-HTTPRoute parent-Gateway-binding
/// container axis + [`GATEWAY_API_KEY_BACKEND_REFS`] per-rule backend-
/// destination container axis it accompanies, and must move together
/// on any future Gateway API rebrand (an upstream SIG-Network Gateway
/// API v2 rename of the L7-listener-set axis from `listeners` to
/// `servers` / `endpoints` / `bindings`, coordinated with the Gateway
/// API deprecation cycle). Until this lift landed the axis carried an
/// inline `listeners` literal at the one production-code occurrence in
/// caixa-mesh/src/lib.rs (the `gateway_routes` per-Aplicacao Gateway's
/// `g_spec.insert("listeners", …)` call) plus a matching test-fixture
/// navigation inside the in-file `gateway_listener_carries_aplicacao_host`
/// pin's `.get("listeners")` traversal — two occurrences of the same
/// load-bearing Gateway-API-CRD-`listeners`-axis-key convention, drift-
/// prone by construction. A drift on the production site to
/// `"listener"` / `"listen"` / `"servers"` would have surfaced as a
/// Gateway API implementation-side schema validator drop at apply time
/// (the affected `Gateway`'s L7-listener-set axis the CRD schema
/// validator recognizes as unknown), with every external `:entrada`
/// flow the Gateway was authored to accept dropping at the gateway-
/// class-controller's per-Gateway reconcile with no field naming the
/// L7-listener-set-drift root cause. A drift on the test-fixture side
/// silently masks the emission-side pin (`.get("listeners")` returns
/// `None` under both the drifted-key emitter and the drifted-key probe
/// — the downstream `.and_then(|l| l.as_sequence())` /
/// `.and_then(|s| s.first())` chain short-circuits vacuously because
/// the outer per-Gateway L7-listener-set lookup is itself `None`).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
/// [`CILIUM_KEY_PORTS`] (1087693) /
/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) lifts established on the sibling
/// canonical-Gateway-API-HTTPRoute-body-axis /
/// canonical-Cilium-CNP-body-axis surfaces — pivots the per-HTTPRoute-
/// body-axis lift discipline onto the sibling per-Gateway-body-axis
/// surface, extending the per-Gateway-API-CRD-body-axis canonical-
/// string-pin set (`parentRefs`, `backendRefs`, `listeners`, future
/// `hostnames`) the M3 Aplicacao mesh renderer's external `:entrada`
/// ingress contract rests on across the Gateway API CRD-side body-
/// shape. The render-side consumer now threads the same `&'static
/// str` through its `g_spec.insert(…)` call so a future Gateway API
/// rebrand on the L7-listener-set axis (or an upstream SIG-Network
/// Gateway API v2 rename to a per-CRD sibling name) lands in one
/// place; every future renderer that reaches for the canonical per-
/// Gateway L7-listener-set axis (the future M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
/// `Gateway` fan-out, a future per-cluster `GatewayClass` /
/// `ReferenceGrant` renderer whose per-Gateway listener-set enumeration
/// binds against this same axis, a future per-listener TLS terminator
/// renderer whose per-listener `tls.mode: Terminate` overlay nests
/// under the same axis convention) inherits the same value by
/// construction with no opportunity for per-renderer drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
/// [`CILIUM_KEY_PORTS`] (1087693) /
/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) lifts apply on the peer canonical-
/// Gateway-API-Gateway-body-axis surface.
///
/// [cm]: ../../caixa_mesh/index.html
pub const GATEWAY_API_KEY_LISTENERS: &str = "listeners";
/// Canonical K8s Gateway API `Gateway` per-listener DNS-host-discriminator
/// axis key every `gateway_routes`-emitted `Gateway` document mounts each
/// listener's virtual-host name under
/// (`spec.listeners[].hostname`). Pairs with the sibling
/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) — the Gateway API v1 CRD schema
/// pins the per-Gateway L7-listener-set fan-out through the
/// `spec.listeners[]` container axis (each entry names one listener the
/// Gateway accepts external traffic on) and pins each entry's per-listener
/// DNS-host discriminator under the nested `hostname` axis (Gateway API v1
/// `Listener.hostname` — `PreciseHostname` string, optional per-listener
/// virtual-host filter the Gateway-API-implementation-side per-Gateway
/// reconcile loop honors when routing external inbound traffic against
/// SNI at the TLS handshake / `Host:` header at the HTTP request line), so
/// drift on the per-listener DNS-host discriminator axis is exactly as
/// load-bearing as drift on the per-Gateway L7-listener-set container
/// axis it nests under (the K8s apiserver-side Gateway API CRD schema
/// validator drops any per-listener entry whose DNS-host discriminator
/// axis carries an unrecognized key — a `"host"` / `"vhost"` /
/// `"serverName"` typo silently emits a `Gateway` whose per-listener
/// virtual-host filter the Gateway API implementation's per-listener SNI /
/// `Host:` header dispatch loop no-ops entirely: the listener accepts
/// traffic on the wildcard host rather than the typed `:entrada :host`
/// the Aplicacao author declared, and every external `:entrada` flow the
/// listener was authored to accept lands on the wrong virtual-host filter
/// with no field naming the DNS-host-discriminator-axis-drift root
/// cause).
///
/// The single source of truth the rendered Aplicacao Gateway-API-side
/// ingress bundle's per-Gateway per-listener DNS-host-discriminator-axis-
/// naming reaches for:
///
/// - the rendered `Gateway` document's `spec.listeners[].hostname` axis
/// (caixa-mesh/src/lib.rs — the `gateway_routes` per-Aplicacao
/// `Gateway`'s per-listener `listener.insert("hostname", …)` call
/// seeded from the Aplicacao's `:entrada :host` slot).
///
/// The per-listener DNS-host discriminator axis names the same Gateway-
/// API-implementation-side per-listener virtual-host filter container as
/// the sibling [`GATEWAY_API_KEY_LISTENERS`] per-Gateway L7-listener-set
/// container axis it nests under, and must move together on any future
/// Gateway API rebrand (an upstream SIG-Network Gateway API v2 rename of
/// the per-listener DNS-host discriminator axis from `hostname` to `host`
/// / `vhost` / `serverName`, coordinated with the Gateway API deprecation
/// cycle). Until this lift landed the axis carried an inline `hostname`
/// literal at the one production-code occurrence in caixa-mesh/src/lib.rs
/// (the `gateway_routes` per-Aplicacao Gateway's per-listener
/// `listener.insert("hostname", …)` call) plus a matching test-fixture
/// navigation inside the in-file `gateway_listener_carries_aplicacao_host`
/// pin's `.get("hostname")` traversal — two occurrences of the same load-
/// bearing Gateway-API-CRD-`hostname`-axis-key convention, drift-prone by
/// construction. A drift on the production site to `"host"` / `"vhost"` /
/// `"serverName"` would have surfaced as a Gateway API implementation-
/// side schema validator drop at apply time (the affected listener's per-
/// listener DNS-host discriminator axis the CRD schema validator
/// recognizes as unknown), with every external `:entrada` flow landing on
/// the wildcard virtual-host filter rather than the typed `:entrada
/// :host` at the gateway-class-controller's per-listener dispatch with no
/// field naming the DNS-host-discriminator-drift root cause. A drift on
/// the test-fixture side silently masks the emission-side pin
/// (`.get("hostname")` returns `None` under both the drifted-key emitter
/// and the drifted-key probe — the downstream `.and_then(|h| h.as_str())`
/// chain short-circuits vacuously because the outer per-listener DNS-
/// host discriminator lookup is itself `None`).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
/// [`CILIUM_KEY_PORTS`] (1087693) /
/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) lifts established on the sibling
/// canonical-Gateway-API-CRD-body-axis /
/// canonical-Cilium-CNP-body-axis surfaces — nests the per-Gateway-API-
/// CRD-body-axis lift discipline one level deeper onto the sibling per-
/// listener body-axis surface, extending the per-Gateway-API-CRD-body-
/// axis canonical-string-pin set (`parentRefs`, `backendRefs`,
/// `listeners`, `hostname`, future `hostnames`) the M3 Aplicacao mesh
/// renderer's external `:entrada` ingress contract rests on across the
/// Gateway API CRD-side body-shape. The render-side consumer now threads
/// the same `&'static str` through its per-listener `listener.insert(…)`
/// call so a future Gateway API rebrand on the per-listener DNS-host
/// discriminator axis (or an upstream SIG-Network Gateway API v2 rename
/// to a per-CRD sibling name) lands in one place; every future renderer
/// that reaches for the canonical per-listener DNS-host discriminator
/// axis (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-Aplicacao `Gateway` fan-out, a future per-listener
/// TLS terminator renderer whose per-listener `tls.certificateRefs[]`
/// resolution keys off the same per-listener virtual-host filter, a
/// future per-cluster wildcard-host `Gateway` renderer whose per-listener
/// SNI wildcard `*.example.com` matcher binds against this same axis)
/// inherits the same value by construction with no opportunity for per-
/// renderer drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
/// [`CILIUM_KEY_PORTS`] (1087693) /
/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) lifts apply on the peer canonical-
/// Gateway-API-Gateway-per-listener-body-axis surface.
///
/// [cm]: ../../caixa_mesh/index.html
pub const GATEWAY_API_KEY_HOSTNAME: &str = "hostname";
/// Canonical K8s Gateway API `HTTPRoute` spec-level DNS-host-filter axis key
/// every `gateway_routes`-emitted `HTTPRoute` document mounts the route's
/// per-route virtual-host filter list under (`spec.hostnames[]`). The
/// plural sibling of [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) — same
/// Gateway-API-CRD DNS-host-discriminator convention nested one level up on
/// the sibling `HTTPRoute` per-route body-axis surface, distinct spelling
/// (`hostnames` — plural — is the `HTTPRoute` spec-level filter list; the
/// singular `hostname` axis it pairs against is the per-`Gateway`-listener
/// virtual-host discriminator).
///
/// The Gateway API v1 CRD schema pins the per-`HTTPRoute` DNS-host filter
/// through the spec-level `hostnames[]` container axis (a list of DNS
/// `PreciseHostname` strings, each one an additional virtual-host filter
/// the Gateway-API-implementation-side per-route reconcile loop honors
/// when routing external inbound traffic against SNI at the TLS handshake
/// / `Host:` header at the HTTP request line and against the sibling
/// [`GATEWAY_API_KEY_PARENT_REFS`]-declared parent Gateway's per-listener
/// [`GATEWAY_API_KEY_HOSTNAME`] filter set). Drift on the per-route DNS-
/// host filter axis is exactly as load-bearing as drift on the sibling
/// per-listener DNS-host discriminator axis (`hostname`): the K8s
/// apiserver-side Gateway API CRD schema validator drops any per-route
/// entry whose DNS-host-filter axis carries an unrecognized key — a
/// `"hosts"` / `"vhosts"` / `"serverNames"` typo silently emits an
/// `HTTPRoute` whose per-route virtual-host filter list the Gateway API
/// implementation's per-route SNI / `Host:` header dispatch loop no-ops
/// entirely: the route accepts traffic on every host the parent Gateway's
/// listener accepts rather than the typed `:entrada :host` the Aplicacao
/// author declared, and every external `:entrada` flow the route was
/// authored to accept lands on the wildcard virtual-host filter with no
/// field naming the DNS-host-filter-axis-drift root cause.
///
/// The single source of truth the rendered Aplicacao Gateway-API-side
/// ingress bundle's per-`HTTPRoute` spec-level DNS-host-filter-axis-naming
/// reaches for:
///
/// - the rendered `HTTPRoute` document's `spec.hostnames[]` axis
/// (caixa-mesh/src/lib.rs — the `gateway_routes` per-Aplicacao
/// `HTTPRoute`'s spec-level `r_spec.insert("hostnames", …)` call
/// seeded from the Aplicacao's `:entrada :host` slot as a
/// single-element sequence).
///
/// The per-route DNS-host filter axis names the same Gateway-API-
/// implementation-side per-route virtual-host filter list container as the
/// sibling [`GATEWAY_API_KEY_PARENT_REFS`] per-route parent-Gateway-
/// binding container axis it sits beside under `spec.*`, and must move
/// together on any future Gateway API rebrand (an upstream SIG-Network
/// Gateway API v2 rename of the per-route DNS-host filter axis from
/// `hostnames` to `hosts` / `vhosts` / `serverNames`, coordinated with
/// the Gateway API deprecation cycle). Until this lift landed the axis
/// carried an inline `hostnames` literal at the one production-code
/// occurrence in caixa-mesh/src/lib.rs (the `gateway_routes` per-
/// Aplicacao `HTTPRoute`'s spec-level `r_spec.insert("hostnames", …)`
/// call) — one occurrence today, but the sibling per-Gateway-API-CRD-
/// body-axis lifts ([`GATEWAY_API_KEY_LISTENERS`] / [`GATEWAY_API_KEY_HOSTNAME`]
/// / [`GATEWAY_API_KEY_PARENT_REFS`] / [`GATEWAY_API_KEY_BACKEND_REFS`])
/// each closed on the same one-production-emitter-plus-future-test-
/// fixture shape before a future per-route DNS-host-filter navigator
/// picked up the second occurrence, and the same lift-before-the-second-
/// site discipline applies here.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
/// [`CILIUM_KEY_PORTS`] (1087693) /
/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) lifts established on the sibling
/// canonical-Gateway-API-CRD-body-axis /
/// canonical-Cilium-CNP-body-axis surfaces — closes the per-Gateway-API-
/// CRD `HTTPRoute` per-route body-axis lift pair across the singular /
/// plural DNS-host discriminator surface (`hostname` at the parent-
/// Gateway per-listener discriminator + `hostnames` at the child
/// HTTPRoute per-route filter list), so both halves of the DNS-host
/// discriminator convention across the `(Gateway, HTTPRoute)` pair the
/// M3 Aplicacao mesh renderer's external `:entrada` ingress contract
/// emits together now live as one lifted `&'static str` apiece. The
/// render-side consumer now threads the same `&'static str` through its
/// spec-level `r_spec.insert(…)` call so a future Gateway API rebrand on
/// the per-route DNS-host filter axis (or an upstream SIG-Network
/// Gateway API v2 rename to a per-CRD sibling name) lands in one place;
/// every future renderer that reaches for the canonical per-route DNS-
/// host filter axis (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-Aplicacao `HTTPRoute` fan-out, a future per-route
/// wildcard-host `*.example.com` filter emitter, a future per-Aplicacao
/// multi-`:entrada` `HTTPRoute` fan-out whose per-route DNS-host filter
/// lists partition inbound traffic across the same parent Gateway's
/// per-listener discriminator) inherits the same value by construction
/// with no opportunity for per-renderer drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
/// [`CILIUM_KEY_PORTS`] (1087693) /
/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) lifts apply on the peer canonical-
/// Gateway-API-HTTPRoute-per-route-body-axis surface.
///
/// [cm]: ../../caixa_mesh/index.html
pub const GATEWAY_API_KEY_HOSTNAMES: &str = "hostnames";
/// Canonical K8s Gateway API `HTTPRoute` per-rule request-timeout-policy
/// body-axis key every `gateway_routes`-emitted `HTTPRoute` document mounts
/// its per-rule `:politicas :timeout` overlay under
/// (`spec.rules[].timeouts`). Sibling per-rule-body-axis peer to
/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) and
/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) — same Gateway-API-CRD-body-axis
/// discipline nested one level deeper onto the per-rule request-deadline
/// slot the Gateway API v1 CRD schema pins under `HTTPRoute.spec.rules[]`.
///
/// The Gateway API v1 CRD schema pins the per-rule request-timeout policy
/// through the `HTTPRouteTimeouts` sub-shape mounted at
/// `spec.rules[].timeouts`, whose `request` / `backendRequest` scalars
/// carry the per-rule deadline the Gateway-API-implementation-side per-
/// rule request-dispatch loop compares each accepted request's
/// wall-clock elapsed time against before cancelling the in-flight
/// backend call. Drift on the per-rule timeout-policy body-axis is
/// exactly as load-bearing as drift on the sibling per-rule backend-
/// destination axis (`backendRefs`): the K8s apiserver-side Gateway API
/// CRD schema validator drops any per-rule entry whose per-rule
/// timeout-policy axis carries an unrecognized key — a
/// `"timeout"` (singular) / `"timeoutPolicy"` / `"deadlines"` typo
/// silently emits an `HTTPRoute` whose per-rule timeout-policy the
/// Gateway API implementation's per-rule request-dispatch loop no-ops
/// entirely: the route accepts every inbound request with no per-rule
/// wall-clock deadline (the "no infinite blocking" guarantee
/// MESH-COMPOSITION.md §V mandates for every rendered per-`:politicas`
/// mesh-composition edge silently regresses to the pre-overlay
/// unbounded-request semantic, and every external `:entrada` flow the
/// route was authored to bound by the typed `:politicas :timeout` slot
/// runs to whatever backend deadline the resolved `ComputeUnit` /
/// `Service` / `ExternalName` backend's downstream infrastructure
/// (Envoy default listener idle timeout, node-local conntrack window,
/// TCP keepalive) picks — with no field naming the per-rule-timeout-
/// policy-axis-drift root cause).
///
/// The single source of truth the rendered Aplicacao Gateway-API-side
/// ingress bundle's per-`HTTPRoute` per-rule request-timeout-policy-
/// axis-naming reaches for:
///
/// - the rendered `HTTPRoute` document's per-rule `timeouts:` axis
/// (caixa-mesh/src/lib.rs — the `gateway_routes` per-Aplicacao
/// `HTTPRoute`'s per-rule `rule.insert("timeouts", …)` call
/// seeded from the Aplicacao's `:politicas :timeout` overlay
/// when the slot is set, elided from the emit sequence when the
/// slot is unset).
///
/// The per-rule request-timeout-policy axis names the same Gateway-
/// API-implementation-side per-rule request-dispatch deadline
/// container as the sibling [`GATEWAY_API_KEY_BACKEND_REFS`] per-rule
/// backend-destination container axis it sits beside under
/// `spec.rules[].*`, and must move together on any future Gateway API
/// rebrand (an upstream SIG-Network Gateway API v2 rename of the per-
/// rule timeout-policy axis from `timeouts` to `timeout` /
/// `timeoutPolicy` / `deadlines`, coordinated with the Gateway API
/// deprecation cycle). Until this lift landed the axis carried an
/// inline `timeouts` literal at nine physical sites in
/// caixa-mesh/src/lib.rs (one production emitter at the
/// `gateway_routes` per-rule `rule.insert(…)` call plus eight test-
/// side navigators pinning the overlay's presence, absence,
/// canonical-duration-format contract, per-rule fan-out under
/// multi-`:entrada :paths`, and independent-axis coexistence with the
/// sibling `retry` per-rule retry-policy axis), the highest per-axis
/// occurrence count of any un-lifted Gateway-API-CRD-body-axis in the
/// crate.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
/// [`CILIUM_KEY_PORTS`] (1087693) /
/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) lifts established on the sibling
/// canonical-Gateway-API-CRD-body-axis /
/// canonical-Cilium-CNP-body-axis surfaces — extends the per-Gateway-
/// API-`HTTPRoute` per-rule body-axis lift set onto the load-bearing
/// per-rule request-timeout-policy axis every downstream Gateway-API-
/// implementation-side per-rule request-dispatch loop keys off before
/// it can commit to a per-request wall-clock deadline. The render-
/// side consumer now threads the same `&'static str` through its
/// per-rule `rule.insert(…)` call and every test-side navigator's
/// `.get(…)` retrieval so a future Gateway API rebrand on the per-
/// rule timeout-policy axis (or an upstream SIG-Network Gateway API
/// v2 rename to a per-CRD sibling name) lands in one place; every
/// future renderer that reaches for the canonical per-rule timeout-
/// policy axis (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-Aplicacao per-rule timeout-policy fan-out, a
/// future per-edge `backendRequest` sub-timeout emitter honoring the
/// downstream `:politicas :backend-timeout` slot the M4 roadmap
/// acknowledges, a future per-cluster per-rule idle-timeout emitter
/// binding against this same axis) inherits the same value by
/// construction with no opportunity for per-renderer drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
/// [`CILIUM_KEY_PORTS`] (1087693) /
/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) lifts apply on the peer canonical-
/// Gateway-API-HTTPRoute-per-rule-body-axis surface.
///
/// [cm]: ../../caixa_mesh/index.html
pub const GATEWAY_API_KEY_TIMEOUTS: &str = "timeouts";
/// Canonical K8s Gateway API `HTTPRoute` per-rule retry-policy body-axis
/// key every `gateway_routes`-emitted `HTTPRoute` document mounts its
/// per-rule `:politicas :retries` overlay under (`spec.rules[].retry`).
/// Sibling per-rule-body-axis peer to [`GATEWAY_API_KEY_TIMEOUTS`]
/// (db31108) — same Gateway-API-CRD-body-axis discipline nested onto the
/// per-rule retry-budget slot the Gateway API v1 CRD schema pins under
/// `HTTPRoute.spec.rules[]` beside the sibling per-rule request-timeout-
/// policy container.
///
/// The Gateway API v1 CRD schema pins the per-rule retry policy through
/// the `HTTPRouteRetry` sub-shape mounted at `spec.rules[].retry`, whose
/// `attempts` scalar (peer to future `codes` retryable-status-code list
/// and `backoff` inter-attempt backoff-window scalars) carries the per-
/// rule retry-budget the Gateway-API-implementation-side per-rule
/// request-dispatch loop compares each failed attempt count against
/// before giving up on the in-flight backend call. Drift on the per-rule
/// retry-policy body-axis is exactly as load-bearing as drift on the
/// sibling per-rule request-timeout-policy axis (`timeouts`): the K8s
/// apiserver-side Gateway API CRD schema validator drops any per-rule
/// entry whose per-rule retry-policy axis carries an unrecognized key —
/// a `"retries"` (plural) / `"retryPolicy"` / `"budget"` typo silently
/// emits an `HTTPRoute` whose per-rule retry-budget the Gateway API
/// implementation's per-rule request-dispatch loop no-ops entirely: the
/// route accepts every inbound request with no per-rule retry budget
/// (the "no infinite retrying without bound" guarantee
/// MESH-COMPOSITION.md §V mandates for every rendered per-`:politicas`
/// mesh-composition edge silently regresses to the pre-overlay
/// unbounded-retry semantic, and every external `:entrada` flow the
/// route was authored to cap by the typed `:politicas :retries` slot
/// runs to whatever retry policy the resolved `ComputeUnit` /
/// `Service` / `ExternalName` backend's downstream infrastructure —
/// Envoy default retry policy, client SDK autoretry, node-local
/// conntrack retries — with no field naming the per-rule-retry-policy-
/// axis-drift root cause).
///
/// The single source of truth the rendered Aplicacao Gateway-API-side
/// ingress bundle's per-`HTTPRoute` per-rule retry-policy-axis-naming
/// reaches for:
///
/// - the rendered `HTTPRoute` document's per-rule `retry:` axis
/// (caixa-mesh/src/lib.rs — the `gateway_routes` per-Aplicacao
/// `HTTPRoute`'s per-rule `rule.insert("retry", …)` call seeded
/// from the Aplicacao's `:politicas :retries` overlay when the
/// slot is set, elided from the emit sequence when the slot is
/// unset).
///
/// The per-rule retry-policy axis names the same Gateway-API-
/// implementation-side per-rule request-dispatch retry-budget container
/// as the sibling [`GATEWAY_API_KEY_TIMEOUTS`] per-rule request-timeout-
/// policy container axis it sits beside under `spec.rules[].*`, and must
/// move together on any future Gateway API rebrand (an upstream
/// SIG-Network Gateway API v2 rename of the per-rule retry-policy axis
/// from `retry` to `retries` / `retryPolicy` / `budget`, coordinated
/// with the Gateway API deprecation cycle). Until this lift landed the
/// axis carried an inline `retry` literal at nine physical sites in
/// caixa-mesh/src/lib.rs (one production emitter at the `gateway_routes`
/// per-rule `rule.insert(…)` call plus eight test-side navigators
/// pinning the overlay's rule-level top-key-set, presence, absence,
/// per-rule fan-out under multi-`:entrada :paths`, round-trip of the
/// typed `u32` attempt count, YAML integer scalar-kind, and independent-
/// axis coexistence with the sibling `timeouts` per-rule request-
/// timeout-policy axis in both directions), the highest per-axis
/// occurrence count of any un-lifted Gateway-API-CRD-body-axis in the
/// crate — same nine-site count the peer sibling
/// [`GATEWAY_API_KEY_TIMEOUTS`] lift closed on the coexisting per-rule
/// request-timeout-policy axis.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) /
/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
/// [`CILIUM_KEY_PORTS`] (1087693) /
/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) lifts established on the sibling
/// canonical-Gateway-API-CRD-body-axis /
/// canonical-Cilium-CNP-body-axis surfaces — closes the pair of per-
/// Gateway-API-`HTTPRoute`-per-rule `:politicas` overlay axes
/// (`timeouts` for `:politicas :timeout`, `retry` for `:politicas
/// :retries`) both MESH-COMPOSITION.md §V "no infinite blocking / no
/// infinite retrying" guarantees rest on. The render-side consumer now
/// threads the same `&'static str` through its per-rule
/// `rule.insert(…)` call and every test-side navigator's `.get(…)`
/// retrieval so a future Gateway API rebrand on the per-rule retry-
/// policy axis lands in one place; every future renderer that reaches
/// for the canonical per-rule retry-policy axis (the future M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
/// per-rule retry-policy fan-out, a future per-edge `codes`
/// retryable-status-code emitter honoring an M4-roadmap `:politicas
/// :retry-codes` slot, a future per-edge `backoff` inter-attempt
/// backoff-window emitter honoring an M4-roadmap `:politicas
/// :retry-backoff` slot) inherits the same value by construction with
/// no opportunity for per-renderer drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) /
/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
/// [`CILIUM_KEY_PORTS`] (1087693) /
/// [`CILIUM_KEY_FROM_ENDPOINTS`] (ecfa557) /
/// [`CILIUM_KEY_INGRESS`] (0400a9b) /
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`] (7088789) /
/// [`CILIUM_KEY_TO_PORTS`] (c8d9cbf) /
/// [`KUBE_KEY_RULES`] (a205eb3) lifts apply on the peer canonical-
/// Gateway-API-HTTPRoute-per-rule-body-axis surface.
///
/// [cm]: ../../caixa_mesh/index.html
pub const GATEWAY_API_KEY_RETRY: &str = "retry";
/// Canonical K8s Gateway API `HTTPRoute` per-rule retry-policy `attempts`
/// leaf scalar-key every `gateway_routes`-emitted `HTTPRoute` document
/// mounts its per-rule `:politicas :retries` typed `u32` attempt count
/// under (`spec.rules[].retry.attempts`). Leaf peer to the container-axis
/// parent [`GATEWAY_API_KEY_RETRY`] (231bbf5) — the sibling per-rule
/// retry-policy body-axis lifted in the immediately-preceding commit;
/// this closes the parent-leaf axis pair (`retry` container +
/// `attempts` leaf) the Gateway API v1 `HTTPRouteRetry` sub-shape pins
/// under `HTTPRoute.spec.rules[].retry.attempts`.
///
/// The Gateway API v1 CRD schema pins the per-rule retry attempt budget
/// through the `HTTPRouteRetry.attempts` scalar (peer to future
/// `HTTPRouteRetry.codes` retryable-status-code list and
/// `HTTPRouteRetry.backoff` inter-attempt backoff-window scalars) the
/// Gateway-API-implementation-side per-rule request-dispatch loop
/// compares each failed backend attempt count against before giving up
/// on the in-flight backend call. Drift on this leaf key is exactly as
/// load-bearing as drift on the parent per-rule retry-policy container
/// axis (`retry`): the K8s apiserver-side Gateway API CRD schema
/// validator drops any per-rule `retry:` entry whose leaf attempt-count
/// key carries an unrecognized name — a `"attempt"` (singular) /
/// `"count"` / `"tries"` / `"maxAttempts"` typo silently emits an
/// `HTTPRoute` whose per-rule retry-budget the Gateway-API-
/// implementation-side per-rule request-dispatch loop no-ops entirely
/// (the sub-shape is parsed as an empty `HTTPRouteRetry` with the
/// typed `u32` attempt count silently discarded, the route accepts
/// every inbound request with no per-rule retry budget — the "no
/// infinite retrying without bound" guarantee MESH-COMPOSITION.md §V
/// mandates for every rendered per-`:politicas` mesh-composition edge
/// silently regresses to the pre-overlay unbounded-retry semantic,
/// and every external `:entrada` flow the route was authored to cap
/// by the typed `:politicas :retries` slot runs to whatever retry
/// policy the resolved backend's downstream infrastructure — Envoy
/// default retry policy, client SDK autoretry, node-local conntrack
/// retries — picks with no field naming the per-rule-retry-attempts-
/// leaf-key drift root cause).
///
/// The single source of truth the rendered Aplicacao Gateway-API-side
/// ingress bundle's per-`HTTPRoute` per-rule retry-attempts-leaf-key-
/// naming reaches for:
///
/// - the rendered `HTTPRoute` document's per-rule
/// `retry.attempts:` leaf (caixa-mesh/src/lib.rs — the
/// `gateway_routes` per-Aplicacao `HTTPRoute`'s per-rule
/// `single_field_overlay(spec.politicas.retries, …)` call seeded
/// from the Aplicacao's `:politicas :retries` overlay when the
/// slot is set, emitting the typed `u32` attempt count under this
/// leaf key inside the sibling [`GATEWAY_API_KEY_RETRY`] container
/// axis).
///
/// The per-rule retry-attempts leaf key names the same Gateway-API-
/// implementation-side per-rule request-dispatch retry-budget scalar
/// as the sibling parent [`GATEWAY_API_KEY_RETRY`] container axis it
/// sits nested inside under `spec.rules[].retry.attempts`, and must
/// move together with the parent on any future Gateway API rebrand
/// (an upstream SIG-Network Gateway API v2 rename of the per-rule
/// retry-attempts leaf key from `attempts` to `attempt` / `count` /
/// `tries` / `maxAttempts`, coordinated with the Gateway API
/// deprecation cycle). Until this lift landed the leaf key carried
/// an inline `attempts` literal at six physical code sites in
/// caixa-mesh/src/lib.rs (one production emitter at the `gateway_routes`
/// per-rule `single_field_overlay(spec.politicas.retries, "attempts", …)`
/// call plus five test-side navigators pinning the overlay's leaf-
/// count value, round-trip of the typed `u32` attempt count, YAML
/// integer scalar-kind, per-rule fan-out under multi-`:entrada
/// :paths`, and independent-axis coexistence with the sibling
/// `timeouts` per-rule request-timeout-policy axis).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`GATEWAY_API_KEY_RETRY`] (231bbf5) /
/// [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) /
/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) lifts established on
/// the sibling canonical-Gateway-API-CRD-body-axis surface — closes
/// the parent-leaf axis pair (`retry` container +
/// `attempts` leaf) the K8s Gateway API v1 `HTTPRouteRetry` sub-shape
/// pins under `HTTPRoute.spec.rules[].retry.attempts`, both
/// MESH-COMPOSITION.md §V "no infinite retrying" guarantees rest on.
/// The render-side consumer now threads the same `&'static str`
/// through its `single_field_overlay` call and every test-side
/// navigator's `.get(…)` retrieval so a future Gateway API rebrand
/// on the per-rule retry-attempts leaf lands in one place; every
/// future renderer that reaches for the canonical per-rule retry-
/// attempts leaf (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao`
/// CR materializer's per-Aplicacao per-rule retry-attempts fan-out)
/// inherits the same value by construction with no opportunity for
/// per-renderer drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`GATEWAY_API_KEY_RETRY`] (231bbf5) /
/// [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) lifts apply on the peer
/// canonical-Gateway-API-HTTPRoute-per-rule-body-axis surface, now
/// extended one nesting level deeper onto the retry-container-leaf
/// scalar.
///
/// [cm]: ../../caixa_mesh/index.html
pub const GATEWAY_API_KEY_ATTEMPTS: &str = "attempts";
/// Canonical K8s Gateway API `HTTPRoute` per-rule request-timeout-policy
/// `request` leaf scalar-key every `gateway_routes`-emitted `HTTPRoute`
/// document mounts its per-rule `:politicas :timeout` typed K8s-duration
/// string under (`spec.rules[].timeouts.request`). Leaf peer to the
/// container-axis parent [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) — the
/// sibling per-rule request-timeout-policy body-axis — and to the peer
/// retry-container leaf [`GATEWAY_API_KEY_ATTEMPTS`] (e2e136b) landed
/// on the parallel `retry.attempts` nesting; this closes the parent-leaf
/// axis pair (`timeouts` container + `request` leaf) the K8s Gateway API
/// v1 `HTTPRouteTimeouts` sub-shape pins under
/// `HTTPRoute.spec.rules[].timeouts.request`.
///
/// The Gateway API v1 CRD schema pins the per-rule request-deadline
/// through the `HTTPRouteTimeouts.request` scalar (peer to
/// `HTTPRouteTimeouts.backendRequest` per-attempt backend-call deadline
/// scalar) the Gateway-API-implementation-side per-rule request-dispatch
/// loop keys off before it commits to a per-request wall-clock deadline.
/// Drift on this leaf key is exactly as load-bearing as drift on the
/// parent per-rule request-timeout-policy container axis (`timeouts`):
/// the K8s apiserver-side Gateway API CRD schema validator drops any
/// per-rule `timeouts:` entry whose leaf request-deadline key carries an
/// unrecognized name — a `"deadline"` / `"requestTimeout"` /
/// `"timeout"` / `"upstreamRequest"` typo silently emits an `HTTPRoute`
/// whose per-rule request-deadline the Gateway-API-implementation-side
/// per-rule request-dispatch loop no-ops entirely (the sub-shape is
/// parsed as an empty `HTTPRouteTimeouts` with the typed duration
/// silently discarded, the route accepts every inbound request with no
/// per-rule request wall-clock deadline — the "no infinite blocking"
/// guarantee MESH-COMPOSITION.md §V mandates for every rendered
/// per-`:politicas` mesh-composition edge silently regresses to the
/// pre-overlay unbounded-blocking semantic, and every external
/// `:entrada` flow the route was authored to cap by the typed
/// `:politicas :timeout` slot runs to whatever request-deadline the
/// resolved backend's downstream infrastructure — Envoy default
/// route-timeout, client SDK deadline, node-local conntrack idle-close
/// — picks with no field naming the per-rule-request-timeout-leaf-key
/// drift root cause).
///
/// The single source of truth the rendered Aplicacao Gateway-API-side
/// ingress bundle's per-`HTTPRoute` per-rule request-deadline-leaf-key-
/// naming reaches for:
///
/// - the rendered `HTTPRoute` document's per-rule
/// `timeouts.request:` leaf (caixa-mesh/src/lib.rs — the
/// `gateway_routes` per-Aplicacao `HTTPRoute`'s per-rule
/// `single_field_overlay(spec.politicas.timeout, …)` call seeded
/// from the Aplicacao's `:politicas :timeout` overlay when the slot
/// is set, emitting the typed K8s-duration string under this leaf
/// key inside the sibling [`GATEWAY_API_KEY_TIMEOUTS`] container
/// axis).
///
/// The per-rule request-deadline leaf key names the same
/// Gateway-API-implementation-side per-rule request-dispatch wall-clock
/// deadline scalar as the sibling parent [`GATEWAY_API_KEY_TIMEOUTS`]
/// container axis it sits nested inside under
/// `spec.rules[].timeouts.request`, and must move together with the
/// parent on any future Gateway API rebrand (an upstream SIG-Network
/// Gateway API v2 rename of the per-rule request-deadline leaf key from
/// `request` to `deadline` / `requestTimeout` / `timeout` /
/// `upstreamRequest`, coordinated with the Gateway API deprecation
/// cycle). Until this lift landed the leaf key carried an inline
/// `request` literal at six physical code sites in
/// caixa-mesh/src/lib.rs (one production emitter at the
/// `gateway_routes` per-rule
/// `single_field_overlay(spec.politicas.timeout, "request", …)` call
/// plus five test-side navigators pinning the overlay's leaf-value
/// presence, the canonical `duration_codec::render` round-trip of a
/// 30s / 90s / 1m typed duration, per-rule fan-out under
/// multi-`:entrada :paths`, and independent-axis coexistence with the
/// sibling `retry` per-rule retry-policy axis).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`GATEWAY_API_KEY_ATTEMPTS`] (e2e136b) /
/// [`GATEWAY_API_KEY_RETRY`] (231bbf5) /
/// [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) /
/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) lifts established on the
/// sibling canonical-Gateway-API-CRD-body-axis surface — closes the
/// second parent-leaf axis pair (`timeouts` container + `request` leaf)
/// the K8s Gateway API v1 `HTTPRouteTimeouts` sub-shape pins under
/// `HTTPRoute.spec.rules[].timeouts.request`, sibling to the parent-
/// leaf pair (`retry` container + `attempts` leaf) closed in the
/// immediately-preceding [`GATEWAY_API_KEY_ATTEMPTS`] lift. Both
/// MESH-COMPOSITION.md §V "no infinite blocking / no infinite retrying"
/// guarantees now rest on typed lifts at both container-axis and leaf-
/// scalar-axis nesting levels of the two per-`:politicas` overlays.
/// The render-side consumer now threads the same `&'static str`
/// through its `single_field_overlay` call and every test-side
/// navigator's `.get(…)` retrieval so a future Gateway API rebrand on
/// the per-rule request-deadline leaf lands in one place; every future
/// renderer that reaches for the canonical per-rule request-deadline
/// leaf (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-Aplicacao per-rule request-deadline fan-out, a
/// future per-edge `backendRequest` per-attempt backend-call deadline
/// emitter) inherits the same value by construction with no opportunity
/// for per-renderer drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`GATEWAY_API_KEY_ATTEMPTS`] (e2e136b) /
/// [`GATEWAY_API_KEY_RETRY`] (231bbf5) /
/// [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) lifts apply on the peer
/// canonical-Gateway-API-HTTPRoute-per-rule-body-axis surface, now
/// extended to the second per-rule container-leaf scalar (parallel to
/// the sibling `retry.attempts` container-leaf pair).
///
/// [cm]: ../../caixa_mesh/index.html
pub const GATEWAY_API_KEY_REQUEST: &str = "request";
/// Canonical Helm library-chart name every `lareira-<nome>` chart depends
/// on — the `pleme-computeunit` library chart in
/// `pleme-io/helmworks/charts/pleme-computeunit` that owns the K8s
/// resource templates (ComputeUnit + Service + ScaledObject + ConfigMap)
/// every per-Servico chart consumes via Helm's per-dep alias convention
/// (when no `alias:` is set on a dependency, values are scoped under the
/// dependency's `name:`).
///
/// The single source of truth all three downstream library-name consumers
/// reach for:
///
/// - [`caixa-helm`][ch]'s `DEFAULT_LIBRARY_NAME` re-export — the
/// default value of `RenderOpts::library_name`, which drives both
/// the Chart.yaml `dependencies[0].name` axis
/// (`build_chart_yaml`) and the values.yaml wrap key
/// (`build_values_yaml`) so the rendered `lareira-<nome>` chart's
/// dep declaration and its values block agree by construction
/// (the 17ebd1a `opts.library_name` lift).
/// - [`caixa-flux`][cf]'s `DEFAULT_LIBRARY_NAME` re-export — the
/// wrap key the `cluster_bundle` `helmrelease.yaml` template uses
/// under `spec.values.<library>:` to thread the per-cluster
/// overrides (`enabled: true`) through to the rendered chart's
/// dep block. Helm's per-dep alias convention scopes those values
/// under the dependency's `name:`, so this wrap key must match the
/// chart's `dependencies[0].name` exactly — drift here silently
/// routes the values block nowhere at `helm template` /
/// `helm install` time, and the cluster comes up with the library
/// chart's defaults rather than the typed per-cluster overrides.
/// - Every future per-Servico renderer the absorption-roadmap
/// acknowledges (the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-edge library-chart resolver, the future
/// per-cluster image-registry mirror's `<registry>-computeunit`
/// fork, the future per-edition library-chart variant the
/// substrate forks once `pleme-computeunit` outlives its scoping
/// intent).
///
/// Until this lift landed the canonical library-chart name lived as
/// two production-code call sites: a `pub const DEFAULT_LIBRARY_NAME:
/// &str = "pleme-computeunit"` in `caixa-helm` (the
/// `RenderOpts::library_name` default, consumed by both the chart's dep
/// name axis and the values.yaml wrap key axis) and an inline literal
/// `pleme-computeunit:` in `caixa-flux`'s `cluster_bundle`
/// `helmrelease.yaml` format-string template (the wrap key the per-
/// cluster `enabled: true` override is scoped under). Both consumers
/// reach for the same load-bearing Helm library-chart name, but no
/// shared constant linked them — the canonical
/// "duplicated `pub const` / inline literal across two renderers"
/// drift footgun the [`DEFAULT_NAMESPACE`] (a085b26) and
/// [`DEFAULT_SERVICO_PORT`] (1e22add) lifts close on the peer
/// canonical-K8s-axis-constant surface.
///
/// A future library-chart rebrand — the substrate forking
/// `pleme-computeunit` to `<registry>-computeunit` for a per-cluster
/// image-registry mirror, or to `aplicacao-computeunit` for the M4
/// typed-Aplicacao renderer's sibling library chart, or to any
/// per-edition variant the absorption-roadmap names — without a
/// coordinated edit on both consumers would have silently emitted a
/// per-Servico chart whose dep declared the new library name (because
/// the chart-side override flowed through `opts.library_name`) but
/// whose flux-side `HelmRelease.values.pleme-computeunit:` wrap key
/// still scoped under the old literal. Helm's per-dep values router
/// would route the per-cluster `enabled: true` override to *nowhere*
/// at `helm template` / `helm install` time, and the cluster's apply
/// would come up with the library chart's defaults — `enabled: false`,
/// the typed values block from the chart's own `values.yaml` rather
/// than the flux-side override — silently no-op'ing every per-cluster
/// override the operator set, far from the rebrand commit's source.
/// The apply-time symptom (the workload comes up with the library
/// chart's defaults instead of the per-cluster overrides) is invisible
/// at admission and surfaces only as "the service is up but not doing
/// what we configured it to do", typically far from the rebrand commit.
///
/// Lifting it to caixa-core's render-constants block alongside the
/// peer [`DEFAULT_NAMESPACE`] / [`DEFAULT_SERVICO_PORT`] makes the
/// library-name axis discipline structural: every renderer that
/// reaches for the canonical library-chart name consults the same
/// `&'static str`, and every future renderer inherits the same value
/// by construction with no opportunity for per-renderer drift. Same
/// "the typed constant lives in one place" discipline the
/// [`PLEME_LABEL_PREFIX`] (a8d4d57) / [`KUBE_KEY_API_VERSION`] /
/// [`LAREIRA_CHART_NAME_PREFIX`] lifts apply on the peer
/// shared-string axes.
///
/// [ch]: ../../caixa_helm/index.html
/// [cf]: ../../caixa_flux/index.html
pub const DEFAULT_LIBRARY_NAME: &str = "pleme-computeunit";
/// Canonical Flux v2 `spec.interval` reconcile-poll cadence duration
/// scalar every [`caixa-flux`][cf]-emitted Flux v2 CR (the per-caixa
/// `cluster_bundle` triplet's `GitRepository` + `HelmRelease` +
/// `Kustomization`) declares as its default reconcile-schedule when the
/// per-caixa [`ClusterBundleOpts::for_caixa`][fc] seed doesn't carry an
/// operator-pinned override. Every rendered per-caixa Flux v2 CR consults
/// the same `&'static str` at seed time so a future substrate-side
/// reconcile-cadence migration (`"10m"` → `"5m"` once the Flux v2 source-
/// controller / helm-controller / kustomize-controller trio ships lower-
/// latency-poll optimizations that make per-CR cluster load safe at a
/// faster cadence, `"10m"` → `"15m"` on cost-optimized clusters where the
/// per-CR source-controller poll cost outweighs the reconcile-freshness
/// gain) is a one-line edit on this canonical declaration, not a
/// coordinated rewrite across the [`ClusterBundleOpts`] default seed and
/// every future per-target renderer the substrate adds.
///
/// The single source of truth the rendered per-caixa Flux v2 cluster
/// bundle's per-CR reconcile-poll cadence default seed reaches for:
///
/// - [`ClusterBundleOpts::for_caixa`][fc]'s per-caixa default seed
/// (caixa-flux/src/lib.rs — the `interval: <DEFAULT>.into()` field of
/// the [`ClusterBundleOpts`] struct default the substrate's per-caixa
/// `cluster_bundle` renderer threads through every emitted Flux v2 CR's
/// [`FLUX_KEY_INTERVAL`] axis verbatim).
///
/// The value is a valid Flux v2 reconcile-poll cadence duration scalar (per
/// the upstream Flux v2 `metav1.Duration` OpenAPI schema on each of the
/// three Flux v2 CRDs — `source.toolkit.fluxcd.io/v1/GitRepository.spec.
/// interval`, `helm.toolkit.fluxcd.io/v2/HelmRelease.spec.interval`,
/// `kustomize.toolkit.fluxcd.io/v1/Kustomization.spec.interval`): a
/// non-empty Go-duration-format string (e.g. `"10m"`, `"5m"`, `"1h30m"`),
/// which the Flux v2 controller-side per-CR admission gate parses via
/// `metav1.ParseDuration` before installing the per-CR watch. A future
/// rebrand on this lift cannot silently land a value the Flux v2
/// controller-side admission gate rejects at the *first* per-caixa
/// `HelmRelease` apply against a cluster, far from the rebrand commit's
/// source — the pin at the canonical lift documents the Go-duration-format
/// grammar contract with the Flux v2 admission gate every downstream
/// consumer of the rendered per-CR reconcile-cadence axis rests on.
///
/// Pairs with the sibling [`FLUX_KEY_INTERVAL`] (48db6e2) per-Flux-v2-CR
/// reconcile-poll cadence scalar-axis key the value the substrate seeds
/// here nests directly under across every rendered per-caixa Flux v2 CR
/// — the key half of the per-CR `spec.interval` scalar-key/scalar-value
/// pair lives at [`FLUX_KEY_INTERVAL`], the value half's substrate-side
/// default seed lives here. Same "the typed constant lives in one place"
/// discipline the [`DEFAULT_NAMESPACE`] (a085b26) /
/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) / [`DEFAULT_LIBRARY_NAME`]
/// (41438dc) / [`DEFAULT_SERVICO_PORT`] (1e22add) /
/// [`DEFAULT_GATEWAY_CLASS_NAME`] (d9b0743) /
/// [`DEFAULT_PUBLISH_TAG_PREFIX`] (0a6a602) lifts apply on the peer
/// canonical-substrate-default-load-bearing-scalar surface — extends the
/// canonical-substrate-default single-sourcing discipline from the peer
/// substrate-side default-namespace / default-library-chart-name /
/// default-Servico-listen-port / default-Gateway-API-controller-name /
/// default-git-publish-tag-prefix surfaces onto the sibling default-Flux-
/// v2-per-CR-reconcile-poll-cadence surface every rendered per-caixa
/// Flux v2 cluster bundle CR carries.
///
/// [cf]: ../../caixa_flux/index.html
/// [fc]: ../../caixa_flux/struct.ClusterBundleOpts.html#method.for_caixa
pub const DEFAULT_FLUX_RECONCILE_INTERVAL: &str = "10m";
/// Canonical Flux v2 `HelmRelease.spec.chart.spec.chart` per-CR chart-
/// directory-in-GitRepository-source sub-path scalar every
/// [`caixa-flux`][cf]-emitted `helmrelease.yaml` document declares as the
/// default chart-directory-in-git-source pointer when the per-caixa
/// [`ClusterBundleOpts::for_caixa`][fc] seed doesn't carry an operator-
/// pinned override. The Flux v2 source-controller resolves the pointer
/// relative to the paired [`FLUX_KIND_GIT_REPOSITORY`] the sibling
/// [`FLUX_KEY_SOURCE_REF`]-keyed `sourceRef:` block names — the substrate's
/// canonical contract with every caixa Servico's git repository is that
/// the per-caixa `lareira-<nome>` chart the peer `caixa-helm` renderer
/// emits lives at the `./chart/` sub-tree of the repository root, so the
/// helm-controller's per-CR chart-open loop keys off this exact scalar to
/// locate the [`HELM_CHART_YAML_FILENAME`] + [`HELM_VALUES_YAML_FILENAME`]
/// pair the per-caixa rendered chart declares. Every rendered per-caixa
/// `HelmRelease` CR consults the same `&'static str` at seed time so a
/// future substrate-side chart-directory-in-git-source rebrand
/// (`"chart"` → `"charts"` once a per-caixa multi-chart layout lands and
/// the substrate publishes N sibling `lareira-<nome>/` charts under one
/// git repository, `"chart"` → `"helm"` on a cross-language convention
/// alignment with sibling wasm-runtime substrates, `"chart"` → `"deploy"`
/// on a per-caixa-deploy-directory naming migration) is a one-line edit
/// on this canonical declaration, not a coordinated rewrite across the
/// [`ClusterBundleOpts`] default seed and every future per-target
/// renderer the substrate adds.
///
/// The single source of truth the rendered per-caixa Flux v2 cluster
/// bundle's per-CR chart-directory-in-git-source default seed reaches for:
///
/// - [`ClusterBundleOpts::for_caixa`][fc]'s per-caixa default seed
/// (caixa-flux/src/lib.rs — the `chart_path: <DEFAULT>.into()` field
/// of the [`ClusterBundleOpts`] struct default the substrate's per-
/// caixa `cluster_bundle` renderer threads through every emitted per-
/// caixa `helmrelease.yaml` document's [`FLUX_HELMCHART_TEMPLATE_KEY_CHART`]
/// -keyed `spec.chart.spec.chart` axis verbatim).
///
/// The value is a valid Flux v2 `HelmRelease.spec.chart.spec.chart` scalar
/// (per the upstream Flux v2 `helm.toolkit.fluxcd.io/v2/HelmRelease` `OpenAPI`
/// schema — a non-empty string interpreted by the source-controller as a
/// relative directory-tree path from the paired `GitRepository` clone
/// root): a non-empty ASCII scalar with no leading path separator (which
/// would break the source-controller's relative-path composition against
/// the per-clone-root anchor). A future rebrand on this lift cannot
/// silently land an empty scalar or a leading-separator scalar the source-
/// controller-side per-CR chart-open loop would then reject at the *first*
/// per-caixa `HelmRelease` apply against a cluster, far from the rebrand
/// commit's source — the [`default_flux_chart_source_subpath_is_a_valid_relative_directory_scalar`]
/// pin trips at caixa-core build time on any drift past the typed floor.
///
/// Pairs with the sibling [`FLUX_HELMCHART_TEMPLATE_KEY_CHART`] (0fef82e)
/// per-Flux-v2-`HelmRelease.spec.chart.spec.chart` leaf-scalar-key the
/// value the substrate seeds here nests directly under across every
/// rendered per-caixa `HelmRelease` CR — the key half of the per-CR
/// `spec.chart.spec.chart` scalar-key/scalar-value pair lives at
/// [`FLUX_HELMCHART_TEMPLATE_KEY_CHART`], the value half's substrate-side
/// default seed lives here. Peer with [`flux_kustomization_source_subtree`]
/// on the sibling `Kustomization.spec.path` per-cluster / per-caixa `GitOps`-
/// repository-relative directory-tree seed composer — both name a load-
/// bearing directory-tree relative path the Flux v2 controller family's
/// per-CR reconcile loop navigates into, at the two paired axes of the
/// per-caixa `cluster_bundle` triplet (the `HelmRelease` chart-directory
/// axis names *where in the caixa's own git repo the chart lives*, the
/// `Kustomization` sub-tree axis names *where in the k8s-GitOps repo the
/// per-cluster manifest sub-tree lives*, and the two together close the
/// Flux v2 kustomize-controller → helm-controller reconcile-chain axis
/// the substrate's per-caixa cluster-bundle-triplet reconcile-topology
/// rests on).
///
/// Same "the typed constant lives in one place" discipline the
/// [`DEFAULT_NAMESPACE`] (a085b26) / [`DEFAULT_FLUX_SYSTEM_NAMESPACE`]
/// (7197d38) / [`DEFAULT_LIBRARY_NAME`] (41438dc) /
/// [`DEFAULT_SERVICO_PORT`] (1e22add) / [`DEFAULT_GATEWAY_CLASS_NAME`]
/// (d9b0743) / [`DEFAULT_PUBLISH_TAG_PREFIX`] (0a6a602) /
/// [`DEFAULT_FLUX_RECONCILE_INTERVAL`] (908180f) /
/// [`DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT`] (64bdb2b) /
/// [`DEFAULT_PLEME_GIT_ORG`] (9952bd9) lifts apply on the peer
/// canonical-substrate-default-load-bearing-scalar surface — extends the
/// canonical-substrate-default single-sourcing discipline from the peer
/// substrate-side default-namespace / default-library-chart-name /
/// default-Servico-listen-port / default-Gateway-API-controller-name /
/// default-git-publish-tag-prefix / default-Flux-v2-per-CR-reconcile-poll-
/// cadence / default-Flux-v2-per-CR-kustomization-reconcile-wall-clock-cap
/// / default-pleme-io-git-org surfaces onto the sibling default-Flux-v2-
/// per-CR-HelmRelease-chart-directory-in-git-source surface every rendered
/// per-caixa Flux v2 cluster bundle `HelmRelease` CR carries.
///
/// [cf]: ../../caixa_flux/index.html
/// [fc]: ../../caixa_flux/struct.ClusterBundleOpts.html#method.for_caixa
pub const DEFAULT_FLUX_CHART_SOURCE_SUBPATH: &str = "chart";
/// Canonical Flux v2 `HelmRelease.spec.{install,upgrade}.remediation.retries`
/// bounded retry-count scalar every [`caixa-flux`][cf]-emitted `helmrelease.yaml`
/// document declares under both the install-path and the upgrade-path
/// `remediation` blocks. The Flux v2 `helm-controller` per-CR `Install` /
/// `Upgrade` action reconciler consumes this scalar as the ceiling on the
/// number of times it will re-attempt a failed Helm install or Helm upgrade
/// before it marks the `HelmRelease` `Ready: False` and stops retrying — the
/// substrate's canonical "how many times we let Flux re-try a chart apply
/// before it stops" contract with the helm-controller-side per-CR
/// remediation loop.
///
/// The single source of truth all two duplicated inline `retries: 3`
/// scalar-value literal sites the substrate's [`cluster_bundle`][cb]
/// `helmrelease.yaml` format-string template reaches for:
///
/// - `helmrelease.yaml` `spec.install.remediation.retries` — the install-
/// path retry cap the helm-controller consumes for the first-time chart
/// apply the `HelmRelease` CR gates. Before this lift landed the value
/// sat as an inline `retries: 3\n` literal inside
/// [`cluster_bundle`][cb]'s `helmrelease.yaml` format-string template's
/// `install:` sub-block (caixa-flux/src/lib.rs — the `install.remediation`
/// sub-block).
/// - `helmrelease.yaml` `spec.upgrade.remediation.retries` — the upgrade-
/// path retry cap the helm-controller consumes for every subsequent
/// chart re-apply the same `HelmRelease` CR gates on a caixa version
/// bump. Before this lift landed the value sat as a second inline
/// `retries: 3\n` literal inside the same
/// [`cluster_bundle`][cb] `helmrelease.yaml` format-string template's
/// `upgrade:` sub-block (caixa-flux/src/lib.rs — the `upgrade.remediation`
/// sub-block).
/// - Every future per-caixa `HelmRelease` renderer the M3.x + M4
/// absorption roadmap acknowledges (the future
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
/// `HelmRelease` synthesis, a future per-cluster override `HelmRelease`
/// the operator emits for the observability-collector pipeline).
///
/// Both existing production-code sites carry the *same* substrate-chosen
/// retry ceiling — the value is one canonical policy choice, not two
/// independent axes: the operator's "how many chart-apply failures we
/// tolerate before Flux stops retrying and surfaces the failure at the
/// per-caixa `HelmRelease.status.conditions[]` axis the substrate's
/// downstream reconciliation-topology consumer watches". A future
/// substrate-side retry-ceiling migration (`3` → `5` once per-caixa
/// idempotency invariants tighten and higher-retry recovery from
/// transient apiserver / registry / oci-source flakes becomes safe, `3`
/// → `1` on hardened per-caixa pipelines where a failed apply should
/// escalate to operator-attention rather than mask under further retries,
/// `3` → `10` on high-churn dev clusters where transient failures
/// dominate) without a coordinated edit on *both* sites would have
/// silently split the substrate's canonical retry-ceiling between the
/// install-path and the upgrade-path — first-time applies would tolerate
/// one ceiling while every subsequent per-version re-apply would tolerate
/// another, with no field naming the ceiling-drift root cause far from
/// the rebrand commit's source. Lifting the value to caixa-core's render-
/// constants block alongside the peer [`DEFAULT_FLUX_RECONCILE_INTERVAL`]
/// makes the retry-ceiling axis discipline structural: both sites consult
/// the same `u32`, and every future per-CR remediation-retries emitter
/// inherits the same value by construction with no opportunity for per-
/// path drift.
///
/// The value is a valid Flux v2 `HelmRelease`-remediation-retries scalar
/// (per the upstream Flux v2 `HelmRelease.spec.{install,upgrade}.remediation.retries`
/// `OpenAPI` schema — a non-negative integer, `-1` reserved as the sentinel
/// for "retry indefinitely" which the substrate opts out of by declaring
/// a bounded ceiling): a positive `u32` bounded above by the substrate's
/// tolerance for silently-masked chart-apply failures. A future rebrand
/// on this lift cannot silently land a negative sentinel by construction:
/// the [`flux_helmrelease_remediation_retries_default_is_a_bounded_positive_scalar`]
/// pin trips at caixa-core build time on any drift past the typed floor.
///
/// Pairs with the sibling [`DEFAULT_FLUX_RECONCILE_INTERVAL`] (908180f)
/// on the peer canonical-Flux-v2-per-CR-substrate-default surface — the
/// reconcile-poll cadence default names how often the helm-controller
/// re-evaluates the per-CR desired state, and this remediation-retries
/// ceiling names how many times a per-evaluation Helm action is allowed
/// to fail-and-retry before the controller stops. Both are substrate-side
/// policy choices the operator inherits when the per-caixa
/// [`ClusterBundleOpts`][co] doesn't pin an override, and both must move
/// together on any coordinated substrate-side Flux v2 per-CR-remediation
/// tuning-cycle promotion.
///
/// Same "the typed constant lives in one place" discipline the
/// [`DEFAULT_NAMESPACE`] (a085b26) / [`DEFAULT_FLUX_SYSTEM_NAMESPACE`]
/// (7197d38) / [`DEFAULT_LIBRARY_NAME`] (41438dc) /
/// [`crate::DEFAULT_SERVICO_PORT`] (1e22add) /
/// [`DEFAULT_GATEWAY_CLASS_NAME`] (d9b0743) /
/// [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] (0a6a602) /
/// [`DEFAULT_FLUX_RECONCILE_INTERVAL`] (908180f) lifts apply on the peer
/// canonical-substrate-default-load-bearing-scalar surface.
///
/// [cb]: ../../caixa_flux/fn.cluster_bundle.html
/// [cf]: ../../caixa_flux/index.html
/// [co]: ../../caixa_flux/struct.ClusterBundleOpts.html
pub const FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT: u32 = 3;
/// Canonical Flux v2 `HelmRelease.spec.{install,upgrade}.remediation.retries`
/// leaf scalar-key every `caixa-flux`-emitted `helmrelease.yaml` document
/// carries at both its install-path + upgrade-path per-CR remediation
/// blocks. Peer to the sibling
/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) scalar-value
/// half of the same `(leaf-key, scalar-value)` per-path retry-cap
/// declaration pair — the Flux v2 helm-controller's per-CR remediation
/// loop reads the scalar under this exact leaf key, so drift on either
/// axis is equally load-bearing (a typo on the leaf-key silently strips
/// the retry-cap declaration from the emitted `remediation:` sub-block —
/// the helm-controller then falls back to the Flux v2 upstream default
/// rather than the substrate's chosen ceiling — with no diagnostic
/// naming the leaf-key-drift root cause far from the source
/// caixa.lisp / the renderer's format-string template).
///
/// The single source of truth every rendered Flux bundle axis that
/// names the per-path per-CR retry-cap leaf reaches for:
///
/// - the rendered `helmrelease.yaml` document's
/// `spec.install.remediation.retries` scalar-key axis
/// (caixa-flux/src/lib.rs — the `cluster_bundle` `helmrelease.yaml`
/// format-string template's install-path retry-cap leaf under the
/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`]-valued sub-block);
/// - the rendered `helmrelease.yaml` document's
/// `spec.upgrade.remediation.retries` scalar-key axis (caixa-flux/src/
/// lib.rs — the sibling `cluster_bundle` `helmrelease.yaml` format-
/// string template's upgrade-path retry-cap leaf under the same
/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`]-valued sub-block);
/// - the two test-fixture navigation sites in caixa-flux's `mod tests`
/// that probe the rendered document's `.get("retries")` container
/// axis to pin the emitted scalar-value against the sibling
/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] canonical-scalar
/// lift (the install-path + upgrade-path production-emit pins
/// [`cluster_bundle_helmrelease_install_remediation_retries_pins_lifted_default`]
/// / [`cluster_bundle_helmrelease_upgrade_remediation_retries_pins_lifted_default`]).
///
/// Both production emit sites + the two test-fixture navigation sites
/// name the same Flux v2 per-path per-CR retry-cap leaf-scalar-key and
/// must move together on any hypothetical Flux v3 rename (upstream Flux
/// v3 roadmap floats candidates like `attempts` / `maxRetries` /
/// `retryCount` in the migration prose — the peer Gateway-API-side
/// `spec.rules[].retry.attempts` leaf already uses `attempts` on the
/// sibling `GATEWAY_API_KEY_ATTEMPTS` axis, an independent CRD group's
/// evolution the two `pub const` declarations stay sibling constants
/// against). Until this lift landed the axis carried inline `retries`
/// literals across the two production emit sites (caixa-flux/src/lib.rs
/// — the two `retries: {retries_default}` sub-block leaf-headers inside
/// the `cluster_bundle` `helmrelease.yaml` format-string template) plus
/// the two test-fixture navigation sites — four occurrences of the same
/// load-bearing Flux-v2-per-CR-retry-cap-leaf-scalar-key convention,
/// drift-prone by construction.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the sibling
/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) scalar-
/// value half established — extends the discipline from the scalar
/// value the leaf holds onto the leaf-key itself, closing the
/// `(leaf-key, scalar-value)` pair on both halves. The two render-side
/// consumers now thread the same `&'static str` through their format-
/// string template via a `{retries_key}` named-arg interpolation so a
/// future Flux v3 rebrand lands in one place; every future renderer
/// that reaches for the canonical Flux v2 per-CR per-path retry-cap
/// leaf-key (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-Aplicacao `HelmRelease`, a future per-edge
/// `HelmRelease` the operator emits for the
/// `CiliumClusterwideEnvoyConfig` pipeline, a future `caixa-otel`
/// collector-pipeline `HelmRelease`) inherits the same value by
/// construction with no opportunity for per-renderer drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) sibling
/// scalar-value lift plus the peer [`FLUX_KEY_SOURCE_REF`] (236ef01) /
/// [`FLUX_KEY_CHART`] / [`FLUX_KEY_VALUES`] / [`FLUX_KEY_INTERVAL`] /
/// [`FLUX_KEY_HEALTH_CHECKS`] container-axis-key lifts apply on the
/// peer canonical-Flux-v2-load-bearing-string surface.
///
/// [cf]: ../../caixa_flux/index.html
pub const FLUX_HELMRELEASE_KEY_RETRIES: &str = "retries";
/// Canonical Flux v2 `HelmRelease.spec.{install,upgrade}.remediation`
/// sub-container-axis-key every `caixa-flux`-emitted `helmrelease.yaml`
/// document nests the sibling
/// [`FLUX_HELMRELEASE_KEY_RETRIES`] retry-cap leaf-scalar-key under, at
/// both the install-path + upgrade-path per-CR remediation blocks. The
/// parent-container-axis-key half of the same
/// `(container-axis-key, leaf-scalar-key, scalar-value)` per-path
/// retry-cap declaration triple the sibling
/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) scalar-value
/// + [`FLUX_HELMRELEASE_KEY_RETRIES`] (a12f9fc) leaf-scalar-key halves
/// closed on the value the leaf holds + the leaf-key itself — this lift
/// closes the third and final axis on the same per-path retry-cap
/// declaration by extending the discipline from the leaf up to the sub-
/// container-axis-key the leaf sits under. The Flux v2 helm-controller
/// per-CR remediation loop navigates through this exact sub-container
/// axis to reach the retry-cap scalar-key, so drift on this axis is
/// equally load-bearing (a typo on the sub-container-axis-key silently
/// strips the entire per-path remediation block from the emitted per-CR
/// document — the helm-controller then falls back to the Flux v2
/// upstream defaults for the whole remediation surface rather than the
/// substrate's chosen ceiling, with no diagnostic naming the container-
/// axis-key-drift root cause far from the source caixa.lisp / the
/// renderer's format-string template).
///
/// The single source of truth every rendered Flux bundle axis that
/// names the per-path per-CR remediation sub-container reaches for:
///
/// - the rendered `helmrelease.yaml` document's `spec.install.remediation`
/// sub-block-header axis (caixa-flux/src/lib.rs — the `cluster_bundle`
/// `helmrelease.yaml` format-string template's install-path
/// remediation sub-block-header nesting the retry-cap leaf under the
/// sibling
/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`]-valued scalar);
/// - the rendered `helmrelease.yaml` document's `spec.upgrade.remediation`
/// sub-block-header axis (caixa-flux/src/lib.rs — the sibling
/// `cluster_bundle` `helmrelease.yaml` format-string template's
/// upgrade-path remediation sub-block-header, additionally nesting
/// the `remediateLastFailure: true` toggle on the upgrade-path
/// sibling axis);
/// - the two test-fixture navigation sites in caixa-flux's `mod tests`
/// that probe the rendered document's `.get("remediation")` container
/// axis to reach the sibling [`FLUX_HELMRELEASE_KEY_RETRIES`] leaf-
/// scalar-key pin (the install-path + upgrade-path production-emit
/// pins
/// [`cluster_bundle_helmrelease_install_remediation_retries_pins_lifted_default`]
/// / [`cluster_bundle_helmrelease_upgrade_remediation_retries_pins_lifted_default`]).
///
/// Both production emit sites + the two test-fixture navigation sites
/// name the same Flux v2 per-path per-CR remediation sub-container-axis
/// key and must move together on any hypothetical Flux v3 rename
/// (upstream Flux v3 roadmap floats candidates like `recovery` /
/// `retryPolicy` / `errorHandling` in the migration prose). Until this
/// lift landed the axis carried inline `remediation` literals across the
/// two production emit sites (caixa-flux/src/lib.rs — the two
/// `remediation:` sub-block-header lines inside the `cluster_bundle`
/// `helmrelease.yaml` format-string template's install-path + upgrade-
/// path per-CR blocks) plus the two test-fixture navigation sites —
/// four occurrences of the same load-bearing Flux-v2-per-CR-remediation-
/// sub-container-axis-key convention, drift-prone by construction.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the sibling [`FLUX_HELMRELEASE_KEY_RETRIES`] (a12f9fc)
/// leaf-scalar-key half + the sibling
/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) scalar-
/// value half established — closes the parent-container-axis-key axis
/// on the same per-path retry-cap declaration triple, so all three
/// halves now live in one place. The two render-side consumers now
/// thread the same `&'static str` through their format-string template
/// via a `{remediation_key}` named-arg interpolation so a future Flux v3
/// rebrand lands in one place; every future renderer that reaches for
/// the canonical Flux v2 per-CR per-path remediation sub-container-axis
/// key inherits the same value by construction with no opportunity for
/// per-renderer drift.
///
/// Same "the typed constant lives in one place" discipline the sibling
/// [`FLUX_HELMRELEASE_KEY_RETRIES`] (a12f9fc) leaf-scalar-key half plus
/// the peer [`FLUX_KEY_SOURCE_REF`] (236ef01) / [`FLUX_KEY_CHART`] /
/// [`FLUX_KEY_VALUES`] / [`FLUX_KEY_INTERVAL`] /
/// [`FLUX_KEY_HEALTH_CHECKS`] container-axis-key lifts apply on the
/// peer canonical-Flux-v2-load-bearing-string surface.
///
/// [cf]: ../../caixa_flux/index.html
pub const FLUX_HELMRELEASE_KEY_REMEDIATION: &str = "remediation";
/// Canonical Flux v2 `HelmRelease.spec.install` per-CR helm-action-phase
/// discriminator parent-container-axis-key every `caixa-flux`-emitted
/// `helmrelease.yaml` document nests the sibling
/// [`FLUX_HELMRELEASE_KEY_REMEDIATION`] (6fe4e7e) sub-container-axis-key
/// under, at the first-time chart apply per-CR phase the Flux v2 helm-
/// controller reconciles when the emitted `HelmRelease` CR first lands in
/// the cluster. Pairs with the sibling [`FLUX_HELMRELEASE_KEY_UPGRADE`]
/// per-CR helm-action-phase discriminator parent-container-axis-key on
/// the peer per-CR upgrade-path phase the helm-controller reconciles on
/// every subsequent per-version chart re-apply the same CR gates. The
/// Flux v2 helm-controller-side per-CR phase-dispatch loop keys off this
/// exact parent-container-axis-key to select the install-path per-CR
/// action pipeline (`createNamespace` seeder, first-time chart values
/// merge, `spec.install.remediation.retries` retry-cap ceiling under the
/// nested [`FLUX_HELMRELEASE_KEY_REMEDIATION`] sub-container), so drift
/// on this axis is exactly as load-bearing as drift on the nested
/// [`FLUX_HELMRELEASE_KEY_REMEDIATION`] sub-container-axis-key it hosts
/// (a `"initialize"` / `"apply"` / `"create"` / `"first-run"` typo at
/// the production-code call site silently strips the entire install-path
/// per-CR phase block from the emitted per-CR document — the helm-
/// controller then falls back to the Flux v2 upstream defaults for the
/// whole install-path phase surface rather than the substrate's chosen
/// per-CR install-path knob-set — `createNamespace` never fires, the
/// per-CR retry-cap ceiling silently drops off the emitted document,
/// with no diagnostic naming the phase-discriminator-drift root cause
/// far from the source `caixa.lisp` / the renderer's format-string
/// template).
///
/// The single source of truth every rendered Flux bundle axis that names
/// the per-CR install-path phase parent-container reaches for:
///
/// - the rendered `helmrelease.yaml` document's `spec.install` sub-
/// block-header axis (caixa-flux/src/lib.rs — the `cluster_bundle`
/// `helmrelease.yaml` format-string template's install-path sub-
/// block-header nesting the `createNamespace: true` seeder + the
/// sibling [`FLUX_HELMRELEASE_KEY_REMEDIATION`]-container-keyed
/// retry-cap sub-block);
/// - the test-fixture navigation site in caixa-flux's `mod tests` that
/// probes the rendered document's `.get("install")` container axis
/// to reach the sibling [`FLUX_HELMRELEASE_KEY_REMEDIATION`] sub-
/// container (the install-path production-emit pin
/// [`cluster_bundle_helmrelease_install_remediation_retries_pins_lifted_default`]).
///
/// Both the production emit site + the test-fixture navigation site name
/// the same Flux v2 per-CR install-path helm-action-phase discriminator
/// parent-container-axis-key and must move together on any hypothetical
/// Flux v3 rename (upstream Flux v3 roadmap floats candidates like
/// `initialize` / `apply` / `create` / `first-run` in the migration
/// prose). Until this lift landed the axis carried inline `install`
/// literals across the one production emit site (caixa-flux/src/lib.rs —
/// the `install:` sub-block-header line inside the `cluster_bundle`
/// `helmrelease.yaml` format-string template's per-CR install-path block)
/// plus the one test-fixture navigation site — two occurrences of the
/// same load-bearing Flux-v2-per-CR-install-path-phase-discriminator-
/// parent-container-axis-key convention, drift-prone by construction.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5, "every
/// recurring shape becomes a generator before it becomes a pattern; every
/// pattern becomes a library before it becomes duplicated code. The
/// duplication budget is zero.") promotes the constant to a typed
/// substrate-side `&'static str` on the same trajectory the sibling
/// [`FLUX_HELMRELEASE_KEY_REMEDIATION`] (6fe4e7e) sub-container-axis-key +
/// [`FLUX_HELMRELEASE_KEY_RETRIES`] (a12f9fc) leaf-scalar-key +
/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) scalar-
/// value halves of the same `(parent-container-key, sub-container-key,
/// leaf-key, scalar-value)` per-path retry-cap declaration quartet
/// established — extends the discipline from the sub-container-axis-key
/// one level up to the parent-container-axis-key hosting it, so the
/// four-level nested `spec.install.remediation.retries` declaration now
/// resolves through four lifted `&'static str` / `u32` values. Companion
/// to the sibling [`FLUX_HELMRELEASE_KEY_UPGRADE`] per-CR upgrade-path
/// phase-discriminator parent-container-axis-key on the peer per-CR
/// helm-action-phase surface — completes the per-CR helm-action-phase
/// discriminator parent-container-axis-key pair the Flux v2 helm-
/// controller reconciles between at first-time chart apply time
/// (install-path phase) vs. every subsequent per-version chart re-apply
/// (upgrade-path phase).
///
/// Same "the typed constant lives in one place" discipline the sibling
/// [`FLUX_HELMRELEASE_KEY_REMEDIATION`] (6fe4e7e) sub-container-axis-key +
/// the peer [`FLUX_KEY_SOURCE_REF`] (236ef01) / [`FLUX_KEY_CHART`] /
/// [`FLUX_KEY_VALUES`] / [`FLUX_KEY_INTERVAL`] /
/// [`FLUX_KEY_HEALTH_CHECKS`] per-CR container-axis-key lifts apply on
/// the peer canonical-Flux-v2-load-bearing-string surface.
///
/// [cf]: ../../caixa_flux/index.html
pub const FLUX_HELMRELEASE_KEY_INSTALL: &str = "install";
/// Canonical Flux v2 `HelmRelease.spec.upgrade` per-CR helm-action-phase
/// discriminator parent-container-axis-key every `caixa-flux`-emitted
/// `helmrelease.yaml` document nests the sibling
/// [`FLUX_HELMRELEASE_KEY_REMEDIATION`] (6fe4e7e) sub-container-axis-key
/// under, at every subsequent per-version chart re-apply per-CR phase the
/// Flux v2 helm-controller reconciles after the initial install-path
/// phase completes. Pairs with the sibling
/// [`FLUX_HELMRELEASE_KEY_INSTALL`] per-CR helm-action-phase discriminator
/// parent-container-axis-key on the peer per-CR install-path phase the
/// helm-controller reconciles at first-time chart apply. The Flux v2
/// helm-controller-side per-CR phase-dispatch loop keys off this exact
/// parent-container-axis-key to select the upgrade-path per-CR action
/// pipeline (`remediateLastFailure` toggle the substrate pins to `true`
/// on the upgrade-path per-CR sibling axis, the per-CR retry-cap ceiling
/// under the nested [`FLUX_HELMRELEASE_KEY_REMEDIATION`] sub-container),
/// so drift on this axis is exactly as load-bearing as drift on the
/// nested [`FLUX_HELMRELEASE_KEY_REMEDIATION`] sub-container-axis-key it
/// hosts (a `"reapply"` / `"reconcile"` / `"update"` / `"promote"` typo
/// at the production-code call site silently strips the entire upgrade-
/// path per-CR phase block from the emitted per-CR document — the helm-
/// controller then falls back to the Flux v2 upstream defaults for the
/// whole upgrade-path phase surface rather than the substrate's chosen
/// per-CR upgrade-path knob-set — `remediateLastFailure` never fires, the
/// per-CR retry-cap ceiling silently drops off the emitted document, with
/// no diagnostic naming the phase-discriminator-drift root cause far
/// from the source `caixa.lisp` / the renderer's format-string template).
///
/// The single source of truth every rendered Flux bundle axis that names
/// the per-CR upgrade-path phase parent-container reaches for:
///
/// - the rendered `helmrelease.yaml` document's `spec.upgrade` sub-
/// block-header axis (caixa-flux/src/lib.rs — the `cluster_bundle`
/// `helmrelease.yaml` format-string template's upgrade-path sub-
/// block-header nesting the substrate's `remediateLastFailure: true`
/// toggle + the sibling [`FLUX_HELMRELEASE_KEY_REMEDIATION`]-
/// container-keyed retry-cap sub-block);
/// - the test-fixture navigation site in caixa-flux's `mod tests` that
/// probes the rendered document's `.get("upgrade")` container axis to
/// reach the sibling [`FLUX_HELMRELEASE_KEY_REMEDIATION`] sub-
/// container (the upgrade-path production-emit pin
/// [`cluster_bundle_helmrelease_upgrade_remediation_retries_pins_lifted_default`]).
///
/// Both the production emit site + the test-fixture navigation site name
/// the same Flux v2 per-CR upgrade-path helm-action-phase discriminator
/// parent-container-axis-key and must move together on any hypothetical
/// Flux v3 rename (upstream Flux v3 roadmap floats candidates like
/// `reapply` / `reconcile` / `update` / `promote` in the migration
/// prose). Until this lift landed the axis carried inline `upgrade`
/// literals across the one production emit site plus the one test-
/// fixture navigation site — two occurrences of the same load-bearing
/// Flux-v2-per-CR-upgrade-path-phase-discriminator-parent-container-
/// axis-key convention, drift-prone by construction.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5, "every
/// recurring shape becomes a generator before it becomes a pattern; every
/// pattern becomes a library before it becomes duplicated code. The
/// duplication budget is zero.") promotes the constant to a typed
/// substrate-side `&'static str` on the same trajectory the sibling
/// [`FLUX_HELMRELEASE_KEY_INSTALL`] per-CR install-path phase-
/// discriminator parent-container-axis-key +
/// [`FLUX_HELMRELEASE_KEY_REMEDIATION`] (6fe4e7e) sub-container-axis-key +
/// [`FLUX_HELMRELEASE_KEY_RETRIES`] (a12f9fc) leaf-scalar-key +
/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) scalar-
/// value halves of the same `(parent-container-key, sub-container-key,
/// leaf-key, scalar-value)` per-path retry-cap declaration quartet
/// established — pairs with the [`FLUX_HELMRELEASE_KEY_INSTALL`]
/// mandatory-arm parent-container-axis-key to close the per-CR helm-
/// action-phase discriminator parent-container-axis-key pair across
/// both per-CR phases the helm-controller reconciles between (install-
/// path at first-time chart apply, upgrade-path at every subsequent
/// per-version chart re-apply).
///
/// Same "the typed constant lives in one place" discipline the sibling
/// [`FLUX_HELMRELEASE_KEY_INSTALL`] per-CR install-path-phase-
/// discriminator + [`FLUX_HELMRELEASE_KEY_REMEDIATION`] (6fe4e7e) sub-
/// container-axis-key + the peer [`FLUX_KEY_SOURCE_REF`] (236ef01) /
/// [`FLUX_KEY_CHART`] / [`FLUX_KEY_VALUES`] / [`FLUX_KEY_INTERVAL`] /
/// [`FLUX_KEY_HEALTH_CHECKS`] per-CR container-axis-key lifts apply on
/// the peer canonical-Flux-v2-load-bearing-string surface.
///
/// [cf]: ../../caixa_flux/index.html
pub const FLUX_HELMRELEASE_KEY_UPGRADE: &str = "upgrade";
/// Canonical Flux v2 `HelmRelease.spec.upgrade.remediation.remediateLastFailure`
/// upgrade-path-only per-CR remediation-toggle leaf-scalar-key every
/// `caixa-flux`-emitted `helmrelease.yaml` document seeds to `true` under
/// the sibling [`FLUX_HELMRELEASE_KEY_UPGRADE`] per-CR upgrade-path phase-
/// discriminator parent-container-axis-key's nested
/// [`FLUX_HELMRELEASE_KEY_REMEDIATION`] sub-container-axis-key. Sibling to
/// the peer [`FLUX_HELMRELEASE_KEY_RETRIES`] retry-cap leaf-scalar-key at
/// the same per-CR upgrade-path per-CR remediation sub-container position —
/// closes the `spec.upgrade.remediation.{retries, remediateLastFailure}`
/// per-path remediation-block leaf-scalar-key pair the substrate seeds into
/// every emitted per-caixa `HelmRelease` CR on the upgrade-path per-CR
/// remediation block, with retries capping the per-version chart re-apply
/// retry-count and remediateLastFailure gating the "the Flux v2 helm-
/// controller must actively remediate — roll back to the prior success —
/// when the final per-version chart re-apply attempt still fails" post-
/// retry-exhaustion behavior. The Flux v2 helm-controller-side per-CR
/// upgrade-path remediation loop keys off this exact leaf to decide
/// whether to leave a failed upgrade in place (`false`) or trigger the
/// prior-release rollback pipeline (`true`); drift on this axis silently
/// drops the substrate's chosen post-retry-exhaustion rollback semantic
/// from every emitted per-caixa `HelmRelease` document (the helm-
/// controller then leaves every terminally-failed upgrade in the failed
/// state without rolling back to the prior last-known-good release the
/// substrate's "no chart apply leaves a per-caixa CR in a stalled,
/// unremediated state" MESH-COMPOSITION.md §V guarantee mandates — with
/// no diagnostic naming the remediation-toggle-drift root cause far from
/// the source `caixa.lisp` / the renderer's format-string template).
///
/// Note the axis is asymmetric across the peer install-path per-CR
/// remediation block: the substrate emits the toggle only under
/// `spec.upgrade.remediation` and not under `spec.install.remediation`
/// because the Flux v2 helm-controller's install-path per-CR remediation
/// loop treats a failed first-time chart apply as an uninstall-and-retry
/// pipeline whose "prior success" state is the empty pre-install cluster
/// state — the "roll back to the prior success" post-retry-exhaustion
/// behavior the toggle gates is well-defined only on the upgrade-path
/// where the prior success is a previous chart-version release, which is
/// why the [`FLUX_HELMRELEASE_KEY_RETRIES`] retry-cap leaf-scalar-key
/// sits under both per-CR remediation sub-containers (retry-cap applies
/// on both paths) but this per-CR remediation-toggle leaf-scalar-key
/// sits under the upgrade-path per-CR remediation sub-container only.
///
/// The single source of truth every rendered Flux bundle axis that names
/// the upgrade-path per-CR remediation-toggle leaf reaches for:
///
/// - the rendered `helmrelease.yaml` document's
/// `spec.upgrade.remediation.remediateLastFailure` leaf-scalar-key
/// axis (caixa-flux/src/lib.rs — the `cluster_bundle` `helmrelease
/// .yaml` format-string template's upgrade-path remediation-toggle
/// leaf under the [`FLUX_HELMRELEASE_KEY_REMEDIATION`]-container-keyed
/// sub-block, threading the same `&'static str` through a new
/// `{remediate_last_failure_key}` named-arg interpolation);
/// - the one test-fixture navigation site in caixa-flux's `mod tests`
/// that probes the rendered document's `.get("remediateLastFailure")`
/// leaf axis to pin the substrate's canonical `true` seed
/// (the [`cluster_bundle_helmrelease_upgrade_remediation_remediate_last_failure_pins_lifted_true`]
/// upgrade-path production-emit pin).
///
/// Both the production emit site + the one test-fixture navigation site
/// name the same Flux v2 per-CR upgrade-path remediation-toggle leaf-
/// scalar-key and must move together on any hypothetical Flux v3 rename
/// (upstream Flux v3 roadmap floats candidates like
/// `rollbackOnFailure` / `remediateOnFailure` / `recoverLastFailure` in
/// the migration prose). Until this lift landed the axis carried inline
/// `remediateLastFailure` literals across the one production emit site
/// (caixa-flux/src/lib.rs — the `remediateLastFailure: true` leaf inside
/// the `cluster_bundle` `helmrelease.yaml` format-string template's per-
/// CR upgrade-path remediation sub-block) — the sole occurrence of the
/// same load-bearing Flux-v2-per-CR-upgrade-path-remediation-toggle-
/// leaf-scalar-key convention, drift-prone by construction ahead of the
/// second occurrence the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-Aplicacao `HelmRelease` synthesis will surface,
/// where a per-renderer local `pub const FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE:
/// &str = "…"` (the canonical drift footgun where a sibling local
/// `pub const` could happen to carry the same string at the source while
/// pointing at a different `&'static` allocation) would let the two
/// renderers silently disagree on the post-retry-exhaustion remediation
/// semantic.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5, "every
/// recurring shape becomes a generator before it becomes a pattern; every
/// pattern becomes a library before it becomes duplicated code. The
/// duplication budget is zero.") promotes the constant to a typed
/// substrate-side `&'static str` in advance of the second occurrence the
/// M4 materializer will surface — so the second consumer inherits the
/// canonical upgrade-path per-CR remediation-toggle leaf-scalar-key by
/// construction without opportunity for per-renderer drift.
///
/// Same "the typed constant lives in one place" discipline the sibling
/// [`FLUX_HELMRELEASE_KEY_RETRIES`] (a12f9fc) leaf-scalar-key +
/// [`FLUX_HELMRELEASE_KEY_REMEDIATION`] (6fe4e7e) sub-container-axis-key +
/// [`FLUX_HELMRELEASE_KEY_INSTALL`] / [`FLUX_HELMRELEASE_KEY_UPGRADE`]
/// (7767c26) parent-container-axis-key pair +
/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) scalar-
/// value halves of the per-path per-CR remediation surface established —
/// closes the sibling upgrade-path-only per-CR remediation-toggle leaf-
/// scalar-key half at the same `spec.upgrade.remediation.*` position the
/// retries leaf sits at.
///
/// [cf]: ../../caixa_flux/index.html
pub const FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE: &str = "remediateLastFailure";
/// Canonical Flux v2 `HelmRelease.spec.upgrade.remediation.remediateLastFailure`
/// upgrade-path-only per-CR remediation-toggle scalar-value default the
/// substrate seeds into every per-caixa `helmrelease.yaml` document at the
/// paired [`FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE`] leaf-scalar-key
/// axis. Pairs with the sibling [`FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE`]
/// (96581b7) leaf-scalar-key half of the same `(leaf-key, scalar-value)`
/// per-CR upgrade-path per-CR post-retry-exhaustion-rollback-toggle
/// declaration pair — the Flux v2 helm-controller's per-CR upgrade-path
/// remediation loop reads the scalar under that exact leaf key to decide
/// whether to trigger the prior-release rollback pipeline once the paired
/// [`FLUX_HELMRELEASE_KEY_RETRIES`] retry-cap ceiling has been exhausted,
/// so drift on either axis is equally load-bearing (a rebrand on this
/// canonical scalar-value default that failed to reach every renderer's
/// emit site would silently split the substrate's chosen post-retry-
/// exhaustion rollback semantic between the operator-facing canonical
/// default and every per-caixa `HelmRelease` document's per-CR upgrade-
/// path remediation-toggle, with no field naming the semantic-drift root
/// cause far from the source `caixa.lisp` / the renderer's format-string
/// template).
///
/// The `true` seed opts every emitted per-caixa `HelmRelease` into the
/// substrate's canonical "no chart apply leaves a per-caixa CR in a
/// stalled, unremediated state" semantic (MESH-COMPOSITION.md §V): once
/// the paired [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] retry-cap
/// ceiling is exhausted on the upgrade-path per-CR reconcile loop the
/// helm-controller rolls the per-caixa release back to the prior last-
/// known-good `HelmRelease.status.lastAppliedRevision` snapshot rather
/// than leaving the per-caixa `HelmRelease` parked at `Ready: False`
/// with no forward-progress on the substrate's per-caixa reconciliation
/// topology. A future substrate-side rebrand to `false` (or a per-caixa
/// opt-out slot the ABSORPTION-ROADMAP.md M4 typed-slot trajectory adds
/// once the substrate grows a `:upgrade :remediate-last-failure` author-
/// side toggle) is a one-line edit on this canonical declaration, not a
/// coordinated rewrite across every future per-target renderer the
/// substrate adds. Peer with the sibling
/// [`FLUX_KUSTOMIZATION_PRUNE_DEFAULT`] (ea857d8) scalar-value default on
/// the peer canonical-Flux-v2-per-CR-substrate-default surface — the
/// garbage-collection-toggle default names whether the per-CR
/// `Kustomization` reconcile loop sweeps orphaned resources at all, and
/// this remediation-toggle default names whether the per-CR `HelmRelease`
/// upgrade-path remediation loop rolls back to the prior last-known-good
/// release once the retry-cap ceiling is exhausted. Both are substrate-
/// side policy choices the operator inherits when the per-caixa
/// [`ClusterBundleOpts`][co] doesn't pin an override, and both must move
/// together on any coordinated substrate-side Flux v2 per-CR
/// tuning-cycle promotion.
///
/// The single source of truth every rendered Flux bundle axis that
/// names the per-CR upgrade-path remediation-toggle scalar reaches for:
///
/// - the rendered `helmrelease.yaml` document's
/// `spec.upgrade.remediation.remediateLastFailure` scalar-value axis
/// (caixa-flux/src/lib.rs — the [`cluster_bundle`][cb]
/// `helmrelease.yaml` format-string template's per-CR upgrade-path
/// remediation-toggle scalar under the
/// [`FLUX_HELMRELEASE_KEY_UPGRADE`]-keyed sub-block, threading the
/// same `bool` through a `{remediate_last_failure_default}` named-arg
/// interpolation);
/// - the one test-fixture navigation site in caixa-flux's `mod tests`
/// that probes the rendered document's
/// `.get("remediateLastFailure")` scalar axis to pin the substrate's
/// canonical `true` seed against the lifted default (the
/// [`cluster_bundle_helmrelease_upgrade_remediation_remediate_last_failure_pins_lifted_true`]
/// per-CR production-emit pin).
///
/// Both the production emit site + the one test-fixture navigation site
/// now consume the same `bool` at emit time through the sibling
/// re-export [`caixa_flux::FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT`][cf],
/// so a future substrate-side toggle migration on the canonical scalar-
/// value axis reaches every consumer through one `bool` by construction —
/// with no opportunity for per-renderer drift where a rebrand on one
/// axis without a coordinated edit on the other would silently disagree
/// on the post-retry-exhaustion rollback semantic. Until this lift
/// landed the axis carried an inline `true` scalar-value literal at the
/// sole production-code call site (the `remediateLastFailure: true` leaf
/// inside the [`cluster_bundle`][cb] `helmrelease.yaml` format-string
/// template's per-CR `spec.upgrade.remediation` sub-block) plus the
/// sibling test-fixture navigation site — two occurrences of the same
/// load-bearing Flux-v2-per-CR-upgrade-path-remediation-toggle-scalar-
/// value convention, drift-prone by construction ahead of the third
/// occurrence the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
/// per-Aplicacao `HelmRelease` synthesis will surface, where a per-
/// renderer local `pub const FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT: bool = …`
/// at any downstream renderer would let the two consumers silently
/// disagree on the substrate's canonical seed.
///
/// Same "the typed constant lives in one place" discipline the
/// [`DEFAULT_NAMESPACE`] (a085b26) / [`DEFAULT_FLUX_SYSTEM_NAMESPACE`]
/// (7197d38) / [`DEFAULT_LIBRARY_NAME`] (41438dc) /
/// [`crate::DEFAULT_SERVICO_PORT`] (1e22add) /
/// [`DEFAULT_GATEWAY_CLASS_NAME`] (d9b0743) /
/// [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] (0a6a602) /
/// [`DEFAULT_FLUX_RECONCILE_INTERVAL`] (908180f) /
/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) /
/// [`FLUX_KUSTOMIZATION_PRUNE_DEFAULT`] (ea857d8) lifts apply on the peer
/// canonical-substrate-default-load-bearing-scalar surface.
///
/// [cb]: ../../caixa_flux/fn.cluster_bundle.html
/// [cf]: ../../caixa_flux/index.html
/// [co]: ../../caixa_flux/struct.ClusterBundleOpts.html
pub const FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT: bool = true;
/// Canonical Flux v2 `HelmRelease.spec.install.createNamespace` install-path-
/// only per-CR namespace-seeder-toggle leaf-scalar-key every `caixa-flux`-
/// emitted `helmrelease.yaml` document seeds to `true` under the sibling
/// [`FLUX_HELMRELEASE_KEY_INSTALL`] per-CR install-path phase-discriminator
/// parent-container-axis-key. Peer to the sibling
/// [`FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE`] upgrade-path-only per-CR
/// remediation-toggle leaf-scalar-key at the co-resident per-CR install/
/// upgrade phase-discriminator parent-container position — closes the
/// `spec.{install.createNamespace, upgrade.remediation.remediateLastFailure}`
/// per-path per-CR phase-specific toggle leaf-scalar-key pair the substrate
/// seeds into every emitted per-caixa `HelmRelease` CR: `createNamespace`
/// gates the "the Flux v2 helm-controller creates the target namespace
/// itself if the emitted `HelmRelease.metadata.namespace` (or its
/// `spec.targetNamespace` override) does not already exist" install-path
/// pre-apply seeder pipeline, while `remediateLastFailure` gates the
/// upgrade-path post-retry-exhaustion rollback pipeline. The Flux v2 helm-
/// controller-side per-CR install-path pre-apply loop keys off this exact
/// leaf to decide whether to first materialize the target namespace or
/// refuse the first-time chart apply when the target namespace does not
/// yet exist (`false`); drift on this axis silently drops the substrate's
/// chosen first-apply namespace-seeder semantic from every emitted per-
/// caixa `HelmRelease` document (the helm-controller then refuses every
/// first-time per-caixa chart apply against a fresh cluster whose target
/// namespace has not been pre-provisioned by an out-of-band pipeline —
/// the substrate's "no per-caixa Servico apply is blocked on manual
/// namespace preprovisioning" MESH-COMPOSITION.md §V install-path-fluency
/// guarantee silently regresses, with no diagnostic naming the seeder-
/// toggle-drift root cause far from the source `caixa.lisp` / the
/// renderer's format-string template).
///
/// Note the axis is asymmetric across the peer upgrade-path per-CR phase
/// block: the substrate emits the toggle only under `spec.install` and not
/// under `spec.upgrade` because the Flux v2 helm-controller's upgrade-path
/// per-CR reconcile loop presupposes the target namespace already carries
/// the prior release's resources (the upgrade-path is by definition a
/// re-apply against an already-materialized namespace whose pre-apply
/// seeding was resolved at the sibling install-path phase's first-time
/// apply), so the "seed the target namespace if it does not already exist"
/// pre-apply behavior the toggle gates is well-defined only on the
/// install-path where the target namespace's existence is not yet
/// established. This is the mirror of the peer sibling
/// [`FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE`] axis, which is
/// upgrade-path-only for the mirror reason (the "roll back to the prior
/// success" post-retry-exhaustion behavior is well-defined only on the
/// upgrade-path where a prior success exists) — the two per-CR phase-
/// specific toggle leaf-scalar-keys sit under mirror-symmetric
/// parent-container-axis-keys and together close the install/upgrade
/// phase-block per-CR-phase-specific toggle leaf-scalar-key pair.
///
/// The single source of truth every rendered Flux bundle axis that names
/// the install-path per-CR namespace-seeder-toggle leaf reaches for:
///
/// - the rendered `helmrelease.yaml` document's
/// `spec.install.createNamespace` leaf-scalar-key axis
/// (caixa-flux/src/lib.rs — the `cluster_bundle` `helmrelease.yaml`
/// format-string template's install-path namespace-seeder-toggle leaf
/// under the [`FLUX_HELMRELEASE_KEY_INSTALL`]-container-keyed sub-block,
/// threading the same `&'static str` through a new
/// `{create_namespace_key}` named-arg interpolation);
/// - the one test-fixture navigation site in caixa-flux's `mod tests`
/// that probes the rendered document's `.get("createNamespace")` leaf
/// axis to pin the substrate's canonical `true` seed
/// (the [`cluster_bundle_helmrelease_install_create_namespace_pins_lifted_true`]
/// install-path production-emit pin).
///
/// Both the production emit site + the one test-fixture navigation site
/// name the same Flux v2 per-CR install-path namespace-seeder-toggle leaf-
/// scalar-key and must move together on any hypothetical Flux v3 rename
/// (upstream Flux v3 roadmap floats candidates like `createTargetNamespace`
/// / `seedNamespace` / `provisionNamespace` in the migration prose). Until
/// this lift landed the axis carried inline `createNamespace` literals
/// across the one production emit site (caixa-flux/src/lib.rs — the
/// `createNamespace: true` leaf inside the `cluster_bundle` `helmrelease
/// .yaml` format-string template's per-CR install-path sub-block) — the
/// sole occurrence of the same load-bearing Flux-v2-per-CR-install-path-
/// namespace-seeder-toggle-leaf-scalar-key convention, drift-prone by
/// construction ahead of the second occurrence the M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
/// `HelmRelease` synthesis will surface, where a per-renderer local
/// `pub const FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE: &str = "…"` (the
/// canonical drift footgun where a sibling local `pub const` could happen
/// to carry the same string at the source while pointing at a different
/// `&'static` allocation) would let the two renderers silently disagree on
/// the install-path namespace-seeder semantic.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5, "every
/// recurring shape becomes a generator before it becomes a pattern; every
/// pattern becomes a library before it becomes duplicated code. The
/// duplication budget is zero.") promotes the constant to a typed
/// substrate-side `&'static str` in advance of the second occurrence the
/// M4 materializer will surface — so the second consumer inherits the
/// canonical install-path per-CR namespace-seeder-toggle leaf-scalar-key
/// by construction without opportunity for per-renderer drift.
///
/// Same "the typed constant lives in one place" discipline the sibling
/// [`FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE`] (96581b7) upgrade-path-
/// only per-CR remediation-toggle leaf-scalar-key +
/// [`FLUX_HELMRELEASE_KEY_RETRIES`] (a12f9fc) leaf-scalar-key +
/// [`FLUX_HELMRELEASE_KEY_REMEDIATION`] (6fe4e7e) sub-container-axis-key +
/// [`FLUX_HELMRELEASE_KEY_INSTALL`] / [`FLUX_HELMRELEASE_KEY_UPGRADE`]
/// (7767c26) parent-container-axis-key pair +
/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) scalar-value
/// halves of the per-path per-CR HelmRelease spec surface established —
/// closes the mirror install-path-only per-CR namespace-seeder-toggle
/// leaf-scalar-key half at the `spec.install.createNamespace` position the
/// peer `spec.upgrade.remediation.remediateLastFailure` upgrade-path-only
/// per-CR remediation-toggle leaf mirrors.
///
/// [cf]: ../../caixa_flux/index.html
pub const FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE: &str = "createNamespace";
/// Canonical Flux v2 `HelmRelease.spec.install.createNamespace` install-path-
/// only per-CR namespace-seeder-toggle scalar-value default the substrate
/// seeds into every per-caixa `helmrelease.yaml` document at the paired
/// [`FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE`] leaf-scalar-key axis. Pairs
/// with the sibling [`FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE`] (ba9ab8b)
/// leaf-scalar-key half of the same `(leaf-key, scalar-value)` per-CR
/// install-path per-CR namespace-seeder-toggle declaration pair — the
/// Flux v2 helm-controller's per-CR install-path pre-apply loop reads
/// the scalar under that exact leaf key to decide whether to first
/// materialize the target namespace before the first-time chart apply,
/// so drift on either axis is equally load-bearing (a rebrand on this
/// canonical scalar-value default that failed to reach every renderer's
/// emit site would silently split the substrate's chosen first-apply
/// namespace-seeder semantic between the operator-facing canonical
/// default and every per-caixa `HelmRelease` document's per-CR install-
/// path namespace-seeder-toggle, with no field naming the semantic-drift
/// root cause far from the source `caixa.lisp` / the renderer's format-
/// string template).
///
/// The `true` seed opts every emitted per-caixa `HelmRelease` into the
/// substrate's canonical "no per-caixa Servico apply is blocked on
/// manual namespace preprovisioning" semantic (MESH-COMPOSITION.md §V
/// install-path-fluency guarantee): on every first-time per-caixa chart
/// apply the helm-controller first materializes the target namespace
/// itself if the emitted `HelmRelease.metadata.namespace` (or its
/// `spec.targetNamespace` override) does not already exist, rather than
/// refusing the apply and requiring an out-of-band pipeline to have
/// pre-provisioned the namespace. A future substrate-side rebrand to
/// `false` (or a per-caixa opt-out slot the ABSORPTION-ROADMAP.md M4
/// typed-slot trajectory adds once the substrate grows a `:install
/// :create-namespace` author-side toggle) is a one-line edit on this
/// canonical declaration, not a coordinated rewrite across every future
/// per-target renderer the substrate adds. Peer with the sibling
/// [`FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT`] mirror-symmetric
/// upgrade-path-only scalar-value default + the peer
/// [`FLUX_KUSTOMIZATION_PRUNE_DEFAULT`] (ea857d8) scalar-value default on
/// the peer canonical-Flux-v2-per-CR-substrate-default surface — the
/// three defaults name the substrate's canonical (install-path
/// namespace-seeder) / (upgrade-path post-retry-exhaustion rollback) /
/// (garbage-collection-toggle) toggle triple across the per-caixa
/// `HelmRelease` and `Kustomization` co-resident CRs. All three are
/// substrate-side policy choices the operator inherits when the per-
/// caixa [`ClusterBundleOpts`][co] doesn't pin an override, and all
/// three must move together on any coordinated substrate-side Flux v2
/// per-CR tuning-cycle promotion.
///
/// The single source of truth every rendered Flux bundle axis that
/// names the per-CR install-path namespace-seeder-toggle scalar reaches
/// for:
///
/// - the rendered `helmrelease.yaml` document's
/// `spec.install.createNamespace` scalar-value axis
/// (caixa-flux/src/lib.rs — the [`cluster_bundle`][cb]
/// `helmrelease.yaml` format-string template's per-CR install-path
/// namespace-seeder-toggle scalar under the
/// [`FLUX_HELMRELEASE_KEY_INSTALL`]-keyed sub-block, threading the
/// same `bool` through a `{create_namespace_default}` named-arg
/// interpolation);
/// - the one test-fixture navigation site in caixa-flux's `mod tests`
/// that probes the rendered document's `.get("createNamespace")`
/// scalar axis to pin the substrate's canonical `true` seed against
/// the lifted default (the
/// [`cluster_bundle_helmrelease_install_create_namespace_pins_lifted_true`]
/// per-CR production-emit pin).
///
/// Both the production emit site + the one test-fixture navigation site
/// now consume the same `bool` at emit time through the sibling
/// re-export [`caixa_flux::FLUX_HELMRELEASE_CREATE_NAMESPACE_DEFAULT`][cf],
/// so a future substrate-side toggle migration on the canonical scalar-
/// value axis reaches every consumer through one `bool` by construction —
/// with no opportunity for per-renderer drift where a rebrand on one
/// axis without a coordinated edit on the other would silently disagree
/// on the first-apply namespace-seeder semantic. Until this lift landed
/// the axis carried an inline `true` scalar-value literal at the sole
/// production-code call site (the `createNamespace: true` leaf inside
/// the [`cluster_bundle`][cb] `helmrelease.yaml` format-string
/// template's per-CR `spec.install` sub-block) plus the sibling test-
/// fixture navigation site — two occurrences of the same load-bearing
/// Flux-v2-per-CR-install-path-namespace-seeder-toggle-scalar-value
/// convention, drift-prone by construction ahead of the third occurrence
/// the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-
/// Aplicacao `HelmRelease` synthesis will surface, where a per-renderer
/// local `pub const FLUX_HELMRELEASE_CREATE_NAMESPACE_DEFAULT: bool = …`
/// at any downstream renderer would let the two consumers silently
/// disagree on the substrate's canonical seed.
///
/// Same "the typed constant lives in one place" discipline the
/// [`DEFAULT_NAMESPACE`] (a085b26) / [`DEFAULT_FLUX_SYSTEM_NAMESPACE`]
/// (7197d38) / [`DEFAULT_LIBRARY_NAME`] (41438dc) /
/// [`crate::DEFAULT_SERVICO_PORT`] (1e22add) /
/// [`DEFAULT_GATEWAY_CLASS_NAME`] (d9b0743) /
/// [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] (0a6a602) /
/// [`DEFAULT_FLUX_RECONCILE_INTERVAL`] (908180f) /
/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) /
/// [`FLUX_KUSTOMIZATION_PRUNE_DEFAULT`] (ea857d8) /
/// [`FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT`] lifts apply on
/// the peer canonical-substrate-default-load-bearing-scalar surface.
///
/// [cb]: ../../caixa_flux/fn.cluster_bundle.html
/// [cf]: ../../caixa_flux/index.html
/// [co]: ../../caixa_flux/struct.ClusterBundleOpts.html
pub const FLUX_HELMRELEASE_CREATE_NAMESPACE_DEFAULT: bool = true;
/// Canonical Flux v2 `Kustomization.spec.prune` per-CR garbage-collection-
/// toggle leaf-scalar-key every `caixa-flux`-emitted `kustomization.yaml`
/// document seeds to `true` at the top-level `spec` position of the
/// emitted [`Kustomization`][kust] CR. The Flux v2 kustomize-controller-
/// side per-CR reconcile loop keys off this exact leaf to decide whether
/// to garbage-collect resources that were previously reconciled by the
/// CR but no longer appear in the CR's current desired-state manifest set
/// (`spec.prune: true` opts every emitted per-caixa `Kustomization` into
/// the substrate's canonical GitOps-side sweep-what-you-removed semantic;
/// `spec.prune: false` (or absent — Flux v2 defaults the axis to `false`
/// on any CR that omits the leaf) leaves orphaned resources dangling in
/// the cluster after the source manifest set removes them, silently
/// splitting per-caixa live cluster state from the caixa's tatara-lisp
/// source-of-truth and every downstream `feira app deploy` / `feira
/// deploy` reconcile the substrate's per-caixa GitOps pipeline emits).
///
/// Drift on this axis silently drops the substrate's chosen sweep-what-
/// you-removed semantic from every emitted per-caixa `Kustomization`
/// document — the kustomize-controller then leaves every per-caixa
/// resource the source manifest set previously reconciled but no longer
/// carries dangling in the cluster with no diagnostic naming the toggle-
/// drift root cause far from the source `caixa.lisp` / the renderer's
/// format-string template, and the substrate's "the cluster's per-caixa
/// live state converges to the caixa's tatara-lisp source-of-truth on
/// every reconcile — resources the source no longer carries are swept
/// by the kustomize-controller, not left dangling" CAIXA-SDLC.md §V
/// author-to-live-convergence guarantee silently regresses.
///
/// Note the axis is asymmetric across the co-resident `HelmRelease` CR:
/// the peer `HelmRelease` document seeds no `spec.prune` leaf because
/// the Flux v2 helm-controller-side per-CR reconcile loop keys off Helm
/// 3's own release-scoped resource-tracking manifest (the per-release
/// `helm.sh/release-name` label + `secrets/sh.helm.release.v1.*` release
/// snapshots) to garbage-collect resources removed between chart
/// versions rather than a CR-level toggle, so the `spec.prune` leaf is
/// well-defined only on the `Kustomization` CR whose kustomize-controller
/// reconcile loop tracks resources by the CR's manifest set rather than
/// Helm's per-release snapshots. This is the mirror of the peer sibling
/// [`FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE`] axis, which is `HelmRelease`-
/// CR-only for the mirror reason (Helm 3's chart-side `Chart.yaml`
/// declares no target-namespace-creation semantic of its own, so the
/// helm-controller carries a per-CR toggle at `spec.install.createNamespace`
/// that the peer kustomize-controller has no need to mirror since the
/// upstream Kustomize project's per-CR spec block establishes the
/// target-namespace independently at each `kustomization.yaml` document's
/// own `metadata.namespace` axis).
///
/// The single source of truth every rendered Flux bundle axis that names
/// the per-CR garbage-collection-toggle leaf reaches for:
///
/// - the rendered `kustomization.yaml` document's `spec.prune` leaf-
/// scalar-key axis (caixa-flux/src/lib.rs — the `cluster_bundle`
/// `kustomization.yaml` format-string template's per-CR garbage-
/// collection-toggle leaf under the top-level `spec` position,
/// threading the same `&'static str` through a new `{prune_key}`
/// named-arg interpolation);
/// - the one test-fixture navigation site in caixa-flux's `mod tests`
/// that probes the rendered document's `.get("prune")` leaf axis to
/// pin the substrate's canonical `true` seed (the
/// [`cluster_bundle_kustomization_prune_pins_lifted_true`] per-CR
/// production-emit pin).
///
/// Both the production emit site + the one test-fixture navigation site
/// name the same Flux v2 per-CR garbage-collection-toggle leaf-scalar-
/// key and must move together on any hypothetical Flux v3 rename
/// (upstream Flux v3 roadmap floats candidates like `garbageCollect` /
/// `sweep` / `pruneOrphaned` / `deleteOrphans` in the migration prose).
/// Until this lift landed the axis carried an inline `prune` literal at
/// the one production emit site (caixa-flux/src/lib.rs — the
/// `prune: true` leaf inside the `cluster_bundle` `kustomization.yaml`
/// format-string template's top-level `spec` position) — the sole
/// occurrence of the same load-bearing Flux-v2-per-CR-garbage-
/// collection-toggle-leaf-scalar-key convention, drift-prone by
/// construction ahead of the second occurrence the M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
/// `Kustomization` synthesis will surface, where a per-renderer local
/// `pub const FLUX_KUSTOMIZATION_KEY_PRUNE: &str = "…"` (the canonical
/// drift footgun where a sibling local `pub const` could happen to
/// carry the same string at the source while pointing at a different
/// `&'static` allocation) would let the two renderers silently disagree
/// on the substrate's canonical sweep-what-you-removed semantic.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` in advance of the
/// second occurrence the M4 materializer will surface — so the second
/// consumer inherits the canonical per-CR garbage-collection-toggle
/// leaf-scalar-key by construction without opportunity for per-renderer
/// drift.
///
/// Same "the typed constant lives in one place" discipline the sibling
/// [`FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE`] (ba9ab8b) install-path-only
/// per-CR namespace-seeder-toggle leaf-scalar-key +
/// [`FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE`] (96581b7) upgrade-
/// path-only per-CR remediation-toggle leaf-scalar-key +
/// [`FLUX_HELMRELEASE_KEY_RETRIES`] (a12f9fc) leaf-scalar-key +
/// [`FLUX_HELMRELEASE_KEY_REMEDIATION`] (6fe4e7e) sub-container-axis-key
/// + [`FLUX_HELMRELEASE_KEY_INSTALL`] / [`FLUX_HELMRELEASE_KEY_UPGRADE`]
/// (7767c26) parent-container-axis-key pair +
/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) scalar-
/// value halves of the per-path per-CR HelmRelease spec surface
/// established — extends the discipline from the co-resident per-caixa
/// `HelmRelease` CR spec surface onto the co-resident per-caixa
/// `Kustomization` CR spec surface at the mirror-symmetric top-level
/// `spec.prune` position.
///
/// [cf]: ../../caixa_flux/index.html
/// [kust]: https://fluxcd.io/flux/components/kustomize/kustomizations/
pub const FLUX_KUSTOMIZATION_KEY_PRUNE: &str = "prune";
/// Canonical Flux v2 `Kustomization.spec.prune` per-CR garbage-collection-
/// toggle scalar-value default the substrate seeds into every per-caixa
/// `kustomization.yaml` document at the paired
/// [`FLUX_KUSTOMIZATION_KEY_PRUNE`] leaf-scalar-key axis. Pairs with the
/// sibling [`FLUX_KUSTOMIZATION_KEY_PRUNE`] (8ec7917) leaf-scalar-key
/// half of the same `(leaf-key, scalar-value)` per-CR garbage-collection-
/// toggle declaration pair — the Flux v2 kustomize-controller's per-CR
/// reconcile loop reads the scalar under that exact leaf key, so drift
/// on either axis is equally load-bearing (a rebrand on this canonical
/// scalar-value default that failed to reach every renderer's emit site
/// would silently split the substrate's chosen sweep-what-you-removed
/// semantic between the operator-facing canonical default and every
/// per-caixa `Kustomization` document's per-CR garbage-collection-toggle,
/// with no field naming the semantic-drift root cause far from the
/// source `caixa.lisp` / the renderer's format-string template).
///
/// The `true` seed opts every emitted per-caixa `Kustomization` into
/// the substrate's canonical GitOps-side sweep-what-you-removed
/// semantic: on every reconcile the kustomize-controller garbage-
/// collects any per-caixa resource the source manifest set previously
/// reconciled but no longer carries, converging the cluster's per-
/// caixa live state to the caixa's tatara-lisp source-of-truth
/// verbatim. A future substrate-side rebrand to `false` (or a per-
/// cluster override the operator pins for a class of clusters where a
/// human is expected to prune orphaned resources by hand, or a
/// per-caixa opt-out slot the ABSORPTION-ROADMAP.md M4 typed-slot
/// trajectory adds once the substrate grows a `:kustomization :prune`
/// author-side toggle) is a one-line edit on this canonical declaration,
/// not a coordinated rewrite across every future per-target renderer
/// the substrate adds. Peer with the sibling
/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) scalar-
/// value default on the peer canonical-Flux-v2-per-CR-substrate-
/// default surface — the retry-cap default names the per-path per-CR
/// remediation retry ceiling, and this garbage-collection-toggle
/// default names whether the per-CR reconcile loop sweeps orphaned
/// resources at all. Both are substrate-side policy choices the
/// operator inherits when the per-caixa [`ClusterBundleOpts`][co]
/// doesn't pin an override.
///
/// The single source of truth every rendered Flux bundle axis that
/// names the per-CR garbage-collection-toggle scalar reaches for:
///
/// - the rendered `kustomization.yaml` document's `spec.prune`
/// scalar-value axis (caixa-flux/src/lib.rs — the [`cluster_bundle`][cb]
/// `kustomization.yaml` format-string template's per-CR garbage-
/// collection-toggle scalar under the top-level `spec` position,
/// threading the same `bool` through a `{prune_default}` named-arg
/// interpolation);
/// - the one test-fixture navigation site in caixa-flux's `mod tests`
/// that probes the rendered document's `.get("prune")` scalar axis
/// to pin the substrate's canonical `true` seed against the lifted
/// default (the
/// [`cluster_bundle_kustomization_prune_pins_lifted_true`] per-CR
/// production-emit pin).
///
/// Both the production emit site + the one test-fixture navigation site
/// now consume the same `bool` at emit time through the sibling
/// re-export [`caixa_flux::FLUX_KUSTOMIZATION_PRUNE_DEFAULT`][cf], so a
/// future substrate-side toggle migration on the canonical scalar-value
/// axis reaches every consumer through one `bool` by construction —
/// with no opportunity for per-renderer drift where a rebrand on one
/// axis without a coordinated edit on the other would silently disagree
/// on the sweep-what-you-removed semantic. Until this lift landed the
/// axis carried an inline `true` scalar-value literal at the sole
/// production-code call site (the `prune: true` leaf inside the
/// [`cluster_bundle`][cb] `kustomization.yaml` format-string template's
/// top-level `spec` position) plus the sibling test-fixture navigation
/// site — two occurrences of the same load-bearing Flux-v2-per-CR-
/// garbage-collection-toggle-scalar-value convention, drift-prone by
/// construction ahead of the third occurrence the M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
/// `Kustomization` synthesis will surface, where a per-renderer local
/// `pub const FLUX_KUSTOMIZATION_PRUNE_DEFAULT: bool = …` at any
/// downstream renderer would let the two consumers silently disagree
/// on the substrate's canonical seed.
///
/// Same "the typed constant lives in one place" discipline the
/// [`DEFAULT_NAMESPACE`] (a085b26) / [`DEFAULT_FLUX_SYSTEM_NAMESPACE`]
/// (7197d38) / [`DEFAULT_LIBRARY_NAME`] (41438dc) /
/// [`crate::DEFAULT_SERVICO_PORT`] (1e22add) /
/// [`DEFAULT_GATEWAY_CLASS_NAME`] (d9b0743) /
/// [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] (0a6a602) /
/// [`DEFAULT_FLUX_RECONCILE_INTERVAL`] (908180f) /
/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) lifts apply
/// on the peer canonical-substrate-default-load-bearing-scalar surface.
///
/// [cb]: ../../caixa_flux/fn.cluster_bundle.html
/// [cf]: ../../caixa_flux/index.html
/// [co]: ../../caixa_flux/struct.ClusterBundleOpts.html
pub const FLUX_KUSTOMIZATION_PRUNE_DEFAULT: bool = true;
/// Canonical substrate-side default for the
/// `HelmRelease.spec.values.<library>.enabled` scalar-value toggle every
/// [`caixa_flux::cluster_bundle`][cb]-emitted `helmrelease.yaml` document
/// seeds inside its per-caixa values overlay to force-on the paired
/// [`DEFAULT_LIBRARY_NAME`] child chart at the per-cluster
/// `HelmRelease`-side apply step. Pairs with the sibling
/// [`HELM_VALUES_KEY_ENABLED`] leaf-scalar-key half of the
/// `(leaf-key, scalar-value)` per-values-overlay child-chart
/// enablement-toggle declaration pair — the key half names the
/// canonical `values.<library>.enabled` leaf-scalar-key axis every
/// consumer (`caixa-helm`'s `values.yaml` per-chart default, this
/// crate's `cluster_bundle` overlay) probes on, and this scalar-value
/// half names the substrate-side default the `cluster_bundle` overlay
/// path seeds under it. Semantically distinct from — and inverse of —
/// the `RenderOpts::enabled_default = false` default that
/// [`caixa_helm::RenderOpts::default`] seeds for the standalone
/// `lareira-<nome>` chart's own `values.yaml` (that path renders
/// `enabled: false` so cluster operators must opt each caixa in
/// per-cluster); the `cluster_bundle` composition path is the
/// substrate-side opt-in path where the operator has already asserted
/// per-caixa cluster-scoped ownership by materializing a per-caixa
/// `GitRepository` + `HelmRelease` + `Kustomization` trio, so the overlay
/// forces the child chart on by seeding `enabled: true` under the
/// `values.<library>` wrap.
///
/// Rendered to canonical YAML `true` verbatim. A future substrate-side
/// rebrand to `false` (or the M4 typed-slot trajectory adding a per-caixa
/// `:cluster-bundle :enabled` author-side toggle the operator flips per
/// caixa) is a one-line edit on this canonical declaration, not a
/// coordinated rewrite across the sole production emit site + its
/// paired test-fixture navigation site. Peer with the sibling
/// [`FLUX_HELMRELEASE_CREATE_NAMESPACE_DEFAULT`] (be1904b),
/// [`FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT`] (be1904b),
/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae), and
/// [`FLUX_KUSTOMIZATION_PRUNE_DEFAULT`] (ea857d8) scalar-value defaults
/// on the peer canonical-Flux-v2-per-CR-substrate-default surface — the
/// four sibling scalar-value defaults name per-CR toggle-shape axes at
/// the `HelmRelease.spec.install.*` / `HelmRelease.spec.upgrade.*` /
/// `HelmRelease.spec.upgrade.remediation.retries` /
/// `Kustomization.spec.prune` sub-block positions, and this
/// scalar-value default names the child-chart-enablement toggle at the
/// deeper `HelmRelease.spec.values.<library>.enabled` values-overlay
/// position — all five are substrate-side policy choices the operator
/// inherits when the per-caixa [`ClusterBundleOpts`][co] doesn't pin an
/// override.
///
/// The single source of truth every rendered Flux bundle axis that
/// names the per-CR values-overlay child-chart-enablement-toggle
/// scalar reaches for:
///
/// - the rendered `helmrelease.yaml` document's
/// `spec.values.<library>.enabled` scalar-value axis
/// (caixa-flux/src/lib.rs — the [`cluster_bundle`][cb]
/// `helmrelease.yaml` format-string template's per-CR values-overlay
/// child-chart-enablement-toggle scalar under the per-`{library_name}`
/// wrap position, threading the same `bool` through a
/// `{lareira_enabled_default}` named-arg interpolation);
/// - the one test-fixture navigation site in caixa-flux's `mod tests`
/// that probes the rendered document's
/// `values.<library>.enabled` scalar axis to pin the substrate's
/// canonical `true` seed against the lifted default (the
/// `cluster_bundle_helmrelease_wrap_key_pins_canonical_pleme_computeunit_string`
/// per-CR production-emit pin's `Some(true)` assertion).
///
/// Both the production emit site + the test-fixture navigation site now
/// consume the same `bool` at emit time through the sibling re-export
/// [`caixa_flux::CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT`][cf], so a
/// future substrate-side toggle migration reaches every consumer through
/// one `bool` by construction — with no opportunity for per-renderer
/// drift where a rebrand on one axis without a coordinated edit on the
/// other would silently disagree on the substrate's chosen child-chart
/// force-on-under-composition semantic. Until this lift landed the
/// axis carried an inline `true` scalar-value literal at the sole
/// production-code call site (the `{enabled_key}: true` leaf inside the
/// [`cluster_bundle`][cb] `helmrelease.yaml` format-string template's
/// per-`{library_name}` wrap position) plus the test-fixture
/// navigation-site `Some(true)` assertion — two occurrences of the same
/// load-bearing values-overlay child-chart-enablement-toggle-scalar-value
/// convention, drift-prone by construction ahead of the third occurrence
/// the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
/// per-Aplicacao `HelmRelease` synthesis will surface.
///
/// [cb]: ../../caixa_flux/fn.cluster_bundle.html
/// [cf]: ../../caixa_flux/index.html
/// [co]: ../../caixa_flux/struct.ClusterBundleOpts.html
pub const CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT: bool = true;
/// Canonical substrate-side default for the
/// `values.<library>.enabled` scalar-value toggle every
/// [`caixa_helm::render_chart_for_servico`][cs]-emitted standalone
/// `lareira-<nome>` chart's `values.yaml` document seeds inside its per-caixa
/// [`DEFAULT_LIBRARY_NAME`] wrap block to leave the paired
/// [`DEFAULT_LIBRARY_NAME`] child chart opted-out at the per-cluster
/// `helm template` / `helm install` apply step. Pairs with the sibling
/// [`HELM_VALUES_KEY_ENABLED`] leaf-scalar-key half of the
/// `(leaf-key, scalar-value)` per-values-block child-chart-enablement-toggle
/// declaration pair — the key half names the canonical
/// `values.<library>.enabled` leaf-scalar-key axis every consumer (this
/// standalone-path default, [`caixa_flux::cluster_bundle`][cb]'s per-CR
/// values-overlay) probes on, and this scalar-value half names the
/// substrate-side default the standalone per-chart path seeds under it.
/// Semantically distinct from — and inverse of — the peer
/// [`CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT`] default that
/// [`caixa_flux::cluster_bundle`][cb]'s `helmrelease.yaml` values overlay
/// seeds for the substrate-side composition-path force-on (that path
/// renders `enabled: true` in the per-cluster `HelmRelease.spec.values.<library>`
/// overlay so the operator's per-caixa cluster-scoped ownership at bundle
/// materialization time carries a force-on for the child chart); the
/// standalone per-chart path is the substrate-side opt-out path where the
/// operator has not yet asserted per-caixa cluster-scoped ownership by
/// materializing a per-caixa `GitRepository` + `HelmRelease` +
/// `Kustomization` trio, so the per-chart `values.yaml` seeds
/// `enabled: false` under the `values.<library>` wrap and cluster operators
/// must opt each caixa in per-cluster.
///
/// Rendered to canonical YAML `false` verbatim. A future substrate-side
/// rebrand to `true` (or the M4 typed-slot trajectory adding a per-caixa
/// `:standalone :enabled` author-side toggle the author flips per caixa) is
/// a one-line edit on this canonical declaration, not a coordinated rewrite
/// across the sole production emit site + its paired test-fixture
/// navigation sites. Peer with the sibling
/// [`CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT`] scalar-value default on the
/// peer canonical-Helm-per-values-block-substrate-default surface — the two
/// sibling scalar-value defaults name mirror-symmetric per-path
/// child-chart-enablement-toggle-scalar-value defaults at the exact same
/// `values.<library>.enabled` sub-block position on the standalone
/// per-chart-`values.yaml` path (this const) and the composition
/// per-cluster-`HelmRelease` values-overlay path
/// ([`CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT`]) — both are substrate-side
/// policy choices the operator inherits when the per-caixa
/// [`caixa_helm::RenderOpts`][cro] / [`caixa_flux::ClusterBundleOpts`][co]
/// doesn't pin an override.
///
/// The single source of truth every rendered `values.yaml` axis that
/// names the per-values-block child-chart-enablement-toggle scalar on the
/// standalone per-chart path reaches for:
///
/// - the rendered `values.yaml` document's
/// `<library>.enabled` scalar-value axis
/// (caixa-helm/src/lib.rs — the [`caixa_helm::build_values_yaml`][cbv]
/// `serde_yaml::Value::Bool(opts.enabled_default)` block-insertion
/// under the per-`{library_name}` wrap position, threading the same
/// `bool` through the [`caixa_helm::RenderOpts::enabled_default`][cro]
/// default-knob);
/// - the [`caixa_helm::RenderOpts::default()`][cro] impl-body
/// `enabled_default: STANDALONE_LAREIRA_ENABLED_DEFAULT` field seed
/// the standalone per-chart path threads into every per-caixa
/// `render_chart_for_servico` call site.
///
/// Both the production emit site + the default-knob seed now consume the
/// same `bool` at emit time through the sibling re-export
/// [`caixa_helm::STANDALONE_LAREIRA_ENABLED_DEFAULT`][ch], so a future
/// substrate-side toggle migration reaches every consumer through one
/// `bool` by construction — with no opportunity for per-renderer drift
/// where a rebrand on one axis without a coordinated edit on the other
/// would silently disagree on the substrate's chosen
/// standalone-per-chart-path opt-out semantic. Until this lift landed
/// the axis carried an inline `enabled_default: false` scalar-value
/// literal at the sole production-code call site (the
/// [`caixa_helm::RenderOpts::default()`][cro] impl-body field seed at
/// `caixa-helm/src/lib.rs:700`) — one occurrence of the same
/// load-bearing per-values-block child-chart-enablement-toggle-scalar-value
/// convention as the peer [`CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT`] on the
/// composition path, drift-prone by construction ahead of the M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
/// per-Servico standalone-chart synthesis surfacing the third occurrence.
///
/// [cb]: ../../caixa_flux/fn.cluster_bundle.html
/// [cs]: ../../caixa_helm/fn.render_chart_for_servico.html
/// [cbv]: ../../caixa_helm/fn.build_values_yaml.html
/// [ch]: ../../caixa_helm/index.html
/// [cro]: ../../caixa_helm/struct.RenderOpts.html
/// [co]: ../../caixa_flux/struct.ClusterBundleOpts.html
pub const STANDALONE_LAREIRA_ENABLED_DEFAULT: bool = false;
/// Canonical Flux v2 `Kustomization.spec.path` per-CR source-sub-tree
/// leaf-scalar-key every `caixa-flux`-emitted `kustomization.yaml`
/// document seeds under its top-level `spec` position to name the sub-
/// tree of the paired [`FLUX_GITREPOSITORY_YAML_FILENAME`] GitRepository
/// the Flux v2 kustomize-controller-side per-CR reconcile loop pulls
/// the desired-state manifest set from at reconcile time. Drift on this
/// leaf silently unbinds every per-caixa `Kustomization` from its
/// paired per-caixa sub-tree of the pleme-io k8s repository — the
/// kustomize-controller then either reconciles the whole GitRepository
/// root (when the CR omits the leaf, the controller defaults to `./`,
/// pulling every unrelated cluster's manifests through the wrong
/// per-caixa `Kustomization`) or refuses to reconcile at all (when the
/// leaf points at a path the GitRepository doesn't carry, the CR sits
/// perpetually at `BuildFailed` naming the missing sub-tree far from
/// the source `caixa.lisp` / the renderer's format-string template).
///
/// Distinct from the sibling K8s-Gateway-API-side [`GATEWAY_API_KEY_PATH`]
/// (9f45aa4) per-`HTTPRouteMatch` path-matcher container-axis key and
/// the sibling Cilium-CNP-side [`CILIUM_KEY_PATH`] (bec2ce9) per-
/// `toPorts[].rules.http[]` URL-path predicate leaf-scalar-key: all
/// three constants spell the same underlying `"path"` string but name
/// distinct schema axes on distinct CRD groups — the Flux-side axis is a
/// per-`Kustomization`-CR source-sub-tree leaf scalar on the Flux v2
/// `kustomize.toolkit.fluxcd.io/v1` `Kustomization` CRD's `spec.path`
/// entry, the Gateway-API-side axis is a per-`HTTPRouteMatch` path-
/// matcher two-leaf container (`{type, value}`) on the K8s Gateway API
/// v1 `HTTPRoute` CRD's `spec.rules[].matches[]` entry, the Cilium-side
/// axis is a per-HTTP-rule URL-path predicate leaf scalar on the Cilium
/// `cilium.io/v2` `CiliumNetworkPolicy` CRD's per-`toPorts[].rules.http[]`
/// entry. Keeping them as sibling `pub const` declarations (rather than
/// coalescing onto a single shared constant that happens to carry the
/// same string) mirrors the deliberate axis-independence discipline the
/// sibling [`CILIUM_KEY_PATH`] / [`GATEWAY_API_KEY_PATH`] pair already
/// codifies on the sibling per-CRD-group axes, so a future Flux v3 per-
/// `Kustomization`-CR source-sub-tree leaf-key rebrand (candidates like
/// `sourcePath` / `manifestsPath` / `sourceRoot` upstream Flux v3
/// roadmap floats in the migration prose) can land independently of
/// any Cilium-side or Gateway-API-side per-CRD-schema rebrand without
/// any cross-CRD coordination footgun where a shared constant would
/// force a coupled edit against schema evolutions the three CRD
/// projects run on independent cadences. Note: Rust's `&'static str`
/// interner coalesces identical byte-sequences onto one storage
/// allocation at codegen time, so at runtime a `.as_ptr()` comparison
/// across the trio can't distinguish "sibling `pub const` declarations
/// carrying identical bytes" from "coalesced canonical declaration" —
/// the axis-independence discipline lives at the rustc symbol-name
/// axis (the three `pub const CILIUM_KEY_PATH` / `pub const
/// GATEWAY_API_KEY_PATH` / `pub const FLUX_KUSTOMIZATION_KEY_PATH`
/// symbols a future rebrand of one leaves the other two structurally
/// untouched under) rather than the runtime-address axis, and the
/// per-axis re-export identity pins in the consuming renderer crates
/// (each pinning the local re-export against its own canonical
/// declaration on its own axis) remain the load-bearing "no sibling
/// local `pub const` drift" gate for the trio.
///
/// The single source of truth every rendered Flux bundle axis that
/// names the per-`Kustomization`-CR source-sub-tree leaf reaches for:
///
/// - the rendered `kustomization.yaml` document's `spec.path` leaf-
/// scalar-key axis (caixa-flux/src/lib.rs — the [`cluster_bundle`]
/// `kustomization.yaml` format-string template's per-CR source-sub-
/// tree leaf under the top-level `spec` position, threading the
/// same `&'static str` through a new `{path_key}` named-arg
/// interpolation);
/// - the one test-fixture navigation site in caixa-flux's `mod tests`
/// that probes the rendered document's `.get("path")` leaf axis to
/// pin the substrate's canonical per-cluster / per-caixa sub-tree
/// path seed (the [`cluster_bundle_kustomization_path_pins_lifted_sub_tree`]
/// per-CR production-emit pin).
///
/// Both the production emit site + the one test-fixture navigation
/// site name the same Flux v2 per-`Kustomization`-CR source-sub-tree
/// leaf-scalar-key and must move together on any hypothetical Flux v3
/// rename. Until this lift landed the axis carried an inline `path`
/// literal at the one production emit site (caixa-flux/src/lib.rs —
/// the `path: ./clusters/{cluster}/services/{name}` leaf inside the
/// `cluster_bundle` `kustomization.yaml` format-string template's top-
/// level `spec` position) — the sole occurrence of the same load-
/// bearing Flux-v2-per-`Kustomization`-CR-source-sub-tree-leaf-scalar-
/// key convention, drift-prone by construction ahead of the second
/// occurrence the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-Aplicacao `Kustomization` synthesis will
/// surface, where a per-renderer local
/// `pub const FLUX_KUSTOMIZATION_KEY_PATH: &str = "…"` (the canonical
/// drift footgun where a sibling local `pub const` could happen to
/// carry the same string at the source while pointing at a different
/// `&'static` allocation) would let the two renderers silently
/// disagree on the substrate's canonical per-`Kustomization`-CR
/// source-sub-tree leaf-scalar-key convention.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5) lifts
/// the constant in advance of the second occurrence the M4 materializer
/// will surface — so the second consumer inherits the canonical per-CR
/// source-sub-tree leaf-scalar-key by construction without opportunity
/// for per-renderer drift. Same "the typed constant lives in one place"
/// discipline the sibling [`FLUX_KUSTOMIZATION_KEY_PRUNE`] (8ec7917)
/// per-CR garbage-collection-toggle leaf-scalar-key lift on the same
/// per-`Kustomization`-CR spec surface established — extends the
/// discipline from the co-resident per-`Kustomization`-CR `spec.prune`
/// top-level per-CR-toggle leaf-scalar-key onto the co-resident per-
/// `Kustomization`-CR `spec.path` top-level per-CR-source-sub-tree
/// leaf-scalar-key at the mirror-symmetric top-level `spec` position.
///
/// [cf]: ../../caixa_flux/index.html
/// [kust]: https://fluxcd.io/flux/components/kustomize/kustomizations/
pub const FLUX_KUSTOMIZATION_KEY_PATH: &str = "path";
/// Canonical substrate-side per-cluster / per-caixa `Kustomization.spec.path`
/// source-sub-tree scalar composer — the `./clusters/<cluster>/services/<nome>`
/// GitRepository-relative directory-tree seed every `caixa-flux`-emitted
/// `kustomization.yaml` document mounts under its lifted
/// [`FLUX_KUSTOMIZATION_KEY_PATH`] leaf-scalar-key at the top-level `spec`
/// position so the Flux v2 kustomize-controller's per-CR reconcile loop
/// walks into the paired per-cluster / per-caixa sub-tree of the pleme-io
/// k8s repository (rather than the `GitRepository` root, which would pull
/// every unrelated cluster's manifests through the wrong per-caixa
/// `Kustomization`).
///
/// The rendered string is the substrate's contract with the pleme-io k8s
/// repository's canonical directory-tree layout: every per-caixa Servico's
/// rendered manifests live at `pleme-io/k8s/clusters/<cluster>/services/<nome>/`,
/// so the Flux v2 kustomize-controller-side per-CR reconcile loop keys off
/// the same GitRepository-relative sub-tree seed by construction — the
/// composer output is the exact `spec.path` scalar the substrate seeds into
/// every emitted per-caixa `kustomization.yaml` document under its top-
/// level `spec` position.
///
/// Composes two axes:
///
/// - the per-cluster prefix — the `./clusters/<cluster>/` half of the
/// sub-tree seed that scopes the emit to the paired cluster's
/// manifest set (so two clusters hosting the same per-caixa Servico —
/// `rio` vs `paris` — land at distinct `spec.path` scalars with no
/// cross-cluster reconcile drift at the kustomize-controller's per-CR
/// apply loop);
/// - the per-caixa suffix — the `/services/<nome>` half of the sub-tree
/// seed that scopes the emit to the paired per-caixa Servico's
/// manifest sub-directory under the cluster's `services/` directory
/// (so two per-caixa Servicos co-resident under the same cluster —
/// `hello-rio` vs `cart` — land at distinct `spec.path` scalars with
/// no per-caixa reconcile drift at the same kustomize-controller
/// apply loop).
///
/// Peer to [`cilium_network_policy_name`] / [`gateway_api_http_route_name`]
/// / [`oci_chart_ref`] / [`lareira_chart_name`] on the sibling substrate-
/// side canonical-composer-of-a-canonical-scalar-that-consumers-key-off
/// axis: every writer-side helper composes a canonical load-bearing
/// scalar the substrate contracts with a downstream consumer's index
/// (Cilium's per-CNP `metadata.name`, Gateway API's per-HTTPRoute
/// `metadata.name`, Helm's OCI-artifact ref, Helm's Chart.yaml `name:`
/// axis). This composer's `Kustomization.spec.path` peer names the Flux
/// v2 kustomize-controller-side per-CR reconcile-target sub-tree index —
/// same "the load-bearing multi-axis composition lives in one place"
/// discipline extended from the mesh renderer's per-CR-identity-scalar
/// axes onto the flux renderer's per-CR-source-sub-tree axis.
///
/// Until this lift landed the two-axis composition sat as a verbatim
/// inline `format!("./clusters/{cluster}/services/{name}")` template at
/// the sole `cluster_bundle` `kustomization.yaml` format-string
/// production emit site plus a mirror-symmetric verbatim inline
/// `format!("./clusters/{cluster}/services/{name}", …)` at the paired
/// `cluster_bundle_kustomization_path_pins_lifted_sub_tree` test-fixture
/// navigation site — the substrate's canonical per-cluster / per-caixa
/// sub-tree seed had no compile-time link between the two sites. A
/// future substrate-side directory-tree axis rebrand (`clusters/` →
/// `environments/` for a multi-env-per-cluster axis extension, `services/`
/// → `servicos/` for a portuguese-canonical directory-name migration
/// matching the sibling `:servicos` slot spelling, a per-tenant scoping
/// prefix for multi-tenant Aplicacao hosting) would have had to be
/// threaded through both sites in lockstep or the two would silently
/// split: the production emit would key off the drifted encoding while
/// the test pin still asserts the original. Lifting closes the drift
/// footgun ahead of the second production-emit occurrence the M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
/// `Kustomization` synthesis will surface — the second consumer inherits
/// the canonical per-cluster / per-caixa sub-tree composition by
/// construction without opportunity for per-renderer drift.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5) lifts
/// the composition in advance of the second occurrence the M4 materializer
/// will surface, so the second consumer inherits the canonical sub-tree
/// seed by construction.
#[must_use]
pub fn flux_kustomization_source_subtree(cluster: &str, nome: &str) -> String {
format!("./clusters/{cluster}/services/{nome}")
}
/// Canonical Flux v2 `Kustomization.spec.timeout` per-CR reconcile wall-
/// clock cap leaf-scalar-key every `caixa-flux`-emitted
/// `kustomization.yaml` document seeds under its top-level `spec`
/// position to name the ceiling on how long the Flux v2 kustomize-
/// controller-side per-CR reconcile loop is allowed to spend applying
/// the paired [`FLUX_KUSTOMIZATION_KEY_PATH`]-scoped sub-tree of the
/// paired [`FLUX_GITREPOSITORY_YAML_FILENAME`] GitRepository before it
/// marks the `Kustomization` `Ready: False` and stops retrying — the
/// substrate's canonical "how long we let a per-caixa manifest-set
/// reconcile run before Flux gives up" contract with the kustomize-
/// controller's per-CR reconcile loop. Drift on this leaf silently
/// strips the substrate's chosen reconcile-ceiling from every emitted
/// per-caixa `Kustomization` document — the kustomize-controller then
/// falls back to the upstream Flux v2 controller-side default cap
/// (which the upstream project ships at a value tuned for the average
/// upstream Flux-managed manifest set, not the substrate's per-caixa
/// idempotency-checkpoint cadence the sibling
/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] retry-ceiling and
/// [`DEFAULT_FLUX_RECONCILE_INTERVAL`] reconcile-poll cadence are
/// jointly tuned against), letting a persistently-failing per-caixa
/// manifest apply consume kustomize-controller reconcile-loop cycles
/// past the substrate's chosen ceiling with no field naming the
/// timeout-drift root cause.
///
/// The single source of truth every rendered Flux bundle axis that
/// names the per-`Kustomization`-CR reconcile wall-clock cap leaf
/// reaches for:
///
/// - the rendered `kustomization.yaml` document's `spec.timeout`
/// leaf-scalar-key axis (caixa-flux/src/lib.rs — the
/// [`cluster_bundle`] `kustomization.yaml` format-string template's
/// per-CR reconcile wall-clock cap leaf under the top-level `spec`
/// position, threading the same `&'static str` through a new
/// `{timeout_key}` named-arg interpolation);
/// - the one test-fixture navigation site in caixa-flux's `mod tests`
/// that probes the rendered document's `.get("timeout")` leaf axis
/// to pin the substrate's canonical wall-clock cap seed.
///
/// Both the production emit site + the one test-fixture navigation
/// site name the same Flux v2 per-`Kustomization`-CR reconcile wall-
/// clock cap leaf-scalar-key and must move together on any
/// hypothetical Flux v3 rename. Until this lift landed the axis
/// carried an inline `timeout` literal at the one production emit site
/// (caixa-flux/src/lib.rs — the `timeout: 5m` leaf inside the
/// `cluster_bundle` `kustomization.yaml` format-string template's top-
/// level `spec` position) — the sole occurrence of the same load-
/// bearing Flux-v2-per-`Kustomization`-CR-reconcile-wall-clock-cap-
/// leaf-scalar-key convention, drift-prone by construction ahead of
/// the second occurrence the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-Aplicacao `Kustomization` synthesis will
/// surface, where a per-renderer local
/// `pub const FLUX_KUSTOMIZATION_KEY_TIMEOUT: &str = "…"` (the
/// canonical drift footgun where a sibling local `pub const` could
/// happen to carry the same string at the source while pointing at a
/// different `&'static` allocation) would let the two renderers
/// silently disagree on the substrate's canonical reconcile-ceiling-
/// declaration leaf-scalar-key convention.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5)
/// lifts the constant in advance of the second occurrence the M4
/// materializer will surface — so the second consumer inherits the
/// canonical per-CR reconcile wall-clock cap leaf-scalar-key by
/// construction without opportunity for per-renderer drift. Pairs
/// with the sibling [`DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT`] scalar-
/// value half of the same `(leaf-key, scalar-value)` per-path
/// reconcile-ceiling-declaration pair — extends the drift-closing
/// discipline the scalar-value lift established from the value the
/// leaf holds onto the leaf-key itself. Same shape as the sibling
/// [`FLUX_KUSTOMIZATION_KEY_PATH`] (613d7ed) per-CR source-sub-tree
/// leaf-scalar-key + [`FLUX_KUSTOMIZATION_KEY_PRUNE`] (8ec7917) per-CR
/// garbage-collection-toggle leaf-scalar-key lifts on the co-resident
/// per-`Kustomization`-CR spec surface — extends the discipline from
/// the co-resident per-`Kustomization`-CR `spec.path` source-sub-tree
/// leaf-scalar-key and per-`Kustomization`-CR `spec.prune` garbage-
/// collection-toggle leaf-scalar-key onto the co-resident per-
/// `Kustomization`-CR `spec.timeout` reconcile wall-clock cap leaf-
/// scalar-key at the mirror-symmetric top-level `spec` position.
///
/// [cf]: ../../caixa_flux/index.html
/// [kust]: https://fluxcd.io/flux/components/kustomize/kustomizations/
pub const FLUX_KUSTOMIZATION_KEY_TIMEOUT: &str = "timeout";
/// Canonical Flux v2 `Kustomization.spec.timeout` per-CR reconcile
/// wall-clock cap default the substrate seeds into every per-caixa
/// `kustomization.yaml` document. Every rendered per-caixa Flux v2
/// `Kustomization` CR consults the same `&'static str` at emit time so
/// a future substrate-side reconcile-ceiling migration (`"5m"` → `"3m"`
/// on faster per-caixa idempotency-checkpoint cadence once the sibling
/// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] retry-ceiling
/// tightens, `"5m"` → `"10m"` on larger per-caixa manifest sets where
/// the upstream Flux v2 kustomize-controller-side per-CR reconcile
/// duration outgrows the substrate's default ceiling — coordinated
/// with the sibling [`DEFAULT_FLUX_RECONCILE_INTERVAL`] reconcile-poll
/// cadence tuning cycle) is a one-line edit on this canonical
/// declaration, not a coordinated rewrite across the
/// [`cluster_bundle`] `kustomization.yaml` template + every future
/// per-target renderer the substrate adds.
///
/// The single source of truth the rendered per-caixa Flux v2 cluster
/// bundle's per-`Kustomization`-CR reconcile wall-clock cap default
/// seed reaches for:
///
/// - the rendered `kustomization.yaml` document's `spec.timeout`
/// scalar-value axis (caixa-flux/src/lib.rs — the
/// [`cluster_bundle`] `kustomization.yaml` format-string template's
/// per-CR reconcile wall-clock cap leaf under the top-level `spec`
/// position, threading the same `&'static str` through a new
/// `{timeout_default}` named-arg interpolation on the leaf keyed
/// by the sibling [`FLUX_KUSTOMIZATION_KEY_TIMEOUT`]).
///
/// The value is a valid Flux v2 reconcile wall-clock cap duration
/// scalar (per the upstream Flux v2
/// `kustomize.toolkit.fluxcd.io/v1/Kustomization.spec.timeout`
/// `metav1.Duration` OpenAPI schema): a non-empty Go-duration-format
/// string (e.g. `"5m"`, `"3m"`, `"1h30m"`), which the Flux v2
/// controller-side per-CR admission gate parses via
/// `metav1.ParseDuration` before installing the per-CR watch. A future
/// rebrand on this lift cannot silently land a value the Flux v2
/// controller-side admission gate rejects at the *first* per-caixa
/// `Kustomization` apply against a cluster, far from the rebrand
/// commit's source — the pin at the canonical lift documents the Go-
/// duration-format grammar contract with the Flux v2 admission gate
/// every downstream consumer of the rendered per-CR reconcile-cap
/// axis rests on.
///
/// Pairs with the sibling [`FLUX_KUSTOMIZATION_KEY_TIMEOUT`] per-Flux-
/// v2-`Kustomization`-CR reconcile wall-clock cap scalar-axis key the
/// value the substrate seeds here nests directly under across every
/// rendered per-caixa Flux v2 `Kustomization` CR — the key half of
/// the per-CR `spec.timeout` scalar-key/scalar-value pair lives at
/// [`FLUX_KUSTOMIZATION_KEY_TIMEOUT`], the value half's substrate-side
/// default seed lives here. Same "the typed constant lives in one
/// place" discipline the [`DEFAULT_FLUX_RECONCILE_INTERVAL`] (908180f)
/// / [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] (30dcdae) /
/// [`DEFAULT_APLICACAO_INSTALL_TIMEOUT`](caixa_tatara::DEFAULT_APLICACAO_INSTALL_TIMEOUT)
/// (813343f) lifts apply on the peer canonical-substrate-default-
/// load-bearing-scalar surface — extends the canonical-substrate-
/// default single-sourcing discipline from the peer per-Flux-v2-CR-
/// reconcile-poll-cadence / per-HelmRelease-CR-remediation-retry-
/// ceiling / per-tatara-Process-install-wall-clock-cap surfaces onto
/// the sibling per-Kustomization-CR-reconcile-wall-clock-cap surface
/// every rendered per-caixa Flux v2 cluster bundle CR carries.
///
/// [cf]: ../../caixa_flux/index.html
pub const DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT: &str = "5m";
/// Canonical K8s Gateway API `GatewayClass` name every `caixa-mesh`-emitted
/// [`Gateway`][gw] document declares at its `spec.gatewayClassName` axis —
/// the controller-discriminator that binds the emitted `Gateway` to a
/// specific `GatewayClass` resource, which in turn names the controller
/// (`spec.controllerName`) that reconciles every `HTTPRoute` /
/// `GRPCRoute` / `TLSRoute` / `TCPRoute` attached to `Gateway`s bound to
/// that class.
///
/// The single source of truth [`caixa-mesh`][cm]'s `gateway_routes`
/// per-`:entrada` `Gateway` emitter (the sole production-code site the
/// prior inline `"cilium".into()` literal sat at — the `spec.gatewayClassName`
/// field of the emitted `Gateway`'s `spec` block) and every future
/// per-target renderer the M3.x + M4 absorption roadmap acknowledges
/// (the future `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
/// `Gateway` synthesis, a future per-cluster / per-edge `Gateway`
/// renderer for non-HTTP `:entrada` shapes) consult for the substrate's
/// chosen Gateway API controller.
///
/// The value pins the substrate on the Cilium Gateway API implementation
/// — the same eBPF-identity data plane that reconciles every
/// [`CILIUM_KIND_NETWORK_POLICY`] the mesh renderer emits alongside the
/// `Gateway`. Same-controller Gateway ingress + intra-mesh identity
/// policy is the load-bearing "one identity layer, one data plane"
/// mesh-composition invariant (MESH-COMPOSITION.md §V — "the `:entrada`
/// external ingress and the intra-mesh `:contratos` identity checks
/// share an eBPF data plane; a per-caixa split between the ingress
/// controller and the identity controller reintroduces the
/// two-data-planes drift the mesh composition invariant closes"), so
/// splitting the controller across renderers would silently reintroduce
/// the exact drift the substrate's mesh composition invariant closes.
///
/// Until this lift landed the substrate's Gateway API controller choice
/// carried an inline `"cilium".into()` literal at the one production-code
/// occurrence in caixa-mesh (the `gateway_routes` `Gateway`
/// `spec.gatewayClassName` field). The PRIME DIRECTIVE duplication-budget
/// rule (THEORY.md §I.3.5, "every recurring shape becomes a generator
/// before it becomes a pattern; every pattern becomes a library before it
/// becomes duplicated code. The duplication budget is zero.") promotes
/// the constant to a typed substrate-side `&'static str` in advance of the
/// second occurrence — the M4 `mesh.pleme.io/v1alpha1/Aplicacao`
/// materializer's per-Aplicacao `Gateway` synthesis, a future per-cluster
/// per-edge `Gateway` renderer, or any per-edition variant the substrate
/// forks — so the second consumer inherits the canonical controller
/// choice by construction without opportunity for per-renderer drift.
///
/// A future substrate-side controller migration (the substrate forking
/// from Cilium Gateway to Envoy Gateway, Istio Gateway, or any
/// per-edition Gateway API v1.x GA controller variant the SIG-Network
/// roadmap names) without a coordinated edit on every renderer's inline
/// literal would have silently emitted a `Gateway` whose
/// `spec.gatewayClassName` referenced a class no controller reconciles —
/// apply-side: the `Gateway` sits at `Programmed: False` with no route
/// reconciled, every external `:entrada` flow drops at the ingress with
/// no field naming the controller-drift root cause. Lifting the value
/// here makes the controller-choice axis discipline structural: the
/// per-`:entrada` `Gateway` and every future per-Aplicacao materializer
/// consult the same `&'static str`, and a future controller migration
/// is a one-line edit on the canonical declaration.
///
/// The value is a valid DNS-1123 label (the K8s apiserver-side floor
/// every cluster-scoped `GatewayClass.metadata.name` axis enforces):
/// lowercase ASCII alphanumeric with `-` separators, no leading /
/// trailing hyphen, length within the [`DNS_1123_LABEL_MAX_LEN`] (63-byte)
/// cap. A future rebrand on this lift cannot silently land a value the
/// apiserver refuses at the *first* `Gateway` apply against a cluster,
/// far from the rebrand commit's source — the typed [`is_dns_1123_label`]
/// floor rejects it at caixa-core build time on the canonical lift,
/// before any renderer consumes the value. Same "the typed constant
/// lives in one place" discipline the [`DEFAULT_NAMESPACE`] (a085b26) /
/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] (7197d38) / [`DEFAULT_LIBRARY_NAME`]
/// (41438dc) / [`DEFAULT_SERVICO_PORT`] (1e22add) lifts apply on the
/// peer canonical-substrate-default-resource-name surface.
///
/// [gw]: https://gateway-api.sigs.k8s.io/api-types/gateway/
/// [cm]: ../../caixa_mesh/index.html
pub const DEFAULT_GATEWAY_CLASS_NAME: &str = "cilium";
/// Canonical K8s Gateway API `Gateway` per-Gateway controller-binding
/// scalar-axis key every `gateway_routes`-emitted `Gateway` document
/// mounts its per-Gateway `GatewayClass.metadata.name` reference under
/// (`spec.gatewayClassName`). Pairs with the sibling
/// [`DEFAULT_GATEWAY_CLASS_NAME`] (d9b0743) — the K8s Gateway API v1 CRD
/// schema pins the per-Gateway controller-binding through the scalar
/// `spec.gatewayClassName` axis (each `Gateway` names exactly one
/// `GatewayClass.metadata.name`; the sibling `spec.listeners[]` +
/// `spec.addresses[]` container axes carry the L7-listener fan-out +
/// per-Gateway address hint under the same `spec` block), so drift on
/// the per-Gateway controller-binding scalar-axis KEY is exactly as
/// load-bearing as drift on the sibling `DEFAULT_GATEWAY_CLASS_NAME`
/// VALUE the axis wraps (the K8s apiserver-side Gateway API CRD schema
/// validator drops any `spec` block whose controller-binding scalar-
/// axis carries an unrecognized key — a `"gatewayClass"` /
/// `"className"` / `"gatewayClassRef"` typo silently emits a `Gateway`
/// whose controller-binding the Gateway API implementation's per-
/// Gateway reconcile loop no-ops entirely: no `GatewayClass` is
/// resolved, no `controllerName` is looked up, and every external
/// `:entrada` flow the Gateway was authored to accept drops at the
/// gateway-class-controller's per-Gateway reconcile with no field
/// naming the controller-binding-axis-drift root cause).
///
/// The single source of truth the rendered Aplicacao Gateway-API-side
/// ingress bundle's per-Gateway controller-binding-axis-naming reaches
/// for:
///
/// - the rendered `Gateway` document's `spec.gatewayClassName` axis
/// (caixa-mesh/src/lib.rs:2016 — the `gateway_routes` per-Aplicacao
/// `Gateway`'s `g_spec.insert("gatewayClassName", …)` call).
///
/// The per-Gateway controller-binding scalar axis names the same
/// Gateway-API-implementation-side per-Gateway `GatewayClass`
/// resolution axis as the sibling [`DEFAULT_GATEWAY_CLASS_NAME`] VALUE
/// it wraps, and must move together on any future Gateway API rebrand
/// (an upstream SIG-Network Gateway API v2 rename of the controller-
/// binding scalar-axis from `gatewayClassName` to `className` /
/// `gatewayClassRef` / `class`, coordinated with the Gateway API
/// deprecation cycle). Until this lift landed the KEY axis carried an
/// inline `gatewayClassName` literal at the one production-code
/// occurrence in caixa-mesh/src/lib.rs:2016 (the `gateway_routes` per-
/// Aplicacao Gateway's `g_spec.insert("gatewayClassName", …)` call)
/// plus a matching test-fixture navigation inside the in-file
/// `gateway_gateway_class_name_uses_lifted_default_gateway_class_name`
/// pin's `.get("gatewayClassName")` traversal (caixa-mesh/src/lib.rs:5315)
/// — two occurrences of the same load-bearing Gateway-API-CRD-
/// `gatewayClassName`-axis-KEY convention, drift-prone by
/// construction. A drift on the production site to `"gatewayClass"` /
/// `"className"` / `"gatewayClassRef"` would have surfaced as a
/// Gateway API implementation-side schema validator drop at apply
/// time (the affected `Gateway`'s controller-binding scalar-axis the
/// CRD schema validator recognizes as unknown), with every external
/// `:entrada` flow the Gateway was authored to accept dropping at the
/// gateway-class-controller's per-Gateway reconcile with no field
/// naming the controller-binding-drift root cause. A drift on the
/// test-fixture side silently masks the emission-side pin
/// (`.get("gatewayClassName")` returns `None` under both the drifted-
/// key emitter and the drifted-key probe — the downstream
/// `.and_then(|c| c.as_str())` chain short-circuits vacuously because
/// the outer per-Gateway controller-binding lookup is itself `None`).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) lifts established on the
/// sibling canonical-Gateway-API-body-axis surfaces — completes the
/// per-Gateway-body-axis canonical-string-pin set the sibling
/// `spec.listeners[]` lift began, closing the (`gatewayClassName`,
/// `listeners`) per-`Gateway`-spec-body-axis pair the M3 Aplicacao
/// mesh renderer's external `:entrada` ingress contract rests on.
/// Together with the peer [`DEFAULT_GATEWAY_CLASS_NAME`] VALUE lift
/// (d9b0743) — the `(key, value)` pair-lift discipline the sibling
/// `(KUBE_KEY_METADATA, {"name","namespace","labels"})` axis
/// established — the per-Gateway controller-binding scalar axis now
/// threads both halves of its `(key, value)` typed contract through
/// one lifted `&'static str` apiece at the substrate boundary. The
/// render-side consumer now threads the same `&'static str` through
/// its `g_spec.insert(…)` call so a future Gateway API rebrand on
/// the controller-binding scalar axis (or an upstream SIG-Network
/// Gateway API v2 rename to a per-CRD sibling name) lands in one
/// place; every future renderer that reaches for the canonical
/// per-Gateway controller-binding scalar axis (the future M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
/// `Gateway` fan-out, a future per-cluster `GatewayClass` /
/// `ReferenceGrant` renderer whose per-Gateway class-name enumeration
/// binds against this same axis, a future per-`Gateway` typed-listener
/// TLS terminator renderer whose per-Gateway `spec` block nests
/// alongside this same axis) inherits the same value by construction
/// with no opportunity for per-renderer drift.
///
/// [cm]: ../../caixa_mesh/index.html
pub const GATEWAY_API_KEY_GATEWAY_CLASS_NAME: &str = "gatewayClassName";
/// Canonical K8s Gateway API `HTTPRoute` per-`HTTPRouteMatch` path-matcher
/// container-axis key every `gateway_routes`-emitted `HTTPRoute` per-rule
/// `matches[]` entry mounts its per-match `{type, value}` path-selection
/// predicate under (`spec.rules[].matches[].path`). Nests one level
/// beneath the sibling [`GATEWAY_API_KEY_MATCHES`] (b9ede1a) per-rule
/// route-match container-axis it hangs off of — the Gateway API v1 CRD
/// schema pins per-`HTTPRouteMatch` request-path selection through the
/// `spec.rules[].matches[].path` container axis (each match entry names
/// one path-selection predicate the request line's `:path` pseudo-header
/// must satisfy under a `type` discriminator of
/// `Exact | PathPrefix | RegularExpression`) alongside the sibling per-
/// `HTTPRouteMatch` `headers[]` / `queryParams[]` / `method` axes it
/// nests under, so drift on the per-match path-matcher container axis
/// is exactly as load-bearing as drift on the per-rule route-match
/// axis it nests inside of (the K8s apiserver-side Gateway API CRD
/// schema validator drops any per-match block whose path-matcher
/// container axis carries an unrecognized key — a `"pathMatch"` /
/// `"prefix"` / `"url"` typo silently emits an `HTTPRoute` whose per-
/// match path-selection axis the Gateway API implementation's per-rule
/// L7 dispatch loop no-ops entirely: no path predicate is evaluated,
/// the match degrades to the wildcard predicate at the gateway-class-
/// controller's per-rule reconcile, the rule matches every request
/// path unconditionally, and every external `:entrada` path filter the
/// rule was authored to enforce drops with no field naming the path-
/// matcher-axis-drift root cause).
///
/// The single source of truth the rendered Aplicacao Gateway-API-side
/// ingress bundle's per-`HTTPRouteMatch` path-matcher-container-axis-
/// naming reaches for:
///
/// - the rendered `HTTPRoute` document's per-match
/// `spec.rules[].matches[].path` axis (caixa-mesh/src/lib.rs — the
/// `gateway_routes` per-Aplicacao `HTTPRoute`'s per-match
/// `match_entry.insert("path", …)` call seeded from the Aplicacao's
/// `:entrada :paths` slot).
///
/// The per-`HTTPRouteMatch` path-matcher container axis names the same
/// Gateway-API-implementation-side per-match request-path-selection
/// predicate container as the sibling
/// [`GATEWAY_API_KEY_MATCHES`] per-rule route-match container axis it
/// nests inside of, and must move together on any future Gateway API
/// rebrand (an upstream SIG-Network Gateway API v2 rename of the path-
/// matcher axis from `path` to `pathMatch` / `prefix` / `url`,
/// coordinated with the Gateway API deprecation cycle). Until this lift
/// landed the axis carried an inline `path` literal at the one
/// production-code occurrence in caixa-mesh/src/lib.rs (the
/// `gateway_routes` per-match `match_entry.insert("path", …)` call) —
/// one occurrence of the same load-bearing Gateway-API-CRD-
/// `path`-axis-key convention, drift-prone by construction. A drift on
/// the production site to `"pathMatch"` / `"prefix"` / `"url"` would
/// have surfaced as a Gateway API implementation-side schema validator
/// drop at apply time (the affected per-match path-matcher axis the
/// CRD schema validator recognizes as unknown), with the per-match
/// path predicate degrading to the wildcard match at the gateway-
/// class-controller's per-rule reconcile with no field naming the
/// path-matcher-drift root cause.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`GATEWAY_API_KEY_MATCHES`] (b9ede1a) /
/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
/// [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) /
/// [`GATEWAY_API_KEY_RETRY`] (231bbf5) /
/// [`GATEWAY_API_KEY_GATEWAY_CLASS_NAME`] (1bc727d) lifts established on
/// the sibling canonical-Gateway-API-HTTPRoute-body-axis / per-Gateway-
/// body-axis surfaces — nests the per-Gateway-API-HTTPRoute-per-rule-
/// body-axis canonical-string-pin set (`matches`, `backendRefs`,
/// `timeouts`, `retry`) one level deeper onto the per-`HTTPRouteMatch`
/// body-axis surface, so the container-axis key beneath the sibling
/// `matches[]` axis now threads a lifted `&'static str` alongside its
/// parent-container-axis key. The render-side consumer now threads the
/// same `&'static str` through its `match_entry.insert(…)` call so a
/// future Gateway API rebrand on the per-`HTTPRouteMatch` path-matcher
/// axis (or an upstream SIG-Network Gateway API v2 rename to a per-
/// `HTTPRouteMatch` sibling name) lands in one place; every future
/// renderer that reaches for the canonical per-`HTTPRouteMatch` path-
/// matcher axis (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-Aplicacao `HTTPRoute` fan-out, a future per-edge
/// `GRPCRoute` renderer whose per-match request-method / service /
/// method predicate nests alongside the path predicate, a future
/// per-match header-match / query-match renderer whose per-predicate
/// list binds against sibling axes of this one under the same match
/// entry) inherits the same value by construction with no opportunity
/// for per-renderer drift.
///
/// Same "the typed constant lives in one place" discipline the
/// [`GATEWAY_API_KEY_MATCHES`] (b9ede1a) /
/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
/// [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) /
/// [`GATEWAY_API_KEY_RETRY`] (231bbf5) /
/// [`GATEWAY_API_KEY_GATEWAY_CLASS_NAME`] (1bc727d) lifts apply on the
/// peer canonical-Gateway-API-HTTPRoute-per-`HTTPRouteMatch`-body-axis
/// surface.
///
/// [cm]: ../../caixa_mesh/index.html
pub const GATEWAY_API_KEY_PATH: &str = "path";
/// Canonical K8s Gateway API v1 `HTTPPathMatch` `value` scalar-axis key
/// every `gateway_routes`-emitted `HTTPRoute` per-match `path` block
/// mounts its request-path-selection scalar payload under
/// (`spec.rules[].matches[].path.value`). Nests one level beneath the
/// sibling [`GATEWAY_API_KEY_PATH`] per-`HTTPRouteMatch` path-matcher
/// container-axis it hangs off of — the Gateway API v1 CRD schema
/// pins per-`HTTPPathMatch` request-path selection through the
/// `{type, value}` two-axis pair (a
/// [`GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX`]-typed `type`
/// discriminator picks `Exact | PathPrefix | RegularExpression`; the
/// `value` scalar carries the per-match request-path string the
/// discriminator is applied against), so drift on the `value` scalar
/// axis is exactly as load-bearing as drift on the peer `type`
/// discriminator axis it nests alongside (the K8s apiserver-side
/// Gateway API CRD schema validator drops any per-match block whose
/// `HTTPPathMatch` scalar-payload axis carries an unrecognized key —
/// a `"path"` / `"prefix"` / `"pattern"` typo silently emits an
/// `HTTPRoute` whose per-match request-path predicate the Gateway API
/// implementation's per-rule L7 dispatch loop treats as bare (no
/// value evaluated against the `type` discriminator), the match
/// degrades to the wildcard predicate at the gateway-class-
/// controller's per-rule reconcile, the rule matches every request
/// path unconditionally, and every external `:entrada` path filter the
/// rule was authored to enforce drops with no field naming the
/// `HTTPPathMatch`-scalar-payload-drift root cause).
///
/// The single source of truth the rendered Aplicacao Gateway-API-side
/// ingress bundle's per-`HTTPPathMatch` scalar-payload-axis-naming
/// reaches for:
///
/// - the rendered `HTTPRoute` document's per-match
/// `spec.rules[].matches[].path.value` axis (caixa-mesh/src/lib.rs
/// — the `gateway_routes` per-Aplicacao `HTTPRoute`'s per-match
/// `path_match.insert("value", …)` call seeded from the
/// Aplicacao's `:entrada :paths` slot).
///
/// The per-`HTTPPathMatch` scalar-payload axis names the same
/// Gateway-API-implementation-side per-match request-path-selection
/// scalar as the sibling [`GATEWAY_API_KEY_PATH`] per-`HTTPRouteMatch`
/// path-matcher container-axis it nests inside of, and must move
/// together on any future Gateway API rebrand (an upstream
/// SIG-Network Gateway API v2 rename of the `HTTPPathMatch` scalar-
/// payload axis from `value` to `path` / `pattern` / `expression`,
/// coordinated with the Gateway API deprecation cycle). Until this
/// lift landed the axis carried an inline `"value"` literal at the
/// one production-code occurrence in caixa-mesh/src/lib.rs (the
/// `gateway_routes` per-match `path_match.insert("value", …)` call) —
/// one occurrence of the same load-bearing Gateway-API-CRD-
/// `HTTPPathMatch`-`value`-axis-key convention, drift-prone by
/// construction. A drift on the production site to `"path"` /
/// `"prefix"` / `"pattern"` would have surfaced as a Gateway API
/// implementation-side schema validator drop at apply time (the
/// affected per-match `HTTPPathMatch` scalar-payload axis the CRD
/// schema validator recognizes as unknown), with the per-match path
/// predicate degrading to the wildcard match at the gateway-class-
/// controller's per-rule reconcile with no field naming the
/// `HTTPPathMatch`-scalar-payload-drift root cause.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`GATEWAY_API_KEY_PATH`] (9f45aa4) /
/// [`GATEWAY_API_KEY_MATCHES`] (b9ede1a) /
/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
/// [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) /
/// [`GATEWAY_API_KEY_RETRY`] (231bbf5) /
/// [`GATEWAY_API_KEY_GATEWAY_CLASS_NAME`] (1bc727d) lifts established
/// on the sibling canonical-Gateway-API-body-axis surfaces — nests
/// the per-Gateway-API-HTTPRoute-per-match-body-axis canonical-
/// string-pin set (`path` container-axis, `type` discriminator
/// scalar-key, `value` scalar-payload key) two levels deeper onto the
/// per-`HTTPPathMatch` body-axis surface, so both halves of the
/// `HTTPPathMatch.{type, value}` typed contract now thread one lifted
/// `&'static str` apiece at the substrate boundary alongside the
/// parent-container-axis key. The render-side consumer now threads
/// the same `&'static str` through its `path_match.insert(…)` call
/// so a future Gateway API rebrand on the `HTTPPathMatch` scalar-
/// payload axis (or an upstream SIG-Network Gateway API v2 rename to
/// a per-`HTTPPathMatch` sibling name) lands in one place; every
/// future renderer that reaches for the canonical per-`HTTPPathMatch`
/// scalar-payload axis (the future M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
/// `HTTPRoute` fan-out, a future per-edge `GRPCRoute` renderer whose
/// per-match `GRPCMethodMatch.method` scalar-payload nests alongside
/// this same axis, a future per-match header-match / query-match
/// renderer whose per-predicate `HTTPHeaderMatch.value` /
/// `HTTPQueryParamMatch.value` scalar-payload binds against sibling
/// axes on the same `value` axis-key) inherits the same value by
/// construction with no opportunity for per-renderer drift.
///
/// [cm]: ../../caixa_mesh/index.html
pub const GATEWAY_API_KEY_VALUE: &str = "value";
/// Canonical K8s Gateway API v1 per-child-object name-reference
/// discriminator axis key every `gateway_routes`-emitted `Gateway`
/// listener + `HTTPRoute` `parentRefs[]` / `backendRefs[]` entry
/// mounts its named-object binding under. Three peer sub-schemas on
/// the shared `spec.…[].name` axis:
///
/// - `Gateway.spec.listeners[].name` — Gateway API v1 `SectionName`,
/// the listener's per-section identifier the sibling
/// `HTTPRoute.spec.parentRefs[].sectionName` binds against;
/// - `HTTPRoute.spec.parentRefs[].name` — Gateway API v1
/// `ObjectName`, the per-`HTTPRoute` parent-Gateway reference the
/// Gateway API implementation's per-HTTPRoute attach reconciler
/// resolves against a `Gateway` object in the same namespace;
/// - `HTTPRoute.spec.rules[].backendRefs[].name` — Gateway API v1
/// `ObjectName`, the per-rule backend-Service reference the
/// Gateway API implementation's per-rule L7 dispatch loop
/// resolves against a `Service` object in the same namespace.
///
/// All three sub-schemas key their named-reference discriminator on
/// the identical three-byte `"name"` axis at every level of the
/// Gateway API v1 CRD schema (`Gateway.spec.listeners[].name`,
/// `HTTPRoute.spec.parentRefs[].name`,
/// `HTTPRoute.spec.rules[].backendRefs[].name`), so drift on any one
/// of them silently splits the substrate's Aplicacao gateway bundle
/// at whichever schema the drift hits (the K8s apiserver-side Gateway
/// API CRD schema validator drops a per-listener / per-parentRef /
/// per-backendRef block whose name-reference axis carries an
/// unrecognized key — a `"Name"` / `"target"` / `"ref"` typo silently
/// emits a `Gateway` whose listener carries no section identity, or
/// an `HTTPRoute` whose parent-Gateway attachment reconciles as
/// unbound, or an `HTTPRoute` whose per-rule backend fan-out resolves
/// no Service, and every external `:entrada` flow the bundle was
/// authored to accept drops at the gateway-class-controller's per-
/// rule/per-listener/per-parentRef reconcile with no field naming the
/// name-reference-axis-drift root cause).
///
/// The single source of truth the rendered Aplicacao Gateway-API-side
/// ingress bundle's per-child-object name-reference-axis-naming
/// reaches for:
///
/// - the rendered `Gateway` document's `spec.listeners[].name` axis
/// (caixa-mesh/src/lib.rs — the `gateway_routes` per-Aplicacao
/// `Gateway`'s per-listener `listener.insert("name", …)` call);
/// - the rendered `HTTPRoute` document's `spec.parentRefs[].name`
/// axis (caixa-mesh/src/lib.rs — the `gateway_routes` per-
/// Aplicacao `HTTPRoute`'s per-parentRef
/// `parent_ref.insert("name", …)` call);
/// - the rendered `HTTPRoute` document's
/// `spec.rules[].backendRefs[].name` axis (caixa-mesh/src/lib.rs
/// — the `gateway_routes` per-rule per-backendRef
/// `backend_ref.insert("name", …)` call).
///
/// The per-child-object name-reference discriminator axis names the
/// same Gateway-API-implementation-side named-object binding container
/// as the sibling [`GATEWAY_API_KEY_LISTENERS`] +
/// [`GATEWAY_API_KEY_PARENT_REFS`] + [`GATEWAY_API_KEY_BACKEND_REFS`]
/// per-container list axes it nests directly beneath, and must move
/// together on any future Gateway API rebrand (an upstream SIG-Network
/// Gateway API v2 rename of the name-reference axis from `name` to
/// `target` / `ref` / `objectName`, coordinated with the Gateway API
/// deprecation cycle). Until this lift landed the axis carried inline
/// `"name"` literals at four occurrences across caixa-mesh — three
/// production emitter sites (the per-listener `listener.insert("name",
/// …)`, the per-parentRef `parent_ref.insert("name", …)`, and the per-
/// backendRef `backend_ref.insert("name", …)` calls in
/// `gateway_routes`) plus one in-file test-fixture navigation (the
/// `httproute_routes_to_entrada_para` fixture's per-backendRef
/// `.get("name")` retrieval) — four occurrences of the same load-
/// bearing Gateway-API-CRD-`name`-axis-key convention, drift-prone by
/// construction. A drift on any one production site to `"Name"` /
/// `"target"` / `"ref"` would have surfaced as a Gateway API
/// implementation-side schema validator drop at apply time (the
/// affected per-listener / per-parentRef / per-backendRef name-
/// reference axis the CRD schema validator recognizes as unknown),
/// with the listener carrying no section identity or the `HTTPRoute`
/// carrying an unbound parent-Gateway attachment or the per-rule
/// backend fan-out resolving no Service at the gateway-class-
/// controller's reconcile with no field naming the name-reference-
/// drift root cause. A drift on the test-fixture side silently masks
/// the emission-side pin (`.get("name")` returns `None` under both
/// the drifted-key emitter and the drifted-key probe — the downstream
/// `.and_then(|n| n.as_str())` chain short-circuits vacuously because
/// the outer per-backendRef name-reference lookup is itself `None`).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the [`GATEWAY_API_KEY_PATH`] (9f45aa4) /
/// [`GATEWAY_API_KEY_MATCHES`] (b9ede1a) /
/// [`GATEWAY_API_KEY_BACKEND_REFS`] (a6c5679) /
/// [`GATEWAY_API_KEY_PARENT_REFS`] (f44e823) /
/// [`GATEWAY_API_KEY_LISTENERS`] (29f2415) /
/// [`GATEWAY_API_KEY_HOSTNAMES`] (b77f744) /
/// [`GATEWAY_API_KEY_HOSTNAME`] (c96fa22) /
/// [`GATEWAY_API_KEY_TIMEOUTS`] (db31108) /
/// [`GATEWAY_API_KEY_RETRY`] (231bbf5) /
/// [`GATEWAY_API_KEY_GATEWAY_CLASS_NAME`] (1bc727d) lifts established
/// on the sibling canonical-Gateway-API-CRD-body-axis surface —
/// completes the four-way per-child-object axis-key set (`name` on
/// listeners + parentRefs + backendRefs, alongside sibling
/// `hostname`/`port`/`protocol` per-listener and `port` per-
/// backendRef) the M3 Aplicacao mesh renderer's external `:entrada`
/// ingress contract rests on. The render-side consumer now threads
/// the same `&'static str` through every one of its `.insert(…)`
/// calls so a future Gateway API rebrand on the name-reference axis
/// (or an upstream SIG-Network Gateway API v2 rename to a per-CRD
/// sibling name) lands in one place; every future renderer that
/// reaches for the canonical per-child-object name-reference axis
/// (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-Aplicacao `Gateway` + `HTTPRoute` fan-out, a
/// future per-edge `GRPCRoute` / `TCPRoute` / `TLSRoute` renderer
/// whose per-rule backend-Service reference binds against this same
/// axis, a future per-Aplicacao `ReferenceGrant` renderer whose per-
/// cross-namespace parent-Gateway attachment resolves against this
/// same axis) inherits the same value by construction with no
/// opportunity for per-renderer drift.
///
/// Byte-identical to [`KUBE_KEY_NAME`] today — both resolve to the
/// same three-byte `"name"` literal — but semantically distinct:
/// [`KUBE_KEY_NAME`] names the K8s CR canonical `metadata.name` axis
/// (every rendered CR's outer-level identity discriminator, spelled
/// per the K8s apiserver's per-object `OpenAPI` v3 schema), while this
/// constant names the Gateway API v1 CRD schema's per-child-object
/// name-reference discriminator axis on `Listener` / `ParentReference`
/// / `BackendObjectReference` sub-schemas (spelled per the Gateway API
/// v1 CRD schema — a separate schema contract). Splitting the two
/// lets each schema's future rebrand land independently at its
/// canonical const definition without coupling the K8s CR canonical-
/// key axis to the Gateway API v1 per-child-object name-reference
/// axis (or vice versa) — the same discipline
/// [`FLEET_PROGRAMS_KEY_NAME`] establishes vs. [`KUBE_KEY_NAME`] on
/// the `lareira-fleet-programs` values-schema per-entry name-axis.
///
/// [cm]: ../../caixa_mesh/index.html
pub const GATEWAY_API_KEY_NAME: &str = "name";
/// Canonical Helm 3 `Chart.yaml` `apiVersion` every `caixa-helm`-rendered
/// `lareira-<nome>` chart declares at its top-level `apiVersion` axis. The
/// Helm 3 chart-schema resolution contract keys off this exact `"v2"` value:
/// `helm dependency build`, `helm lint`, and `helm template` all parse the
/// chart under the Helm 3 v2 schema (which requires
/// [`ChartYaml::description`][chart-yaml-desc] and permits
/// `dependencies:` at the top level); drift to the legacy Helm 2 `"v1"`
/// (the pre-Helm-3 chart schema every upstream Helm-3-migration doc names)
/// silently reroutes the rendered `Chart.yaml` through the Helm 2 parser,
/// where the top-level `dependencies:` block is unknown and the chart's
/// dep on the `pleme-computeunit` library chart never resolves —
/// `helm dependency build` reports "no requirements found" and every
/// downstream `helm template` / `helm install` on the rendered chart
/// emits an empty release (no ComputeUnit / Service / ScaledObject
/// resources land) far from the source caixa.lisp / the renderer's
/// `build_chart_yaml` call site.
///
/// The single source of truth the [`caixa-helm`][ch]'s `build_chart_yaml`
/// `Chart.yaml` `apiVersion` axis reaches for (caixa-helm/src/lib.rs:298).
/// Peer with the [`FLUX_HELMRELEASE_API_VERSION`] (55f0fd9) /
/// [`FLUX_GITREPOSITORY_API_VERSION`] (dbbcf29) /
/// [`FLUX_KUSTOMIZATION_API_VERSION`] (d2dd1b1) /
/// [`GATEWAY_API_API_VERSION`] (3c6cfc3) / [`CILIUM_API_VERSION`] (279d611)
/// lifts on the sibling cluster-side-CRD-apiVersion surface — those pin
/// the K8s apiserver-side `(apiVersion, kind)` `RESTMapper` contract,
/// this one pins the Helm-side chart-schema-parser contract that gates
/// every rendered `lareira-<nome>` chart's dependency resolution before
/// any K8s resource lands. Both axes are load-bearing schema-version
/// discriminators drift-prone by construction across renderer forks.
///
/// A future Helm 4 chart-schema promotion (the upstream Helm roadmap
/// names a `"v3"` apiVersion once the Helm 3 LTS branch closes) is a
/// coordinated migration alongside the upstream Helm chart-schema
/// deprecation cycle, not an incidental edit — pinning it here means
/// the migration lands as one edit at the const + a re-run of the
/// pin tests rather than a per-renderer sweep with no single source
/// of truth to consult. Same "the typed constant lives in one place"
/// discipline the [`DEFAULT_LIBRARY_NAME`] (41438dc) /
/// [`LAREIRA_CHART_NAME_PREFIX`] / [`FLUX_HELMRELEASE_API_VERSION`]
/// (55f0fd9) lifts apply on the peer canonical-Helm-load-bearing-string
/// and cluster-side-CRD-apiVersion axes.
///
/// [chart-yaml-desc]: https://helm.sh/docs/topics/charts/#the-chartyaml-file
/// [ch]: ../../caixa_helm/index.html
pub const HELM_CHART_API_VERSION: &str = "v2";
/// Canonical Helm 3 `Chart.yaml` `type` field per-chart-kind discriminator
/// scalar-value every rendered `lareira-<nome>` chart declares. The Helm
/// chart-schema pins the per-chart-kind axis to the closed set
/// `{"application", "library"}` (see [chart-type-doc]) — the
/// `application` chart-kind is Helm's default install-shape (an
/// application chart that installs into a namespace as a workload +
/// rendered manifests), while the `library` chart-kind is Helm's
/// dependency-only shape (a chart authored as a shared-template
/// substrate that can only be consumed as a dependency, never installed
/// directly). Each `lareira-<nome>` chart the caixa-helm renderer emits
/// declares itself as an `application` chart because it is the per-
/// Servico install shape a cluster operator's `helm install` /
/// `helm upgrade` per-Servico release cycle materializes — the sibling
/// [`DEFAULT_LIBRARY_NAME`] `pleme-computeunit` chart (the substrate-
/// side library-chart the `lareira-<nome>` chart depends on for
/// template-shape) carries the sibling `library` value verbatim in its
/// authored Chart.yaml (out-of-tree at the `pleme-io/helmworks` repo,
/// so not this crate's authority).
///
/// The single source of truth the rendered `lareira-<nome>` chart's
/// Chart.yaml per-chart-kind discriminator axis naming reaches for:
///
/// - [`caixa-helm`][ch]'s `build_chart_yaml` `chart_type` field
/// assignment (caixa-helm/src/lib.rs — the sole production emitter
/// site the prior inline `"application".into()` literal sat at,
/// writing the per-chart-kind discriminator scalar-value the
/// `helm install` / `helm upgrade` per-release install-shape dispatch
/// loop keys off to select the per-chart-kind install pathway).
///
/// Until this lift landed the axis carried an inline `"application"`
/// literal at the one production-code site (`build_chart_yaml`'s
/// `chart_type` field assignment). A drift on the value at the emitter
/// (a `"Application"` / `"APPLICATION"` / `"app"` / `"workload"` typo,
/// or an accidental collapse onto the sibling `"library"` shape) would
/// have surfaced as one of two silent failure modes at `helm install`
/// time:
///
/// - a value outside the schema's admitted set (`{"application",
/// "library"}`) — Helm's chart-schema parser silently treats an
/// unrecognized `type:` scalar as the default `application` shape,
/// so a typo like `"Application"` still installs but with no
/// drift-signal in the process log, silently masking the schema
/// violation;
/// - a schema-admitted-but-wrong-shape drift onto `"library"` —
/// `helm install lareira-<nome>` refuses the release with an
/// "Error: library charts cannot be installed" error, and the
/// per-Servico release cycle drops with no field naming the
/// chart-kind-drift root cause (the operator sees "the chart won't
/// install" far from the drift site, and troubleshooting has no
/// canonical anchor to compare the rendered value against).
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// constant to a typed substrate-side `&'static str` on the same
/// trajectory the peer [`HELM_CHART_API_VERSION`] /
/// [`DEFAULT_LIBRARY_NAME`] / [`LAREIRA_CHART_NAME_PREFIX`] lifts
/// established on the sibling canonical-Helm-load-bearing-string axes —
/// extends the canonical-Helm-chart-schema-axis single-sourcing
/// discipline the `apiVersion` lift established onto the sibling
/// per-chart-kind discriminator scalar-value axis every rendered
/// `lareira-<nome>` chart declares in its Chart.yaml. Peer to the
/// canonical-cluster-side-OpenAPI-schema-enum-value lifts
/// ([`KUBE_PROTOCOL_TCP`] / [`GATEWAY_API_PROTOCOL_HTTP`] /
/// [`GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX`] /
/// [`CILIUM_AUTH_MODE_REQUIRED`] / [`CILIUM_AUTH_MODE_DISABLED`]) on
/// the sibling K8s-CR-side enum-value surfaces — pivots the discipline
/// from the K8s-CR-side OpenAPI-schema-enum-value axis onto the
/// Helm-chart-schema-enum-value axis every rendered Chart.yaml carries
/// at its per-chart-kind discriminator field.
///
/// [chart-type-doc]: https://helm.sh/docs/topics/charts/#chart-types
/// [ch]: ../../caixa_helm/index.html
pub const HELM_CHART_TYPE_APPLICATION: &str = "application";
/// Canonical Helm 3 `Chart.yaml` `type` field per-chart-kind discriminator
/// scalar-value the sibling library-chart shape lands on — the second and
/// only other arm of the closed set `{"application", "library"}` the Helm
/// chart-schema pins the per-chart-kind axis to (see [chart-type-doc]).
/// The `library` chart-kind is Helm's dependency-only install-shape: a
/// chart authored as a shared-template substrate the per-Aplicacao
/// `lareira-<nome>` application charts depend on for their emitted-
/// object templates (the [`DEFAULT_LIBRARY_NAME`] `pleme-computeunit`
/// chart out-of-tree at `pleme-io/helmworks` is the substrate's
/// canonical instance today), and Helm refuses to install it directly
/// (`helm install <library-chart>` fails with "Error: library charts
/// cannot be installed") — a chart declaring itself under this
/// scalar-value is only ever consumed as a dependency by a sibling
/// `application`-typed chart.
///
/// Peer of [`HELM_CHART_TYPE_APPLICATION`] on the same closed
/// canonical-Helm-chart-schema-per-chart-kind-discriminator axis: the
/// two consts together name the two-arm schema-admitted set as a pair
/// of `&'static str`s at the substrate-side canonical surface, so any
/// consumer that reaches for either shape (the caixa-helm renderer at
/// [`HELM_CHART_TYPE_APPLICATION`]'s single emitter site today; the
/// future per-Aplicacao library chart the [`HELM_CHART_TYPE_APPLICATION`]
/// docstring names as a trajectory item, whose emit site would land at
/// this const; the future `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-chart-kind admission gate that needs to accept
/// exactly the two-arm closed set) reads from one canonical declaration
/// per arm, not a scattered mix of substrate-side const + prose-only
/// sibling. Same "one canonical declaration per arm, next to the
/// closed set's peer" discipline the peer
/// [`CILIUM_AUTH_MODE_REQUIRED`] / [`CILIUM_AUTH_MODE_DISABLED`]
/// (2c3f11b — the two-arm Cilium `MutualAuthenticationMode` `OpenAPI`
/// enum's closed set) established for the sibling Cilium-CR-side
/// per-enum-value axis, and the peer
/// [`M3_PLACEMENT_ESTRATEGIA_SINGLE_NODE`] /
/// [`M3_PLACEMENT_ESTRATEGIA_REPLICATED`] /
/// [`M3_PLACEMENT_ESTRATEGIA_SHARDED`] (b0ce0a5 — the three-arm typed
/// [`crate::PlacementStrategy`] variant discriminator-value set) applies
/// on the sibling M3 typed-enum discriminator-scalar axis — extends the
/// discipline onto the Helm-chart-schema-enum-value closed set every
/// rendered Chart.yaml declares its per-chart-kind axis over.
///
/// Until this lift landed the sibling `"library"` value lived only in
/// prose across the [`HELM_CHART_TYPE_APPLICATION`] docstring's
/// closed-set enumeration (3+ mentions naming the sibling `library`
/// shape as the schema-admitted second arm, including the accidental-
/// collapse-onto-sibling failure-mode arm the pin test
/// [`tests::helm_chart_type_application_and_library_are_distinct`]
/// closes), with no compile-time link between the substrate-side
/// canonical const and the sibling closed-set arm the docstring
/// referenced — a hypothetical future consumer reaching for the
/// sibling shape (an operator-side per-chart-kind classifier, a
/// helmworks-side value-drift detector, the future per-Aplicacao
/// library chart's emit site) had to re-derive the value from the
/// prose enumeration rather than reading the same `&'static str` the
/// substrate declares. This lift closes that gap by pairing the
/// canonical-Helm-chart-schema-per-chart-kind axis at both closed-set
/// arms, so drift-detection between the two shapes is a build-time
/// constant-value comparison at
/// [`tests::helm_chart_type_application_and_library_are_distinct`]
/// rather than a runtime silent-collapse-onto-sibling far from the
/// drift's source.
///
/// [chart-type-doc]: https://helm.sh/docs/topics/charts/#chart-types
pub const HELM_CHART_TYPE_LIBRARY: &str = "library";
/// Canonical Helm 3 `Chart.yaml` top-level YAML axis-key naming the
/// per-chart chart-schema-apiVersion field whose scalar-value
/// [`HELM_CHART_API_VERSION`] already owns as the peer axis-value
/// lift. Where the peer axis-value lift pins the byte-shape of the
/// `apiVersion:` field's admitted scalar (Helm 3's `"v2"`), this
/// axis-key lift pins the byte-shape of the `apiVersion:` field's
/// YAML-key name itself: the load-bearing serde-rename literal at
/// [`caixa-helm`][ch]'s `ChartYaml` struct
/// (`caixa-helm/src/lib.rs:145`, `#[serde(rename = "apiVersion")]`)
/// that selects how the Rust field `api_version` serializes into
/// the rendered `Chart.yaml` YAML mapping.
///
/// The byte-shape (`"apiVersion"`) is byte-identical to the K8s-CR
/// top-level per-CR schema-apiVersion axis key ([`KUBE_KEY_API_VERSION`])
/// by Helm's design decision to inherit the K8s CR top-level shape
/// verbatim (see [chart-yaml-desc]) — the paired
/// `helm_chart_key_api_version_matches_kube_key_api_version` pin
/// asserts the two byte-shapes coincide, so a future K8s-side
/// rebrand at [`KUBE_KEY_API_VERSION`] that dropped the byte-
/// identity would fail the pin, surfacing the axis divergence at
/// substrate-build time rather than as a silent Helm-chart-schema-
/// parser rejection at `helm lint` / `helm template` time. The two
/// axes are structurally-independent schema surfaces (the Helm 3
/// chart-schema top-level shape vs. the K8s apiserver-side CR
/// top-level shape) whose byte-shapes happen to coincide today; the
/// paired pin makes the coincidence load-bearing rather than
/// accidental.
///
/// The single source of truth every consumer that names the per-
/// Chart.yaml top-level chart-schema-apiVersion YAML key reaches for:
///
/// - [`caixa-helm`][ch]'s `ChartYaml` struct's `api_version` field
/// `#[serde(rename = "apiVersion")]` attribute (the sole
/// production serialize-side site the literal appears at as a
/// syntactic serde-rename argument; the attribute itself cannot
/// consume a `const` because Rust's attribute grammar admits
/// only string literals, so the discipline here is: the const's
/// byte-shape must remain byte-identical to the literal the
/// attribute pins, and the paired drift-detection pin at
/// [`caixa-helm`]'s
/// `chart_yaml_serializes_api_version_axis_under_lifted_helm_chart_key_api_version`
/// round-trips a rendered [`caixa-helm`]-emitted `Chart.yaml`
/// through `serde_yaml::from_str::<serde_yaml::Value>` and
/// asserts the top-level `Mapping::get(HELM_CHART_KEY_API_VERSION)`
/// resolves — closing the drift the syntactic-literal-only
/// attribute would otherwise leave silent);
/// - every test-side navigator that inspects the serialized
/// [`caixa-helm`]-emitted `Chart.yaml` YAML mapping by the top-
/// level chart-schema-apiVersion key.
///
/// A drift on the emitter's serde-rename literal (a future refactor
/// that dropped the `#[serde(rename = "apiVersion")]` attribute or
/// changed the target key to `"ApiVersion"` / `"apiversion"` /
/// `"schemaVersion"`) would silently serialize the field under
/// Rust's default snake_case `api_version:` key, which Helm's
/// chart-schema parser rejects at `helm lint` / `helm dependency
/// build` / `helm template` time with an "apiVersion is required"
/// error — the failure surfaces far from the drift site, and every
/// downstream `lareira-<nome>` chart consumer drops with no field
/// naming the serde-rename-drift root cause. Same drift-detection-
/// pin discipline the peer [`HELM_CHART_KEY_TYPE`] /
/// [`HELM_CHART_KEY_APP_VERSION`] lifts (d29bc23) established on the
/// sibling per-Chart.yaml serde-rename-literal-only axis pair —
/// extends the discipline from the two axes those lifts closed onto
/// the third and last serde-rename-literal-only axis at
/// [`caixa-helm`]'s `ChartYaml` struct, so every `#[serde(rename =
/// "...")]` literal on the struct threads through a canonical
/// substrate-side `&'static str` with a paired drift-detection pin.
///
/// [chart-yaml-desc]: https://helm.sh/docs/topics/charts/#the-chartyaml-file
/// [ch]: ../../caixa_helm/index.html
pub const HELM_CHART_KEY_API_VERSION: &str = "apiVersion";
/// Canonical Helm 3 `Chart.yaml` top-level YAML axis-key naming the
/// per-chart-kind discriminator field whose closed-set scalar-value
/// pair [`HELM_CHART_TYPE_APPLICATION`] / [`HELM_CHART_TYPE_LIBRARY`]
/// already owns as the peer axis-value lift. Where the peer
/// axis-value lifts pin the byte-shape of the `type:` field's
/// admitted-value set, this axis-key lift pins the byte-shape of the
/// `type:` field's YAML-key name itself: the load-bearing serde-
/// rename literal at [`caixa-helm`][ch]'s `ChartYaml` struct
/// (`caixa-helm/src/lib.rs:149`, `#[serde(rename = "type")]`) that
/// selects how the Rust field `chart_type` serializes into the
/// rendered `Chart.yaml` YAML mapping.
///
/// The single source of truth every consumer that names the per-
/// Chart.yaml top-level per-chart-kind discriminator key reaches for:
///
/// - [`caixa-helm`][ch]'s `ChartYaml` struct's `chart_type` field
/// `#[serde(rename = "type")]` attribute (the sole production
/// serialize-side site the literal appears at as a syntactic
/// serde-rename argument; the attribute itself cannot consume a
/// `const` because Rust's attribute grammar admits only string
/// literals, so the discipline here is: the const's byte-shape
/// must remain byte-identical to the literal the attribute pins,
/// and the drift-detection pin at
/// [`caixa-helm`]'s
/// `chart_yaml_serializes_type_axis_under_lifted_helm_chart_key_type`
/// round-trips a rendered [`caixa-helm`]-emitted `Chart.yaml`
/// through `serde_yaml::from_str::<serde_yaml::Value>` and
/// asserts the top-level `Mapping::get(HELM_CHART_KEY_TYPE)`
/// resolves — closing the drift the syntactic-literal-only
/// attribute would otherwise leave silent);
/// - every test-side navigator that inspects the serialized
/// [`caixa-helm`]-emitted `Chart.yaml` YAML mapping by the top-
/// level per-chart-kind discriminator key.
///
/// A drift on the emitter's serde-rename literal (a future refactor
/// that dropped the `#[serde(rename = "type")]` attribute or
/// changed the target key to `"Type"` / `"kind"` / `"chartType"`)
/// would surface as one of two silent failure modes at
/// `helm dependency build` / `helm lint` / `helm template` time
/// far from the drift site: the rendered `Chart.yaml`'s top-level
/// mapping carries an unrecognized key (`chart_type:` from Rust's
/// default snake_case serialization) that Helm's chart-schema
/// parser silently ignores, defaulting the per-chart-kind axis to
/// `application` with no process-log drift-signal (masking the
/// schema-shape violation); or the drift accidentally collapses
/// the key onto the sibling `kind` / K8s-CR `KUBE_KEY_KIND`
/// axis (byte-distinct today at the substrate — see the paired
/// `helm_chart_key_type_is_byte_distinct_from_kube_key_kind` pin)
/// that Helm's chart-schema parser silently treats as an unknown
/// field, again defaulting the per-chart-kind axis.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5)
/// promotes the axis-key to a typed substrate-side `&'static str`
/// on the same trajectory the peer axis-value lifts
/// ([`HELM_CHART_TYPE_APPLICATION`] / [`HELM_CHART_TYPE_LIBRARY`])
/// established — completes the per-Chart.yaml per-chart-kind
/// discriminator axis single-sourcing at both the key and value
/// halves (`{HELM_CHART_KEY_TYPE, HELM_CHART_TYPE_APPLICATION,
/// HELM_CHART_TYPE_LIBRARY}`), so the full
/// `(key, admitted-value-set)` per-axis lift lives at one canonical
/// declaration site. Same "(key, value) axis-pair lift completes at
/// one canonical source per half" discipline the peer
/// [`KUBE_KEY_API_VERSION`] (7994) + [`HELM_CHART_API_VERSION`]
/// (14580) pair carries on the sibling apiVersion axis, and the
/// [`FLEET_PROGRAMS_KEY_NAME`] (7651) + `Servico :nome` value pair
/// carries on the sibling per-fleet-programs-entry axis.
///
/// [chart-type-doc]: https://helm.sh/docs/topics/charts/#chart-types
/// [ch]: ../../caixa_helm/index.html
pub const HELM_CHART_KEY_TYPE: &str = "type";
/// Canonical Helm 3 `Chart.yaml` top-level YAML axis-key naming the
/// per-chart underlying-application-version field — the load-bearing
/// serde-rename literal at [`caixa-helm`][ch]'s `ChartYaml` struct
/// (`caixa-helm/src/lib.rs:152`, `#[serde(rename = "appVersion")]`)
/// that selects how the Rust field `app_version` serializes into the
/// rendered `Chart.yaml` YAML mapping. Distinct from the sibling
/// [`Chart.yaml` `version:` field][chart-yaml-desc] (the chart's own
/// SemVer, incremented per release of the chart itself); the
/// `appVersion:` field the Helm 3 chart-schema pins carries the
/// underlying application's version (see [app-version-doc]) — the
/// version the containerized workload the chart installs advertises
/// (an OCI image tag, a wasm-component `:versao`, a package release
/// tag). At the caixa-helm renderer today the two axes both draw
/// from the caixa's `:versao` at [`build_chart_yaml`] because a
/// [`caixa-core::Caixa`]'s `:versao` names both the chart's own
/// release cadence and the underlying wasm-component release
/// cadence in one axis (`caixa`'s per-caixa BLAKE3-closure identity
/// binds a caixa's chart + wasm-binary + declared source at exactly
/// one release axis), but the Chart.yaml schema pins the two YAML
/// keys distinctly regardless — every downstream Helm-consumer
/// (Artifact Hub's per-chart-search index, `helm search` /
/// `helm show chart` operator surfaces) routes the two axes onto
/// distinct display fields at chart-inspection time.
///
/// The single source of truth every consumer that names the per-
/// Chart.yaml top-level app-version YAML key reaches for:
///
/// - [`caixa-helm`][ch]'s `ChartYaml` struct's `app_version` field
/// `#[serde(rename = "appVersion")]` attribute (the sole
/// production serialize-side site the literal appears at as a
/// syntactic serde-rename argument; the same
/// attribute-literal-only-grammar constraint the peer
/// [`HELM_CHART_KEY_TYPE`] docstring enumerates applies, and
/// the paired drift-detection pin at [`caixa-helm`]'s
/// `chart_yaml_serializes_app_version_axis_under_lifted_helm_chart_key_app_version`
/// round-trips a rendered `Chart.yaml` and asserts the top-level
/// `Mapping::get(HELM_CHART_KEY_APP_VERSION)` resolves);
/// - every test-side navigator that inspects the serialized
/// [`caixa-helm`]-emitted `Chart.yaml` YAML mapping by the top-
/// level per-chart-app-version key.
///
/// A drift on the emitter's serde-rename literal (a future refactor
/// that dropped the `#[serde(rename = "appVersion")]` attribute or
/// changed the target key to `"AppVersion"` / `"applicationVersion"`
/// / `"version"`) would surface as one of two silent failure modes
/// at Helm-chart-consumption time far from the drift site: the
/// rendered `Chart.yaml`'s top-level mapping carries an unrecognized
/// key (`app_version:` from Rust's default snake_case serialization)
/// that Helm's chart-schema parser silently drops from the parsed
/// chart-metadata shape (masking the schema-shape violation with no
/// process-log drift-signal, and every downstream Artifact Hub /
/// `helm search` per-chart index falls back to "no application
/// version" for the rendered chart); or the drift accidentally
/// collapses the app-version key onto the sibling chart-own-version
/// `version:` axis (byte-distinct today at the substrate — see the
/// paired
/// `helm_chart_key_app_version_is_byte_distinct_from_helm_chart_key_version`
/// pin) that Helm's chart-schema parser then silently reads under
/// the wrong axis, and the chart's own SemVer collides with the
/// underlying-application version at every downstream Helm-consumer.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5)
/// promotes the axis-key to a typed substrate-side `&'static str`
/// on the same trajectory the peer [`HELM_CHART_KEY_TYPE`] lift
/// established — extends the per-Chart.yaml top-level YAML axis-key
/// single-sourcing discipline from the per-chart-kind discriminator
/// key onto the sibling per-chart-app-version key, so every
/// substrate-side renderer that emits or navigates a `Chart.yaml`
/// top-level mapping consults one canonical `&'static str` per axis.
///
/// [chart-yaml-desc]: https://helm.sh/docs/topics/charts/#the-chartyaml-file
/// [app-version-doc]: https://helm.sh/docs/topics/charts/#the-appversion-field
/// [ch]: ../../caixa_helm/index.html
pub const HELM_CHART_KEY_APP_VERSION: &str = "appVersion";
/// Canonical Helm 3 `Chart.yaml` top-level YAML axis-key naming the
/// per-chart dependency-list field — the load-bearing serde
/// field-name at [`caixa-helm`][ch]'s `ChartYaml` struct's
/// `dependencies` field, the parent list-container the already-lifted
/// [`HELM_CHART_DEPENDENCY_KEY_NAME`] / [`HELM_CHART_DEPENDENCY_KEY_VERSION`]
/// / [`HELM_CHART_DEPENDENCY_KEY_REPOSITORY`] /
/// [`HELM_CHART_DEPENDENCY_KEY_ALIAS`] per-entry sub-mapping tetrad
/// (69f62db) mounts under. The chart-schema top-level `dependencies:`
/// field pins the list of chart-registry references Helm's per-dep
/// resolver consults at `helm dependency build` /
/// `helm dependency update` time to vendor each dependency chart
/// under the substrate's canonical [`DEFAULT_LIBRARY_NAME`] wrap-key
/// convention. Every rendered `lareira-<nome>` chart declares exactly
/// one entry today (the [`DEFAULT_LIBRARY_NAME`] `pleme-computeunit`
/// library-chart dep the sibling [`caixa-helm`][ch]'s `build_chart_yaml`
/// mounts) — see [chart-dependencies-doc] for the Helm 3 upstream axis
/// documentation.
///
/// The single source of truth every consumer that names the per-
/// Chart.yaml top-level dependency-list key reaches for:
///
/// - [`caixa-helm`][ch]'s `ChartYaml` struct's `dependencies` field
/// (the sole production serialize-side site the wire-key appears
/// at — Rust's default field-name-verbatim serde emission means
/// no `#[serde(rename = "…")]` attribute pins the key today; the
/// paired drift-detection pin at [`caixa-helm`]'s
/// `chart_yaml_serializes_dependencies_axis_under_lifted_helm_chart_key_dependencies`
/// round-trips a rendered `Chart.yaml` through
/// `serde_yaml::from_str::<serde_yaml::Value>` and asserts the
/// top-level `Mapping::get(HELM_CHART_KEY_DEPENDENCIES)` resolves —
/// closing the drift a future hostile refactor could otherwise
/// leave silent: a rename of the Rust field to `Vec<ChartDependency>
/// under a `deps:` / `chartDependencies:` name, or an accidental
/// `#[serde(rename_all = "camelCase")]` attribute on `ChartYaml`
/// that stays a no-op on the four identity-mapped top-level keys
/// today but silently activates on a future multi-word field
/// addition);
/// - every test-side navigator that inspects the serialized
/// [`caixa-helm`]-emitted `Chart.yaml` YAML mapping by the top-
/// level per-chart-dependency-list key.
///
/// A drift on this per-Chart.yaml top-level list-container axis-key
/// would silently rebrand the wire key — Helm's chart-schema parser
/// silently drops the dep list from the parsed chart-metadata shape,
/// `helm dependency build` finds no chart to vendor, and every
/// rendered `lareira-<nome>` chart's install fails with
/// `template: no template ... associated with template ...` far from
/// the drift site with no field naming the top-level-list-key-drift
/// root cause. The failure mode is byte-shape-symmetric with the peer
/// [`HELM_CHART_DEPENDENCY_KEY_NAME`] drift narrative (which closes on
/// the per-entry name axis one level down) — both close on the
/// `helm dependency build` / apply-time path.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5)
/// promotes the top-level list-container axis-key to a typed
/// substrate-side `&'static str` on the same trajectory the peer
/// [`HELM_CHART_KEY_TYPE`] / [`HELM_CHART_KEY_APP_VERSION`] /
/// [`HELM_CHART_KEY_API_VERSION`] top-level axis-key lifts (d29bc23,
/// cc44e4b) established — completes the parent+children canonical-pin
/// pair with the already-lifted per-`dependencies[]`-entry
/// sub-mapping tetrad. Where the child tetrad pins the byte-shape of
/// each per-dep entry's four sub-mapping keys (`name`, `version`,
/// `repository`, `alias`), this parent-axis lift pins the byte-shape
/// of the top-level list-container the tetrad mounts under, so the
/// full `(dependencies: → [name/version/repository/alias])`
/// per-Chart.yaml dependency-list schema surface lives at one
/// canonical `&'static str` per YAML axis-key. Same
/// "parent list-container + child sub-mapping tetrad" canonical-pin
/// discipline the peer [`SUPERVISOR_KEY_CHILDREN`] (parent) +
/// [`SUPERVISOR_CHILD_KEY_CAIXA`] / [`SUPERVISOR_CHILD_KEY_VERSAO`] /
/// [`SUPERVISOR_CHILD_KEY_RESTART`] (children) pair (40cc4e5, ef912df)
/// established on the sibling per-`:supervisor :children` axis, and the
/// peer [`M2_KEY_UPGRADE_FROM`] (parent) +
/// [`M2_UPGRADE_FROM_KEY_FROM`] / [`M2_UPGRADE_FROM_KEY_INSTRUCTIONS`]
/// (children) pair established on the sibling per-`:upgrade-from` axis.
///
/// [chart-yaml-desc]: https://helm.sh/docs/topics/charts/#the-chartyaml-file
/// [chart-dependencies-doc]: https://helm.sh/docs/topics/charts/#chart-dependencies
/// [ch]: ../../caixa_helm/index.html
pub const HELM_CHART_KEY_DEPENDENCIES: &str = "dependencies";
/// Canonical Helm 3 `Chart.yaml` per-`dependencies[]`-entry sub-mapping
/// YAML axis-key naming the per-dep chart-name field — the load-bearing
/// serde field-name at [`caixa-helm`][ch]'s `ChartDependency` struct's
/// `name` field. Byte-identical to the sibling K8s CR
/// [`KUBE_KEY_NAME`] axis-key by Helm's design decision to inherit the
/// K8s CR body-key vocabulary at every schema surface it consumes
/// (chart-metadata, per-CR install-payload, per-dep dependency-list);
/// the paired
/// [`tests::helm_chart_dependency_key_name_matches_kube_key_name`] pin
/// asserts the two byte-shapes coincide, so a future K8s-side rebrand
/// at [`KUBE_KEY_NAME`] that dropped the byte-identity would fail the
/// pin at substrate-build time rather than silently drop the per-dep
/// name lookup at `helm dependency build` time far from the drift site.
///
/// The chart-schema per-dep entry's `name:` value pins the exact
/// Helm-registry chart-name Helm's per-dep alias convention scopes the
/// per-dep values sub-block under when no `alias:` is set (see the
/// sibling [`HELM_CHART_DEPENDENCY_KEY_ALIAS`] docstring for the alias
/// axis) — every rendered `lareira-<nome>` chart's Chart.yaml
/// `dependencies[0].name:` binds to the same `&'static str` as its
/// values.yaml wrap key (see [`caixa-helm`][ch]'s
/// `values_yaml_wrap_key_matches_chart_dependency_name` pin on the
/// structural alignment). A drift on this per-dep sub-key (a future
/// refactor that renamed the `ChartDependency::name` Rust field to
/// `ChartDependency::nome`, or added a
/// `#[serde(rename_all = "camelCase")]` attribute that stays a no-op
/// on the four identity-mapped keys today but silently activates on a
/// future field addition) would rebrand the wire key silently — Helm's
/// per-dep dependency-router silently drops the dep from the parsed
/// chart-metadata (the substrate ships a Chart.yaml that lists no
/// `pleme-computeunit` dep, `helm dependency build` finds no chart to
/// vendor, and every rendered lareira-`<nome>` chart's install fails
/// with "template: no template ... associated with template ..." far
/// from the drift site). Peer to [`HELM_CHART_DEPENDENCY_KEY_VERSION`]
/// / [`HELM_CHART_DEPENDENCY_KEY_REPOSITORY`] /
/// [`HELM_CHART_DEPENDENCY_KEY_ALIAS`] on the sibling per-dep sub-key
/// axes — completes the per-`dependencies[]`-entry YAML axis-key
/// canonical-pin tetrad at the substrate. Same per-entry-sub-key
/// canonical-lift discipline the peer
/// [`SUPERVISOR_CHILD_KEY_CAIXA`] / [`SUPERVISOR_CHILD_KEY_VERSAO`] /
/// [`SUPERVISOR_CHILD_KEY_RESTART`] triad (ef912df) established on the
/// sibling per-`:children` sub-mapping surface, and the
/// [`ENTRADA_KEY_HOST`] / [`ENTRADA_KEY_PARA`] / [`ENTRADA_KEY_PATHS`]
/// / [`ENTRADA_KEY_PORT`] tetrad (a3d6162) established on the sibling
/// per-`:entrada` sub-mapping surface.
///
/// [ch]: ../../caixa_helm/index.html
pub const HELM_CHART_DEPENDENCY_KEY_NAME: &str = "name";
/// Canonical Helm 3 `Chart.yaml` per-`dependencies[]`-entry sub-mapping
/// YAML axis-key naming the per-dep chart-version-constraint field —
/// the load-bearing serde field-name at [`caixa-helm`][ch]'s
/// `ChartDependency` struct's `version` field. Distinct from the
/// sibling per-Chart.yaml top-level chart-own-SemVer axis-key
/// (`version:` at the top level, whose byte-shape coincides with this
/// per-dep sub-key at the wire — a coincidence the substrate-side
/// paired [`tests::helm_chart_dependency_key_version_pins_canonical_value`]
/// pin holds byte-verbatim). The chart-schema per-dep entry's
/// `version:` value pins the SemVer-range constraint Helm's per-dep
/// resolver matches against the target dep's Chart.yaml `version:`
/// scalar at `helm dependency build` / `helm dependency update` time.
/// A drift on this per-dep sub-key would surface as one of two silent
/// failure modes at chart-vendor time far from the drift site: Helm's
/// per-dep chart-schema parser silently drops the version-constraint
/// scalar from the parsed dep-entry (the per-dep resolver falls back
/// to the wildcard `*` shape and vendors whatever chart-version the
/// upstream registry currently advertises, silently promoting a chart
/// upgrade the operator never authored), or a subsequent
/// `#[serde(rename_all)]` addition rebrands the key to Helm's
/// unrecognized shape and the per-dep entry silently vanishes from the
/// parsed dep-list. Peer to [`HELM_CHART_DEPENDENCY_KEY_NAME`] /
/// [`HELM_CHART_DEPENDENCY_KEY_REPOSITORY`] /
/// [`HELM_CHART_DEPENDENCY_KEY_ALIAS`] on the sibling per-dep sub-key
/// axes — extends the per-entry-sub-key canonical-lift tetrad at the
/// substrate. See [`HELM_CHART_DEPENDENCY_KEY_NAME`] for the shared
/// per-entry-sub-mapping lift rationale.
///
/// [ch]: ../../caixa_helm/index.html
pub const HELM_CHART_DEPENDENCY_KEY_VERSION: &str = "version";
/// Canonical Helm 3 `Chart.yaml` per-`dependencies[]`-entry sub-mapping
/// YAML axis-key naming the per-dep chart-registry URL field — the
/// load-bearing serde field-name at [`caixa-helm`][ch]'s
/// `ChartDependency` struct's `repository` field. The chart-schema
/// per-dep entry's `repository:` value pins the Helm-registry URL
/// (`file://…`, `https://…`, `oci://…`) Helm's per-dep resolver
/// consults at `helm dependency build` time to fetch the per-dep
/// chart bytes. At the caixa-helm substrate the default value is the
/// canonical [`caixa_helm::DEFAULT_LIBRARY_REPO`] pointing at the
/// helmworks file:// path; the future per-edition library-chart
/// re-emission for the OCI registry (once `pleme-io/helmworks/charts`
/// lands as an OCI-registry-backed chart-source) reaches this axis
/// through a paired scalar-value lift on the per-dep repo axis. A
/// drift on this per-dep sub-key would surface as one of two silent
/// failure modes at chart-vendor time far from the drift site: Helm's
/// per-dep resolver silently drops the repository scalar from the
/// parsed dep-entry (the per-dep resolver falls back to the "no
/// repository set" shape and refuses to vendor the dep with
/// `no repository defined`), or the per-dep chart-schema parser
/// silently absorbs a rename drift via `#[serde(default)]`
/// fall-through at the struct-side and the per-dep repo axis lands
/// under Rust's `""` default — Helm rejects the empty URL at
/// `helm dependency build` time. Peer to
/// [`HELM_CHART_DEPENDENCY_KEY_NAME`] /
/// [`HELM_CHART_DEPENDENCY_KEY_VERSION`] /
/// [`HELM_CHART_DEPENDENCY_KEY_ALIAS`] on the sibling per-dep sub-key
/// axes. See [`HELM_CHART_DEPENDENCY_KEY_NAME`] for the shared
/// per-entry-sub-mapping lift rationale.
///
/// [ch]: ../../caixa_helm/index.html
pub const HELM_CHART_DEPENDENCY_KEY_REPOSITORY: &str = "repository";
/// Canonical Helm 3 `Chart.yaml` per-`dependencies[]`-entry sub-mapping
/// YAML axis-key naming the per-dep chart-alias override field — the
/// load-bearing serde field-name at [`caixa-helm`][ch]'s
/// `ChartDependency` struct's `alias` field. The chart-schema per-dep
/// entry's `alias:` value, when set, overrides the per-dep values
/// wrap-key (Helm's per-dep alias convention scopes the per-dep values
/// sub-block under `alias:` when set, and under the sibling
/// [`HELM_CHART_DEPENDENCY_KEY_NAME`] `name:` value otherwise); the
/// caixa-helm substrate today emits the axis as `None` at every
/// rendered `lareira-<nome>` chart's `dependencies[0].alias:` (the
/// `#[serde(default, skip_serializing_if = "Option::is_none")]`
/// attribute on the `alias` field elides the axis entirely from the
/// emitted YAML when unset), so the values wrap-key defaults to the
/// per-dep `name:` value — but the axis-key remains part of the
/// substrate-side chart-schema-per-dep-entry contract for the future
/// per-Aplicacao library chart's per-Servico per-dep aliasing
/// [`HELM_CHART_TYPE_LIBRARY`] docstring names as a trajectory item.
/// A drift on this per-dep sub-key (a future refactor that renamed
/// the `ChartDependency::alias` Rust field, or added a
/// `#[serde(rename_all = "camelCase")]` attribute that silently
/// activates on a future field addition) would rebrand the wire key
/// silently — Helm's per-dep alias-convention router would silently
/// drop the alias from the parsed dep-entry (the per-dep values wrap-
/// key falls back to the sibling `name:` value, and every per-cluster
/// per-Servico per-dep values override the operator authored under
/// the alias-key silently routes nowhere at `helm template` time). Peer
/// to [`HELM_CHART_DEPENDENCY_KEY_NAME`] /
/// [`HELM_CHART_DEPENDENCY_KEY_VERSION`] /
/// [`HELM_CHART_DEPENDENCY_KEY_REPOSITORY`] on the sibling per-dep
/// sub-key axes — completes the per-`dependencies[]`-entry YAML
/// axis-key canonical-pin tetrad. See [`HELM_CHART_DEPENDENCY_KEY_NAME`]
/// for the shared per-entry-sub-mapping lift rationale.
///
/// [ch]: ../../caixa_helm/index.html
pub const HELM_CHART_DEPENDENCY_KEY_ALIAS: &str = "alias";
/// Canonical Helm 3 per-chart-directory metadata-file filename every
/// rendered `lareira-<nome>` chart carries at its top-level directory —
/// the fixed filename Helm's chart-schema parser (`helm dependency
/// build`, `helm lint`, `helm template`, `helm install`) looks up by
/// name at the chart-directory root to locate the per-chart
/// [`HELM_CHART_API_VERSION`] + [`HELM_CHART_TYPE_APPLICATION`] +
/// name/version/dependencies scalars each `lareira-<nome>` chart
/// declares (see [chart-yaml-desc]). The single source of truth every
/// consumer that names the metadata file — the sole caixa-helm
/// production emit site the prior inline `"Chart.yaml"` literal sat at
/// ([`caixa-helm`][ch]'s [`render_chart_for_servico`][rcs] `ChartDir`
/// assembly's per-file `path` axis, one of the three canonical
/// `lareira-<nome>` chart-directory files the renderer emits as a
/// bundle) plus every test-side round-trip navigator that reaches into
/// the rendered `ChartDir` by the metadata filename (six sites across
/// [`caixa-helm`][ch]'s per-chart-metadata-field sweep tests +
/// [`ChartDir::write_to`] post-write existence pin) — reaches for the
/// same `&'static str` by construction.
///
/// Until this lift landed the filename `"Chart.yaml"` lived as seven
/// verbatim inline literals (one production `PathBuf::from("Chart.yaml")`
/// at the `ChartDir` files-vec construction site + six test-side
/// `PathBuf::from("Chart.yaml")` / `chart_root.join("Chart.yaml")` /
/// `names.contains(&"Chart.yaml".to_string())` fixture navigators).
/// A drift on the emit side (a `"chart.yaml"` / `"chart.YAML"` /
/// `"Chart.yml"` / `"chart.yaml.tmpl"` typo, or an accidental collapse
/// onto Helm 2's sibling per-chart-metadata-filename axis, or a
/// per-fork `Chartfile.yaml` rebrand any per-edition packaging
/// substrate might introduce) at any one site would surface as one of
/// two silent failure modes at chart-consumption time:
///
/// - Helm's chart-schema parser refuses to open the rendered chart-
/// directory as a chart at all — `helm lint` / `helm dependency
/// build` fails with "Error: Chart.yaml file is missing" far from
/// the emit-drift commit's source, and the per-Servico release
/// cycle drops with no field naming the metadata-filename-drift
/// root cause (the operator sees "the chart isn't being recognized"
/// with no canonical anchor to compare the rendered filename
/// against);
/// - the rendered chart's `ChartFile` collection lists a file at the
/// emit-side drifted name (e.g. `"chart.yaml"`) while the sibling
/// [`caixa-flux`][cf] `Kustomization` bundle-path emitter's per-
/// chart reference (a future per-cluster snapshot bundle that
/// re-lists the chart-dir contents by filename) continues to look
/// under the canonical `"Chart.yaml"` — the two-crate pair silently
/// goes out of sync, with the flux bundle's chart-directory
/// resolver returning `None` for the metadata file at cluster-side
/// `feira app deploy` time.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// filename to a typed substrate-side `&'static str` on the same
/// trajectory the peer [`HELM_CHART_API_VERSION`] /
/// [`HELM_CHART_TYPE_APPLICATION`] / [`DEFAULT_LIBRARY_NAME`] /
/// [`LAREIRA_CHART_NAME_PREFIX`] lifts established on the sibling
/// canonical-Helm-load-bearing-string axes — pivots the discipline
/// from the per-Chart.yaml top-level *body* axes (`apiVersion`,
/// `type`) onto the sibling per-chart-directory *filename* axis every
/// rendered chart directory carries as the fixed lookup name Helm's
/// chart-schema parser consults at chart-open time. Peer to the
/// canonical-Helm-chart-schema-axis lifts on the sibling per-Chart.yaml
/// body surfaces — completes the per-`lareira-<nome>`-chart-directory
/// `(filename, apiVersion, type)` canonical-scalar-axis re-export triple
/// every rendered chart declares at its top-level metadata file.
///
/// [chart-yaml-desc]: https://helm.sh/docs/topics/charts/#the-chartyaml-file
/// [ch]: ../../caixa_helm/index.html
/// [cf]: ../../caixa_flux/index.html
/// [rcs]: ../../caixa_helm/fn.render_chart_for_servico.html
pub const HELM_CHART_YAML_FILENAME: &str = "Chart.yaml";
/// Canonical Helm 3 per-chart-directory values-file filename every
/// rendered `lareira-<nome>` chart carries at its top-level directory —
/// the fixed filename Helm's chart-schema parser (`helm dependency
/// build`, `helm lint`, `helm template`, `helm install`) looks up by
/// name at the chart-directory root to locate the per-chart
/// [`DEFAULT_LIBRARY_NAME`]-wrapped values block that
/// [`HELM_VALUES_KEY_ENABLED`] toggles (see [values-yaml-desc]). The
/// single source of truth every consumer that names the values file —
/// the sole caixa-helm production emit site the prior inline
/// `"values.yaml"` literal sat at ([`caixa-helm`][ch]'s
/// [`render_chart_for_servico`][rcs] `ChartDir` assembly's per-file
/// `path` axis, the second of the three canonical `lareira-<nome>`
/// chart-directory files the renderer emits as a bundle, sibling to
/// the metadata-file [`HELM_CHART_YAML_FILENAME`] axis) plus every
/// test-side round-trip navigator that reaches into the rendered
/// `ChartDir` by the values filename (eleven sites across
/// [`caixa-helm`][ch]'s per-chart-values-field sweep tests +
/// [`ChartDir::write_to`] post-write existence pin) — reaches for the
/// same `&'static str` by construction.
///
/// Until this lift landed the filename `"values.yaml"` lived as twelve
/// verbatim inline literals (one production `PathBuf::from("values.yaml")`
/// at the `ChartDir` files-vec construction site + eleven test-side
/// `PathBuf::from("values.yaml")` / `chart_root.join("values.yaml")` /
/// `names.contains(&"values.yaml".to_string())` fixture navigators).
/// A drift on the emit side (a `"Values.yaml"` / `"values.YAML"` /
/// `"values.yml"` / `"values.yaml.tmpl"` typo, or an accidental collapse
/// onto Helm 2's sibling per-chart-values-filename axis, or a per-fork
/// `defaults.yaml` rebrand any per-edition packaging substrate might
/// introduce) at any one site would surface as one of two silent
/// failure modes at chart-consumption time:
///
/// - Helm's per-chart values-loader silently falls back to the empty
/// values block — `helm template` / `helm install` emits the
/// `pleme-computeunit` library chart under its admission-time
/// defaults (`enabled: false`, no per-`:limits` / `:behavior` /
/// `:upgrade-from` M2 overlay), the workload silently comes up
/// disabled or without any per-Servico M2 overlay applied, and
/// the per-Servico release cycle drops with no field naming the
/// values-filename-drift root cause (the operator sees "the
/// Servico isn't doing what we configured it to do" with no
/// canonical anchor to compare the rendered filename against);
/// - the rendered chart's `ChartFile` collection lists a file at the
/// emit-side drifted name (e.g. `"Values.yaml"`) while the sibling
/// [`caixa-flux`][cf] `Kustomization` bundle-path emitter's per-
/// chart reference (a future per-cluster snapshot bundle that
/// re-lists the chart-dir contents by filename to route per-cluster
/// values overlays through the canonical values file) continues to
/// look under the canonical `"values.yaml"` — the two-crate pair
/// silently goes out of sync, with the flux bundle's chart-directory
/// resolver returning `None` for the values file at cluster-side
/// `feira app deploy` time, and every per-cluster overlay the
/// bundle path threads through silently drops.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// filename to a typed substrate-side `&'static str` on the same
/// trajectory the peer [`HELM_CHART_YAML_FILENAME`] (c2c99b0) /
/// [`HELM_CHART_API_VERSION`] / [`HELM_CHART_TYPE_APPLICATION`] /
/// [`HELM_VALUES_KEY_ENABLED`] / [`DEFAULT_LIBRARY_NAME`] /
/// [`LAREIRA_CHART_NAME_PREFIX`] lifts established on the sibling
/// canonical-Helm-load-bearing-string axes — pivots the discipline
/// from the metadata-file half of the `(Chart.yaml, values.yaml)`
/// canonical per-chart-directory filename pair onto the values-file
/// half, completing the per-`lareira-<nome>`-chart-directory
/// canonical-scalar-axis re-export triple every rendered chart declares
/// as its `ChartDir::files` entries (`{Chart.yaml, values.yaml,
/// README.md}` — the two schema-load-bearing filenames now share the
/// same substrate-side single-source discipline).
///
/// [values-yaml-desc]: https://helm.sh/docs/chart_template_guide/values_files/
/// [ch]: ../../caixa_helm/index.html
/// [cf]: ../../caixa_flux/index.html
/// [rcs]: ../../caixa_helm/fn.render_chart_for_servico.html
pub const HELM_VALUES_YAML_FILENAME: &str = "values.yaml";
/// Canonical `lareira-<nome>` chart-directory human-facing readme filename
/// every rendered chart carries at its top-level directory — the fixed
/// filename the `caixa-helm` renderer emits alongside the two schema-load-
/// bearing [`HELM_CHART_YAML_FILENAME`] + [`HELM_VALUES_YAML_FILENAME`]
/// files as the third leg of the canonical `{Chart.yaml, values.yaml,
/// README.md}` per-`lareira-<nome>` chart-directory `ChartFile` triple the
/// peer [`HELM_CHART_YAML_FILENAME`] docstring explicitly acknowledges is
/// the one axis where the substrate-side single-source discipline had not
/// yet landed at the third file. The single source of truth every
/// consumer that names the readme file — the sole caixa-helm production
/// emit site the prior inline `"README.md"` literal sat at
/// ([`caixa-helm`][ch]'s [`render_chart_for_servico`][rcs] `ChartDir`
/// assembly's per-file `path` axis, the third of the three canonical
/// `lareira-<nome>` chart-directory files the renderer emits as a bundle,
/// sibling to the metadata-file [`HELM_CHART_YAML_FILENAME`] +
/// values-file [`HELM_VALUES_YAML_FILENAME`] axes) plus every test-side
/// round-trip navigator that reaches into the rendered `ChartDir` by the
/// readme filename (two sites: the `renders_three_files` files-vec-
/// membership pin + the `ChartDir::write_to` post-write existence pin) —
/// reaches for the same `&'static str` by construction.
///
/// Until this lift landed the filename `"README.md"` lived as three
/// verbatim inline literals (one production `ChartFile::new("README.md",
/// …)` at the `ChartDir` files-vec construction site + two test-side
/// `names.contains(&"README.md".to_string())` / `chart_root.join("README.md")`
/// fixture navigators). A drift on the emit side (a `"readme.md"` /
/// `"Readme.md"` / `"README"` / `"README.MD"` typo, or an accidental
/// collapse onto the sibling per-workspace `readme.txt` axis any
/// per-edition packaging substrate might introduce) at any one site would
/// surface as one of two silent failure modes at chart-consumption time:
///
/// - GitHub / Artifact Hub / any downstream per-chart README-surfacing
/// UI silently falls back to "no README available" — the chart lists
/// with no per-chart elevator pitch or install instructions far from
/// the drift commit's source, and the operator sees a chart in the
/// hub without the canonical `## Install` block the emitter wrote,
/// with no field naming the readme-filename-drift root cause;
/// - the rendered chart's `ChartFile` collection lists a file at the
/// emit-side drifted name (e.g. `"readme.md"`) while the sibling
/// [`caixa-flux`][cf] `Kustomization` bundle-path emitter's future
/// per-chart-directory resolver — a per-cluster snapshot bundle that
/// re-lists the chart-dir contents by filename to surface the
/// canonical README to per-cluster tooling — continues to look under
/// the canonical `"README.md"` — the two-crate pair silently goes out
/// of sync, with the flux bundle's chart-directory resolver returning
/// `None` for the readme file at cluster-side `feira app deploy`
/// time, and every downstream README-consuming path silently drops.
///
/// The PRIME DIRECTIVE duplication-budget rule (THEORY.md §I.3.5,
/// "every recurring shape becomes a generator before it becomes a
/// pattern; every pattern becomes a library before it becomes
/// duplicated code. The duplication budget is zero.") promotes the
/// filename to a typed substrate-side `&'static str` on the same
/// trajectory the peer [`HELM_CHART_YAML_FILENAME`] (c2c99b0) /
/// [`HELM_VALUES_YAML_FILENAME`] (9a980ba) lifts established on the
/// sibling canonical-Helm-per-chart-directory-filename axes — pivots the
/// discipline from the two schema-load-bearing filename halves onto the
/// human-facing readme-file half, completing the per-`lareira-<nome>`-
/// chart-directory `(Chart.yaml, values.yaml, README.md)` canonical-per-
/// chart-directory-filename-axis re-export triple every rendered chart
/// declares as its three `ChartDir::files` entries — the third file the
/// peer [`HELM_VALUES_YAML_FILENAME`] docstring explicitly names as the
/// missing leg of the triple at its "completing the per-`lareira-<nome>`-
/// chart-directory canonical-scalar-axis re-export triple every rendered
/// chart declares as its `ChartDir::files` entries (`{Chart.yaml,
/// values.yaml, README.md}` — the two schema-load-bearing filenames now
/// share the same substrate-side single-source discipline)" close.
///
/// [ch]: ../../caixa_helm/index.html
/// [cf]: ../../caixa_flux/index.html
/// [rcs]: ../../caixa_helm/fn.render_chart_for_servico.html
pub const HELM_CHART_README_FILENAME: &str = "README.md";
/// Canonical `pleme-computeunit` library-chart values-block enable-toggle
/// key — the `enabled: <bool>` axis every `lareira-<nome>` chart's values
/// block carries under its [`DEFAULT_LIBRARY_NAME`] wrap key, and every
/// [`caixa-flux`][cf]-rendered `HelmRelease` `spec.values.<library>.enabled`
/// per-cluster override targets. The single source of truth all four
/// downstream consumers reach for:
///
/// - [`caixa-helm`][ch]'s [`build_values_yaml`][bvy] inserts
/// `enabled: <opts.enabled_default>` under the values wrap key
/// (caixa-helm/src/lib.rs:389) — the rendered `values.yaml`'s
/// default-off toggle a cluster operator flips on per environment;
/// - [`caixa-flux`][cf]'s [`cluster_bundle`][cb] emits
/// `<library>: { enabled: true }` under the `HelmRelease`
/// `spec.values` block (caixa-flux/src/lib.rs:844) — the per-cluster
/// override the bundle path threads through so a Servico deployed via
/// the bundle path lands enabled at the target cluster;
/// - the peer test-fixture navigators in both crates
/// (`caixa-helm/src/lib.rs:566, 616` sweeping the default-off arm +
/// `caixa-flux/src/lib.rs:1889` sweeping the bundle-path enabled-true
/// override arm) resolve the same `&'static str` when parsing back the
/// rendered `values.yaml` / `helmrelease.yaml` to pin the round-trip;
/// - every future per-Servico renderer the absorption-roadmap
/// acknowledges (the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-member values fan-out, a future per-cluster
/// values overlay emitter, a future per-edition `<lib>-computeunit`
/// values-block schema fork) that reads or emits the same values-
/// block-toggle key.
///
/// Until this lift landed the value `"enabled"` lived as two production-
/// code call sites (caixa-helm's `build_values_yaml` insert +
/// caixa-flux's `cluster_bundle` `helmrelease.yaml` format-string) plus
/// three test-fixture-navigation sites (caixa-helm's default-off round-
/// trip + caixa-flux's bundle-path round-trip). A future rebrand of the
/// library-chart's per-values enable-toggle axis (the `pleme-computeunit`
/// library chart moving to a `chart.enabled` / `spec.enabled` scoping to
/// leave room for a sibling `component.enabled` sub-chart toggle, the
/// substrate forking the library chart to `<edition>-computeunit` with a
/// migrated toggle key, or Helm's own per-values-block convention drift)
/// without a coordinated edit on both consumers would silently emit a
/// chart whose default-off toggle lands in the values block under one key
/// while the cluster-side override lands under another — Helm's per-values
/// merge treats them as sibling scalars, the enable-toggle the library
/// chart's own template consults never sees the flip, and the workload
/// silently comes up with the library chart's admission-time defaults
/// (disabled, or the sibling schema fork's own default) instead of the
/// per-cluster override the operator set. The apply-time symptom (the
/// workload is registered but not running, or is running without the
/// per-cluster overlay) surfaces only as "the service isn't doing what we
/// configured it to do" far from the rebrand commit, with no field
/// naming the enable-toggle-drift root cause. Lifting the literal to
/// a shared constant closes the drift footgun structurally — both
/// production emit sites and every test-side round-trip navigator now
/// consult the same `&'static str`, so any rebrand reaches every consumer
/// by construction.
///
/// Same "the typed constant lives in one place" discipline the peer
/// [`DEFAULT_LIBRARY_NAME`] (41438dc) / [`HELM_CHART_API_VERSION`]
/// (7e4bdb8) / [`KUBE_KEY_SPEC`] lifts apply on the sibling canonical-
/// Helm-load-bearing-string / canonical-Helm-chart-schema-axis /
/// canonical-K8s-CR-body-axis surfaces — extends the discipline from
/// the Chart.yaml schema axes and the K8s CR body axes onto the Helm
/// values-block schema axis nested inside every `lareira-<nome>` chart
/// under its [`DEFAULT_LIBRARY_NAME`] wrap key.
///
/// [ch]: ../../caixa_helm/index.html
/// [cf]: ../../caixa_flux/index.html
/// [bvy]: ../../caixa_helm/fn.build_values_yaml.html
/// [cb]: ../../caixa_flux/fn.cluster_bundle.html
pub const HELM_VALUES_KEY_ENABLED: &str = "enabled";
/// Canonical Helm chart-name prefix for every per-Servico chart the
/// substrate emits — the `"lareira-"` segment of the well-known
/// `lareira-<nome>` shape every caixa Servico renderer prepends to a
/// caixa's `:nome` to derive its [`Chart.yaml` `name:`][chart-yaml] field,
/// its OCI artifact reference (`oci://<registry>/lareira-<nome>`), and
/// the resulting cluster-side `HelmRelease` `release_name`. The single
/// source of truth all three downstream Servico renderers consult —
/// [`caixa-helm`][cf]'s `render_chart_for_servico` chart-dir name
/// (caixa-helm/src/lib.rs:207), [`caixa-flux`][cm]'s `cluster_bundle`
/// `HelmRelease` `chart:` field (caixa-flux/src/lib.rs:329), and
/// [`caixa-tatara`][ct]'s `process_for_aplicacao` `release_name` +
/// `derive_chart_ref` OCI ref (caixa-tatara/src/lib.rs:124,182) — so a
/// future per-chart-name-prefix rebrand (e.g. moving to `forno-` once
/// `lareira-` outlives its scoping intent, or any segment-namespace
/// migration the chart-publishing pipeline requires) is a one-line edit
/// here, not a coordinated rewrite across every renderer crate's chart-
/// name-derivation site.
///
/// Until this lift landed all three renderers carried inline
/// `format!("lareira-{}", caixa.nome)` / `format!("lareira-{name}")` /
/// `format!("oci://{}/lareira-{}", registry, caixa.nome.as_str())`
/// expressions — three verbatim copies of the same substrate-wide
/// naming convention. The PRIME DIRECTIVE duplication budget of zero
/// (THEORY.md §I.3.5) lands the lift here at the third occurrence: a
/// future rebrand on any one site without a coordinated edit on the
/// others would have silently published a chart at one name, registered
/// its OCI ref at a second, and resolved the `HelmRelease` at a third —
/// the cluster's apply would surface as a `chart pull failed: image not
/// found` error far from the source rebrand commit, with no field
/// naming the prefix-drift root cause.
///
/// Lifting it to caixa-core's render-constants block alongside the peer
/// [`DEFAULT_NAMESPACE`] (a085b26) makes the chart-name-prefix axis
/// discipline structural: every renderer that derives a per-Servico
/// chart name consults [`lareira_chart_name`], and every future renderer
/// (the future per-cluster snapshot bundle emitter, the future M4
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's chart-ref slot,
/// the future caixa-otel collector chart name) inherits the same prefix
/// by construction, with no opportunity for per-renderer drift. Same
/// "the typed constant lives in one place" discipline the
/// [`PLEME_LABEL_PREFIX`] / [`DEFAULT_NAMESPACE`] / [`KUBE_KEY_API_VERSION`]
/// lifts apply on the peer shared-string axes.
///
/// [chart-yaml]: https://helm.sh/docs/topics/charts/#the-chartyaml-file
/// [cf]: ../../caixa_helm/index.html
/// [cm]: ../../caixa_flux/index.html
/// [ct]: ../../caixa_tatara/index.html
pub const LAREIRA_CHART_NAME_PREFIX: &str = "lareira-";
/// Derive the canonical per-Servico Helm chart name from a caixa's
/// `:nome` — the substrate-wide `lareira-<nome>` shape every
/// per-Servico renderer ([`caixa-helm`][cf]'s `render_chart_for_servico`
/// chart-dir name, [`caixa-flux`][cm]'s `cluster_bundle` `HelmRelease`
/// `chart:` field, [`caixa-tatara`][ct]'s `process_for_aplicacao`
/// `release_name`, and the `oci://<registry>/lareira-<nome>` OCI ref)
/// composes by prepending [`LAREIRA_CHART_NAME_PREFIX`].
///
/// Single source of truth for the prefix-application: every consumer
/// reaches for this helper rather than re-deriving the `format!(…)`
/// shape inline, so a future change to the prefix axis (the lift's
/// raison d'être) is one edit here, not a coordinated sweep across
/// every renderer.
///
/// The input `nome` is the caixa's typed `:nome` field, already
/// DNS-1123-label-validated at [`Caixa::validate_nome`] (6c992f8) —
/// every value reaching this helper is structurally a valid Helm
/// chart-name segment. The prepended prefix is a fixed lowercase ASCII
/// alphanumeric + hyphen string, so the concatenation is structurally a
/// valid Helm chart name by construction (Helm's chart-name accepted
/// set is the DNS-1123 label rule, and DNS-1123 labels concatenate with
/// the prefix-and-hyphen separator into valid DNS-1123 labels as long
/// as the joint length stays ≤ 63 bytes; the M4 admission webhook will
/// pin the joint-length invariant when it lands).
///
/// [cf]: ../../caixa_helm/index.html
/// [cm]: ../../caixa_flux/index.html
/// [ct]: ../../caixa_tatara/index.html
#[must_use]
pub fn lareira_chart_name(nome: &str) -> String {
format!("{LAREIRA_CHART_NAME_PREFIX}{nome}")
}
/// Canonical substrate-fixed Chart.yaml `keywords:` entries every
/// rendered `lareira-<nome>` Helm chart carries — the ordered
/// (`BTreeSet`-canonical, ascii-alphabetical) list of registry-search
/// tags `caixa-helm`'s `build_chart_yaml` unions in on top of the
/// caixa author's own `:etiquetas` before folding the joint set into a
/// `BTreeSet<String>` for the emitted `Chart.yaml`. Every entry —
/// `"caixa-servico"` (the substrate-wide per-`:kind Servico` marker
/// axis), `"lareira"` (the [`LAREIRA_CHART_NAME_PREFIX`] chart-family
/// tag), `"tatara-lisp"` (the tatara-lisp source-language marker), and
/// `"wasm"` (the runtime execution-format marker) — is a load-bearing
/// discovery axis for the Artifact Hub keyword-search index and the
/// future caixa-registry keyword axis, so a drift between the
/// production emit at `caixa-helm::build_chart_yaml` and the two
/// substrate-side positive-set sweep tests
/// ([`crate::manifest::tests::validate_etiquetas_accepts_canonical_shaped_forms`]
/// and this crate's own `chart_keyword_shape_accepts_canonical_forms`)
/// would silently cause every rendered chart to miss the search-index
/// axis the substrate-fixed tag encodes — a chart published without
/// the `"caixa-servico"` tag would silently drop off the
/// `helm search hub caixa-servico` results the substrate's chart
/// discovery pipeline promises. Two production-side call sites
/// (this crate's `is_chart_keyword_shape` docstring narrates the
/// four canonical tags verbatim + [`caixa-helm`][ch]'s `build_chart_yaml`
/// unions them into the emitted `keywords:` sequence) and two
/// test-side positive-sweep sites this array anchors under one source
/// of truth.
///
/// The array is `BTreeSet`-canonical-ordered (ascii-alphabetical: the
/// same order the emitted `Chart.yaml` `keywords:` sequence lists them
/// after `build_chart_yaml`'s intermediate `BTreeSet<String>` fold), so
/// a future substrate-fixed keyword addition (an `"opentelemetry"`
/// entry once the caixa-otel collector-pipeline chart lands, a
/// `"lunatic"` entry once the wasm-process-runtime marker lands, a
/// `"gen_server"` entry once the OTP-shape callback marker lands per
/// the [`crate::behavior`] surface) lands at one edit point rather
/// than a coordinated four-file sweep across the production emit
/// site, the two test-side sweeps, and this docstring. Same
/// "one canonical typed array lives in one place" discipline as
/// the peer [`crate::aplicacao::WIT_HTTP_SHAPE_PREFIXES`] /
/// [`crate::aplicacao::WIT_PUBSUB_SHAPE_PREFIXES`] /
/// [`crate::aplicacao::WIT_STORE_SHAPE_PREFIXES`] arm-shape-prefix
/// arrays apply on the sibling `:contratos :wit` dispatch-shape axis.
///
/// Every entry structurally satisfies [`is_chart_keyword_shape`] (the
/// substrate's per-`Chart.yaml` `keywords:` entry validation
/// predicate) — the substrate-side pin
/// `lareira_chart_keywords_each_entry_passes_is_chart_keyword_shape`
/// enforces the invariant so a future addition that happens to break
/// the shape rule (a leading digit, an uppercase letter, a byte over
/// the [`CHART_KEYWORD_MAX_LEN`] cap) fails at caixa-core build time
/// rather than surfacing at chart-lint time downstream.
///
/// [ch]: ../../caixa_helm/index.html
pub const LAREIRA_CHART_KEYWORDS: &[&str] = &["caixa-servico", "lareira", "tatara-lisp", "wasm"];
/// Canonical OCI URL scheme prefix — the `"oci://"` byte-string every
/// substrate-side renderer that composes an OCI artifact reference for a
/// Helm chart prepends. The Helm 3 OCI storage protocol (Helm 3.8+) and
/// the `FluxCD` `HelmRepository` `type: oci` source both key off this
/// literal — `helm pull` / `helm install` / `helm registry login` /
/// `FluxCD`'s source-controller all reject any other scheme on the OCI
/// path — so a byte-shape drift on this prefix silently splits the
/// substrate's published chart references from the cluster-side
/// resolvers that consume them at `helm registry` / `FluxCD` reconcile
/// time far from the source renderer.
///
/// The single source of truth every downstream renderer that composes
/// an `oci://<registry>/<chart>` reference reaches for —
/// [`caixa-tatara`][ct]'s `derive_chart_ref` OCI ref
/// (caixa-tatara/src/lib.rs:202), and every future OCI-ref emitter
/// (the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
/// `chart_ref` slot on the tatara `Process` intent, the future
/// per-cluster snapshot bundle's OCI chart references, the future
/// caixa-otel collector chart's OCI publish shape) inherits the prefix
/// through this const by construction. Same "one canonical scheme /
/// prefix / separator lives in one place" discipline the peer
/// [`LAREIRA_CHART_NAME_PREFIX`] (f7320d7), [`CONTRATO_EDGE_LABEL_SEPARATOR`]
/// (6d9b04e), [`PLEME_LABEL_PREFIX`] (b473c00 / 9d9813f) lifts apply
/// on the sibling canonical-load-bearing-substrate-string axes.
///
/// [ct]: ../../caixa_tatara/index.html
pub const OCI_SCHEME_PREFIX: &str = "oci://";
/// Compose the canonical OCI artifact reference for a per-Servico Helm
/// chart — the `oci://<registry>/lareira-<nome>` shape every renderer
/// that materializes a chart-publish target (or a cluster-side chart
/// resolver keyed off one) composes by prepending
/// [`OCI_SCHEME_PREFIX`], joining the caller-supplied registry, and
/// appending the per-Servico chart name derived through the canonical
/// [`lareira_chart_name`] helper.
///
/// Single source of truth for the two-axis composition: every consumer
/// reaches for this helper rather than re-deriving the
/// `format!("oci://{}/lareira-{}", …)` shape inline, so a future change
/// to either input axis (the [`OCI_SCHEME_PREFIX`] rebrand once Helm /
/// `FluxCD` introduce a new registry protocol, the
/// [`LAREIRA_CHART_NAME_PREFIX`] rebrand once `lareira-` outlives its
/// scoping intent) is one edit here, not a coordinated sweep across
/// every renderer crate's OCI-ref composition site.
///
/// The rendered reference is the substrate's contract with the
/// chart-publishing pipeline (`helm registry login` +
/// `helm push chart.tgz oci://<registry>/lareira-<nome>`), the
/// cluster-side `FluxCD` `HelmRelease` `chart:` field (which Flux's
/// source-controller resolves through the same OCI ref), and the
/// tatara `Process` CR's `intent.aplicacao.chart_ref` slot the
/// reconciler feeds into `helm install`. Every consumer keys off the
/// same byte-shape by construction.
///
/// [ct]: ../../caixa_tatara/index.html
#[must_use]
pub fn oci_chart_ref(registry: &str, nome: &str) -> String {
let chart = lareira_chart_name(nome);
format!("{OCI_SCHEME_PREFIX}{registry}/{chart}")
}
/// The `:nome`-side budget the [`lareira_chart_name`] composition
/// imposes on every caixa `:nome` reaching a renderer that derives a
/// `lareira-<nome>` artifact (`caixa-helm`'s `ChartDir.name` +
/// `Chart.yaml` `name:`, `caixa-flux`'s `cluster_bundle` `HelmRelease`
/// `chart:` slot, `caixa-tatara`'s `process_for_aplicacao`
/// `release_name` + `oci://<registry>/lareira-<nome>` chart ref).
///
/// The joint length of `lareira-` + `<nome>` must satisfy the K8s
/// DNS-1123 label cap ([`DNS_1123_LABEL_MAX_LEN`] = 63) every downstream
/// consumer enforces — Helm's `Chart.yaml::name` field (`helm lint`
/// rejects at chart-package time per the DNS-1123 rule), the
/// `HelmRelease`'s `release_name` field (the Helm operator's tracking
/// secret name is derived from `release_name` and is itself a DNS-1123
/// label), the rendered chart's K8s object `metadata.name` axes that
/// embed the chart name as a prefix. The arithmetic is therefore
/// `DNS_1123_LABEL_MAX_LEN - LAREIRA_CHART_NAME_PREFIX.len()` = 63 - 8
/// = 55 bytes the caixa's `:nome` may itself occupy.
///
/// Lifted to a `pub const` so a future change to either axis
/// ([`LAREIRA_CHART_NAME_PREFIX`] rebrand, [`DNS_1123_LABEL_MAX_LEN`]
/// shift if Helm/K8s ever relax the chart-name rule) re-derives the
/// budget mechanically — every per-axis call site
/// ([`is_lareira_chart_name_shape`] consults it, the
/// `Caixa::validate_nome_chart_name_budget` diagnostic names it
/// verbatim) inherits the new value with no coordinated edit.
pub const LAREIRA_CHART_NAME_NOME_MAX_LEN: usize =
DNS_1123_LABEL_MAX_LEN - LAREIRA_CHART_NAME_PREFIX.len();
/// Predicate: assert that `nome` produces a [`lareira_chart_name`]
/// output satisfying the K8s DNS-1123 label rule — the joint-length
/// invariant the canonical `lareira_chart_name` helper's doc comment
/// (f7320d7) defers to "the M4 admission webhook will pin … when it
/// lands". This predicate lands it at the manifest-validate layer
/// rather than waiting for the apiserver.
///
/// Returns the parser-shaped reason on rejection (without wrapping in
/// any error variant) — same call-site discipline as the peer
/// [`is_dns_1123_label`] predicate. Each per-axis caller wraps the
/// returned reason in its own typed `*Error::*Exceeded { … }` variant
/// (today: `Caixa::validate_nome_chart_name_budget` → the new
/// [`crate::ManifestError::NomeChartNameBudgetExceeded`] arm).
///
/// The predicate composes via [`lareira_chart_name`] + [`is_dns_1123_label`]
/// — the same two primitives every renderer consults — so a future
/// rebrand of either axis (`LAREIRA_CHART_NAME_PREFIX`,
/// `DNS_1123_LABEL_MAX_LEN`) re-derives the budget mechanically. A
/// `:nome` that already passes [`is_dns_1123_label`] (≤63 bytes,
/// boundary-anchored, `[a-z0-9-]` only) but whose prefixed chart name
/// exceeds the joint cap is what this gate catches — every byte the
/// inner DNS-1123 check accepts the prefixed form may still reject.
///
/// # Errors
///
/// Returns a parser-shaped reason naming the budget
/// ([`LAREIRA_CHART_NAME_NOME_MAX_LEN`]), the offending `:nome`
/// length, and the rendered chart name's length — so the diagnostic is
/// self-locating and the author can shorten in one edit.
pub fn is_lareira_chart_name_shape(nome: &str) -> Result<(), String> {
let chart_name = lareira_chart_name(nome);
if chart_name.len() > DNS_1123_LABEL_MAX_LEN {
return Err(format!(
"produces `{chart_name}` ({chart_len} bytes), which exceeds the \
DNS-1123 label max length of {DNS_1123_LABEL_MAX_LEN} bytes that \
Helm's `Chart.yaml::name` field and every downstream K8s artifact \
derived from the chart name enforce; the per-`:nome` budget is \
{budget} bytes (DNS-1123 cap minus the `{prefix}` prefix), shorten \
`:nome` to ≤ {budget} bytes",
chart_name = chart_name,
chart_len = chart_name.len(),
budget = LAREIRA_CHART_NAME_NOME_MAX_LEN,
prefix = LAREIRA_CHART_NAME_PREFIX,
));
}
Ok(())
}
/// Build the canonical Cilium `matchLabels` selector for a single
/// pleme-io program **scoped to its Aplicacao** — the safe default
/// every per-Aplicacao mesh renderer (caixa-mesh's
/// `cilium_network_policies` `fromEndpoints`, future per-edge policy
/// emission, Gateway API `backendRefs` filters) should use, since
/// two different Aplicacaos can carry programs with the same `:nome`
/// in the same cluster (e.g. two `cart` Servicos under different
/// applications) and a `LABEL_PROGRAM`-only selector would match
/// pods belonging to the wrong Aplicacao.
///
/// Returned as a [`BTreeMap`] keyed by `&'static str` so iteration is
/// alphabetical (THEORY.md §V.2.7 render determinism: the rendered
/// YAML's `matchLabels:` block appears in a deterministic order
/// independent of source-code declaration order). The two keys
/// alphabetize as [`LABEL_APLICACAO`] before [`LABEL_PROGRAM`], the
/// same order the renderer's `serde_yaml::Mapping` iteration will
/// preserve through to the rendered YAML.
#[must_use]
pub fn pleme_program_in_aplicacao_selector(
program: &str,
aplicacao: &str,
) -> BTreeMap<&'static str, String> {
let mut out = BTreeMap::new();
out.insert(LABEL_APLICACAO, aplicacao.to_string());
out.insert(LABEL_PROGRAM, program.to_string());
out
}
/// Build the canonical Cilium `matchLabels` selector for a single
/// pleme-io program **without** the Aplicacao constraint —
/// deliberately broader than [`pleme_program_in_aplicacao_selector`]
/// for the cases where matching a program across every Aplicacao that
/// hosts it is the *intent* (cluster-wide rate limits, breakglass
/// observability, the per-cluster operator identity scope).
///
/// **Prefer [`pleme_program_in_aplicacao_selector`]** for typed
/// per-Aplicacao mesh emission — using `pleme_program_selector` there
/// would let a policy unintentionally match a same-named program in
/// a different Aplicacao. Both helpers exist so the caller's *intent*
/// (Aplicacao-scoped vs. cluster-wide) is named at the call site,
/// not buried in inline label-key string literals.
#[must_use]
pub fn pleme_program_selector(program: &str) -> BTreeMap<&'static str, String> {
let mut out = BTreeMap::new();
out.insert(LABEL_PROGRAM, program.to_string());
out
}
/// Convert a typed string-valued mapping (e.g. one of the canonical
/// [`pleme_program_selector`] / [`pleme_program_in_aplicacao_selector`]
/// selectors, or any caller-built `BTreeMap<&'static str, String>`)
/// into a [`serde_yaml::Value::Mapping`] with `String → String` shape —
/// the surface every Cilium / Gateway / HTTPRoute / ComputeUnit
/// `matchLabels` / `metadata.labels` / `selector` field expects.
///
/// Iteration order is whatever the input iterator yields; pass a
/// [`BTreeMap`] for alphabetical determinism (THEORY.md §V.2.7 render
/// determinism: rendered YAML key order is independent of source-code
/// declaration order). The two pleme-io selector helpers above already
/// return `BTreeMap`s for exactly this reason.
///
/// Lifted from `caixa-mesh`'s prior `yaml_string_mapping` private
/// helper to make the same primitive available to every other
/// `caixa-<target>` renderer that needs to emit a string→string YAML
/// mapping (the future per-Aplicacao Gateway-API filter rules, the
/// caixa-otel resource-attribute emitter, the `app-operator`'s typed
/// CR materializer, the per-cluster CiliumClusterwideEnvoyConfig
/// renderer for `:politicas` defaults). Without the lift each new
/// renderer would re-inline the same five-line `for (k, v)` body and
/// inherit the same drift footguns.
#[must_use]
pub fn yaml_string_mapping<K, V, M>(m: M) -> serde_yaml::Value
where
M: IntoIterator<Item = (K, V)>,
K: Into<String>,
V: Into<String>,
{
let mut out = serde_yaml::Mapping::new();
for (k, v) in m {
out.insert_str_key(&k.into(), serde_yaml::Value::String(v.into()));
}
serde_yaml::Value::Mapping(out)
}
/// Wrap a typed string-valued label mapping in the canonical K8s
/// [`LabelSelector`][k8s-ls] shape — `{matchLabels: <string-string-map>}`
/// — and return it as a [`serde_yaml::Value::Mapping`] ready to drop
/// directly under any K8s field that takes a label selector
/// (Cilium `endpointSelector` / `fromEndpoints[].matchLabels`, Gateway
/// API `BackendRef` filters, ComputeUnit `selector`, Service
/// `spec.selector`, the future `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// `spec.selector`).
///
/// Lifted from two inline `serde_yaml::Mapping::new() +
/// insert(Value::String("matchLabels".into()), yaml_string_mapping(_))`
/// blocks in `caixa-mesh::cilium_network_policies` (the destination
/// `endpointSelector` and the source `fromEndpoints[0]` selector) so
/// the next renderer to land — the per-`:politicas`
/// `CiliumClusterwideEnvoyConfig` emitter (MESH-COMPOSITION §III.2 #3),
/// the `app-operator`'s typed `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer (§III.2 #5), the M4 cross-cluster fan-out's per-cluster
/// `Service`/`HTTPRoute backendRefs` selectors, the future `caixa-otel`
/// OpenTelemetry-Collector resource-selector pipeline — gets the
/// canonical K8s label-selector shape for free with one function call,
/// instead of re-inlining the same four-line `Mapping::new() +
/// insert("matchLabels", yaml_string_mapping(_))` boilerplate.
///
/// V0 emits the equality-based selector axis only (`matchLabels`); the
/// set-based axis ([`matchExpressions`][k8s-ls]) is deliberately out
/// of scope. A future `:contratos` axis whose selector needs
/// `matchExpressions` (e.g. `In`, `NotIn`, `Exists`, `DoesNotExist`
/// operators against a label key) is a future struct-shaped extension
/// of this helper —
/// e.g. a richer [`LabelSelector`] view type with `match_labels` +
/// `match_expressions` fields — not a per-renderer rewrite of
/// every selector emission site.
///
/// Iteration order is whatever the input iterator yields; pass a
/// [`BTreeMap`] for alphabetical determinism (THEORY.md §V.2.7 render
/// determinism: rendered YAML key order is independent of source-code
/// declaration order). The two pleme-io selector helpers
/// ([`pleme_program_selector`] / [`pleme_program_in_aplicacao_selector`])
/// already return `BTreeMap`s for exactly this reason, so a
/// `label_selector(pleme_program_in_aplicacao_selector(_, _))` call
/// renders deterministically end-to-end.
///
/// [k8s-ls]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#labelselector-v1-meta
#[must_use]
pub fn label_selector<K, V, M>(labels: M) -> serde_yaml::Value
where
M: IntoIterator<Item = (K, V)>,
K: Into<String>,
V: Into<String>,
{
let mut out = serde_yaml::Mapping::new();
out.insert_str_key(KUBE_KEY_MATCH_LABELS, yaml_string_mapping(labels));
serde_yaml::Value::Mapping(out)
}
/// Build the canonical K8s-resource skeleton — the
/// `apiVersion` + `kind` + `metadata.{name, namespace, labels?}`
/// block every cluster artifact emitted by every caixa-side renderer
/// carries — and return it as a fresh [`serde_yaml::Mapping`] the
/// caller adds its `spec:` (and any other top-level keys) to.
///
/// `labels` is inserted under `metadata.labels` only when non-empty.
/// An empty `labels` map leaves the labels key absent — the K8s API
/// server's interpretation of "no labels declared" is "labels key
/// missing", not `labels: {}` (which serializes differently in some
/// YAML libraries and is a sharp tool for label-based selectors that
/// match the empty set silently).
///
/// Iteration order under `metadata` is alphabetical (the inner
/// projection is a [`BTreeMap`] keyed by `&'static str`), so the
/// rendered YAML's `metadata:` block appears in
/// `labels?, name, namespace` order regardless of source-code
/// declaration order. Same render-determinism contract the M2 overlay
/// helper and the pleme-io selector helpers enshrine.
///
/// Lifted from three inline `serde_yaml::Mapping::new()` blocks in
/// `caixa-mesh` ([`cilium_network_policies`][cnp] CNP construction,
/// [`gateway_routes`][gw] Gateway construction, the same fn's
/// HTTPRoute construction) so the next renderer to land — the
/// per-`:politicas` `CiliumClusterwideEnvoyConfig` emitter, the
/// `app-operator`'s typed `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer, the M4 cross-cluster fan-out's per-cluster Kustomization
/// and HelmRelease emission, the future `caixa-otel`
/// OpenTelemetry-Collector pipeline emitter — gets the canonical
/// skeleton for free with one function call, instead of re-inlining
/// the same five-key insert() boilerplate.
///
/// [cnp]: https://docs.cilium.io/en/stable/security/policy/index.html
/// [gw]: https://gateway-api.sigs.k8s.io/
#[must_use]
pub fn kube_resource_skeleton(
api_version: &str,
kind: &str,
name: &str,
namespace: &str,
labels: BTreeMap<&'static str, String>,
) -> serde_yaml::Mapping {
let mut metadata: BTreeMap<&'static str, serde_yaml::Value> = BTreeMap::new();
metadata.insert(KUBE_KEY_NAME, serde_yaml::Value::String(name.to_string()));
metadata.insert(
KUBE_KEY_NAMESPACE,
serde_yaml::Value::String(namespace.to_string()),
);
if !labels.is_empty() {
metadata.insert(KUBE_KEY_LABELS, yaml_string_mapping(labels));
}
let mut metadata_map = serde_yaml::Mapping::new();
for (k, v) in metadata {
metadata_map.insert_str_key(k, v);
}
let mut out = serde_yaml::Mapping::new();
out.insert_string(KUBE_KEY_API_VERSION, api_version.to_string());
out.insert_string(KUBE_KEY_KIND, kind.to_string());
out.insert_mapping(KUBE_KEY_METADATA, metadata_map);
out
}
/// Build a single-field [`serde_yaml::Value::Mapping`] from a typed
/// `Option<T>` slot — `None` when the slot is unset, `Some(Mapping {
/// inner_key: f(t) })` otherwise.
///
/// The canonical shape every per-`:politicas` overlay across `caixa-mesh`
/// uses to wire a typed `MeshPolicy` axis through to its single-key
/// cluster artifact:
///
/// * `:politicas :timeout` → `timeouts: { request: <duration> }`
/// (Gateway API `HTTPRoute.spec.rules[].timeouts`, wired in 5f477a6)
/// * `:politicas :retries` → `retry: { attempts: <number> }`
/// (Gateway API `HTTPRoute.spec.rules[].retry`, wired in 23b7f00)
/// * `:politicas :mtls-required` → `authentication: { mode: <enum> }`
/// (Cilium `CiliumNetworkPolicy.spec.ingress[].authentication`,
/// wired in 878bf81)
///
/// Until this lift the three call sites each carried a verbatim copy
/// of the same six-line block — `let mut m = serde_yaml::Mapping::new();
/// m.insert(Value::String(<key>.into()), <value>); Value::Mapping(m)` —
/// wrapped in `spec.politicas.<axis>.map(|v| { … })`. Three-of-the-pattern
/// across one emit-site (and now structurally one-of-the-pattern in each
/// of the next two emit-sites the M3.x roadmap acknowledges: the
/// `:circuit-breaker` and `:rate-limit` axes' `CiliumClusterwideEnvoyConfig`
/// emitter, MESH-COMPOSITION §III.2 #3) overflows the duplication
/// budget; this helper is the lifted typed primitive.
///
/// The caller passes:
/// * the typed `Option<T>` slot,
/// * the inner YAML key the artifact's per-axis schema names
/// (`request` / `attempts` / `mode` for the three landed overlays;
/// `consecutiveErrors` / `requestsPerUnit` for the two roadmap
/// axes), and
/// * a closure converting the typed `T` into the inner field's
/// [`serde_yaml::Value`] (typically a `String` for canonical
/// duration / enum scalars or a `Number` for typed integer
/// attempt counts).
///
/// Returns `Some(Mapping)` when the slot is `Some`, `None` otherwise —
/// the caller's `if let Some(overlay) = … { rule.insert(<outer_key>,
/// overlay.clone()) }` guard for the *outer* key (`timeouts` / `retry`
/// / `authentication` — which the per-rule iteration applies to every
/// emitted item) becomes the single emission gate, and the *inner*
/// shape is built once by the closure.
///
/// Pairs with the `MeshPolicy::is_empty` predicate at the typed-axis
/// emptiness layer: `is_empty()` short-circuits the whole `:politicas`
/// block when every axis is `None`; this helper short-circuits the
/// per-axis overlay when its single axis is `None`. Two layers, same
/// "named-axis-with-None-means-skip-emit" contract THEORY.md §V.2.7
/// render determinism extends to.
#[must_use]
pub fn single_field_overlay<T, F>(
slot: Option<T>,
inner_key: &'static str,
f: F,
) -> Option<serde_yaml::Value>
where
F: FnOnce(T) -> serde_yaml::Value,
{
slot.map(|v| {
let mut m = serde_yaml::Mapping::new();
m.insert_str_key(inner_key, f(v));
serde_yaml::Value::Mapping(m)
})
}
/// Wrap a single [`serde_yaml::Mapping`] as the sole element of a
/// [`serde_yaml::Value::Sequence`], returning the ready-to-drop
/// singleton-mapping-sequence `Value`.
///
/// The canonical shape every K8s-CRD schema-list-shape-required field
/// with exactly one entry to emit lands the same
/// `Value::Sequence(vec![Value::Mapping(m)])` three-token block in
/// front of. Seven identical-shape call sites across
/// [`caixa-mesh`][mesh] collapse onto this helper:
///
/// * Cilium `CiliumNetworkPolicy.spec.ingress[].toPorts[].ports`
/// (one `port_entry` per typed edge, wrapped in the CRD's
/// required-list-shape `ports:` axis);
/// * Cilium `CiliumNetworkPolicy.spec.ingress[].toPorts[].rules.http`
/// (one `http_rule` per L7-introspection-capable
/// [`crate::WitTarget::Http`] contract, wrapped in the CRD's
/// required-list-shape `http:` axis);
/// * Cilium `CiliumNetworkPolicy.spec.ingress` (one `ingress_rule`
/// per policy — Cilium's CRD schema lists the per-policy ingress
/// ruleset even though V0 emits exactly one entry);
/// * Gateway API `Gateway.spec.listeners` (one `listener` per
/// Gateway — V0 emits the single HTTP-listener shape the sibling
/// [`GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME`] +
/// [`GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT`] consts pin);
/// * Gateway API `HTTPRoute.spec.rules[].matches` (one `match_entry`
/// per rule — V0 emits a single per-path prefix-match);
/// * Gateway API `HTTPRoute.spec.rules[].backendRefs` (one
/// `backend_ref` per rule — V0 emits a single-backend fan-in on
/// the `:entrada :para` destination Servico);
/// * Gateway API `HTTPRoute.spec.parentRefs` (one `parent_ref` per
/// route — every route attaches to exactly one Gateway).
///
/// Until this lift landed all seven call sites re-inlined the same
/// three-token boilerplate — `serde_yaml::` path re-quote,
/// `Value::Sequence(_)` promotion, `vec![serde_yaml::Value::Mapping(_)]`
/// singleton-list wrapping — around a one-token semantic payload (the
/// per-site `Mapping`). Lifting collapses the boilerplate into one
/// function call the caller reads as intent (`singleton_mapping_sequence
/// (<mapping>)` — "wrap this single mapping as the CRD-required list-
/// shape") rather than three hand-spelled positional artifacts. The
/// next renderer to land — the per-`:politicas`
/// `CiliumClusterwideEnvoyConfig` emitter (MESH-COMPOSITION §III.2 #3,
/// which drops singleton `resources:[]` / `listeners:[]` /
/// `virtualHosts:[]` blocks under its per-policy CR spec), the
/// `app-operator`'s typed `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer (§III.2 #5, whose `spec.selectors:[]` / `spec.gates:[]`
/// blocks list-shape a single per-Aplicacao entry), the M4 cross-
/// cluster fan-out's per-cluster `Service.spec.ports[]` /
/// `HTTPRoute.spec.rules[].backendRefs[]` emission, the future
/// `caixa-otel` OpenTelemetry-Collector `pipelines.traces.receivers[]`
/// / `pipelines.traces.exporters[]` singleton-list-shape emission —
/// gets the canonical CRD-list-shape-wrap for free with one function
/// call, instead of re-inlining the same three-token block. Peer with
/// the sibling render-side helpers on the [`serde_yaml::Value`]-
/// construction surface ([`yaml_string_mapping`], [`label_selector`],
/// [`kube_resource_skeleton`], [`single_field_overlay`], the sibling
/// [`MappingExt::insert_str_key`] primitive) — each closes a distinct
/// axis of the K8s-artifact-emit surface's "same shape, written N
/// times" duplication.
///
/// The helper takes an owned [`serde_yaml::Mapping`] (moving into the
/// wrapping `vec!` without a clone) because every call site has just
/// finished building the mapping locally and passes it by value to the
/// insert-under-outer-key step. A [`Value::Mapping`] wrapping of the
/// same mapping is one step further along the emit trajectory — the
/// helper closes the gap in one primitive.
///
/// The seven caixa-mesh call sites all followed the same
/// insert-under-outer-key step, so the composition
/// `mapping.insert_str_key(K, singleton_mapping_sequence(m))` is
/// itself lifted onto the sibling [`MappingExt::insert_singleton_mapping_sequence`]
/// method — every caixa-mesh site now reaches for the composed
/// method rather than nesting the two calls at the call site. This
/// standalone helper remains the semantic primitive for the
/// singleton-Mapping-list-shape `Value` (the trait method's impl
/// composes it internally), and stays public for future callers that
/// want the raw `Value::Sequence(vec![Value::Mapping(m)])` payload
/// without inserting it under a schema key.
///
/// [mesh]: https://docs.rs/caixa-mesh
#[must_use]
#[inline]
pub fn singleton_mapping_sequence(m: serde_yaml::Mapping) -> serde_yaml::Value {
serde_yaml::Value::Sequence(vec![serde_yaml::Value::Mapping(m)])
}
/// Iterator over the string-keyed entries of a [`serde_yaml::Value`]
/// that may or may not be a [`serde_yaml::Mapping`] — the canonical
/// shape both per-Servico renderers reach for when splicing the
/// upstream `ComputeUnit` YAML's `spec.*` fields into their emitted
/// output map.
///
/// Two identical-shape call sites collapse onto this helper — both
/// per-Servico renderers previously carried a five-line
/// `if let Value::Mapping(_) = spec { for (k, v) in _ { if let
/// Some(s) = k.as_str() { <dst>.insert(s, v.clone()) } } }` block:
///
/// * [`caixa_flux`][flux-programs]'s `programs_yaml_entry` splices
/// `computeunit_yaml.spec.*` into the emitted programs.yaml entry
/// ([`serde_yaml::Mapping`] destination, via
/// [`MappingExt::insert_str_key`]);
/// * [`caixa_helm`][helm-values]'s `build_values_yaml` splices the
/// same `computeunit_yaml.spec.*` into the values.yaml wrapped
/// block ([`std::collections::BTreeMap`]`<String, Value>`
/// destination, via `BTreeMap::insert`).
///
/// Both sites need the same walk (destructure as [`serde_yaml::Mapping`],
/// iterate its entries, keep only string-keyed pairs, hand the caller
/// each `(&str, &Value)` pair) but drop the values into different
/// destination map types, so the lift is at the iterator layer, not
/// the insert layer. The caller keeps its own insert idiom (
/// [`MappingExt::insert_str_key`] on a [`serde_yaml::Mapping`],
/// `BTreeMap::insert` on the [`BTreeMap`]-shaped values block, a
/// future renderer's own destination) but reaches through one lifted
/// walk with one contract on how non-string-keyed entries are handled:
/// silently dropped, matching the behavior both renderers implemented
/// inline via the `if let Some(s) = k.as_str()` filter.
///
/// Returns an empty iterator when `v` is not a
/// [`serde_yaml::Value::Mapping`] — the shape the prior `if let
/// Value::Mapping(_) = v` arm silently no-ops on (so a Null / String
/// / Sequence / Number / Bool `spec` field, itself schema-invalid
/// upstream but tolerated by the renderer, contributes zero entries
/// to the destination map instead of raising a per-shape error).
/// Non-string-keyed entries within a valid Mapping are silently
/// dropped — the same behavior the prior `if let Some(s) = k.as_str()`
/// arm carried, since `serde_yaml` permits arbitrary [`Value`] keys
/// (numeric, boolean, sub-mapping) that don't round-trip through the
/// downstream K8s YAML-key surface (which requires string keys).
///
/// The next per-Servico renderer to land — the future per-Servico
/// OCI packager whose emitted `Dockerfile` LABEL block spliced through
/// the same `computeunit_yaml.spec.*` string-key set, the M4
/// per-Servico `wasm.pleme.io/v1alpha1/ComputeUnit` CR materializer
/// whose emitted `spec.*` block splices the same set through onto the
/// typed [`kube::api::CustomResource`] view, the future `caixa-otel`
/// renderer's per-Servico OpenTelemetry-Collector resource-attribute
/// splice — gets the canonical string-key filter for free with one
/// method call, instead of re-inlining the same five-line
/// `if let Value::Mapping(_) = _` walk.
///
/// [flux-programs]: https://docs.rs/caixa-flux
/// [helm-values]: https://docs.rs/caixa-helm
pub fn string_keyed_entries(
v: &serde_yaml::Value,
) -> impl Iterator<Item = (&str, &serde_yaml::Value)> + '_ {
v.as_mapping()
.into_iter()
.flat_map(|m| m.iter())
.filter_map(|(k, v)| k.as_str().map(|s| (s, v)))
}
/// Read the string-scalar value at `metadata.<field>` on a K8s custom
/// resource YAML document, returning `None` when either the top-level
/// [`KUBE_KEY_METADATA`] block is absent (a defensively-tolerated
/// missing sub-mapping — the caller's own test-side `expect(...)` /
/// production-side `unwrap_or(...)` names the axis), the requested
/// `<field>` scalar is absent under it, or the scalar is present but
/// carries a non-string YAML type (a numeric, boolean, or nested
/// mapping — invalid K8s CR shape per the apiserver's OpenAPI schema
/// but tolerated here as `None` so the readback stays a total
/// function). The returned `&str` borrows into the input `Value` — the
/// caller decides whether to compare (`==`), clone (`.to_string()`),
/// or unwrap-then-panic. The three-hop navigation happens in one
/// method call the caller reads as intent
/// (`kube_metadata_str_field(<value>, <FIELD>)` — "read this
/// `metadata.<FIELD>` string-scalar off this K8s CR document") rather
/// than three hand-spelled positional artifacts (the
/// `get(KUBE_KEY_METADATA)` outer hop, the `and_then(|m| m.get(<FIELD>))`
/// inner hop, the `and_then(|n| n.as_str())` shape gate).
///
/// The canonical shape 8 call sites across `caixa-mesh` (six tests) +
/// `caixa-flux` (one production, one test) previously carried inline
/// as the three-line block
///
/// ```ignore
/// value
/// .get(KUBE_KEY_METADATA)
/// .and_then(|m| m.get(<FIELD>))
/// .and_then(|n| n.as_str())
/// ```
///
/// around a one-token semantic payload (the `<FIELD>` axis-key —
/// [`KUBE_KEY_NAME`] on the six `metadata.name` per-CNP filter /
/// per-CNP name-collect sites in caixa-mesh, [`KUBE_KEY_NAMESPACE`] on
/// the caixa-flux `programs_yaml_entry` production readback with
/// [`DEFAULT_NAMESPACE`] fallback + the caixa-flux `cluster_bundle`
/// test-side `kustomization.yaml` pin).
///
/// Sites lifted:
///
/// * caixa-mesh's `cilium_network_policies_emit_per_de_para_edges` —
/// the per-CNP names collect ([`KUBE_KEY_NAME`] readback across
/// every emitted policy);
/// * caixa-mesh's `cilium_fans_same_de_para_edges_into_one_policy` —
/// the per-CNP filter on the merged `cart-to-catalog` name
/// ([`KUBE_KEY_NAME`] readback + string equality);
/// * caixa-mesh's `cilium_pubsub_contracts_skip_l7_rules` — the
/// per-CNP find on the `cart-to-catalog` L7-emission witness
/// ([`KUBE_KEY_NAME`] readback + string equality);
/// * caixa-mesh's `cnp_l4_fallback_port_routes_through_lifted_
/// default_servico_port` — the per-CNP find on the
/// `payment-to-cart` L4-fallback witness ([`KUBE_KEY_NAME`]
/// readback + string equality);
/// * caixa-mesh's `cilium_mtls_required_contract_emits_
/// authentication_required` — the per-CNP find on the
/// `payment-to-cart` mTLS overlay witness ([`KUBE_KEY_NAME`]
/// readback + string equality);
/// * caixa-mesh's `cilium_mtls_not_required_omits_authentication` —
/// the per-CNP find on the `cart-to-payment` overlay-omit
/// witness ([`KUBE_KEY_NAME`] readback + string equality);
/// * caixa-flux's `programs_yaml_entry` — the production
/// `computeunit_yaml.metadata.namespace` readback with
/// [`DEFAULT_NAMESPACE`] fallback ([`KUBE_KEY_NAMESPACE`] readback
/// + `unwrap_or(DEFAULT_NAMESPACE)`);
/// * caixa-flux's `cluster_bundle_kustomization_metadata_namespace_
/// pins_flux_system_default` test-side pin — the emitted
/// `kustomization.yaml`'s `metadata.namespace` readback
/// ([`KUBE_KEY_NAMESPACE`] readback + string equality).
///
/// Peer to the sibling emit-side [`kube_resource_skeleton`] on the K8s
/// CR-document surface: [`kube_resource_skeleton`] closes the per-CR
/// `apiVersion` + `kind` + `metadata.{name,namespace,labels}` build
/// primitive on the emit side; this closes the reverse per-CR
/// `metadata.<field>` readback primitive on the readback side. The
/// two together bracket the K8s-CR-YAML round-trip axis so the same
/// [`KUBE_KEY_METADATA`] navigation string sits in exactly one place
/// on both the write and the read side, and a future
/// [`KUBE_KEY_METADATA`] rebrand — a schema-migration to a versioned
/// `metadataV2:` axis in a future K8s API-machinery revision, a
/// per-CRD-side rename to a wrapped `spec.metadata:` sub-mapping
/// under Server-Side-Apply's per-field ownership annotations —
/// reaches both sides through the same lifted constant + the same
/// lifted helper, not a coordinated rewrite across the emitter +
/// every per-CR readback path across every renderer.
///
/// The next renderer to land — the per-`:politicas`
/// `CiliumClusterwideEnvoyConfig` emitter (whose per-policy test
/// harness reaches through `metadata.name` to pin per-`(:de, :para)`
/// naming and through `metadata.namespace` to pin the
/// [`DEFAULT_NAMESPACE`] contract, MESH-COMPOSITION §III.2 #3), the
/// `app-operator`'s typed `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-CR readback (per-Aplicacao `metadata.name` /
/// `metadata.namespace` pins on the emitted `Aplicacao` CR, §III.2 #5),
/// the M4 cross-cluster fan-out's per-cluster `HelmRelease.metadata.
/// namespace` readback, the future `caixa-otel` per-Servico
/// OpenTelemetry-Collector CR's `metadata.name` pin — gets the
/// canonical `metadata.<field>` string readback for free with one
/// function call, instead of re-inlining the same three-hop chain.
///
/// The `field` axis stays parametric (rather than pinned to
/// [`KUBE_KEY_NAME`] or [`KUBE_KEY_NAMESPACE`] as two separate
/// helpers) so the same lift closes every string-scalar sub-field
/// under `metadata.*` a future K8s API-machinery revision surfaces
/// (`metadata.generateName` on Server-Side-Apply-authored CRs,
/// `metadata.resourceVersion` on optimistic-concurrency-controlled
/// updates, `metadata.uid` on cross-CR ownerReference bookkeeping) —
/// each new axis reaches for the same helper with a new
/// [`KUBE_KEY_<AXIS>`] const, not a fresh per-axis helper.
pub fn kube_metadata_str_field<'a>(value: &'a serde_yaml::Value, field: &str) -> Option<&'a str> {
// Post-lift body: composes on the substrate-primitive
// [`kube_metadata_field`] (450f1ff) scalar-Value accessor rather
// than re-inlining the two-hop `get(KUBE_KEY_METADATA).and_then(|m|
// m.get(field))` navigation. Structural mirror of the sibling
// [`kube_spec_str_field`] (c4fe21d) which composes on
// [`kube_spec_field`] the same way, and of [`kube_metadata_map_field`]
// (d03cc08) / [`kube_metadata_seq_field`] which fold their trailing
// shape-gate closure through the same composed scalar-Value primitive.
// Every K8s API-machinery rebrand on the outer `metadata:` navigation
// now reaches exactly one substrate helper on the sub-`metadata:`
// axis — no per-shape-gate-arity duplication of the two-hop walk.
kube_metadata_field(value, field).and_then(|v| v.as_str())
}
/// Read the string-scalar value at a top-level `<field>` axis-key on a
/// K8s custom resource YAML document — the root-level readback peer to
/// [`kube_metadata_str_field`] on the sub-`metadata:` axis. Returns
/// `None` when either the requested `<field>` scalar is absent
/// (defensively tolerated — the caller's own `unwrap_or(...)` /
/// `expect(...)` names the axis) or the scalar is present but carries a
/// non-string YAML type (a numeric, boolean, or nested mapping —
/// invalid K8s CR shape per the apiserver's OpenAPI schema but
/// tolerated here as `None` so the readback stays a total function).
/// The returned `&str` borrows into the input `Value` — the caller
/// decides whether to compare (`==`), clone (`.to_string()`), or
/// unwrap-then-panic. The two-hop navigation happens in one function
/// call the caller reads as intent (`kube_root_str_field(<value>,
/// <FIELD>)` — "read this K8s CR's top-level `<FIELD>` string-scalar")
/// rather than two hand-spelled positional artifacts (the
/// `get(<FIELD>)` outer hop, the `and_then(|n| n.as_str())` shape gate).
///
/// The canonical shape 32 call sites across `caixa-mesh` (24) +
/// `caixa-flux` (8) previously carried inline as the two-line block
///
/// ```ignore
/// value
/// .get(<FIELD>)
/// .and_then(|n| n.as_str())
/// ```
///
/// around a one-token semantic payload (the `<FIELD>` axis-key —
/// [`KUBE_KEY_KIND`] on 22 sites, [`KUBE_KEY_API_VERSION`] on 10
/// sites). Every routed caller keeps its downstream idiom
/// (`.unwrap()`, `.expect(...)`, `== Some(<KIND>)`, `assert_eq!(...,
/// Some(<API_VERSION>))`) unchanged — the lift closes the navigation
/// surface, not the per-site error-handling posture.
///
/// Sites lifted include:
///
/// * caixa-flux's `cluster_bundle_helmrelease_uses_lifted_flux_api_version`
/// + peer test-side pins on the emitted `helmrelease.yaml`,
/// `gitrepository.yaml`, `kustomization.yaml` per-document
/// top-level [`KUBE_KEY_API_VERSION`] axis;
/// * caixa-flux's per-document top-level [`KUBE_KEY_KIND`] axis pins
/// across the same `cluster_bundle` multi-file sequence;
/// * caixa-mesh's `docs.iter().find(|d| d.get(KUBE_KEY_KIND).
/// and_then(|k| k.as_str()) == Some(<KIND>))` per-CR filter over
/// the emitted `Gateway` + `HTTPRoute` multi-doc sequence — the 15
/// `gateway_routes` test-harness `find` sites plus the sibling
/// [`CILIUM_KIND_NETWORK_POLICY`] filter in
/// `cilium_authentication_mode_serialized_as_yaml_string`;
/// * caixa-mesh's per-CR top-level [`KUBE_KEY_API_VERSION`] +
/// [`KUBE_KEY_KIND`] discriminator-pair pins across
/// `cilium_network_policies_emit_per_de_para_edges` +
/// `gateway_routes_emit_gateway_and_httproute_per_aplicacao` +
/// sibling gateway/route pins.
///
/// Peer to sibling [`kube_metadata_str_field`] (6809867) on the K8s
/// CR-document readback surface: [`kube_metadata_str_field`] closes
/// the `metadata.<field>` string-scalar readback at the sub-`metadata:`
/// axis; this closes the root-level `<field>` string-scalar readback at
/// the top-level axis. The two together bracket the K8s-CR YAML
/// readback surface so every navigation into a rendered K8s CR
/// document — the top-level `(apiVersion, kind)` discriminator pair,
/// the sub-`metadata.(name, namespace)` identity pair — reaches
/// through one canonical lifted helper. A future K8s API-machinery
/// rebrand on either axis (a hypothetical `apiVersionV2:` scalar under
/// a wrapper CRD group's schema-migration, a Server-Side-Apply-driven
/// `metadata.name` rename under per-field ownership annotations)
/// reaches every consumer through one lifted helper, not a coordinated
/// rewrite across every renderer + every test-side per-CR readback
/// path.
///
/// The `field` axis stays parametric (rather than pinned to
/// [`KUBE_KEY_KIND`] or [`KUBE_KEY_API_VERSION`] as two separate
/// helpers) so the same lift closes every top-level string-scalar
/// axis a future K8s API-machinery revision surfaces (e.g. the
/// `caixa-otel` per-Servico OpenTelemetry-Collector CR's top-level
/// scalar pins, the future `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-CR discriminator readback in the app-operator,
/// MESH-COMPOSITION §III.2 #5) — each new axis reaches for the same
/// helper with a new [`KUBE_KEY_<AXIS>`] const, not a fresh per-axis
/// helper.
pub fn kube_root_str_field<'a>(value: &'a serde_yaml::Value, field: &str) -> Option<&'a str> {
// Post-lift body: composes on the substrate-primitive
// [`kube_root_field`] parametric root-axis scalar-Value accessor
// rather than re-inlining the two-hop `value.get(field)
// .and_then(|n| n.as_str())` chain. Structural mirror at the root
// axis of the way sibling [`kube_metadata_str_field`] (6809867)
// composes on [`kube_metadata_field`] (450f1ff) and
// [`kube_spec_str_field`] (c4fe21d) composes on
// [`kube_spec_field`] (23bd568) — each shape-gate arity peer folds
// its trailing `.as_str()` closure onto its axis's scalar-Value
// accessor primitive rather than re-walking the two-hop navigation
// on every axis.
kube_root_field(value, field).and_then(|v| v.as_str())
}
/// Predicate: does the K8s custom resource YAML document at `value`
/// declare its top-level `kind` discriminator axis as exactly `kind`?
///
/// Composes on top of [`kube_root_str_field`] (ae83f4e) — same two-hop
/// `.get(KUBE_KEY_KIND).and_then(as_str)` navigation — and closes the
/// "top-level kind-discriminator equality" predicate axis every
/// multi-doc mesh emission traversal reaches for to split the emitted
/// sequence by CRD-kind.
///
/// The canonical shape 15 test-side `.find(|d| kube_root_str_field(d,
/// KUBE_KEY_KIND) == Some(<KIND>))` + `.filter(|d| … == Some(<KIND>))`
/// call sites in `caixa-mesh` previously carried inline as the
/// three-token composition
///
/// ```ignore
/// kube_root_str_field(d, KUBE_KEY_KIND) == Some(<KIND>)
/// ```
///
/// around a one-token semantic payload (the `<KIND>` axis-value —
/// [`GATEWAY_API_KIND_GATEWAY`] on the per-Gateway filter sites,
/// [`GATEWAY_API_KIND_HTTP_ROUTE`] on the per-HTTPRoute filter sites,
/// [`CILIUM_KIND_NETWORK_POLICY`] on the sibling CNP filter site). The
/// lift collapses the three-token composition — the readback helper
/// call, the `== Some(...)` equality wrap, the discriminator-axis pin
/// on [`KUBE_KEY_KIND`] — onto one predicate function the caller
/// reads as intent (`kube_kind_is(d, <KIND>)` — "is this K8s CR
/// document of kind `<KIND>`") rather than as a three-hop
/// `readback → wrap → compare` chain.
///
/// The [`KUBE_KEY_KIND`] axis is pinned inside the helper (unlike the
/// parametric `field` axis of the underlying [`kube_root_str_field`])
/// because the "does this CR document match kind X" question is a
/// semantically-distinct discriminator predicate, not a generic
/// scalar-readback: the K8s CRD schema pins `kind` as the load-bearing
/// discriminator on every `CustomResource` across every group/version,
/// so this predicate lives one abstraction step above the generic
/// readback. Peer predicates for other top-level discriminators
/// (e.g. `kube_api_version_is` on a hypothetical multi-version
/// migration harness) land as sibling helpers with their own
/// pinned axis, not as re-parameterizations of this one.
///
/// Sites lifted:
///
/// * caixa-mesh's `gateway_routes` test-harness — 14
/// `docs.iter().find(|d| kube_root_str_field(d, KUBE_KEY_KIND) ==
/// Some(GATEWAY_API_KIND_{GATEWAY,HTTP_ROUTE}))` sites splitting
/// the multi-doc emission by `Gateway` vs `HTTPRoute` for per-CR
/// body-axis assertions;
/// * caixa-mesh's `cilium_authentication_mode_serialized_as_yaml_string`
/// — 1 `docs.iter().filter(|d| kube_root_str_field(d,
/// KUBE_KEY_KIND) == Some(CILIUM_KIND_NETWORK_POLICY))` filter
/// over the emitted CNP sequence.
///
/// Every future per-CRD-kind traversal (the per-`:politicas`
/// `CiliumClusterwideEnvoyConfig` emitter's per-CR filter,
/// MESH-COMPOSITION §III.2 #3; the `app-operator`'s
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-status
/// discriminator predicate, §III.2 #5; the M4 cross-cluster fan-out's
/// per-cluster `HelmRelease` vs `Kustomization` split by kind) reaches
/// the same helper by construction, with no `== Some(...)` inline
/// composition and no drift surface on the `kind` scalar-key axis.
///
/// Body now delegates through the sibling accessor peer [`kube_kind`]
/// as `kube_kind(value) == Some(kind)`, matching the sibling-axis
/// predicate shapes ([`kube_name_is`] `= kube_name(value) == Some(name)`,
/// [`kube_namespace_is`] `= kube_namespace(value) == Some(namespace)`) —
/// a future rebrand on the accessor's readback surface (a schema-migration
/// on the underlying `kind:` axis, a defensive short-circuit for
/// pre-materialization CR observations) reaches this predicate through
/// one composition-link, not a re-inlined `kube_root_str_field(_,
/// KUBE_KEY_KIND) == Some(...)` two-token composition.
pub fn kube_kind_is(value: &serde_yaml::Value, kind: &str) -> bool {
kube_kind(value) == Some(kind)
}
/// Locate the first K8s CR YAML document in `docs` whose top-level
/// `kind` discriminator axis equals `kind`.
///
/// Composes on top of [`kube_kind_is`] (2902d9d) — same one-hop
/// `.get(KUBE_KEY_KIND).and_then(as_str) == Some(kind)` predicate —
/// and closes the "find the one document of a given kind inside a
/// multi-doc mesh emission" navigator axis every per-Aplicacao
/// renderer's post-emit test harness reaches for to split the
/// emitted sequence by CRD-kind before probing a per-CR body-axis.
///
/// The canonical shape 14 test-side
///
/// ```ignore
/// docs.iter().find(|d| kube_kind_is(d, <KIND>))
/// ```
///
/// call sites in [`caixa-mesh`][mesh]'s `gateway_routes` +
/// `cilium_network_policies` test harnesses previously threaded the
/// three-token `.iter().find(closure)` combinator chain around a
/// one-token semantic payload (the `<KIND>` axis-value —
/// [`GATEWAY_API_KIND_GATEWAY`] on the per-Gateway navigator sites,
/// [`GATEWAY_API_KIND_HTTP_ROUTE`] on the per-HTTPRoute navigator
/// sites). The lift collapses the three-token chain — the `.iter()`
/// receiver-widen, the `.find(closure)` combinator, the inline
/// closure wrap around [`kube_kind_is`] — onto one navigator
/// function the caller reads as intent (`find_by_kind(&docs,
/// <KIND>)` — "give me the K8s CR document of kind `<KIND>`")
/// rather than as a receiver-widen → combinator → predicate chain.
///
/// Composition-symmetric to [`kube_kind_is`]: the lifted predicate
/// answers "does *this* one document match kind `<KIND>`?", the
/// lifted navigator answers "find the one document of kind
/// `<KIND>` in *this list*?". Same axis, different arity — the two
/// call shapes emit-side test harnesses reach for when splitting
/// multi-doc CR emissions by top-level kind.
///
/// Every future per-CRD-kind multi-doc-navigator site (the
/// per-`:politicas` `CiliumClusterwideEnvoyConfig` emitter's post-
/// emit test harness, MESH-COMPOSITION §III.2 #3; the
/// `app-operator`'s `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-status doc-navigator, §III.2 #5; the M4
/// cross-cluster fan-out's per-cluster multi-doc split by kind)
/// reaches the same helper by construction, with no inline
/// `.iter().find(closure)` combinator chain and no drift surface
/// on the receiver-widen or combinator axes.
///
/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
#[must_use]
pub fn find_by_kind<'a>(
docs: &'a [serde_yaml::Value],
kind: &str,
) -> Option<&'a serde_yaml::Value> {
docs.iter().find(|d| kube_kind_is(d, kind))
}
/// Read the top-level `kind:` string-scalar CRD-discriminator axis of a
/// K8s custom resource YAML document as `Option<&str>` — the pinned peer
/// on the CRD-discriminator axis to the parametric [`kube_root_str_field`]
/// on the top-level `<field>` sub-axis surface, and the accessor-arity
/// peer that closes the three-arity `(accessor / predicate / navigator)`
/// closure on the `kind:` discriminator axis: [`kube_kind`] reads,
/// [`kube_kind_is`] tests, [`find_by_kind`] locates — the structural
/// mirror of the same closure the sibling `metadata.name` identity axis
/// (accessor [`kube_name`] c9cdecb, predicate [`kube_name_is`] 092965d,
/// navigator [`find_by_name`] 092965d) and the sibling
/// `metadata.namespace` namespace-scoping axis (accessor
/// [`kube_namespace`] e18297b, predicate [`kube_namespace_is`] 9f4a600,
/// navigator [`find_by_namespace`] 9f4a600) already close on the two
/// sub-`metadata.*` coordinates.
///
/// Returns `None` when either the top-level `kind:` scalar is absent or
/// the `kind:` scalar carries a non-string YAML type — the same two-way
/// vacuous-`None` short-circuit the parent [`kube_root_str_field`] closes
/// on the underlying one-hop navigation.
///
/// The [`KUBE_KEY_KIND`] axis is pinned inside the helper (unlike the
/// parametric `field` axis of the underlying [`kube_root_str_field`])
/// because the K8s CRD schema pins `kind` as the load-bearing per-CR
/// discriminator on every `CustomResource` across every group/version
/// (paired with `apiVersion` for CRD-registration disambiguation) — the
/// same load-bearing status the sibling `metadata.name` +
/// `metadata.namespace` coordinates carry on the sub-`metadata:` axis
/// pair that already lifted an accessor peer under the same discipline.
/// Every readback consumer downstream (`.unwrap()`, `.expect(...)`,
/// `== Some(...)` equality wraps, `.to_string()` clone) drives off the
/// same pinned return; a hypothetical future K8s API-machinery rebrand on
/// the `kind:` axis (a schema-migration to a wrapped `kindV2:` scalar
/// under a per-group versioning axis, a Server-Side-Apply-driven
/// per-field-ownership migration under an aliased `resource:` scalar)
/// reaches every caller through one lift, not a coordinated rewrite
/// across every per-CR discriminator readback site.
///
/// The canonical shape 7 emit-side test-harness readback sites across
/// [`caixa-flux`][flux] (3) + [`caixa-mesh`][mesh] (4) previously
/// carried inline as the two-token composition
///
/// ```ignore
/// kube_root_str_field(<value>, KUBE_KEY_KIND)
/// ```
///
/// around a one-token semantic payload (the readback intent — "what
/// kind did the emitter write into this CR?"). The lift collapses the
/// two-token composition — the parametric readback helper, the pinned
/// discriminator-axis scalar-key argument — onto one accessor the
/// caller reads as intent (`kube_kind(<value>)` — "what is this K8s CR
/// document's top-level `kind:`?") rather than a `readback → axis-pin`
/// two-arg call. Peer predicates for other top-level discriminators
/// (a hypothetical `kube_api_version` accessor on a multi-version
/// migration harness, a `kube_group` accessor for CRD-group filtering)
/// land as sibling helpers with their own pinned axis, not as
/// re-parameterizations of this one.
///
/// Structural peer to sibling [`kube_name`] (c9cdecb) / [`kube_namespace`]
/// (e18297b) on the two sub-`metadata.*` coordinates: [`kube_name`]
/// closes the accessor arity on the identity axis; [`kube_namespace`]
/// closes it on the namespace-scoping axis; [`kube_kind`] closes it on
/// the top-level CRD-discriminator axis. Same accessor-arity shape,
/// different pinned scalar-key on a different navigation depth (root
/// vs sub-`metadata:`) — together the three accessors bracket the
/// K8s-CR YAML readback surface every renderer + every test-side
/// per-CR readback path reaches through, closing the three-arity
/// `(accessor / predicate / navigator)` structural closure on each of
/// the three canonical per-CR coordinates the K8s API-machinery pins
/// as load-bearing per-CR axes.
///
/// Sites lifted:
///
/// * caixa-flux's three
/// `cluster_bundle_{gitrepository,helmrelease,kustomization}_uses_lifted_flux_kind_<crd>`
/// tests — each per-emitted-file
/// `kube_root_str_field(&parsed, KUBE_KEY_KIND) == Some(FLUX_KIND_<CRD>)`
/// lifted-uses pin on the per-Flux-CR bundle-path emission
/// (`gitrepository.yaml`, `helmrelease.yaml`, `kustomization.yaml`
/// — the three Flux v2 controller-triplet CRD kinds);
/// * caixa-mesh's per-CNP top-level `kind:` readback loop in the
/// two test bodies `cilium_network_policies_use_lifted_cilium_kind_network_policy`
/// and `cilium_policy_carries_canonical_kube_skeleton` — each
/// `for p in &policies { assert_eq!(kube_root_str_field(p,
/// KUBE_KEY_KIND), Some(CILIUM_KIND_NETWORK_POLICY)); }` loop
/// over the multi-doc CNP emission;
/// * caixa-mesh's per-Gateway / per-HTTPRoute top-level `kind:`
/// readback across the two test bodies
/// `gateway_routes_gateway_uses_lifted_gateway_api_kind_gateway`
/// and `gateway_routes_httproute_uses_lifted_gateway_api_kind_http_route` —
/// each `find_by_kind(&docs, <KIND>) → kube_root_str_field(_,
/// KUBE_KEY_KIND) == Some(caixa_core::GATEWAY_API_KIND_<CRD>)`
/// chain over the paired-Gateway/HTTPRoute emission.
///
/// Every future per-CR `kind:` readback (the future per-`:politicas`
/// `CiliumClusterwideEnvoyConfig` emitter's per-CR discriminator pin,
/// MESH-COMPOSITION §III.2 #3; the future `app-operator`'s
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-status
/// discriminator readback, §III.2 #5; the future M4 cross-cluster
/// fan-out's per-cluster `HelmRelease` vs `Kustomization` split by
/// `kind:`) reaches the same pinned accessor by construction, with no
/// axis-key argument drift and no re-inlined
/// `kube_root_str_field(_, KUBE_KEY_KIND)` two-token composition.
///
/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
/// [flux]: https://github.com/pleme-io/caixa/tree/main/caixa-flux
#[must_use]
pub fn kube_kind(value: &serde_yaml::Value) -> Option<&str> {
kube_root_str_field(value, KUBE_KEY_KIND)
}
/// Read the top-level `apiVersion:` string-scalar CRD-group/version axis
/// of a K8s custom resource YAML document as `Option<&str>` — the pinned
/// peer on the CRD-group/version axis to the parametric
/// [`kube_root_str_field`] on the top-level `<field>` sub-axis surface,
/// and the accessor-arity peer that closes the two-axis top-level
/// `(apiVersion, kind)` discriminator-pair the K8s API-machinery pins as
/// the load-bearing per-CR-registration coordinates. [`kube_kind`]
/// (89a49a4) closes the accessor arity on the top-level `kind:` half of
/// the discriminator pair; [`kube_api_version`] closes it on the
/// top-level `apiVersion:` half — together the two accessors bracket
/// the CRD-registration coordinate pair every K8s CR readback at
/// controller / API-server admission time keys off. Structural mirror
/// of the sibling closure the sub-`metadata.{name, namespace}`
/// coordinate pair already carries at accessor arity ([`kube_name`]
/// c9cdecb + [`kube_namespace`] e18297b).
///
/// Returns `None` when either the top-level `apiVersion:` scalar is
/// absent or the `apiVersion:` scalar carries a non-string YAML type —
/// the same two-way vacuous-`None` short-circuit the parent
/// [`kube_root_str_field`] closes on the underlying one-hop navigation.
///
/// The [`KUBE_KEY_API_VERSION`] axis is pinned inside the helper
/// (unlike the parametric `field` axis of the underlying
/// [`kube_root_str_field`]) because the K8s CRD registration schema
/// pins `apiVersion` as the load-bearing per-CR group/version
/// discriminator on every `CustomResource` across every group
/// (paired with `kind` for CRD-registration disambiguation) — the
/// same load-bearing status the sibling `kind:` discriminator carries
/// on the sibling top-level scalar-axis that already lifted an
/// accessor peer under the same discipline. Every readback consumer
/// downstream (`.unwrap()`, `.expect(...)`, `== Some(...)` equality
/// wraps, `.to_string()` clone) drives off the same pinned return;
/// a hypothetical future K8s API-machinery rebrand on the
/// `apiVersion:` axis (a hypothetical Server-Side-Apply-driven
/// per-field-ownership migration under an aliased
/// `group/version` scalar pair, a schema-migration to a wrapped
/// `apiVersionV2:` scalar under a CRD group's per-conformance
/// evolution axis) reaches every caller through one lift, not a
/// coordinated rewrite across every per-CR CRD-registration
/// readback site.
///
/// The canonical shape 10 emit-side test-harness readback sites across
/// [`caixa-flux`][flux] (4) + [`caixa-mesh`][mesh] (6) previously
/// carried inline as the two-token composition
///
/// ```ignore
/// kube_root_str_field(<value>, KUBE_KEY_API_VERSION)
/// ```
///
/// around a one-token semantic payload (the readback intent — "what
/// apiVersion did the emitter write into this CR?"). The lift collapses
/// the two-token composition — the parametric readback helper, the
/// pinned CRD-group/version-axis scalar-key argument — onto one
/// accessor the caller reads as intent (`kube_api_version(<value>)` —
/// "what is this K8s CR document's top-level `apiVersion:`?") rather
/// than a `readback → axis-pin` two-arg call. Peer accessors for other
/// top-level discriminators (a hypothetical `kube_group` accessor for
/// CRD-group filtering on the pre-`/`-slash prefix of the same
/// `apiVersion:` scalar, a `kube_version` accessor for the
/// post-`/`-slash version suffix on a multi-version migration harness)
/// land as sibling helpers with their own pinned axis, not as
/// re-parameterizations of this one.
///
/// Structural peer to sibling [`kube_kind`] (89a49a4) on the sibling
/// top-level CRD-discriminator half of the same canonical `(apiVersion,
/// kind)` coordinate pair: [`kube_kind`] closes the accessor arity on
/// the `kind:` half; [`kube_api_version`] closes it on the
/// `apiVersion:` half. Same accessor-arity shape, different pinned
/// scalar-key on the same navigation depth (root) — together the two
/// accessors bracket the top-level K8s-CR CRD-registration coordinate
/// pair every renderer + every test-side per-CR readback path reaches
/// through, closing the accessor-arity peer-set on the same load-
/// bearing per-CR discriminator pair the K8s API-machinery threads
/// through every controller / API-server admission decision.
///
/// Sites lifted:
///
/// * caixa-flux's per-emitted-file top-level `apiVersion:` readback
/// across the 4 emit-side pins on the `cluster_bundle`
/// multi-file sequence —
/// `cluster_bundle_helmrelease_uses_lifted_flux_api_version`
/// (`helmrelease.yaml`),
/// `cluster_bundle_kustomization_health_check_uses_lifted_flux_api_version`
/// (per-entry `kustomization.yaml` `spec.healthChecks[].apiVersion`
/// loop),
/// `cluster_bundle_gitrepository_uses_lifted_flux_api_version`
/// (`gitrepository.yaml`),
/// `cluster_bundle_kustomization_uses_lifted_flux_api_version`
/// (`kustomization.yaml`) — each per-file
/// `kube_root_str_field(&parsed, KUBE_KEY_API_VERSION) ==
/// Some(FLUX_<CRD>_API_VERSION)` lifted-uses pin on the Flux v2
/// controller-triplet CRD-group/version axis;
/// * caixa-mesh's per-CNP top-level `apiVersion:` readback loop in
/// the two test bodies
/// `cilium_network_policies_use_lifted_cilium_api_version` +
/// `cilium_policy_carries_canonical_kube_skeleton` — each
/// `for p in &policies { assert_eq!(kube_root_str_field(p,
/// KUBE_KEY_API_VERSION), Some(CILIUM_API_VERSION)); }` loop
/// over the multi-doc CNP emission;
/// * caixa-mesh's per-Gateway / per-HTTPRoute top-level `apiVersion:`
/// readback across the four test bodies
/// `gateway_carries_canonical_kube_skeleton_without_labels`
/// (`Gateway`),
/// `httproute_carries_canonical_kube_skeleton_without_labels`
/// (`HTTPRoute`),
/// `gateway_routes_gateway_uses_lifted_gateway_api_api_version`
/// (`Gateway`),
/// `gateway_routes_httproute_uses_lifted_gateway_api_api_version`
/// (`HTTPRoute`) — each `find_by_kind(&docs, <KIND>) →
/// kube_root_str_field(_, KUBE_KEY_API_VERSION) ==
/// Some(caixa_core::GATEWAY_API_API_VERSION)` chain over the
/// paired-Gateway/HTTPRoute emission.
///
/// Every future per-CR `apiVersion:` readback (the future
/// per-`:politicas` `CiliumClusterwideEnvoyConfig` emitter's per-CR
/// CRD-group/version pin, MESH-COMPOSITION §III.2 #3; the future
/// `app-operator`'s `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's per-status CRD-group/version readback, §III.2 #5;
/// the future M4 cross-cluster fan-out's per-cluster Flux-triplet
/// CRD-group/version pin across the `.toolkit.fluxcd.io` root)
/// reaches the same pinned accessor by construction, with no
/// axis-key argument drift and no re-inlined
/// `kube_root_str_field(_, KUBE_KEY_API_VERSION)` two-token
/// composition.
///
/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
/// [flux]: https://github.com/pleme-io/caixa/tree/main/caixa-flux
#[must_use]
pub fn kube_api_version(value: &serde_yaml::Value) -> Option<&str> {
kube_root_str_field(value, KUBE_KEY_API_VERSION)
}
/// Predicate: does the K8s custom resource YAML document at `value`
/// declare its top-level `apiVersion:` CRD-group/version axis as
/// exactly `api_version`? The pinned predicate peer of the
/// [`kube_api_version`] (cf97d0a) accessor on the CRD-group/version
/// half of the canonical top-level `(apiVersion, kind)` per-CR-
/// registration coordinate pair, and the structural mirror on the
/// CRD-group/version axis of the [`kube_kind_is`] (2902d9d) predicate
/// on the peer CRD-kind half. Composes as `kube_api_version(value) ==
/// Some(api_version)` — same one-hop readback + equality-wrap shape
/// the sibling predicate carries on the `kind:` half, differing only
/// in which of the two canonical top-level CRD-registration
/// coordinates it pins.
///
/// The [`KUBE_KEY_API_VERSION`] axis is pinned inside the helper
/// (unlike the parametric `field` axis of the underlying
/// [`kube_root_str_field`]) because the "does this CR document
/// declare CRD-group/version X" question is a semantically-distinct
/// CRD-group/version predicate, not a generic scalar-readback: the
/// K8s API-machinery pins `apiVersion` as the load-bearing per-CR
/// CRD-group/version discriminator on every `CustomResource` across
/// every group (paired with `kind` for CRD-registration
/// disambiguation), so this predicate lives one abstraction step
/// above the generic readback. Peer predicates for other top-level
/// discriminators (a hypothetical `kube_group_is` on the pre-`/`-
/// slash CRD-group prefix of the same `apiVersion:` scalar, a
/// `kube_version_is` on the post-`/`-slash version suffix on a
/// multi-version migration harness) land as sibling helpers with
/// their own pinned axis, not as re-parameterizations of this one.
///
/// Structural peer to [`kube_kind_is`] (2902d9d) on the sibling
/// top-level `kind:` half of the same canonical `(apiVersion, kind)`
/// coordinate pair: [`kube_kind_is`] answers "does this document
/// match CRD-kind X" (the CR shape coordinate);
/// [`kube_api_version_is`] answers "does this document match
/// CRD-group/version X" (the CR registration coordinate). Same
/// one-hop readback + equality-wrap shape, different pinned scalar-
/// key on the same navigation depth (root) — together they bracket
/// the two canonical top-level CRD-registration coordinates every
/// K8s CR readback at controller / API-server admission time keys
/// off. Same three-arity closure discipline the sibling
/// sub-`metadata.{name, namespace}` per-CR coordinate pair already
/// carries — the accessor ([`kube_api_version`]) reads, the
/// predicate ([`kube_api_version_is`]) tests, the navigator
/// ([`find_by_api_version`]) locates — each pinned to
/// [`KUBE_KEY_API_VERSION`] inside the helper so the axis-key drift
/// class is closed across every consumer surface.
///
/// Sites lifted:
///
/// * caixa-flux's four per-emitted-file top-level `apiVersion:`
/// lifted-uses pins across the `cluster_bundle` multi-file
/// sequence — `cluster_bundle_helmrelease_uses_lifted_flux_api_version`
/// (`helmrelease.yaml`),
/// `cluster_bundle_kustomization_health_check_uses_lifted_flux_api_version`
/// (per-entry `kustomization.yaml` `spec.healthChecks[].apiVersion`
/// loop),
/// `cluster_bundle_gitrepository_uses_lifted_flux_api_version`
/// (`gitrepository.yaml`),
/// `cluster_bundle_kustomization_uses_lifted_flux_api_version`
/// (`kustomization.yaml`) — each per-file `assert_eq!(
/// kube_api_version(&parsed), Some(FLUX_<CRD>_API_VERSION))`
/// equality-wrap on the Flux v2 controller-triplet CRD-group/
/// version axis;
/// * caixa-mesh's per-CNP + per-Gateway + per-HTTPRoute top-level
/// `apiVersion:` equality-wraps across the six test bodies
/// `cilium_network_policies_use_lifted_cilium_api_version` (per-
/// CNP loop),
/// `cilium_policy_carries_canonical_kube_skeleton` (per-CNP
/// loop),
/// `gateway_carries_canonical_kube_skeleton_without_labels`
/// (`Gateway`),
/// `httproute_carries_canonical_kube_skeleton_without_labels`
/// (`HTTPRoute`),
/// `gateway_routes_gateway_uses_lifted_gateway_api_api_version`
/// (`Gateway`),
/// `gateway_routes_httproute_uses_lifted_gateway_api_api_version`
/// (`HTTPRoute`) — each `assert_eq!(kube_api_version(v),
/// Some(<AXIS>))` equality-wrap over the paired-Gateway/HTTPRoute
/// + fan-in CNP emission.
///
/// Every future per-CR CRD-group/version-filter site (the future
/// per-`:politicas` `CiliumClusterwideEnvoyConfig` per-CR CRD-group/
/// version equality gate, MESH-COMPOSITION §III.2 #3; the future
/// `app-operator`'s per-Aplicacao
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR CRD-group/version
/// equality join over emitted status docs, §III.2 #5; the future
/// M4 cross-cluster fan-out's per-cluster Flux-triplet CRD-group/
/// version equality gate across the `.toolkit.fluxcd.io` root)
/// reaches this same predicate by construction, with no inline
/// `kube_api_version(v) == Some(...)` equality wrap and no
/// re-parameterization on the pinned [`KUBE_KEY_API_VERSION`]
/// axis-key.
#[must_use]
pub fn kube_api_version_is(value: &serde_yaml::Value, api_version: &str) -> bool {
kube_api_version(value) == Some(api_version)
}
/// Locate the first K8s CR YAML document in `docs` whose top-level
/// `apiVersion:` CRD-group/version axis equals `api_version`.
///
/// Composes on top of [`kube_api_version_is`] — same one-hop
/// `.get(KUBE_KEY_API_VERSION).and_then(as_str) == Some(api_version)`
/// predicate — and closes the "find the first document of a given
/// CRD-group/version inside a multi-doc mesh emission" navigator
/// axis every future per-CRD-group / per-CRD-version fan-out slicer
/// reaches for to split the emitted sequence by per-CR CRD-group/
/// version-registration before probing a per-CR body-axis.
///
/// Composition-symmetric to [`kube_api_version_is`]: the lifted
/// predicate answers "does *this* one document match CRD-group/
/// version `<GV>`?", the lifted navigator answers "find the first
/// document of CRD-group/version `<GV>` in *this list*?". Same
/// axis, different arity — the two call shapes emit-side /
/// operator-side harnesses reach for when splitting multi-doc CR
/// emissions by per-CR CRD-group/version-registration. Peer of
/// [`find_by_kind`] (b73a13e) on the sibling `kind:` half of the
/// same canonical `(apiVersion, kind)` coordinate pair:
/// [`find_by_kind`] navigates by CR shape coordinate (there is
/// exactly one document per unique `kind:` inside a per-Aplicacao
/// mesh emission at V0); [`find_by_api_version`] navigates by CR
/// CRD-group/version-registration coordinate (there may be many
/// CRs sharing an `apiVersion:` — the "first match" contract
/// deliberately returns the first-emitted, matching the sibling
/// navigator's first-match contract on the identity + namespace-
/// scoping axes [`find_by_name`] / [`find_by_namespace`]).
///
/// This closes the three-arity closure on the top-level
/// `apiVersion:` per-CR CRD-group/version axis — accessor
/// [`kube_api_version`] (cf97d0a), predicate
/// [`kube_api_version_is`], navigator [`find_by_api_version`] —
/// bringing it to structural parity with the three-arity closure on
/// the sibling top-level `kind:` half (accessor [`kube_kind`]
/// 89a49a4, predicate [`kube_kind_is`] 2902d9d, navigator
/// [`find_by_kind`] b73a13e) + the two sub-`metadata.*` coordinates
/// (accessor [`kube_name`] c9cdecb, predicate [`kube_name_is`]
/// 092965d, navigator [`find_by_name`] 092965d; accessor
/// [`kube_namespace`] e18297b, predicate [`kube_namespace_is`]
/// 9f4a600, navigator [`find_by_namespace`] 9f4a600). Together the
/// four three-arity closures bracket every accessor arity on the
/// canonical per-CR-registration + per-CR-identity/scoping
/// coordinates the K8s API-machinery pins as the four load-bearing
/// axes every namespaced `CustomResource` carries.
///
/// Every future per-CRD-group/version multi-doc-navigator site (the
/// future `app-operator`'s per-Aplicacao
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR CRD-group/version join
/// over emitted status docs, MESH-COMPOSITION §III.2 #5; the M4
/// cross-cluster fan-out's per-cluster Flux-triplet split by
/// `apiVersion:` across the `.toolkit.fluxcd.io` root, §III.2 #3;
/// the future per-`:politicas` `CiliumClusterwideEnvoyConfig`
/// per-CRD-group/version audit surface that locates the first
/// emitted L7 policy inside a given CRD-group/version slice)
/// reaches this same helper by construction, with no inline
/// `.iter().find(closure)` combinator chain and no drift surface on
/// the receiver-widen or combinator axes.
#[must_use]
pub fn find_by_api_version<'a>(
docs: &'a [serde_yaml::Value],
api_version: &str,
) -> Option<&'a serde_yaml::Value> {
docs.iter().find(|d| kube_api_version_is(d, api_version))
}
/// Predicate: does the K8s custom resource YAML document at `value`
/// declare its `metadata.name` identity axis as exactly `name`?
///
/// Composes on top of [`kube_metadata_str_field`] (6809867) — same
/// two-hop `.get(KUBE_KEY_METADATA).and_then(get(KUBE_KEY_NAME))
/// .and_then(as_str)` navigation — and closes the
/// "metadata.name identity equality" predicate axis every multi-doc
/// mesh emission traversal reaches for to split the emitted sequence
/// by per-CR name (the CR identity axis) rather than by CRD-kind
/// (the CR shape axis) the sibling [`kube_kind_is`] already closes.
///
/// The canonical shape 6 test-side
///
/// ```ignore
/// kube_metadata_str_field(p, KUBE_KEY_NAME) == Some(<NAME>)
/// ```
///
/// call sites in [`caixa-mesh`][mesh]'s per-CNP-name /
/// per-Aplicacao-edge test harnesses previously carried inline as
/// the three-token composition — the readback helper call, the
/// `== Some(...)` equality wrap, the identity-axis pin on
/// [`KUBE_KEY_NAME`] — around a one-token semantic payload (the
/// `<NAME>` axis-value: `"checkout-cart-to-catalog"`,
/// `"checkout-payment-to-cart"`, `"checkout-cart-to-payment"`, each
/// a [`cilium_network_policy_name`]-composed byte-string). The lift
/// collapses the three-token composition onto one predicate the
/// caller reads as intent (`kube_name_is(p, <NAME>)` — "is this K8s
/// CR document named `<NAME>`") rather than as a
/// `readback → wrap → compare` chain.
///
/// The [`KUBE_KEY_NAME`] axis is pinned inside the helper (unlike
/// the parametric `field` axis of the underlying
/// [`kube_metadata_str_field`]) because the "is this CR document
/// named X" question is a semantically-distinct identity predicate,
/// not a generic scalar-readback: the K8s API-machinery pins
/// `metadata.name` as the load-bearing per-CR identity axis on every
/// `CustomResource` across every group/version (paired with
/// `metadata.namespace` for cluster-scoped-vs-namespaced disambiguation),
/// so this predicate lives one abstraction step above the generic
/// readback. Peer predicates for other `metadata.*` sub-axes (e.g. a
/// hypothetical `kube_namespace_is` on a per-namespace router harness,
/// a future `kube_uid_is` for ownerReference bookkeeping) land as
/// sibling helpers with their own pinned axis, not as
/// re-parameterizations of this one.
///
/// Structural peer to [`kube_kind_is`] (2902d9d) on the sibling
/// top-level `kind:` discriminator axis: [`kube_kind_is`] answers
/// "does this document match kind X" (the CR shape axis);
/// [`kube_name_is`] answers "does this document match name X" (the
/// CR identity axis). Same one-hop readback + equality-wrap shape,
/// different pinned scalar-key — together they bracket the two
/// canonical CR discriminator axes every multi-doc mesh emission
/// traversal reaches for.
///
/// Sites lifted:
///
/// * caixa-mesh's `cilium_network_policies` test harness — 6
/// `.find(|p| kube_metadata_str_field(p, KUBE_KEY_NAME) ==
/// Some(<NAME>))` + `.filter(|p| … == Some(<NAME>))` sites
/// splitting the emitted CNP multi-doc sequence by the
/// [`cilium_network_policy_name`]-composed `<aplicacao>-<de>-to-
/// <para>` byte-string for per-CR body-axis assertions.
///
/// Every future per-CR-name traversal (the M4 cross-cluster fan-out's
/// per-cluster `HelmRelease`-name-router; the `app-operator`'s
/// per-Aplicacao `mesh.pleme.io/v1alpha1/Aplicacao` CR status-name
/// join; the future per-`:contratos`
/// `CiliumClusterwideEnvoyConfig`-name filter) reaches the same
/// helper by construction, with no `== Some(...)` inline composition
/// and no drift surface on the `metadata.name` scalar-key axis.
///
/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
#[must_use]
pub fn kube_name_is(value: &serde_yaml::Value, name: &str) -> bool {
kube_name(value) == Some(name)
}
/// Read the `metadata.name` string-scalar identity axis of a K8s
/// custom resource YAML document as `Option<&str>` — the pinned peer
/// on the identity axis to the parametric [`kube_metadata_str_field`]
/// on the two-hop `metadata.<field>` sub-axis surface. Returns `None`
/// when either the enclosing `metadata:` block is absent, the sub-
/// `name:` scalar is absent, or the sub-`name:` scalar carries a
/// non-string YAML type — the same three-way vacuous-`None` short-
/// circuit the parent [`kube_metadata_str_field`] closes on the
/// underlying two-hop navigation.
///
/// The [`KUBE_KEY_NAME`] axis is pinned inside the helper (unlike
/// the parametric `field` axis of the underlying
/// [`kube_metadata_str_field`]) because the K8s API-machinery pins
/// `metadata.name` as the load-bearing per-CR identity axis on every
/// `CustomResource` across every group/version. Every readback
/// consumer downstream (`.unwrap()`, `.expect(...)`, `== Some(...)`
/// equality wraps, `.to_string()` clone, `.strip_prefix(...)` /
/// `.split_once(...)` decompose chains) drives off the same pinned
/// return; a hypothetical future K8s API-machinery rename on the
/// `metadata.name` axis (a Server-Side-Apply-driven identity
/// migration under per-field ownership annotations, an alias table
/// bridging a new `metadata.identity` sub-axis) reaches every
/// caller through one lift, not a coordinated rewrite across every
/// per-CR readback site.
///
/// The canonical shape 12 emit-side test-harness readback sites
/// across [`caixa-mesh`][mesh] (9) + [`caixa-flux`][flux] (3)
/// previously carried inline as the two-token composition
///
/// ```ignore
/// kube_metadata_str_field(<value>, KUBE_KEY_NAME)
/// ```
///
/// around a one-token semantic payload (the readback intent — "what
/// name did the emitter write into this CR?"). The lift collapses
/// the two-token composition — the parametric readback helper, the
/// pinned identity-axis scalar-key argument — onto one accessor the
/// caller reads as intent (`kube_name(<value>)` — "what is this K8s
/// CR document's `metadata.name`?") rather than a
/// `readback → axis-pin` two-arg call.
///
/// Structural peer to sibling [`kube_kind_is`] (predicate arity) /
/// [`find_by_kind`] (navigator arity) / [`kube_name_is`] (predicate
/// arity) / [`find_by_name`] (navigator arity) on the same canonical
/// K8s CR discriminator+identity axis pair: this closes the accessor
/// arity on the identity axis — the "what is this document's name?"
/// question the peer predicate answers as equality and the peer
/// navigator answers as filter-then-first-hit. Same axis, three
/// arities — the accessor (`kube_name`) reads, the predicate
/// (`kube_name_is`) tests, the navigator (`find_by_name`) locates —
/// each pinned to [`KUBE_KEY_NAME`] inside the helper so the axis-
/// key drift class is closed across every consumer surface.
///
/// Sites lifted:
///
/// * caixa-mesh's per-CNP `metadata.name` readback loop in the
/// five test bodies `cilium_network_policy_metadata_name_uses_lifted_composer`,
/// `cilium_network_policy_metadata_name_derives_from_caixa_nome_accessor`,
/// `cilium_emits_one_policy_per_de_para_pair`, and
/// `cilium_network_policy_l4_port_matches_dest_servico_port` —
/// each `p → kube_metadata_str_field(p, KUBE_KEY_NAME).expect|unwrap`
/// readback inside the fan-in `.iter().map(...)` or per-policy
/// `for` loop over the multi-doc CNP emission;
/// * caixa-mesh's per-Gateway / per-HTTPRoute `metadata.name`
/// readback across the four test bodies
/// `gateway_routes_httproute_metadata_name_uses_lifted_composer`,
/// `gateway_routes_gateway_metadata_name_routes_through_caixa_nome_accessor`,
/// `gateway_routes_httproute_metadata_name_routes_through_caixa_nome_accessor`,
/// and the per-`:entrada :para` parametric permutation harness —
/// each `find_by_kind(&docs, <KIND>) → kube_metadata_str_field(..,
/// KUBE_KEY_NAME).expect(...)` chain over the paired-Gateway/HTTPRoute
/// emission;
/// * caixa-flux's three
/// `cluster_bundle_{gitrepository,helmrelease,kustomization}_metadata_name_routes_through_caixa_nome_accessor`
/// tests — each per-emitted-file
/// `parsed → kube_metadata_str_field(&parsed, KUBE_KEY_NAME).expect(...)`
/// site on the per-Flux-CR bundle-path emission.
///
/// Every future per-CR `metadata.name` readback (the M4 cross-cluster
/// fan-out's per-cluster `HelmRelease` name-router, the `app-
/// operator`'s per-Aplicacao `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// status-name join, MESH-COMPOSITION §III.2 #5; the future per-
/// `:contratos` `CiliumClusterwideEnvoyConfig`-name introspection
/// filter) reaches the same pinned accessor by construction, with no
/// axis-key argument drift and no re-inlined
/// `kube_metadata_str_field(_, KUBE_KEY_NAME)` two-token composition.
///
/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
/// [flux]: https://github.com/pleme-io/caixa/tree/main/caixa-flux
#[must_use]
pub fn kube_name(value: &serde_yaml::Value) -> Option<&str> {
kube_metadata_str_field(value, KUBE_KEY_NAME)
}
/// Read the `metadata.namespace` string-scalar per-CR-namespace-scoping
/// axis of a K8s custom resource YAML document as `Option<&str>` — the
/// pinned peer on the namespace-scoping axis to the parametric
/// [`kube_metadata_str_field`] on the two-hop `metadata.<field>` sub-axis
/// surface, and the sibling on the identity-axis pair to the just-landed
/// [`kube_name`] (c9cdecb) accessor on the `metadata.name` per-CR
/// identity axis. Returns `None` when either the enclosing `metadata:`
/// block is absent, the sub-`namespace:` scalar is absent, or the sub-
/// `namespace:` scalar carries a non-string YAML type — the same three-
/// way vacuous-`None` short-circuit the parent [`kube_metadata_str_field`]
/// closes on the underlying two-hop navigation.
///
/// The [`KUBE_KEY_NAMESPACE`] axis is pinned inside the helper (unlike
/// the parametric `field` axis of the underlying
/// [`kube_metadata_str_field`]) because the K8s API-machinery pins
/// `metadata.namespace` as the load-bearing per-CR namespace-scoping
/// axis on every namespaced `CustomResource` across every group/version
/// (paired with `metadata.name` for cluster-scoped-vs-namespaced
/// disambiguation — the [`kube_name`] sibling closes the identity half
/// of the pair; this one closes the namespace-scoping half). Every
/// readback consumer downstream (`.unwrap()`, `.expect(...)`,
/// `== Some(...)` equality wraps, `.unwrap_or(DEFAULT_NAMESPACE)` fallback,
/// `.to_string()` clone) drives off the same pinned return; a hypothetical
/// future K8s API-machinery rename on the `metadata.namespace` axis (a
/// tenancy-driven migration to a wrapped `metadata.tenant:` sub-axis
/// under a per-tenant namespace-slice model, a Server-Side-Apply-driven
/// per-field-ownership migration under a versioned `metadata.namespaceV2:`
/// axis) reaches every caller through one lift, not a coordinated
/// rewrite across every per-CR namespace-scoping readback site.
///
/// The canonical shape 4 emit-side sites across [`caixa-flux`][flux] (1
/// production + 1 test) + [`caixa-mesh`][mesh] (2 test) previously
/// carried inline as either the two-token parametric composition
///
/// ```ignore
/// kube_metadata_str_field(<value>, KUBE_KEY_NAMESPACE)
/// ```
///
/// (the caixa-flux [`programs_yaml_entry`] production readback with
/// [`DEFAULT_NAMESPACE`] fallback + the sibling `cluster_bundle`
/// kustomization.yaml pin) or the three-token raw two-hop navigation
///
/// ```ignore
/// metadata.get(KUBE_KEY_NAMESPACE).and_then(|v| v.as_str())
/// ```
///
/// on an already-extracted `metadata: &Mapping` sub-view (the two
/// caixa-mesh CNP + Gateway skeleton pins on the emitted CR fixture's
/// `metadata:` sub-mapping) — a two-shape open-coded readback surface
/// where a future rebrand on either shape (a schema-migration on the
/// [`KUBE_KEY_NAMESPACE`] const the parametric shape reads through, an
/// intermediate `metadata: &Mapping` extraction the raw two-hop shape
/// walks) would silently split the two-shape readers into disagreement
/// on which per-CR namespace-scoping scalar a given emitted CR resolves
/// to. Lifting the resolution to one accessor pinned on the substrate
/// primitive means every downstream consumer of the per-CR namespace-
/// scoping surface reaches for exactly one typed dispatch — the
/// resolver's accept-set migrates as a unit on any future axis addition.
///
/// Structural peer to sibling [`kube_name`] (c9cdecb) on the identity-
/// axis half of the canonical `metadata.{name, namespace}` per-CR
/// disambiguation pair the K8s API-machinery pins as the two load-
/// bearing per-CR coordinates every `CustomResource` carries: [`kube_name`]
/// answers "what is this document's identity coordinate?"; [`kube_namespace`]
/// answers "what is this document's namespace-scoping coordinate?". Same
/// two-hop `metadata.<field>` readback shape, different pinned scalar-
/// key — together they bracket the two canonical per-CR coordinates
/// every namespaced-CR readback site reaches for.
///
/// Sites lifted:
///
/// * caixa-flux's `programs_yaml_entry` — the production
/// `computeunit_yaml.metadata.namespace` readback with
/// [`DEFAULT_NAMESPACE`] `.unwrap_or(...)` fallback (the load-
/// bearing per-programs-entry namespace-scoping resolver the
/// `lareira-fleet-programs` aggregator + wasm-operator per-
/// `ComputeUnit` dispatch both key off);
/// * caixa-flux's `cluster_bundle_kustomization_metadata_namespace_
/// pins_flux_system_default` test-side pin — the emitted
/// `kustomization.yaml`'s `metadata.namespace` readback against
/// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`];
/// * caixa-mesh's `cilium_policy_carries_canonical_kube_skeleton`
/// test-side pin — the per-CNP `metadata.namespace` readback
/// against [`DEFAULT_NAMESPACE`] across every emitted CNP;
/// * caixa-mesh's `gateway_carries_canonical_kube_skeleton_without_labels`
/// test-side pin — the per-Gateway `metadata.namespace` readback
/// against [`DEFAULT_NAMESPACE`] on the single emitted Gateway CR.
///
/// Every future per-CR `metadata.namespace` readback (the future M4
/// per-Aplicacao `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
/// per-CR namespace-scoping join, MESH-COMPOSITION §III.2 #5; the future
/// per-`:politicas` `CiliumClusterwideEnvoyConfig` emitter's per-CR
/// namespace-scoping pin, §III.2 #3; the future `caixa-otel`
/// per-Servico OpenTelemetry-Collector CR's namespace-scoping pin; the
/// future M4 cross-cluster fan-out's per-cluster `HelmRelease.metadata.
/// namespace` readback) reaches the same pinned accessor by
/// construction, with no axis-key argument drift and no re-inlined
/// `kube_metadata_str_field(_, KUBE_KEY_NAMESPACE)` two-token composition
/// or `metadata.get(KUBE_KEY_NAMESPACE).and_then(|v| v.as_str())` three-
/// token raw two-hop navigation.
///
/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
/// [flux]: https://github.com/pleme-io/caixa/tree/main/caixa-flux
/// [`programs_yaml_entry`]: https://docs.rs/caixa-flux
#[must_use]
pub fn kube_namespace(value: &serde_yaml::Value) -> Option<&str> {
kube_metadata_str_field(value, KUBE_KEY_NAMESPACE)
}
/// Test whether a K8s custom resource YAML document's `metadata.namespace`
/// per-CR namespace-scoping axis equals `namespace` — the pinned predicate
/// peer of the [`kube_namespace`] (e18297b) accessor on the namespace-
/// scoping half of the canonical `metadata.{name, namespace}` per-CR
/// coordinate pair, and the structural mirror on the namespace-scoping
/// axis of the [`kube_name_is`] (092965d) predicate on the identity axis.
/// Composes as `kube_namespace(value) == Some(namespace)` — same one-hop
/// readback + equality-wrap shape the sibling predicate carries on the
/// identity axis, differing only in which of the two canonical per-CR
/// coordinates it pins.
///
/// The [`KUBE_KEY_NAMESPACE`] axis is pinned inside the helper (unlike
/// the parametric `field` axis of the underlying
/// [`kube_metadata_str_field`]) because the "is this CR document scoped
/// to namespace X" question is a semantically-distinct namespace-scoping
/// predicate, not a generic scalar-readback: the K8s API-machinery pins
/// `metadata.namespace` as the load-bearing per-CR namespace-scoping
/// axis on every namespaced `CustomResource` across every group/version
/// (paired with `metadata.name` for cluster-scoped-vs-namespaced
/// disambiguation), so this predicate lives one abstraction step above
/// the generic readback. Peer predicates for other `metadata.*` sub-
/// axes (a hypothetical `kube_uid_is` for ownerReference bookkeeping, a
/// future `kube_resource_version_is` for optimistic-concurrency
/// bookkeeping) land as sibling helpers with their own pinned axis, not
/// as re-parameterizations of this one.
///
/// Structural peer to [`kube_name_is`] (092965d) on the sibling
/// `metadata.name` identity axis: [`kube_name_is`] answers "does this
/// document match name X" (the identity coordinate); [`kube_namespace_is`]
/// answers "does this document match namespace X" (the namespace-
/// scoping coordinate). Same one-hop readback + equality-wrap shape,
/// different pinned scalar-key — together they bracket the two
/// canonical per-CR coordinates every namespaced-CR filter reaches
/// for. Same three-arity closure discipline the sibling identity axis
/// carries — the accessor ([`kube_namespace`]) reads, the predicate
/// ([`kube_namespace_is`]) tests, the navigator ([`find_by_namespace`])
/// locates — each pinned to [`KUBE_KEY_NAMESPACE`] inside the helper
/// so the axis-key drift class is closed across every consumer surface.
///
/// Every future per-CR namespace-filter site (the future M4 cross-
/// cluster fan-out's per-tenant `HelmRelease` router split by
/// `metadata.namespace`, MESH-COMPOSITION §III.2 #3; the future per-
/// `:politicas` `CiliumClusterwideEnvoyConfig` per-namespace
/// introspection filter; the future `app-operator`'s per-Aplicacao
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR namespace-scoping equality
/// join, §III.2 #5; the future per-tenant CNP audit surface that
/// filters emitted CNPs by their per-tenant namespace-scoping
/// coordinate) reaches this same predicate by construction, with no
/// inline `kube_namespace(v) == Some(...)` equality wrap and no
/// re-parameterization on the pinned [`KUBE_KEY_NAMESPACE`] axis-key.
#[must_use]
pub fn kube_namespace_is(value: &serde_yaml::Value, namespace: &str) -> bool {
kube_namespace(value) == Some(namespace)
}
/// Locate the first K8s CR YAML document in `docs` whose
/// `metadata.name` identity axis equals `name`.
///
/// Composes on top of [`kube_name_is`] — same one-hop
/// `.get(KUBE_KEY_METADATA).and_then(get(KUBE_KEY_NAME))
/// .and_then(as_str) == Some(name)` predicate — and closes the
/// "find the one document with a given name inside a multi-doc mesh
/// emission" navigator axis every per-Aplicacao renderer's post-emit
/// test harness reaches for to split the emitted sequence by per-CR
/// identity before probing a per-CR body-axis.
///
/// The canonical shape 5 test-side
///
/// ```ignore
/// docs.iter().find(|d| kube_name_is(d, <NAME>))
/// ```
///
/// call sites in [`caixa-mesh`][mesh]'s `cilium_network_policies`
/// test harness previously threaded the three-token
/// `.iter().find(closure)` combinator chain around a one-token
/// semantic payload (the [`cilium_network_policy_name`]-composed
/// `<aplicacao>-<de>-to-<para>` byte-string). The lift collapses
/// the three-token chain — the `.iter()` receiver-widen, the
/// `.find(closure)` combinator, the inline closure wrap around
/// [`kube_name_is`] — onto one navigator function the caller reads
/// as intent (`find_by_name(&docs, <NAME>)` — "give me the K8s CR
/// document named `<NAME>`") rather than as a
/// receiver-widen → combinator → predicate chain.
///
/// Composition-symmetric to [`kube_name_is`]: the lifted predicate
/// answers "does *this* one document match name `<NAME>`?", the
/// lifted navigator answers "find the one document of name
/// `<NAME>` in *this list*?". Same axis, different arity — the two
/// call shapes emit-side test harnesses reach for when splitting
/// multi-doc CR emissions by per-CR identity. Peer of
/// [`find_by_kind`] (b73a13e) on the sibling `kind:` discriminator
/// axis: [`find_by_kind`] navigates by CR shape (there is exactly
/// one `Gateway` + one `HTTPRoute` per Aplicacao at V0); this navigates
/// by CR identity (there is one CNP per `(:de, :para)` fan-in
/// group, and the per-CNP identity is the
/// [`cilium_network_policy_name`]-composed edge label).
///
/// Every future per-CR-name multi-doc-navigator site (the future
/// `app-operator`'s per-Aplicacao CR-name join over emitted status
/// docs, MESH-COMPOSITION §III.2 #5; the M4 cross-cluster fan-out's
/// per-cluster `HelmRelease`-name split; the future per-`:contratos`
/// `CiliumClusterwideEnvoyConfig`-name filter over the sibling
/// L7-policy emission) reaches the same helper by construction, with
/// no inline `.iter().find(closure)` combinator chain and no drift
/// surface on the receiver-widen or combinator axes.
///
/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
#[must_use]
pub fn find_by_name<'a>(
docs: &'a [serde_yaml::Value],
name: &str,
) -> Option<&'a serde_yaml::Value> {
docs.iter().find(|d| kube_name_is(d, name))
}
/// Locate the first K8s CR YAML document in `docs` whose
/// `metadata.namespace` per-CR namespace-scoping axis equals `namespace`.
///
/// Composes on top of [`kube_namespace_is`] — same one-hop
/// `.get(KUBE_KEY_METADATA).and_then(get(KUBE_KEY_NAMESPACE))
/// .and_then(as_str) == Some(namespace)` predicate — and closes the
/// "find the first document scoped to a given namespace inside a multi-
/// doc mesh emission" navigator axis every future per-tenant / per-
/// cluster-namespace fan-out slicer reaches for to split the emitted
/// sequence by per-CR namespace-scoping before probing a per-CR body-
/// axis.
///
/// Composition-symmetric to [`kube_namespace_is`]: the lifted predicate
/// answers "does *this* one document match namespace `<NS>`?", the
/// lifted navigator answers "find the first document of namespace
/// `<NS>` in *this list*?". Same axis, different arity — the two call
/// shapes emit-side / operator-side harnesses reach for when splitting
/// multi-doc CR emissions by per-CR namespace-scoping. Peer of
/// [`find_by_name`] (092965d) on the sibling `metadata.name` identity
/// axis: [`find_by_name`] navigates by CR identity coordinate (there
/// is exactly one CR per unique `metadata.name` inside a scope);
/// [`find_by_namespace`] navigates by CR namespace-scoping coordinate
/// (there may be many CRs sharing a `metadata.namespace` — the "first
/// match" contract deliberately returns the first-emitted, matching
/// the sibling navigator's first-match contract on the identity axis).
///
/// This closes the three-arity closure on the `metadata.namespace`
/// per-CR namespace-scoping axis — accessor [`kube_namespace`]
/// (e18297b), predicate [`kube_namespace_is`], navigator
/// [`find_by_namespace`] — bringing it to structural parity with the
/// three-arity closure on the sibling identity axis: accessor
/// [`kube_name`] (c9cdecb), predicate [`kube_name_is`] (092965d),
/// navigator [`find_by_name`] (092965d). Together the two closures
/// bracket every accessor arity on the canonical
/// `metadata.{name, namespace}` per-CR coordinate pair the K8s
/// API-machinery pins as the two load-bearing coordinates every
/// namespaced `CustomResource` carries.
///
/// Every future per-CR-namespace multi-doc-navigator site (the M4
/// cross-cluster fan-out's per-tenant `HelmRelease` split by
/// `metadata.namespace`, MESH-COMPOSITION §III.2 #3; the future
/// `app-operator`'s per-Aplicacao `mesh.pleme.io/v1alpha1/Aplicacao`
/// CR namespace-scoping join over emitted status docs, §III.2 #5; the
/// future per-tenant CNP audit surface that locates the first emitted
/// CNP inside a given tenant's namespace-scoping slice) reaches this
/// same helper by construction, with no inline `.iter().find(closure)`
/// combinator chain and no drift surface on the receiver-widen or
/// combinator axes.
#[must_use]
pub fn find_by_namespace<'a>(
docs: &'a [serde_yaml::Value],
namespace: &str,
) -> Option<&'a serde_yaml::Value> {
docs.iter().find(|d| kube_namespace_is(d, namespace))
}
/// Read the `metadata.labels` sub-mapping of a K8s custom resource YAML
/// document as `Option<&serde_yaml::Mapping>` — the first non-scalar
/// sub-`metadata:` accessor peer to the scalar-arity parametric
/// [`kube_metadata_str_field`] on the same two-hop `metadata.<field>`
/// navigation depth. Where [`kube_metadata_str_field`] reads
/// `metadata.<field>` **string-scalar** sub-fields (the pinned
/// [`kube_name`] / [`kube_namespace`] accessors on the identity +
/// namespace-scoping halves of the canonical `metadata.{name, namespace}`
/// coordinate pair both compose on that scalar-arity primitive), this
/// closes the peer sub-**mapping** readback on the load-bearing
/// `metadata.labels` sub-block — the third canonical `metadata.*`
/// sub-axis every K8s CR document the emit-side [`kube_resource_skeleton`]
/// renders carries, alongside the two scalar-axis sub-fields the sibling
/// accessors already pin.
///
/// Returns `None` when either the enclosing `metadata:` block is absent
/// (a defensively-tolerated missing sub-mapping — the caller's own
/// test-side `expect(...)` / production-side `unwrap_or_default(...)`
/// names the axis), the sub-`labels:` sub-block is absent (a legally-
/// omitted label surface on a CR that carries no per-CR label metadata
/// — the emit-side [`kube_resource_skeleton`]'s `labels.is_empty()`
/// short-circuit skips the block entirely, and this readback preserves
/// the same short-circuit on the reverse), or the sub-`labels:` sub-
/// block is present but carries a non-Mapping YAML type (a schema-
/// invalid CR shape per the apiserver's `OpenAPI` schema but tolerated
/// here as `None` so the readback stays a total function). The returned
/// `&serde_yaml::Mapping` borrows into the input `Value` — the caller
/// decides whether to enumerate (`.iter()`), probe a specific key
/// (`.get(<LABEL>)`), or delegate through the composed sibling
/// [`kube_metadata_label`] scalar-arity peer. The three-hop navigation
/// happens in one method call the caller reads as intent
/// (`kube_metadata_labels(<value>)` — "read this K8s CR's
/// `metadata.labels` sub-mapping") rather than three hand-spelled
/// positional artifacts (the `get(KUBE_KEY_METADATA)` outer hop, the
/// `and_then(|m| m.get(KUBE_KEY_LABELS))` middle hop, the
/// `and_then(|l| l.as_mapping())` shape gate).
///
/// The [`KUBE_KEY_LABELS`] axis is pinned inside the helper (unlike
/// the parametric `field` axis of the underlying
/// [`kube_metadata_str_field`]) because the K8s API-machinery pins
/// `metadata.labels` as the load-bearing per-CR label-surface axis on
/// every `CustomResource` across every group/version — the same load-
/// bearing status the sibling scalar-axis coordinates
/// (`metadata.name`, `metadata.namespace`) carry on the two identity /
/// namespace-scoping halves that already lifted pinned accessor peers
/// under the same discipline. Every readback consumer downstream
/// (`.get(<LABEL>)` per-label probe, `.iter()` enumeration for prefix
/// filtering, `.as_str()` shape-gate on a probed value) drives off
/// the same pinned return; a hypothetical future K8s API-machinery
/// rebrand on the `metadata.labels` axis (a schema-migration to a
/// wrapped `metadata.labelsV2:` sub-block under a versioned CRD
/// evolution axis, a per-tenant migration to a nested
/// `metadata.labels.tenant.*` scoped sub-namespace under Server-Side-
/// Apply's per-field ownership annotations) reaches every caller
/// through one lift, not a coordinated rewrite across every per-CR
/// label-readback site.
///
/// The canonical shape 3 emit-side test-harness readback sites in
/// [`caixa-mesh`][mesh] (the per-CNP contrato-values-collect, the
/// per-CNP [`LABEL_APLICACAO`] readback, the per-CNP labels sub-mapping
/// enumeration) previously carried inline as either the three-token
/// composition
///
/// ```ignore
/// value
/// .get(KUBE_KEY_METADATA)
/// .and_then(|m| m.get(KUBE_KEY_LABELS))
/// .and_then(|l| l.as_mapping())
/// ```
///
/// (the per-CNP labels sub-mapping enumeration site) or the four-token
/// composition
///
/// ```ignore
/// value
/// .get(KUBE_KEY_METADATA)
/// .and_then(|m| m.get(KUBE_KEY_LABELS))
/// .and_then(|l| l.get(<LABEL>))
/// .and_then(|v| v.as_str())
/// ```
///
/// (the per-CNP contrato-values-collect + [`LABEL_APLICACAO`] readback
/// sites — closed by the composed sibling [`kube_metadata_label`]
/// scalar-arity peer that composes on top of this sub-mapping
/// accessor). A two-shape open-coded readback surface where a future
/// rebrand on either shape (a schema-migration on the
/// [`KUBE_KEY_LABELS`] const the parametric shape reads through, an
/// intermediate `metadata: &Mapping` extraction the raw walks route
/// through) would silently split the readers into disagreement on
/// which per-CR label surface a given emitted CR resolves to. Lifting
/// the resolution to one accessor pinned on the substrate primitive
/// means every downstream consumer of the per-CR label-surface reaches
/// for exactly one typed dispatch — the resolver's accept-set
/// migrates as a unit on any future axis addition.
///
/// Structural peer to sibling [`kube_metadata_str_field`] on the same
/// sub-`metadata.*` navigation depth: [`kube_metadata_str_field`]
/// reads the scalar-arity sub-fields (parametric on the `<field>`
/// axis-key); [`kube_metadata_labels`] reads the sub-mapping-arity
/// sub-block (pinned on the [`KUBE_KEY_LABELS`] axis-key). Same two-
/// hop `metadata.<sub-block>` readback shape, different return
/// (`Option<&str>` scalar-arity vs. `Option<&serde_yaml::Mapping>`
/// sub-mapping-arity) — together they bracket the two canonical
/// per-`metadata` readback shapes every K8s CR document carries.
///
/// The composed sibling scalar-arity peer [`kube_metadata_label`]
/// lands on top of this accessor as the parametric label-value
/// accessor on the `metadata.labels.<label>` axis — the "read one
/// specific label value" question folds onto this accessor's
/// enumeration through one `.get(<LABEL>).and_then(as_str)` chain,
/// composition-symmetric to the way the sibling identity /
/// namespace-scoping accessors compose on [`kube_metadata_str_field`].
///
/// Every future per-CR `metadata.labels` readback (the future
/// per-`:politicas` `CiliumClusterwideEnvoyConfig` emitter's per-CR
/// label surface, MESH-COMPOSITION §III.2 #3; the `app-operator`'s
/// per-Aplicacao `mesh.pleme.io/v1alpha1/Aplicacao` CR label-based
/// selector join, §III.2 #5; the future `caixa-otel` per-Servico
/// OpenTelemetry-Collector CR's per-Servico label surface; the M4
/// per-tenant fan-out's per-tenant label-prefix filter) reaches the
/// same pinned accessor by construction, with no axis-key argument
/// drift and no re-inlined three-hop chain.
///
/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
#[must_use]
pub fn kube_metadata_labels(value: &serde_yaml::Value) -> Option<&serde_yaml::Mapping> {
kube_metadata_map_field(value, KUBE_KEY_LABELS)
}
/// Read a single string-scalar label value at
/// `metadata.labels.<label>` on a K8s custom resource YAML document as
/// `Option<&str>` — the parametric scalar-arity accessor peer that
/// composes on top of the lifted sub-mapping-arity
/// [`kube_metadata_labels`] accessor to close the four-hop per-label
/// readback the two caixa-mesh test-side per-CNP label-value probes
/// (contrato-values-collect, [`LABEL_APLICACAO`] readback) previously
/// walked inline. Composition-symmetric to the way the sibling scalar-
/// arity [`kube_name`] / [`kube_namespace`] accessors compose on the
/// parametric scalar-arity [`kube_metadata_str_field`] primitive: the
/// scalar-arity sub-mapping-value accessor stands on the sub-mapping-
/// arity sub-block accessor, folding the "read one specific label
/// value" question onto one `.get(<LABEL>).and_then(as_str)` chain.
///
/// Returns `None` when either the enclosing `metadata.labels` sub-
/// mapping is absent (the same three-way vacuous-`None` short-circuit
/// the parent [`kube_metadata_labels`] closes — missing `metadata:`
/// block, missing `labels:` sub-block, non-Mapping `labels:` value),
/// the requested `<label>` scalar-key is absent under the labels sub-
/// block (a legally-omitted per-label surface on a CR that carries
/// other labels but not this one), or the `<label>` value is present
/// but carries a non-string YAML type (a schema-invalid label shape
/// per the K8s labels contract that pins values as string scalars,
/// but tolerated here as `None` so the readback stays a total
/// function). The returned `&str` borrows into the input `Value` —
/// the caller decides whether to compare (`==`), clone
/// (`.to_string()`), or unwrap-then-panic. The four-hop navigation
/// happens in one method call the caller reads as intent
/// (`kube_metadata_label(<value>, <LABEL>)` — "read this K8s CR's
/// `metadata.labels.<LABEL>` string-scalar") rather than four hand-
/// spelled positional artifacts (the outer `get(KUBE_KEY_METADATA)`
/// hop, the inner `and_then(|m| m.get(KUBE_KEY_LABELS))` hop, the
/// per-label `and_then(|l| l.get(<LABEL>))` sub-hop, the trailing
/// `and_then(|v| v.as_str())` shape gate).
///
/// The `label` axis stays parametric (rather than pinned to a specific
/// label-key like [`LABEL_APLICACAO`] or [`LABEL_CONTRATO`] as
/// separate helpers) so the same lift closes every string-scalar
/// label a caixa-mesh emitter writes today ([`LABEL_APLICACAO`],
/// [`LABEL_CONTRATO`], [`LABEL_PROGRAM`]) and every string-scalar
/// label a future renderer surfaces (per-tenant label-prefix filters,
/// per-Servico OTel-collector labels, per-Aplicacao CR
/// materializer's selector labels) — each new label reaches for the
/// same helper with a new [`crate::LABEL_*`] const argument, not a
/// fresh per-label helper. Peer of the composed scalar-arity
/// [`kube_name`] / [`kube_namespace`] accessors on the sub-`metadata:`
/// scalar-axis pair: those pin the [`KUBE_KEY_NAME`] /
/// [`KUBE_KEY_NAMESPACE`] axis-keys inside the helper because the K8s
/// API-machinery pins those two specific coordinates as the load-
/// bearing per-CR discriminators; this stays parametric on the
/// label-key argument because the K8s labels contract deliberately
/// admits an open-ended per-CR label surface, and pinning a specific
/// label would foreclose reuse across the label set.
///
/// The canonical shape 2 emit-side test-harness readback sites in
/// [`caixa-mesh`][mesh] previously carried inline as the four-token
/// composition
///
/// ```ignore
/// value
/// .get(KUBE_KEY_METADATA)
/// .and_then(|m| m.get(KUBE_KEY_LABELS))
/// .and_then(|l| l.get(<LABEL>))
/// .and_then(|v| v.as_str())
/// ```
///
/// around a one-token semantic payload (the `<LABEL>` axis-key —
/// [`LABEL_CONTRATO`] on the per-CNP contrato-values-collect site,
/// [`LABEL_APLICACAO`] on the per-CNP parent-Aplicacao readback
/// site). The lift collapses the four-token composition — the outer
/// two hops (folded onto the sibling [`kube_metadata_labels`] sub-
/// mapping accessor), the per-label sub-hop, the trailing shape gate
/// — onto one accessor the caller reads as intent
/// (`kube_metadata_label(<value>, <LABEL>)`) rather than a four-hop
/// hand-walked chain.
///
/// Sites lifted:
///
/// * caixa-mesh's `cilium_network_policies_emit_per_de_para_edges`
/// — the per-CNP [`LABEL_CONTRATO`] values collect
/// ([`LABEL_CONTRATO`] readback + `.map(String::from)` clone across
/// every emitted policy);
/// * caixa-mesh's `cilium_network_policies_label_aplicacao_routes_
/// through_caixa_nome_accessor` — the per-CNP [`LABEL_APLICACAO`]
/// readback + string-equality against
/// [`crate::Caixa::nome`][caixa-nome].
///
/// Every future per-CR label-value readback (the future
/// per-`:politicas` `CiliumClusterwideEnvoyConfig` emitter's per-
/// policy `LABEL_POLITICA` readback, MESH-COMPOSITION §III.2 #3; the
/// `app-operator`'s per-Aplicacao label-based `spec.selector`
/// materialization, §III.2 #5; the future `caixa-otel` per-Servico
/// OpenTelemetry-Collector label-based routing filter; the M4 per-
/// tenant fan-out's per-tenant label-prefix probe) reaches the same
/// pinned accessor by construction, with no axis-key argument drift
/// and no re-inlined four-hop chain.
///
/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
/// [caixa-nome]: https://docs.rs/caixa-core
#[must_use]
pub fn kube_metadata_label<'a>(value: &'a serde_yaml::Value, label: &str) -> Option<&'a str> {
kube_metadata_labels(value)
.and_then(|labels| labels.get(label))
.and_then(|v| v.as_str())
}
/// Predicate: does the K8s custom resource YAML document at `value`
/// carry `metadata.labels.<label>` with the string-scalar value
/// `expected`?
///
/// Composes on top of [`kube_metadata_label`] as
/// `kube_metadata_label(value, label) == Some(expected)` — the same
/// one-hop `readback → equality-wrap` shape the sibling identity-axis
/// predicates [`kube_name_is`] / [`kube_namespace_is`] and the sibling
/// top-level [`kube_kind_is`] / [`kube_api_version_is`] carry on their
/// respective pinned single-scalar-key axes, extended onto the
/// parametric `label` axis at the `metadata.labels.<label>` sub-mapping
/// depth. Returns `false` on any of the four vacuous-`None` short-
/// circuits the underlying [`kube_metadata_label`] accessor closes
/// (missing `metadata:` block, missing `metadata.labels` sub-block,
/// non-Mapping `labels:` value, requested `<label>` key absent, or
/// non-string label value): the predicate treats the "label absent"
/// arm and the "label present but non-string" arm the same as the
/// "label present but wrong value" arm — every downstream selector-
/// filter caller (`kubectl -l pleme.pleme.io/aplicacao=<name>` grep-
/// by-label, the future `app-operator`'s per-Aplicacao `spec.selector`
/// join, MESH-COMPOSITION §III.2 #5) treats every non-match the same,
/// so the predicate's boolean shape matches the selector's boolean
/// verdict rather than exposing the underlying `Option`'s three-way
/// split.
///
/// Unlike the sibling scalar-axis predicates ([`kube_name_is`],
/// [`kube_namespace_is`], [`kube_kind_is`], [`kube_api_version_is`])
/// whose `field` axis is pinned inside the helper because the K8s
/// API-machinery pins those specific coordinates as the load-bearing
/// per-CR discriminators, this predicate stays parametric on the
/// `label` axis-key argument because the K8s labels contract
/// deliberately admits an open-ended per-CR label surface — the same
/// three-arity closure (accessor / predicate / navigator) applies to
/// every label a caixa-mesh emitter writes today ([`LABEL_APLICACAO`],
/// [`LABEL_CONTRATO`], [`LABEL_PROGRAM`]) and every label a future
/// renderer surfaces (per-tenant label-prefix filters, per-Servico
/// OTel-collector labels, per-`:politicas` `LABEL_POLITICA`
/// discriminators MESH-COMPOSITION §III.2 #3 acknowledges), with the
/// axis-key threaded through the call rather than pinned inside.
///
/// The canonical shape 1 test-side
///
/// ```ignore
/// kube_metadata_label(p, LABEL_APLICACAO) == Some("checkout")
/// ```
///
/// call site in [`caixa-mesh`][mesh]'s
/// `cilium_policy_metadata_labels_use_lifted_consts` test previously
/// carried inline as the three-token composition — the readback
/// helper call, the `== Some(...)` equality wrap, the string-scalar
/// axis-value pin — around a two-token semantic payload (the
/// `<label>` axis-key + the `<expected>` axis-value). The lift
/// collapses the three-token composition — the accessor call, the
/// equality wrap, the `Some(...)` constructor — onto one predicate
/// the caller reads as intent (`kube_metadata_label_is(p, LABEL_X, "v")`
/// — "does this K8s CR document carry label `LABEL_X` with value
/// `v`") rather than as a `readback → wrap → compare` chain.
///
/// Together with the sibling navigator [`find_by_label`] this closes
/// the three-arity closure on the `metadata.labels.<label>` per-CR
/// selector-axis — accessor [`kube_metadata_label`] (f3d9fcd),
/// predicate [`kube_metadata_label_is`], navigator
/// [`find_by_label`] — bringing it to structural parity with the
/// three-arity closures on the sibling identity axis (accessor
/// [`kube_name`], predicate [`kube_name_is`], navigator
/// [`find_by_name`]), the sibling namespace-scoping axis (accessor
/// [`kube_namespace`], predicate [`kube_namespace_is`], navigator
/// [`find_by_namespace`]), the sibling top-level `kind:` discriminator
/// axis (accessor [`kube_kind`], predicate [`kube_kind_is`], navigator
/// [`find_by_kind`]), and the sibling top-level `apiVersion:` axis
/// (accessor [`kube_api_version`], predicate [`kube_api_version_is`],
/// navigator [`find_by_api_version`]) — the K8s API-machinery pins
/// the identity and shape axes as the load-bearing per-CR
/// coordinates, and pins the labels sub-mapping as the load-bearing
/// per-CR selector-axis every label-selector consumer (Cilium
/// `endpointSelector.matchLabels`, Gateway API `HTTPRoute` parent-
/// selector, `kubectl -l` grep-by-label, Hubble flow grouping) keys
/// off. This closure brackets that selector-axis so every future
/// per-label predicate site (the future `app-operator`'s per-
/// Aplicacao `spec.selector.matchLabels` reconciler `label_selector`
/// pin, MESH-COMPOSITION §III.2 #5; the future per-`:politicas`
/// `CiliumClusterwideEnvoyConfig`-per-policy `LABEL_POLITICA` audit
/// surface, §III.2 #3; the future per-tenant CNP filter that gates
/// per-tenant slice CRs by the tenant-scoping label) reaches this
/// helper by construction, with no `== Some(...)` inline composition
/// and no drift surface on the `metadata.labels.<label>` sub-mapping
/// axis.
///
/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
#[must_use]
pub fn kube_metadata_label_is(value: &serde_yaml::Value, label: &str, expected: &str) -> bool {
kube_metadata_label(value, label) == Some(expected)
}
/// Locate the first K8s custom resource YAML document in `docs` whose
/// `metadata.labels.<label>` sub-mapping value byte-equals `expected`.
///
/// Composes on top of [`kube_metadata_label_is`] as
/// `docs.iter().find(|d| kube_metadata_label_is(d, label, expected))`
/// — the same one-hop `.iter().find(predicate)` navigator shape the
/// sibling identity-axis / namespace-scoping-axis / kind-axis /
/// apiVersion-axis navigators [`find_by_name`], [`find_by_namespace`],
/// [`find_by_kind`], [`find_by_api_version`] carry on their
/// respective pinned single-scalar-key axes, extended onto the
/// parametric `label` axis at the `metadata.labels.<label>` sub-
/// mapping depth. Returns `None` when no document in `docs` carries
/// the requested `(<label>, <expected>)` binding (the same "no match"
/// arm the sibling navigators return `None` on).
///
/// Composition-symmetric to [`kube_metadata_label_is`]: the lifted
/// predicate answers "does *this* one document carry label
/// `<label>` with value `<expected>`?", the lifted navigator answers
/// "find the first document carrying label `<label>` with value
/// `<expected>` in *this list*?". Same axis, different arity — the
/// two call shapes emit-side / operator-side / audit-side harnesses
/// reach for when slicing multi-doc CR emissions by per-CR selector-
/// value. Unlike the sibling scalar-axis navigators whose axis-key
/// is pinned inside the helper, this stays parametric on the
/// `<label>` axis-key argument because the K8s labels contract
/// admits an open-ended per-CR selector surface — the same navigator
/// applies to every emitted label ([`LABEL_APLICACAO`],
/// [`LABEL_CONTRATO`], [`LABEL_PROGRAM`], future
/// `LABEL_POLITICA` / tenant-scoping labels) with the axis-key
/// threaded through the call rather than pinned inside.
///
/// This closes the three-arity closure on the `metadata.labels.<label>`
/// per-CR selector-axis — accessor [`kube_metadata_label`] (f3d9fcd),
/// predicate [`kube_metadata_label_is`], navigator [`find_by_label`]
/// — bringing it to structural parity with the sibling three-arity
/// closures on the identity / namespace-scoping / kind / apiVersion
/// axes ([`kube_name`] / [`kube_name_is`] / [`find_by_name`];
/// [`kube_namespace`] / [`kube_namespace_is`] / [`find_by_namespace`];
/// [`kube_kind`] / [`kube_kind_is`] / [`find_by_kind`];
/// [`kube_api_version`] / [`kube_api_version_is`] /
/// [`find_by_api_version`]). Together the five closures bracket every
/// accessor arity on the K8s API-machinery's four load-bearing per-CR
/// discriminator axes plus the parametric labels selector-axis, so
/// every future multi-doc CR traversal — by identity, by namespace-
/// scoping, by CR shape, by CRD-group/version, or by open-ended
/// selector-value — reaches an axis-symmetric helper trio by
/// construction, with no inline `.iter().find(closure)` combinator
/// chain and no drift surface on the receiver-widen, combinator, or
/// axis-key axes.
///
/// Every future per-label multi-doc-navigator site (the M4 cross-
/// cluster fan-out's per-tenant `HelmRelease` split by tenant-scoping
/// label, MESH-COMPOSITION §III.2 #3; the future `app-operator`'s
/// per-Aplicacao CR selector-based join over emitted status docs,
/// §III.2 #5; the future per-`:politicas`
/// `CiliumClusterwideEnvoyConfig`-per-policy `LABEL_POLITICA` locator
/// over the sibling L7-policy emission) reaches this same helper by
/// construction, with no inline `.iter().find(closure)` combinator
/// chain and no drift surface on the receiver-widen, combinator, or
/// axis-key axes.
#[must_use]
pub fn find_by_label<'a>(
docs: &'a [serde_yaml::Value],
label: &str,
expected: &str,
) -> Option<&'a serde_yaml::Value> {
docs.iter()
.find(|d| kube_metadata_label_is(d, label, expected))
}
/// Read the top-level `spec:` sub-mapping on a K8s custom resource YAML
/// document as `Option<&serde_yaml::Mapping>` — the sub-mapping-arity
/// accessor peer on the sibling top-level `spec:` sub-block, structural
/// mirror of the recently-lifted [`kube_metadata_labels`] (f3d9fcd) on
/// the sub-`metadata.labels` sub-block. Where [`kube_metadata_labels`]
/// closes the two-hop `metadata → labels → as_mapping` readback on the
/// per-CR labels-selector sub-mapping, this closes the one-hop
/// `spec → as_mapping` readback on the per-CR body sub-mapping every
/// K8s API-machinery `CustomResource` pins as the sibling load-bearing
/// per-CR sub-block. Body folds the two-hop
/// `.get(KUBE_KEY_SPEC).and_then(|s| s.as_mapping())` chain onto one
/// substrate-primitive method call the caller reads as intent
/// (`kube_spec(<value>)` — "read this K8s CR's `spec:` sub-mapping").
///
/// Returns `None` on either short-circuit arm the underlying inline
/// chain closes: the outer `spec:` block is absent (a legally-omitted
/// per-CR body sub-block on `List`-shaped documents or otherwise
/// spec-less CRs), or the `spec:` value is present but carries a non-
/// Mapping YAML type (a schema-invalid body shape per the K8s API-
/// machinery contract that pins the per-CR body sub-block as a
/// Mapping, but tolerated here as `None` so the readback stays a total
/// function). The returned `&Mapping` borrows into the input `Value`
/// — the caller decides whether to enumerate (`for (k, v) in
/// spec { ... }`), further-navigate (`spec.get(CILIUM_KEY_INGRESS)`),
/// or clone.
///
/// Structural peer to sibling [`kube_metadata_labels`] on the sub-
/// mapping-arity readback axis: both accessors gate on Mapping shape
/// (folding the trailing `.as_mapping()` closure onto the helper),
/// pin their respective canonical sub-block axis-key inside the
/// helper (`KUBE_KEY_LABELS` for [`kube_metadata_labels`],
/// [`KUBE_KEY_SPEC`] for this accessor), and return
/// `Option<&serde_yaml::Mapping>` — together they bracket the two
/// canonical top-level sub-mapping readbacks every K8s CR document
/// the emit-side [`kube_resource_skeleton`] renders carries
/// (`metadata.labels` for the per-CR selector surface via the sibling
/// sub-`metadata:` accessor's composition, `spec` for the per-CR body
/// surface via this accessor's direct navigation).
///
/// The canonical shape ≥30 test-side per-CR readback sites across
/// [`caixa-mesh`][mesh] previously carried inline as the two-token
/// composition
///
/// ```ignore
/// value
/// .get(KUBE_KEY_SPEC)
/// .and_then(|s| s.get(<SUB_FIELD>))
/// ...
/// ```
///
/// around a one-token semantic payload (the `<SUB_FIELD>` axis-key —
/// [`CILIUM_KEY_INGRESS`] on the per-CNP ingress-rules readback,
/// [`GATEWAY_API_KEY_PARENT_REFS`] on the per-HTTPRoute parent-Gateway
/// readback, [`GATEWAY_API_KEY_LISTENERS`] on the per-Gateway
/// listener-set readback). After this lift, every routed consumer
/// folds the outer navigation onto `kube_spec(value).and_then(|s|
/// s.get(<SUB_FIELD>))` — the two-hop `spec → as_mapping` outer walk
/// happens once inside the helper, and the trailing `Mapping::get`
/// stays composition-symmetric with the sibling `Value::get` shape the
/// unlifted chain carried (`Mapping::get(&str)` and
/// `Value::get(&str)` both return `Option<&Value>` in `serde_yaml` 0.9,
/// so the fold is a drop-in for every routed callback body).
///
/// Every future per-CR body-sub-block readback (the future
/// per-`:politicas` `CiliumClusterwideEnvoyConfig` emitter's per-
/// policy `spec.rules[]` readback, MESH-COMPOSITION §III.2 #3; the
/// `app-operator`'s per-Aplicacao `mesh.pleme.io/v1alpha1/Aplicacao`
/// CR materializer's `spec.membros`/`spec.contratos` navigation,
/// §III.2 #5; the future `caixa-otel` per-Servico OTel-Collector CR's
/// `spec.receivers` readback; every future test-side `spec.*` probe
/// the M3.x + M4 renderer set adds) reaches through this one accessor
/// by construction — no per-consumer two-hop chain re-inline, no
/// per-consumer `KUBE_KEY_SPEC` axis-key drift, no coordinated rewrite
/// across every per-CR body-sub-block readback on a future K8s API-
/// machinery rebrand of the top-level `spec:` axis (a schema-migration
/// to a wrapped `specV2:` sub-block under a versioned CRD evolution
/// axis, a per-tenant migration to a nested `spec.tenant.*` scoped
/// sub-namespace under Server-Side-Apply's per-field ownership
/// annotations).
///
/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
#[must_use]
pub fn kube_spec(value: &serde_yaml::Value) -> Option<&serde_yaml::Mapping> {
// Post-lift body: composes on the substrate-primitive
// [`kube_root_map_field`] parametric root-axis accessor rather
// than re-inlining the two-hop `value.get(KUBE_KEY_SPEC)
// .and_then(|s| s.as_mapping())` chain. Structural mirror of the
// sibling [`kube_metadata`] recomposition on the same lifted helper.
kube_root_map_field(value, KUBE_KEY_SPEC)
}
/// Read the sub-`spec.<field>` value on a K8s custom resource YAML
/// document as `Option<&serde_yaml::Value>` — the composed scalar-arity
/// per-sub-field-Value accessor peer that stands on the sub-mapping-arity
/// [`kube_spec`] (9b028ee) accessor, closing the two-hop
/// `spec → as_mapping → get(<field>)` composition the load-bearing
/// per-CR body sub-block admits. Structural mirror of the way the
/// sibling scalar-arity per-sub-field accessors on the sub-`metadata:`
/// axis compose on the parametric [`kube_metadata_str_field`] (6809867)
/// primitive: the scalar-arity per-sub-field-Value accessor stands on
/// the sub-mapping-arity sub-block accessor, folding the "read one
/// specific sub-field Value" question onto one parametric method call
/// the caller reads as intent (`kube_spec_field(<value>, <FIELD>)` —
/// "read this K8s CR's `spec.<FIELD>` sub-Value") rather than as a
/// hand-spelled two-hop `outer readback → sub-mapping shape gate →
/// per-key lookup` chain the routed convergence pattern
/// [`kube_spec_composes_with_further_sub_field_navigation`] already
/// pinned as the drop-in fold shape.
///
/// Returns `None` on any of the three short-circuit arms folded through
/// the underlying composition: the outer `spec:` block is absent (the
/// [`kube_spec`] outer-arm short-circuit), the `spec:` value is present
/// but carries a non-Mapping YAML type (the [`kube_spec`] shape-gate
/// short-circuit), or the requested sub-field `<field>` axis-key is
/// absent from the `spec:` sub-mapping (the trailing `Mapping::get`
/// none-arm). The returned `&Value` borrows into the input `Value` —
/// the caller decides whether to further-navigate (`.as_sequence()`,
/// `.get(<DEEPER_KEY>)`), scalar-readback (`.as_str()`, `.as_u64()`),
/// or clone. The `field` axis stays parametric (rather than pinned to
/// a specific canonical sub-field like [`CILIUM_KEY_INGRESS`] or
/// [`GATEWAY_API_KEY_LISTENERS`] as separate helpers) because the K8s
/// `CustomResource` per-CRD-kind spec-body contract admits an
/// open-ended per-CR sub-field surface — one helper covers every
/// emitted `spec.<field>` axis-key today ([`CILIUM_KEY_INGRESS`],
/// [`CILIUM_KEY_ENDPOINT_SELECTOR`], [`GATEWAY_API_KEY_LISTENERS`],
/// [`GATEWAY_API_KEY_PARENT_REFS`], [`GATEWAY_API_KEY_HOSTNAMES`],
/// [`GATEWAY_API_KEY_GATEWAY_CLASS_NAME`], and every future
/// per-CRD-kind body-axis) with the axis-key threaded through the call.
///
/// The canonical shape 32 test-side per-CR readback sites across
/// [`caixa-mesh`][mesh] previously carried inline as the two-line
/// composition
///
/// ```ignore
/// kube_spec(value)
/// .and_then(|s| s.get(<FIELD>))
/// ...
/// ```
///
/// around a one-token semantic payload (the `<FIELD>` sub-field
/// axis-key). After this lift, every routed consumer folds the
/// outer two-hop navigation onto `kube_spec_field(value, <FIELD>)`
/// — the outer `spec → as_mapping → get(<field>)` walk happens once
/// inside the helper, and the caller keeps its downstream idiom
/// (`.and_then(|i| i.as_sequence())`, `.and_then(|c| c.get(...))`,
/// `.and_then(|v| v.as_str())`) unchanged.
///
/// Structural peer on the composition-on-lifted-sub-mapping axis to
/// sibling [`kube_metadata_label`] (f3d9fcd) on the sub-
/// `metadata.labels.<label>` per-selector axis: both accessors compose
/// on top of a same-crate sub-mapping-arity accessor primitive, both
/// close a canonical two-hop `sub-block → per-key lookup` navigation
/// onto one parametric substrate helper the caller reaches for by
/// intent, and both stay parametric on the per-key axis-key (labels
/// stays open-ended on the K8s selector surface, spec-body stays
/// open-ended on the K8s CRD-schema surface). Return-shape splits on
/// the sibling's shape gate — [`kube_metadata_label`] returns
/// `Option<&str>` because the labels contract pins every value as a
/// string scalar, this returns `Option<&Value>` because the spec-body
/// contract admits nested Mappings, Sequences, scalars, and unions
/// per-CRD-kind. The caller closes the trailing shape-gate at
/// call-site, mirroring the `metadata.labels` sub-mapping-arity
/// accessor's caller-side-shape-gate discipline
/// ([`kube_metadata_labels`] returns `Option<&Mapping>` and callers
/// pick their per-consumer shape-gate on top).
///
/// Every future per-CR body-sub-field readback (the future
/// per-`:politicas` `CiliumClusterwideEnvoyConfig` emitter's per-policy
/// `spec.rules[]` readback, MESH-COMPOSITION §III.2 #3; the
/// `app-operator`'s per-Aplicacao `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's `spec.membros`/`spec.contratos` navigation, §III.2 #5;
/// the future `caixa-otel` per-Servico OTel-Collector CR's
/// `spec.receivers` readback; every future test-side `spec.<field>`
/// probe the M3.x + M4 renderer set adds) reaches this same helper by
/// construction — no per-consumer two-hop chain re-inline, no
/// per-consumer `KUBE_KEY_SPEC` axis-key drift, no coordinated rewrite
/// across every per-CR body-sub-field readback on a future K8s
/// API-machinery rebrand of the top-level `spec:` axis (a schema-
/// migration to a wrapped `specV2:` sub-block under a versioned CRD
/// evolution axis, a per-tenant migration to a nested `spec.tenant.*`
/// scoped sub-namespace under Server-Side-Apply's per-field ownership
/// annotations).
///
/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
#[must_use]
pub fn kube_spec_field<'a>(
value: &'a serde_yaml::Value,
field: &str,
) -> Option<&'a serde_yaml::Value> {
kube_spec(value).and_then(|s| s.get(field))
}
/// Read the sub-`spec.<field>` string-scalar on a K8s custom resource YAML
/// document as `Option<&str>` — the composed scalar-str-arity per-sub-
/// field-string accessor peer that stands on the composed scalar-arity
/// [`kube_spec_field`] (23bd568) accessor, folding the trailing
/// `.and_then(|v| v.as_str())` shape-gate closure into the helper for
/// callers that always want a string scalar (the load-bearing per-CR
/// body-sub-field scalar readbacks — `spec.path` on `Kustomization`,
/// `spec.url` on `GitRepository`, `spec.timeout` on `Kustomization`,
/// `spec.interval` on the Flux v2 controller CR family,
/// `spec.gatewayClassName` on `Gateway`). Structural mirror of the
/// sibling [`kube_metadata_str_field`] (6809867) on the sub-`metadata:`
/// axis: both accessors fold a trailing `.as_str()` shape-gate onto a
/// two-hop sub-block-then-per-field navigation, both return
/// `Option<&str>` for callers that pin a string scalar downstream, both
/// stay parametric on the per-`<field>` sub-field axis-key. Where
/// [`kube_metadata_str_field`] closes the `metadata.<field>` string-
/// scalar readback at the sub-`metadata:` axis, this closes the
/// `spec.<field>` string-scalar readback at the sub-`spec:` axis. The
/// two together bracket the two canonical top-level sub-mapping-and-
/// per-sub-field string-scalar readback surfaces every K8s CR document
/// the emit-side [`kube_resource_skeleton`] renders admits:
/// `metadata.<field>` for per-CR identity string coordinates via the
/// sibling, `spec.<field>` for per-CR body string coordinates via this
/// accessor.
///
/// Returns `None` on any of the four short-circuit arms folded through
/// the underlying composition: the outer `spec:` block is absent (the
/// [`kube_spec`] outer-arm short-circuit), the `spec:` value is present
/// but carries a non-Mapping YAML type (the [`kube_spec`] shape-gate
/// short-circuit), the requested sub-field `<field>` axis-key is absent
/// from the `spec:` sub-mapping (the [`kube_spec_field`] trailing
/// `Mapping::get` none-arm), or the sub-field value is present but
/// carries a non-string YAML type (the trailing `.as_str()` shape gate
/// short-circuit — a schema-invalid per-CR body-sub-field type per the
/// K8s apiserver's `OpenAPI` schema but tolerated here as `None` so the
/// readback stays a total function). The returned `&str` borrows into
/// the input `Value` — the caller decides whether to compare (`==`),
/// clone (`.to_string()`), unwrap-then-panic (`.expect(...)`), or route
/// a fallback (`.unwrap_or(...)`).
///
/// The canonical shape 7 test-side per-CR readback sites across
/// [`caixa-flux`][flux] (6) + [`caixa-mesh`][mesh] (1) previously
/// carried inline as the two-line composition
///
/// ```ignore
/// kube_spec_field(<value>, <FIELD>)
/// .and_then(|v| v.as_str())
/// ...
/// ```
///
/// around a one-token semantic payload (the `<FIELD>` sub-field axis-
/// key — [`FLUX_KUSTOMIZATION_KEY_PATH`] on 2 sites,
/// [`FLUX_KUSTOMIZATION_KEY_TIMEOUT`] on 1 site,
/// [`FLUX_GITREPOSITORY_KEY_URL`] on 1 site, [`FLUX_KEY_INTERVAL`] on
/// 1 site, [`GATEWAY_API_KEY_GATEWAY_CLASS_NAME`] on 1 site). After
/// this lift, every routed consumer folds the two-hop-plus-shape-gate
/// navigation onto `kube_spec_str_field(<value>, <FIELD>)` — the outer
/// `spec → as_mapping → get(<field>) → as_str` walk happens once inside
/// the helper, and the caller keeps its downstream idiom (`.expect(...)`,
/// `.unwrap_or_else(...)`, `== Some(<VALUE>)`, `assert_eq!(..,
/// Some(<VALUE>))`) unchanged — the lift closes the navigation surface,
/// not the per-site error-handling posture.
///
/// Sites lifted include caixa-flux's
/// `cluster_bundle_kustomization_path_pins_lifted_sub_tree` and
/// `cluster_bundle_kustomization_path_matches_lifted_sub_tree_composer`
/// (per-`Kustomization` `spec.path`),
/// `cluster_bundle_kustomization_timeout_uses_lifted_default` (per-
/// `Kustomization` `spec.timeout`),
/// `cluster_bundle_gitrepository_url_pins_lifted_default` (per-
/// `GitRepository` `spec.url`),
/// `cluster_bundle_flux_cr_docs_carry_lifted_interval` (per-CR
/// `spec.interval`), plus caixa-mesh's
/// `gateway_routes_gateway_uses_lifted_default_gateway_class_name`
/// (per-`Gateway` `spec.gatewayClassName`).
///
/// Every future per-CR `spec.<field>` string-scalar readback (the future
/// per-`:politicas` `CiliumClusterwideEnvoyConfig` emitter's per-policy
/// `spec.rules[].name` navigation, MESH-COMPOSITION §III.2 #3; the
/// `app-operator`'s per-Aplicacao `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's `spec.*` scalar readbacks, §III.2 #5; the future
/// `caixa-otel` per-Servico OpenTelemetry-Collector CR's `spec.*`
/// scalar navigation; every future test-side `spec.<field>` string-
/// scalar probe the M3.x + M4 renderer set adds) reaches this same
/// helper by construction — no per-consumer two-hop-plus-shape-gate
/// chain re-inline, no per-consumer `KUBE_KEY_SPEC` axis-key drift, no
/// coordinated rewrite across every per-CR body-sub-field string
/// readback on a future K8s API-machinery rebrand of the top-level
/// `spec:` axis.
///
/// [flux]: https://github.com/pleme-io/caixa/tree/main/caixa-flux
/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
#[must_use]
pub fn kube_spec_str_field<'a>(value: &'a serde_yaml::Value, field: &str) -> Option<&'a str> {
kube_spec_field(value, field).and_then(|v| v.as_str())
}
/// Read the sub-`spec.<field>` YAML sequence on a K8s custom resource YAML
/// document as `Option<&serde_yaml::Sequence>` — the composed sequence-
/// arity per-sub-field-sequence accessor peer that stands on the composed
/// scalar-arity [`kube_spec_field`] (23bd568) accessor, folding the
/// trailing `.and_then(|v| v.as_sequence())` shape-gate closure into the
/// helper for callers that always want a sequence (the load-bearing per-
/// CR body-sub-field sequence readbacks — `spec.ingress[]` on the
/// `CiliumNetworkPolicy` per-`:contratos` L4/L7 rule fan-out,
/// `spec.listeners[]` / `spec.parentRefs[]` / `spec.hostnames[]` on the
/// Gateway API `Gateway` / `HTTPRoute` per-`:entrada` overlay,
/// `spec.rules[]` on the Gateway API `HTTPRoute` per-rule match/backend
/// fan-out, `spec.healthChecks[]` on the Flux v2 `Kustomization`
/// per-CR health-check block). Structural mirror of the sibling
/// [`kube_spec_str_field`] (c4fe21d) on the same sub-`spec.<field>` axis:
/// both accessors fold a trailing shape-gate closure onto the composed
/// scalar-arity [`kube_spec_field`] two-hop navigation, both stay
/// parametric on the per-`<field>` sub-field axis-key. Where
/// [`kube_spec_str_field`] closes the `spec.<field>` string-scalar
/// readback (the leaf-scalar arm), this closes the `spec.<field>`
/// sequence readback (the multi-entry sub-block arm). Together they
/// bracket the two canonical composed per-`spec.<field>` shape-gate
/// arities every K8s CR document the emit-side [`kube_resource_skeleton`]
/// renders carries under its `spec:` body: scalar-string leaves via the
/// sibling, ordered sub-sequences via this accessor.
///
/// Returns `None` on any of the four short-circuit arms folded through
/// the underlying composition: the outer `spec:` block is absent (the
/// [`kube_spec`] outer-arm short-circuit), the `spec:` value is present
/// but carries a non-Mapping YAML type (the [`kube_spec`] shape-gate
/// short-circuit), the requested sub-field `<field>` axis-key is absent
/// from the `spec:` sub-mapping (the [`kube_spec_field`] trailing
/// `Mapping::get` none-arm), or the sub-field value is present but
/// carries a non-sequence YAML type (the trailing `.as_sequence()`
/// shape-gate short-circuit — a schema-invalid per-CR body-sub-field
/// type per the K8s apiserver's `OpenAPI` schema but tolerated here as
/// `None` so the readback stays a total function). The returned
/// `&Sequence` borrows into the input `Value` — the caller decides
/// whether to iterate (`.iter()`), pick the first entry
/// (`.first()`), enumerate for length (`.len()`), or clone.
///
/// The canonical shape ≥30 test-side per-CR readback sites across
/// [`caixa-mesh`][mesh] + [`caixa-flux`][flux] previously carried inline
/// as the two-line composition
///
/// ```ignore
/// kube_spec_field(<value>, <FIELD>)
/// .and_then(|v| v.as_sequence())
/// ...
/// ```
///
/// around a one-token semantic payload (the `<FIELD>` sub-field axis-
/// key — [`CILIUM_KEY_INGRESS`] on the per-CNP ingress-rules readback,
/// [`GATEWAY_API_KEY_LISTENERS`] on the per-`Gateway` listener-set
/// readback, [`GATEWAY_API_KEY_PARENT_REFS`] on the per-`HTTPRoute`
/// parent-`Gateway` readback, [`GATEWAY_API_KEY_HOSTNAMES`] on the
/// per-`HTTPRoute` host-set readback, [`KUBE_KEY_RULES`] on the per-
/// `HTTPRoute` rule-set readback, `FLUX_KEY_HEALTH_CHECKS` on the per-
/// `Kustomization` health-check-set readback). After this lift, every
/// routed consumer
/// folds the two-hop-plus-shape-gate navigation onto
/// `kube_spec_seq_field(<value>, <FIELD>)` — the outer
/// `spec → as_mapping → get(<field>) → as_sequence` walk happens once
/// inside the helper, and the caller keeps its downstream idiom
/// (`.and_then(|s| s.first())`, `.iter().find(...)`, `.expect(...)`,
/// `.unwrap_or_else(...)`) unchanged — the lift closes the navigation
/// surface, not the per-site continuation posture.
///
/// Every future per-CR `spec.<field>` sequence readback (the future
/// per-`:politicas` `CiliumClusterwideEnvoyConfig` emitter's per-policy
/// `spec.rules[]` navigation, MESH-COMPOSITION §III.2 #3; the
/// `app-operator`'s per-Aplicacao `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's `spec.membros[]` / `spec.contratos[]` readback, §III.2
/// #5; the future `caixa-otel` per-Servico OpenTelemetry-Collector CR's
/// `spec.receivers[]` navigation; every future test-side
/// `spec.<field>[]` sequence probe the M3.x + M4 renderer set adds)
/// reaches this same helper by construction — no per-consumer two-hop-
/// plus-shape-gate chain re-inline, no per-consumer `KUBE_KEY_SPEC`
/// axis-key drift, no coordinated rewrite across every per-CR body-
/// sub-field sequence readback on a future K8s API-machinery rebrand of
/// the top-level `spec:` axis.
///
/// [flux]: https://github.com/pleme-io/caixa/tree/main/caixa-flux
/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
#[must_use]
pub fn kube_spec_seq_field<'a>(
value: &'a serde_yaml::Value,
field: &str,
) -> Option<&'a serde_yaml::Sequence> {
kube_spec_field(value, field).and_then(|v| v.as_sequence())
}
/// Read the sub-`spec.<field>` YAML sub-mapping on a K8s custom resource
/// YAML document as `Option<&serde_yaml::Mapping>` — the composed sub-
/// mapping-arity per-sub-field-mapping accessor peer that stands on the
/// composed scalar-arity [`kube_spec_field`] (23bd568) accessor, folding
/// the trailing `.and_then(|v| v.as_mapping())` shape-gate closure into
/// the helper for callers that always want a mapping (the load-bearing
/// per-CR body-sub-field mapping readbacks — `spec.values` on the
/// Flux v2 `HelmRelease` per-cluster override wrap, `spec.chart` on the
/// Flux v2 `HelmRelease` nested `HelmChartTemplate.spec` sub-document,
/// `spec.sourceRef` on the Flux v2 `Kustomization` / `HelmChart`
/// per-CR source reference block, the future per-`:politicas`
/// `CiliumClusterwideEnvoyConfig` emitter's per-policy
/// `spec.endpointSelector` navigation, and every other sub-`spec.<field>`
/// nested-object axis the M3.x + M4 renderer set materializes).
/// Structural mirror of the sibling [`kube_spec_str_field`] (c4fe21d)
/// and [`kube_spec_seq_field`] (fc64ed7) accessors on the same sub-
/// `spec.<field>` axis: all three accessors fold a trailing shape-gate
/// closure onto the composed scalar-arity [`kube_spec_field`] two-hop
/// navigation, all three stay parametric on the per-`<field>` sub-field
/// axis-key. Where [`kube_spec_str_field`] closes the `spec.<field>`
/// string-scalar readback (the leaf-scalar arm) and [`kube_spec_seq_field`]
/// closes the `spec.<field>` sequence readback (the multi-entry sub-
/// block arm), this closes the `spec.<field>` sub-mapping readback (the
/// nested-object sub-block arm). Together the three bracket the three
/// canonical composed per-`spec.<field>` shape-gate arities every K8s CR
/// document the emit-side [`kube_resource_skeleton`] renders admits
/// under its `spec:` body: scalar-string leaves via the first sibling,
/// ordered sub-sequences via the second sibling, nested-object sub-
/// mappings via this accessor. Closes the composed shape-gate family on
/// the sub-`spec.<field>` axis to structural parity with `serde_yaml`'s
/// own `Value::{as_str, as_sequence, as_mapping}` shape-gate trio on the
/// outer `Value`.
///
/// Returns `None` on any of the four short-circuit arms folded through
/// the underlying composition: the outer `spec:` block is absent (the
/// [`kube_spec`] outer-arm short-circuit), the `spec:` value is present
/// but carries a non-Mapping YAML type (the [`kube_spec`] shape-gate
/// short-circuit), the requested sub-field `<field>` axis-key is absent
/// from the `spec:` sub-mapping (the [`kube_spec_field`] trailing
/// `Mapping::get` none-arm), or the sub-field value is present but
/// carries a non-mapping YAML type (the trailing `.as_mapping()` shape-
/// gate short-circuit — a schema-invalid per-CR body-sub-field type per
/// the K8s apiserver's `OpenAPI` schema but tolerated here as `None` so
/// the readback stays a total function). The returned `&Mapping` borrows
/// into the input `Value` — the caller decides whether to look up a
/// nested key (`.get(<KEY>)`), enumerate for length (`.len()`), iterate
/// (`.iter()`), or check emptiness (`.is_empty()`).
///
/// The canonical shape 4 test-side per-CR readback sites in
/// [`caixa-flux`][flux] previously carried inline as the two-line
/// composition
///
/// ```ignore
/// kube_spec_field(<value>, <FIELD>)
/// .and_then(|v| v.as_mapping())
/// ...
/// ```
///
/// around a one-token semantic payload (the `<FIELD>` sub-field axis-
/// key — [`FLUX_KEY_VALUES`] on the per-`HelmRelease` per-cluster
/// override wrap, [`FLUX_KEY_SOURCE_REF`] on the per-`Kustomization`
/// bootstrap source reference, [`FLUX_KEY_CHART`] on the per-
/// `HelmRelease` nested `HelmChartTemplate.spec` sub-document). After
/// this lift, every routed consumer folds the two-hop-plus-shape-gate
/// navigation onto `kube_spec_map_field(<value>, <FIELD>)` — the outer
/// `spec → as_mapping → get(<field>) → as_mapping` walk happens once
/// inside the helper, and the caller keeps its downstream idiom
/// (`.get(<KEY>)`, `.is_empty()`, `.len()`, `.expect(...)`) unchanged —
/// the lift closes the navigation surface, not the per-site readback
/// posture.
///
/// Every future per-CR `spec.<field>` sub-mapping readback (the future
/// per-`:politicas` `CiliumClusterwideEnvoyConfig` emitter's per-policy
/// `spec.endpointSelector` navigation, MESH-COMPOSITION §III.2 #3; the
/// `app-operator`'s per-Aplicacao `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer's `spec.entrada` / `spec.placement` nested-object
/// readback, §III.2 #5; the future `caixa-otel` per-Servico
/// OpenTelemetry-Collector CR's `spec.exporters.<name>` nested-object
/// navigation; every future test-side `spec.<field>` sub-mapping probe
/// the M3.x + M4 renderer set adds) reaches this same helper by
/// construction — no per-consumer two-hop-plus-shape-gate chain re-
/// inline, no per-consumer `KUBE_KEY_SPEC` axis-key drift, no
/// coordinated rewrite across every per-CR body-sub-field sub-mapping
/// readback on a future K8s API-machinery rebrand of the top-level
/// `spec:` axis.
///
/// [flux]: https://github.com/pleme-io/caixa/tree/main/caixa-flux
#[must_use]
pub fn kube_spec_map_field<'a>(
value: &'a serde_yaml::Value,
field: &str,
) -> Option<&'a serde_yaml::Mapping> {
kube_spec_field(value, field).and_then(|v| v.as_mapping())
}
/// Read the top-level `metadata:` sub-mapping on a K8s custom resource
/// YAML document as `Option<&serde_yaml::Mapping>` — the sub-mapping-
/// arity accessor peer on the sibling top-level `metadata:` sub-block,
/// structural mirror of the recently-lifted [`kube_spec`] (9b028ee) on
/// the top-level `spec:` sub-block. Where [`kube_spec`] closes the one-
/// hop `spec → as_mapping` readback on the per-CR body sub-mapping,
/// this closes the same one-hop `metadata → as_mapping` readback on
/// the per-CR identity/labels sub-mapping every K8s API-machinery
/// `ObjectMeta` pins as the load-bearing sibling per-CR sub-block.
/// Body folds the two-hop `.get(KUBE_KEY_METADATA).and_then(|m|
/// m.as_mapping())` chain onto one substrate-primitive method call the
/// caller reads as intent (`kube_metadata(<value>)` — "read this K8s
/// CR's `metadata:` sub-mapping").
///
/// Returns `None` on either short-circuit arm the underlying inline
/// chain closes: the outer `metadata:` block is absent (a legally-
/// omitted per-CR identity sub-block on `List`-shaped documents that
/// carry no per-item metadata, or on external YAML shapes that carry
/// no [`ObjectMeta`][om]-flavoured header), or the `metadata:` value
/// is present but carries a non-Mapping YAML type (a schema-invalid
/// identity shape per the K8s API-machinery contract that pins the
/// per-CR identity sub-block as a Mapping, but tolerated here as
/// `None` so the readback stays a total function). The returned
/// `&Mapping` borrows into the input `Value` — the caller decides
/// whether to enumerate (`for (k, v) in metadata { ... }`), further-
/// navigate (`metadata.get(KUBE_KEY_LABELS)`), or clone.
///
/// Structural peer to sibling [`kube_spec`] on the sub-mapping-arity
/// readback axis: both accessors gate on Mapping shape (folding the
/// trailing `.as_mapping()` closure onto the helper), pin their
/// respective canonical sub-block axis-key inside the helper
/// ([`KUBE_KEY_METADATA`] for this accessor, [`KUBE_KEY_SPEC`] for
/// [`kube_spec`]), and return `Option<&serde_yaml::Mapping>` —
/// together they bracket the two canonical top-level sub-mapping
/// readbacks every K8s CR document the emit-side
/// [`kube_resource_skeleton`] renders carries (`metadata` for the
/// per-CR identity/labels surface via this accessor's direct
/// navigation, `spec` for the per-CR body surface via [`kube_spec`]).
///
/// The canonical shape 4 caixa-mesh test-side per-CR readback sites
/// (across the `cilium_policy_carries_canonical_kube_skeleton`,
/// `gateway_carries_canonical_kube_skeleton_without_labels`,
/// `httproute_carries_canonical_kube_skeleton_without_labels`, and
/// `cilium_policy_metadata_iterates_alphabetically` test-harness
/// probes) previously carried inline as the two-token composition
///
/// ```ignore
/// value
/// .get(KUBE_KEY_METADATA)
/// .and_then(|m| m.as_mapping())
/// ```
///
/// around a downstream `metadata` sub-view bind that then reaches
/// for further per-metadata-key probes (`.len()` for the axis-count
/// pin, `.get(KUBE_KEY_LABELS)` for the label-block presence probe,
/// `.iter().filter_map(...)` for the alphabetical-iteration
/// determinism pin, `.get(KUBE_KEY_NAME).and_then(|v| v.as_str())`
/// for the metadata.name scalar-readback continuation the sibling
/// [`kube_name`] pinned accessor resolves through its own pinned
/// two-hop chain). After this lift, every routed test site folds the
/// outer navigation onto `kube_metadata(value).expect("metadata
/// mapping")` — the two-hop `metadata → as_mapping` outer walk
/// happens once inside the helper, and every downstream `.get(...)`,
/// `.iter()`, `.len()` continuation stays composition-symmetric
/// against the returned `&Mapping`.
///
/// Sibling scalar-arity peers [`kube_name`] (c9cdecb) /
/// [`kube_namespace`] (e18297b) and the composed
/// [`kube_metadata_labels`] (f3d9fcd) / [`kube_metadata_label`]
/// (f3d9fcd) sub-mapping-arity peer resolve their respective
/// sub-`metadata.*` axes through their own pinned inline chains;
/// this accessor closes the *outer* one-hop readback so a caller
/// that needs the whole `metadata` sub-mapping (for a length /
/// iteration / whole-block presence pin the per-axis accessor does
/// not close) reaches through one navigation surface rather than a
/// per-consumer re-inlined two-hop chain.
///
/// Every future per-CR `metadata` sub-block readback (the future
/// per-`:politicas` `CiliumClusterwideEnvoyConfig` emitter's
/// per-policy metadata pins, MESH-COMPOSITION §III.2 #3; the
/// `app-operator`'s per-Aplicacao `mesh.pleme.io/v1alpha1/Aplicacao`
/// CR materializer's `metadata.annotations` navigation for
/// per-tenant scoping, §III.2 #5; the future `caixa-otel` per-
/// Servico OpenTelemetry-Collector CR's `metadata.ownerReferences`
/// readback for controller-owned GC-cascade wiring; every future
/// test-side `metadata.*` probe the M3.x + M4 renderer set adds)
/// reaches through this one accessor by construction — no
/// per-consumer two-hop chain re-inline, no per-consumer
/// [`KUBE_KEY_METADATA`] axis-key drift, no coordinated rewrite
/// across every per-CR identity-sub-block readback on a future K8s
/// API-machinery rebrand of the top-level `metadata:` axis.
///
/// [om]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#objectmeta-v1-meta
#[must_use]
pub fn kube_metadata(value: &serde_yaml::Value) -> Option<&serde_yaml::Mapping> {
// Post-lift body: composes on the substrate-primitive
// [`kube_root_map_field`] parametric root-axis accessor rather
// than re-inlining the two-hop `value.get(KUBE_KEY_METADATA)
// .and_then(|m| m.as_mapping())` chain. Structural mirror of the
// sibling [`kube_spec`] recomposition on the same lifted helper —
// both pinned peers on this root-level sub-mapping-arity axis now
// stand on one navigation primitive with only their pinned
// [`KUBE_KEY_<AXIS>`] const differing. A future K8s API-machinery
// rebrand on the `metadata:` axis reaches one substrate helper
// rather than a coordinated rewrite across every per-CR identity-
// sub-block accessor site.
kube_root_map_field(value, KUBE_KEY_METADATA)
}
/// Read the sub-`metadata.<field>` value on a K8s custom resource YAML
/// document as `Option<&serde_yaml::Value>` — the composed scalar-arity
/// per-sub-field-Value accessor peer that stands on the sub-mapping-
/// arity [`kube_metadata`] (f41c4fe) accessor, closing the two-hop
/// `metadata → as_mapping → get(<field>)` composition the load-bearing
/// per-CR identity sub-block admits. Structural mirror of the sibling
/// [`kube_spec_field`] (23bd568) on the sub-`spec:` axis: same
/// composition shape (scalar-arity sub-field-Value accessor stands on
/// sub-mapping-arity sub-block accessor), same return shape
/// (`Option<&serde_yaml::Value>` so the caller closes the trailing
/// shape-gate at call site), same parametric `field` axis (open-ended
/// on the K8s [`ObjectMeta`][om]-flavoured sub-field surface every
/// per-CR identity readback reaches for). The composition-pin test
/// [`kube_metadata_composes_with_further_sub_field_navigation`]
/// already documented this fold shape as the drop-in for a routed
/// caller — this lift makes the drop-in a substrate primitive rather
/// than a per-caller re-inlined two-hop chain.
///
/// Returns `None` on any of the three short-circuit arms folded
/// through the underlying composition: the outer `metadata:` block is
/// absent (the [`kube_metadata`] outer-arm short-circuit — a legally-
/// omitted per-CR identity sub-block on `List`-shaped documents or on
/// external YAML shapes that carry no [`ObjectMeta`][om]-flavoured
/// header), the `metadata:` value is present but carries a non-Mapping
/// YAML type (the [`kube_metadata`] shape-gate short-circuit — a
/// schema-invalid identity shape per the K8s API-machinery contract
/// tolerated here as `None` so the readback stays a total function),
/// or the requested sub-field `<field>` axis-key is absent from the
/// `metadata:` sub-mapping (the trailing `Mapping::get` none-arm — a
/// legally-omitted per-metadata-axis surface on a CR that carries
/// other identity sub-fields but not this one). The returned `&Value`
/// borrows into the input `Value` — the caller decides whether to
/// further-navigate (`.as_mapping()` to descend into
/// `metadata.labels`, `.as_sequence()` to descend into
/// `metadata.ownerReferences`, `.get(<DEEPER_KEY>)` to reach a
/// specific per-sub-field axis), scalar-readback (`.as_str()` for the
/// per-CR identity coordinates the sibling scalar-arity
/// [`kube_metadata_str_field`] pinned accessor closes on the trailing
/// shape gate, `.as_u64()` for numeric sub-fields a future CRD may
/// surface), or clone.
///
/// The `field` axis stays parametric (rather than pinned to a specific
/// canonical sub-field like [`KUBE_KEY_NAME`] or [`KUBE_KEY_LABELS`]
/// as separate helpers) because the K8s API-machinery
/// [`ObjectMeta`][om] contract admits an open-ended per-CR identity
/// sub-field surface — one helper covers every readback axis today
/// ([`KUBE_KEY_NAME`], [`KUBE_KEY_NAMESPACE`], [`KUBE_KEY_LABELS`],
/// and every future per-`ObjectMeta`-sub-field readback like
/// `metadata.annotations` for per-tenant scoping,
/// `metadata.ownerReferences` for GC-cascade wiring,
/// `metadata.generateName` on Server-Side-Apply-authored CRs,
/// `metadata.resourceVersion` on optimistic-concurrency-controlled
/// updates, `metadata.uid` on cross-CR owner-reference bookkeeping)
/// with the axis-key threaded through the call. The sibling scalar-
/// arity pinned peers on this same sub-mapping ([`kube_name`] c9cdecb
/// / [`kube_namespace`] e18297b / [`kube_metadata_label`] f3d9fcd)
/// still pin their respective canonical axis-keys inside the helper
/// because the K8s API-machinery pins those specific coordinates as
/// the load-bearing per-CR identity coordinates; this Value-returning
/// helper stays parametric for the rest of the open-ended sub-field
/// surface and for callers that need the raw `&Value` rather than a
/// shape-gated projection.
///
/// Structural peer on the composition-on-lifted-sub-mapping axis to
/// sibling [`kube_spec_field`] on the sub-`spec:` axis: both accessors
/// compose on top of a same-crate sub-mapping-arity accessor primitive
/// ([`kube_metadata`] here, [`kube_spec`] on the sibling), both close a
/// canonical two-hop `sub-block → per-key lookup` navigation onto one
/// parametric substrate helper the caller reaches for by intent, and
/// both stay parametric on the per-key axis-key (metadata stays open-
/// ended on the K8s [`ObjectMeta`][om] surface, spec-body stays open-
/// ended on the K8s CRD-schema surface). Together they bracket the two
/// canonical top-level sub-mapping-and-per-sub-field readback surfaces
/// every K8s CR document the emit-side [`kube_resource_skeleton`]
/// renders admits: `metadata.<field>` for per-CR identity coordinates
/// via this accessor, `spec.<field>` for per-CR body coordinates via
/// [`kube_spec_field`].
///
/// Peer on the return-shape refinement axis to sibling
/// [`kube_metadata_str_field`] (6809867) on the same sub-`metadata.*`
/// navigation depth: [`kube_metadata_str_field`] returns
/// `Option<&str>` because it folds a trailing `.as_str()` shape-gate
/// closure into the helper for callers that always want a
/// string-scalar (the load-bearing per-CR `metadata.name` /
/// `metadata.namespace` identity coordinates); this returns
/// `Option<&Value>` because the sub-metadata sub-field surface admits
/// nested Mappings (`metadata.labels`, `metadata.annotations`,
/// `metadata.ownerReferences`), Sequences, scalars, and unions per-
/// CRD-kind. The caller closes the trailing shape-gate at call site
/// when the specific axis admits a non-scalar shape, mirroring the
/// sibling [`kube_spec_field`]'s caller-side-shape-gate discipline.
///
/// Every future per-CR `metadata.<field>` sub-field readback (the
/// future per-`:politicas` `CiliumClusterwideEnvoyConfig` emitter's
/// per-policy `metadata.labels` / `metadata.annotations` navigation,
/// MESH-COMPOSITION §III.2 #3; the `app-operator`'s per-Aplicacao
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
/// `metadata.ownerReferences` readback for controller-owned GC-cascade
/// wiring, §III.2 #5; the future `caixa-otel` per-Servico
/// OpenTelemetry-Collector CR's `metadata.annotations` navigation for
/// per-tenant scoping; every future test-side `metadata.<field>` probe
/// the M3.x + M4 renderer set adds) reaches this same helper by
/// construction — no per-consumer two-hop chain re-inline, no per-
/// consumer [`KUBE_KEY_METADATA`] axis-key drift, no coordinated
/// rewrite across every per-CR identity-sub-field readback on a future
/// K8s API-machinery rebrand of the top-level `metadata:` axis (a
/// schema-migration to a wrapped `metadataV2:` sub-block under a
/// versioned CRD evolution axis, a per-CRD-side rename to a wrapped
/// `spec.metadata:` sub-mapping under Server-Side-Apply's per-field
/// ownership annotations).
///
/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
/// [om]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#objectmeta-v1-meta
#[must_use]
pub fn kube_metadata_field<'a>(
value: &'a serde_yaml::Value,
field: &str,
) -> Option<&'a serde_yaml::Value> {
kube_metadata(value).and_then(|m| m.get(field))
}
/// Read the sub-`metadata.<field>` YAML sub-mapping on a K8s custom
/// resource YAML document as `Option<&serde_yaml::Mapping>` — the
/// composed sub-mapping-arity per-sub-field-mapping accessor peer that
/// stands on the composed scalar-arity [`kube_metadata_field`]
/// (450f1ff) accessor, folding the trailing
/// `.and_then(|v| v.as_mapping())` shape-gate closure into the helper
/// for callers that always want a mapping (the load-bearing per-CR
/// identity sub-block mapping readbacks — `metadata.labels` on every
/// K8s CR the label-based selector join reaches through,
/// `metadata.annotations` on per-tenant scoping / Server-Side-Apply
/// field-ownership CRs, and every other sub-`metadata.<field>` nested-
/// object axis the [`ObjectMeta`][om] contract admits). Structural
/// mirror of the sibling [`kube_spec_map_field`] (27fc2ee) on the sub-
/// `spec.<field>` axis: both accessors fold a trailing
/// `.as_mapping()` shape-gate closure onto their crate's composed
/// scalar-arity per-sub-field-Value accessor primitive
/// ([`kube_metadata_field`] here, [`kube_spec_field`] on the sibling),
/// both stay parametric on the per-`<field>` sub-field axis-key
/// (metadata stays open-ended on the K8s [`ObjectMeta`][om]-sub-field
/// surface, spec-body stays open-ended on the K8s CRD-schema surface).
/// Together they bracket the two canonical composed per-sub-mapping-
/// field sub-mapping readback surfaces every K8s CR document the emit-
/// side [`kube_resource_skeleton`] renders admits:
/// `metadata.<field>` for per-CR identity nested-object coordinates
/// via this accessor, `spec.<field>` for per-CR body nested-object
/// coordinates via the sibling.
///
/// Returns `None` on any of the four short-circuit arms folded through
/// the underlying composition: the outer `metadata:` block is absent
/// (the [`kube_metadata`] outer-arm short-circuit — a legally-omitted
/// per-CR identity sub-block on `List`-shaped documents or on external
/// YAML shapes that carry no [`ObjectMeta`][om]-flavoured header),
/// the `metadata:` value is present but carries a non-Mapping YAML
/// type (the [`kube_metadata`] shape-gate short-circuit — a
/// schema-invalid identity shape per the K8s API-machinery contract
/// tolerated here as `None` so the readback stays a total function),
/// the requested sub-field `<field>` axis-key is absent from the
/// `metadata:` sub-mapping (the [`kube_metadata_field`] trailing
/// `Mapping::get` none-arm — a legally-omitted per-metadata-axis
/// surface on a CR that carries other identity sub-fields but not
/// this one), or the sub-field value is present but carries a non-
/// mapping YAML type (the trailing `.as_mapping()` shape-gate short-
/// circuit — a schema-invalid per-CR identity-sub-field type per the
/// K8s apiserver's `OpenAPI` schema but tolerated here as `None` so
/// the readback stays a total function). The returned `&Mapping`
/// borrows into the input `Value` — the caller decides whether to
/// look up a nested key (`.get(<KEY>)`), enumerate for length
/// (`.len()`), iterate (`.iter()`), or check emptiness
/// (`.is_empty()`).
///
/// The immediate load-bearing recomposition target is the sibling
/// pinned peer [`kube_metadata_labels`] (f3d9fcd) on the
/// `metadata.labels` sub-mapping axis: pre-lift it carried the raw
/// three-hop `value.get(KUBE_KEY_METADATA).and_then(|m|
/// m.get(KUBE_KEY_LABELS)).and_then(|l| l.as_mapping())` chain (the
/// same shape this parametric helper closes on with the sub-field
/// axis-key pinned to [`KUBE_KEY_LABELS`]); post-lift it composes as
/// `kube_metadata_map_field(value, KUBE_KEY_LABELS)`, folding onto
/// the substrate primitive rather than re-inlining the three-hop
/// walk. Structural mirror of the way sibling [`kube_name`] /
/// [`kube_namespace`] compose on [`kube_metadata_str_field`] rather
/// than re-walking the two-hop `metadata.<field>` navigation.
///
/// The `field` axis stays parametric (rather than pinned to a specific
/// canonical sub-field like [`KUBE_KEY_LABELS`] as separate helpers)
/// because the K8s API-machinery [`ObjectMeta`][om] contract admits an
/// open-ended per-CR identity sub-mapping-field surface — one helper
/// covers every readback axis today ([`KUBE_KEY_LABELS`] via the
/// composed pinned [`kube_metadata_labels`] peer, and every future
/// sub-mapping-shaped `ObjectMeta` sub-field like
/// `metadata.annotations` for per-tenant scoping / Server-Side-Apply
/// field-ownership annotations, per-CR admission-webhook config
/// hints, per-tenant label-prefix routing hints under M4 fan-out)
/// with the axis-key threaded through the call. Every future per-CR
/// `metadata.<field>` sub-mapping readback (the future
/// per-`:politicas` `CiliumClusterwideEnvoyConfig` emitter's per-
/// policy `metadata.annotations` navigation, MESH-COMPOSITION §III.2
/// #3; the `app-operator`'s per-Aplicacao
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
/// `metadata.annotations` readback for controller-owned admission-
/// hint wiring, §III.2 #5; the future `caixa-otel` per-Servico
/// OpenTelemetry-Collector CR's `metadata.annotations` navigation for
/// per-tenant scoping; every future test-side `metadata.<field>` sub-
/// mapping probe the M3.x + M4 renderer set adds) reaches this same
/// helper by construction — no per-consumer three-hop chain re-
/// inline, no per-consumer [`KUBE_KEY_METADATA`] axis-key drift, no
/// coordinated rewrite across every per-CR identity-sub-field sub-
/// mapping readback on a future K8s API-machinery rebrand of the
/// top-level `metadata:` axis.
///
/// [om]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#objectmeta-v1-meta
#[must_use]
pub fn kube_metadata_map_field<'a>(
value: &'a serde_yaml::Value,
field: &str,
) -> Option<&'a serde_yaml::Mapping> {
kube_metadata_field(value, field).and_then(|v| v.as_mapping())
}
/// Read the sub-`metadata.<field>` YAML sequence on a K8s custom
/// resource YAML document as `Option<&serde_yaml::Sequence>` — the
/// composed sequence-arity per-sub-field-sequence accessor peer that
/// stands on the composed scalar-arity [`kube_metadata_field`]
/// (450f1ff) accessor, folding the trailing
/// `.and_then(|v| v.as_sequence())` shape-gate closure into the helper
/// for callers that always want a sequence (the K8s API-machinery
/// [`ObjectMeta`][om]-flavoured sub-`metadata:` sequence axes —
/// `metadata.ownerReferences` on every controller-owned CR the
/// GC-cascade wiring reaches through, MESH-COMPOSITION §III.2 #5;
/// `metadata.finalizers` on every CR whose deletion drives an operator-
/// side pre-delete hook; `metadata.managedFields` on every
/// Server-Side-Apply-authored CR the per-field-ownership contract
/// enumerates). Structural mirror of the sibling [`kube_spec_seq_field`]
/// (fc64ed7) on the sub-`spec.<field>` axis: both accessors fold a
/// trailing `.as_sequence()` shape-gate closure onto their crate's
/// composed scalar-arity per-sub-field-Value accessor primitive
/// ([`kube_metadata_field`] here, [`kube_spec_field`] on the sibling),
/// both stay parametric on the per-`<field>` sub-field axis-key
/// (metadata stays open-ended on the K8s [`ObjectMeta`][om]-sub-field
/// surface, spec-body stays open-ended on the K8s CRD-schema surface).
/// Closes the three-arity shape-gate family on the sub-`metadata:`
/// axis to structural parity with the sub-`spec:` axis: the
/// [`kube_metadata_str_field`] scalar-string arm, this
/// [`kube_metadata_seq_field`] sequence arm, and the
/// [`kube_metadata_map_field`] (d03cc08) sub-mapping arm — all three
/// now compose on the same [`kube_metadata_field`] two-hop
/// `metadata → as_mapping → get(<field>)` primitive, structural mirror
/// of `serde_yaml`'s own `Value::{as_str, as_sequence, as_mapping}`
/// shape-gate trio on the outer `Value` closed over the sub-
/// `metadata:` axis.
///
/// Returns `None` on any of the four short-circuit arms folded through
/// the underlying composition: the outer `metadata:` block is absent
/// (the [`kube_metadata`] outer-arm short-circuit — a legally-omitted
/// per-CR identity sub-block on `List`-shaped documents or on external
/// YAML shapes that carry no [`ObjectMeta`][om]-flavoured header),
/// the `metadata:` value is present but carries a non-Mapping YAML
/// type (the [`kube_metadata`] shape-gate short-circuit — a
/// schema-invalid identity shape per the K8s API-machinery contract
/// tolerated here as `None` so the readback stays a total function),
/// the requested sub-field `<field>` axis-key is absent from the
/// `metadata:` sub-mapping (the [`kube_metadata_field`] trailing
/// `Mapping::get` none-arm — a legally-omitted per-metadata-axis
/// surface on a CR that carries other identity sub-fields but not
/// this one), or the sub-field value is present but carries a non-
/// sequence YAML type (the trailing `.as_sequence()` shape-gate
/// short-circuit — a schema-invalid per-CR identity-sub-field type
/// per the K8s apiserver's `OpenAPI` schema but tolerated here as
/// `None` so the readback stays a total function). The returned
/// `&Sequence` borrows into the input `Value` — the caller decides
/// whether to iterate (`.iter()`), pick the first entry
/// (`.first()`), enumerate for length (`.len()`), or clone.
///
/// The `field` axis stays parametric (rather than pinned to a specific
/// canonical sub-field like `metadata.ownerReferences` as a separate
/// helper) because the K8s API-machinery [`ObjectMeta`][om] contract
/// admits an open-ended per-CR identity sequence-field surface — one
/// helper covers every sequence-shaped `ObjectMeta` axis today
/// (`metadata.ownerReferences` for GC-cascade wiring,
/// `metadata.finalizers` for pre-delete-hook coordination,
/// `metadata.managedFields` for per-field-ownership enumeration under
/// Server-Side-Apply) with the axis-key threaded through the call.
/// Every future per-CR `metadata.<field>` sequence readback (the
/// `app-operator`'s per-Aplicacao `mesh.pleme.io/v1alpha1/Aplicacao`
/// CR materializer's `metadata.ownerReferences` readback for
/// controller-owned GC-cascade wiring, MESH-COMPOSITION §III.2 #5;
/// the future `caixa-operator`'s per-`Caixa`/`Lacre`/`CaixaBuild` CR
/// `metadata.finalizers` navigation for the build-lifecycle pre-
/// delete-hook contract; every future test-side `metadata.<field>[]`
/// sequence probe the M3.x + M4 renderer set adds) reaches this same
/// helper by construction — no per-consumer three-hop chain re-
/// inline, no per-consumer [`KUBE_KEY_METADATA`] axis-key drift, no
/// coordinated rewrite across every per-CR identity-sub-field sequence
/// readback on a future K8s API-machinery rebrand of the top-level
/// `metadata:` axis.
///
/// [om]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#objectmeta-v1-meta
#[must_use]
pub fn kube_metadata_seq_field<'a>(
value: &'a serde_yaml::Value,
field: &str,
) -> Option<&'a serde_yaml::Sequence> {
kube_metadata_field(value, field).and_then(|v| v.as_sequence())
}
/// Read a top-level `<field>:` sub-mapping on a K8s custom resource
/// YAML document as `Option<&serde_yaml::Mapping>` — the composed
/// sub-mapping-arity per-top-level-field accessor at the root axis,
/// folding the shape `value.get(field).and_then(|v| v.as_mapping())`
/// onto one substrate-primitive method call the caller reads as
/// intent (`kube_root_map_field(<value>, <FIELD>)` — "read this K8s
/// CR's top-level `<FIELD>:` sub-mapping") rather than as a two-hop
/// `readback → shape-gate` chain. Structural mirror at the root axis
/// of the sibling composed sub-mapping-arity accessors on the sub-
/// axes: [`kube_metadata_map_field`] (d03cc08) on the sub-
/// `metadata.<field>` axis and [`kube_spec_map_field`] (27fc2ee) on
/// the sub-`spec.<field>` axis — same trailing `.as_mapping()` shape-
/// gate closure folded onto the readback, differing only in which
/// axis the caller navigates (top-level `<field>:` here, sub-
/// `metadata.<field>` on the metadata peer, sub-`spec.<field>` on the
/// spec peer). The root-level variant needs no outer shape gate on
/// `value` itself — [`serde_yaml::Value::get`] already short-circuits
/// to `None` on non-Mapping outer values — so the composition folds
/// on one hop rather than the two hops the sub-axis peers close.
///
/// The two canonical pinned peers on this root-level sub-mapping-
/// arity axis — [`kube_metadata`] (f41c4fe) on the [`KUBE_KEY_METADATA`]
/// half of the K8s CR skeleton every controller admits and
/// [`kube_spec`] (9b028ee) on the [`KUBE_KEY_SPEC`] half — now
/// compose through this lifted primitive: [`kube_metadata`] becomes
/// `kube_root_map_field(value, KUBE_KEY_METADATA)` and [`kube_spec`]
/// becomes `kube_root_map_field(value, KUBE_KEY_SPEC)`. The two
/// previously carried identical two-hop
/// `value.get(<KUBE_KEY_*>).and_then(|v| v.as_mapping())` bodies
/// differing only in the pinned top-level axis-key — exactly the
/// two-verbatim-copy shape a substrate-primitive lift closes into
/// one navigation surface.
///
/// Returns `None` on either short-circuit arm the underlying inline
/// chain closes: the requested top-level `<field>:` axis-key is
/// absent (a legally-omitted top-level sub-block — e.g. a
/// `List`-shaped document that carries no per-item `metadata:`
/// header, or a bare status-scoped document that carries no `spec:`
/// body), or the top-level `<field>:` value is present but carries a
/// non-Mapping YAML type (a schema-invalid top-level sub-block per
/// the K8s API-machinery contract that pins both `metadata:` and
/// `spec:` as Mappings, but tolerated here as `None` so the readback
/// stays a total function). The returned `&Mapping` borrows into
/// the input `Value` — the caller decides whether to enumerate
/// (`for (k, v) in sub { ... }`), further-navigate
/// (`sub.get(KUBE_KEY_LABELS)`), pin its length (`.len()`), or clone.
///
/// The `field` axis stays parametric (rather than pinned to
/// [`KUBE_KEY_METADATA`] or [`KUBE_KEY_SPEC`] — those pinned peers
/// [`kube_metadata`] / [`kube_spec`] still exist and now compose on
/// this primitive) so the same lift closes every top-level sub-
/// mapping-arity axis a future K8s API-machinery revision surfaces:
/// a `status:` sub-block readback for the future caixa-operator's
/// per-`Caixa`/`Lacre`/`CaixaBuild` CR reconciler's
/// `status.observedGeneration` navigation, a `data:` sub-block
/// readback on `ConfigMap` / `Secret` shapes the mesh renderer
/// materializes for per-Aplicacao config surfaces, a
/// `spec.template` nested pod-template readback each future per-
/// Deployment renderer reaches for — each new top-level sub-mapping
/// axis reaches this helper with a new [`KUBE_KEY_<AXIS>`] const,
/// not a fresh per-axis helper. Root-axis peer to [`kube_root_str_field`]
/// (ae83f4e) which closes the same one-hop-plus-shape-gate
/// navigation on the string-scalar-arity top-level `<field>:` axis
/// — together they open a two-arity family at the root that mirrors
/// the sub-`metadata:` and sub-`spec:` three-arity families
/// (`{str, seq, map}`) already closed at the sub-axis level.
#[must_use]
pub fn kube_root_map_field<'a>(
value: &'a serde_yaml::Value,
field: &str,
) -> Option<&'a serde_yaml::Mapping> {
// Post-lift body: composes on the substrate-primitive
// [`kube_root_field`] parametric root-axis scalar-Value accessor
// rather than re-inlining the two-hop `value.get(field)
// .and_then(|v| v.as_mapping())` chain. Structural mirror at the
// root axis of the way sibling [`kube_metadata_map_field`]
// (d03cc08) composes on [`kube_metadata_field`] (450f1ff) and
// [`kube_spec_map_field`] (27fc2ee) composes on
// [`kube_spec_field`] (23bd568) — each shape-gate arity peer
// folds its trailing `.as_mapping()` closure onto its axis's
// scalar-Value accessor primitive.
kube_root_field(value, field).and_then(|v| v.as_mapping())
}
/// Read a top-level `<field>:` sub-Value on a K8s custom resource or
/// helm-values-shaped YAML document as `Option<&serde_yaml::Value>` —
/// the composed scalar-arity per-top-level-field-Value accessor at the
/// root axis, folding the one-hop `value.get(field)` navigation onto
/// one substrate-primitive method call the caller reads as intent
/// (`kube_root_field(<value>, <FIELD>)` — "read this K8s CR's
/// top-level `<FIELD>:` sub-Value") rather than as a hand-spelled
/// [`serde_yaml::Value::get`] site the shape-gate peers each re-typed
/// verbatim. Root-axis peer to the sibling composed scalar-Value
/// accessors on the sub-axes — [`kube_metadata_field`] (450f1ff) on
/// the sub-`metadata:` axis and [`kube_spec_field`] (23bd568) on the
/// sub-`spec:` axis — completing the substrate-primitive
/// scalar-Value accessor family at every axis every K8s CR document
/// the emit-side [`kube_resource_skeleton`] renders admits:
/// [`kube_root_field`] for top-level `<field>:` sub-Values here,
/// [`kube_metadata_field`] for sub-`metadata.<field>` sub-Values on
/// the metadata peer, [`kube_spec_field`] for sub-`spec.<field>`
/// sub-Values on the spec peer. Where the two sub-axis peers each
/// close a two-hop `outer-block → per-field-value` navigation
/// through their axis's [`kube_metadata`] / [`kube_spec`] outer
/// sub-mapping accessor, the root-axis variant needs no outer shape
/// gate on `value` itself — [`serde_yaml::Value::get`] already
/// short-circuits to `None` on non-Mapping outer values — so the
/// composition folds on the single hop the sub-axis peers close
/// under an outer sub-mapping walk.
///
/// The three canonical shape-gated pinned peers on this root-level
/// axis — [`kube_root_str_field`] (ae83f4e) on the string-scalar arity,
/// [`kube_root_map_field`] (723f6d7) on the sub-mapping arity, and
/// [`kube_root_seq_field`] (d61e46e) on the sequence arity — now all
/// compose through this lifted primitive: each folds its trailing
/// `.as_str()` / `.as_mapping()` / `.as_sequence()` shape-gate closure
/// onto `kube_root_field(value, field)` rather than re-inlining the
/// `value.get(field)` outer hop. The three previously carried
/// identical one-hop `value.get(field).and_then(|v| v.as_<shape>())`
/// bodies differing only on the trailing shape-gate closure — exactly
/// the three-verbatim-copy shape a substrate-primitive lift closes
/// into one navigation surface, structural mirror of the way
/// [`kube_metadata_field`] and [`kube_spec_field`] lifted the same
/// axis-key-parametric readback under their respective outer
/// sub-mapping walks.
///
/// Returns `None` on either short-circuit arm the underlying
/// [`serde_yaml::Value::get`] closes: the requested top-level
/// `<field>:` axis-key is absent (a legally-omitted top-level
/// sub-block — e.g. a bare status-scoped document that carries no
/// `spec:` body, a `List`-shaped document that carries no per-item
/// `metadata:` header), or the outer `value` is not a Mapping at all
/// (`Value::Null`, `Value::String`, `Value::Sequence`, …). The
/// returned `&Value` borrows into the input `Value` — the caller
/// decides whether to shape-gate downstream (`.as_str()`,
/// `.as_mapping()`, `.as_sequence()`, `.as_u64()`, `.as_bool()`),
/// further-navigate (`.get(<sub-KEY>)`), or route through one of the
/// composed pinned shape-gate peers above.
///
/// The `field` axis stays parametric (rather than pinned to
/// [`KUBE_KEY_METADATA`] / [`KUBE_KEY_SPEC`] / [`KUBE_KEY_KIND`] /
/// [`KUBE_KEY_API_VERSION`] as separate helpers) so the same lift
/// closes every top-level `<field>:` sub-Value axis a future K8s
/// API-machinery revision surfaces (a `status:` sub-block readback
/// for the future caixa-operator's per-`Caixa`/`Lacre`/`CaixaBuild`
/// CR reconciler's `status.observedGeneration` navigation, a
/// `data:` sub-Value readback on `ConfigMap` / `Secret` shapes the
/// mesh renderer materializes for per-Aplicacao config surfaces, a
/// hypothetical top-level `webhooks:` sequence-of-mappings readback
/// on a future `MutatingWebhookConfiguration` shape) — each new
/// top-level axis reaches this helper with a new
/// [`KUBE_KEY_<AXIS>`] const, not a fresh per-axis helper.
///
/// Every future top-level `<field>:` sub-Value readback (a
/// hypothetical top-level `bool`-arity or `u64`-arity axis a future
/// per-CR emitter surfaces — a top-level `paused: true` toggle on a
/// future Flux-controller CR, a top-level `port: <n>` scalar on a
/// hypothetical simplified `ServiceLike` shape — that would want a
/// future `kube_root_bool_field` or `kube_root_u64_field` shape-gate
/// peer alongside the three lifted here) reaches this same accessor
/// primitive by construction, folding its trailing arity-specific
/// shape gate onto `kube_root_field(value, field)` rather than
/// re-inlining a new `value.get(field).and_then(|v| v.as_<shape>())`
/// two-hop chain per fresh arity. Structural mirror of the way the
/// sub-`metadata:` and sub-`spec:` axes admit new shape-gate peers
/// through their own `kube_metadata_field` / `kube_spec_field`
/// primitives without a per-arity two-hop chain re-inline.
#[must_use]
pub fn kube_root_field<'a>(
value: &'a serde_yaml::Value,
field: &str,
) -> Option<&'a serde_yaml::Value> {
value.get(field)
}
/// Read a top-level `<field>:` sub-sequence on a K8s custom resource or
/// helm-values-shaped YAML document as `Option<&serde_yaml::Sequence>` —
/// the composed sequence-arity per-top-level-field accessor at the root
/// axis, folding the shape `value.get(field).and_then(|v|
/// v.as_sequence())` onto one substrate-primitive method call the caller
/// reads as intent (`kube_root_seq_field(<value>, <FIELD>)` — "read this
/// document's top-level `<FIELD>:` sequence") rather than as a two-hop
/// `readback → shape-gate` chain. Structural mirror at the root axis of
/// the sibling composed sequence-arity accessors on the sub-axes:
/// [`kube_metadata_seq_field`] (139a94b) on the sub-`metadata.<field>`
/// axis and [`kube_spec_seq_field`] (fc64ed7) on the sub-`spec.<field>`
/// axis — same trailing `.as_sequence()` shape-gate closure folded onto
/// the readback, differing only in which axis the caller navigates (top-
/// level `<field>:` here, sub-`metadata.<field>` on the metadata peer,
/// sub-`spec.<field>` on the spec peer). The root-level variant needs
/// no outer shape gate on `value` itself — [`serde_yaml::Value::get`]
/// already short-circuits to `None` on non-Mapping outer values — so
/// the composition folds on one hop rather than the two hops the sub-
/// axis peers close. Peer of [`kube_root_str_field`] (ae83f4e) on the
/// string-scalar-arity root axis and [`kube_root_map_field`] (723f6d7)
/// on the sub-mapping-arity root axis: the three together close the
/// root-axis `{str, seq, map}` three-arity family to structural parity
/// with the sub-`metadata:` `{str, seq, map}` and sub-`spec:` `{str,
/// seq, map}` three-arity families already closed at the sub-axis
/// level.
///
/// Returns `None` on either short-circuit arm the underlying inline
/// chain closes: the requested top-level `<field>:` axis-key is absent
/// (a legally-omitted top-level sub-block — e.g. a values.yaml document
/// that carries no `programs:` sequence yet, a `List`-shaped document
/// whose `items:` sequence is absent, a bare `HelmRelease` that carries
/// no root-level `programs:` when the emitter routes through
/// `spec.values.programs` instead), or the top-level `<field>:` value
/// is present but carries a non-sequence YAML type (a schema-invalid
/// top-level sub-block per the fleet-programs / `HelmRelease` values
/// contract that pins `programs:` as a Sequence, but tolerated here as
/// `None` so the readback stays a total function). The returned
/// `&Sequence` borrows into the input `Value` — the caller decides
/// whether to iterate (`.iter()`), pick the first entry (`.first()`),
/// enumerate for length (`.len()`), or clone.
///
/// The `field` axis stays parametric (rather than pinned to
/// [`FLEET_PROGRAMS_KEY_PROGRAMS`] as a separate helper) so the same
/// lift closes every top-level sequence-shaped axis a future emitter
/// surfaces: a root-level `items:` readback on a `List`-shaped multi-
/// doc envelope, a root-level `documents:` readback on a hypothetical
/// M4 aggregator envelope the future `app-operator` per-Aplicacao
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer emits
/// (MESH-COMPOSITION §III.2 #5), a root-level `imports:` readback on
/// a future `lacre.lisp` closure-manifest envelope — each new top-
/// level sequence axis reaches this helper with a new axis-key const,
/// not a fresh per-axis helper.
///
/// Two identical-shape test-side call sites in [`caixa-flux`] converge
/// onto this helper — the [`caixa_flux::upsert_into_programs_yaml`]
/// round-trip pins at [`caixa_flux::tests::upsert_inserts_new_entry`]
/// and [`caixa_flux::tests::upsert_replaces_existing_entry`] both
/// reach through the root-level `programs:` sequence readback on the
/// bare-values.yaml shape, and both re-derived the same two-hop
/// `.get(FLEET_PROGRAMS_KEY_PROGRAMS).unwrap().as_sequence().unwrap()`
/// chain inline. Post-lift each site composes as
/// `kube_root_seq_field(&modified, FLEET_PROGRAMS_KEY_PROGRAMS).unwrap()`
/// — the shape they now name is the substrate primitive rather than the
/// hand-spelled readback chain.
#[must_use]
pub fn kube_root_seq_field<'a>(
value: &'a serde_yaml::Value,
field: &str,
) -> Option<&'a serde_yaml::Sequence> {
// Post-lift body: composes on the substrate-primitive
// [`kube_root_field`] parametric root-axis scalar-Value accessor
// rather than re-inlining the two-hop `value.get(field)
// .and_then(|v| v.as_sequence())` chain. Structural mirror at the
// root axis of the way sibling [`kube_metadata_seq_field`]
// (139a94b) composes on [`kube_metadata_field`] (450f1ff) and
// [`kube_spec_seq_field`] (fc64ed7) composes on
// [`kube_spec_field`] (23bd568) — each shape-gate arity peer folds
// its trailing `.as_sequence()` closure onto its axis's
// scalar-Value accessor primitive.
kube_root_field(value, field).and_then(|v| v.as_sequence())
}
/// Read the `matchLabels:` string-scalar sub-mapping on a K8s
/// `LabelSelector`-shaped YAML value (Cilium `EndpointSelector`,
/// `fromEndpoints[]`, Gateway API selectors, `NetworkPolicy` peers,
/// RBAC subject selectors, `spec.selector` on every `Deployment` /
/// `StatefulSet` / `DaemonSet`) as `Option<&serde_yaml::Mapping>` — the
/// selector-side counterpart of the sub-`metadata.labels` accessor
/// [`kube_metadata_labels`] (f3d9fcd) on the CR-side of the K8s
/// label-selection contract. Where [`kube_metadata_labels`] reads the
/// per-CR labels sub-mapping *the emitted CR carries* (the
/// selection-target axis), [`kube_match_labels`] reads the
/// `matchLabels:` sub-mapping *a selector selects on* (the
/// selection-source axis) — together the two accessors bracket both
/// sides of the K8s API-machinery's `LabelSelector.matchLabels ⊆
/// ObjectMeta.labels` selection contract every label-selecting
/// consumer (Cilium `EndpointSelector` at the CNP data-plane admission
/// path, `spec.selector.matchLabels` at every `Deployment` /
/// `StatefulSet` / `DaemonSet` controller-side pod-owner reconciliation,
/// Gateway API
/// per-Route parent-selector at the Envoy-side listener attachment
/// join, `kubectl -l <label>=<value>` grep-by-label at the client-side
/// selection surface) keys off.
///
/// Composes on top of the substrate-primitive [`kube_root_map_field`]
/// (723f6d7) as `kube_root_map_field(value, KUBE_KEY_MATCH_LABELS)` —
/// same one-hop `value.get(<field>).and_then(as_mapping)` shape,
/// structural mirror of the way sibling [`kube_metadata_labels`]
/// composes on [`kube_metadata_map_field`] (d03cc08) — with the axis-
/// key pinned inside the helper because the K8s `LabelSelector` schema
/// pins `matchLabels` as the load-bearing positive-selector sub-block
/// (paired with `matchExpressions:` for the operator-based selector
/// arm the V0 [`label_selector`] emitter deliberately excludes,
/// [`KUBE_KEY_MATCH_LABELS`] docstring at
/// caixa-core/src/render.rs:10120). Every readback consumer downstream
/// (`.get(<label>)`, `.len()`, `for (k, _) in selector { ... }`,
/// `.is_some()` presence probes) drives off the same pinned return;
/// the pinned axis-key inside the helper mirrors the discipline the
/// sibling [`kube_metadata_labels`] applies to the sub-`metadata:`
/// labels sub-block, and mirrors the discipline the sibling top-level
/// pinned-axis accessors [`kube_kind`] / [`kube_api_version`] /
/// [`kube_name`] / [`kube_namespace`] apply to their pinned CR-
/// discriminator coordinates.
///
/// Returns `None` on either short-circuit arm the underlying inline
/// chain closes: the outer `matchLabels:` block is absent (a legally-
/// admitted arm on `matchExpressions:`-only selectors — the K8s
/// `LabelSelector` schema pins `matchLabels:` and `matchExpressions:`
/// as an OR-composed pair, either arm may be omitted), or the
/// `matchLabels:` value is present but carries a non-Mapping YAML
/// type (a schema-invalid selector shape per the K8s API-machinery
/// contract that pins the sub-block as `map[string]string`, but
/// tolerated here as `None` so the readback stays a total function).
/// The returned `&Mapping` borrows into the input `Value` — the caller
/// decides whether to enumerate (`for (k, v) in selector { ... }`),
/// further-navigate (`selector.get(LABEL_APLICACAO)`), or clone.
///
/// The input `value` is the [`serde_yaml::Value`] carrying the
/// selector's outer surface — e.g. the [`crate::label_selector`] /
/// [`crate::pleme_program_selector`] /
/// [`crate::pleme_program_in_aplicacao_selector`] emitter's return, or
/// a caller-side descended `spec.endpointSelector` /
/// `spec.ingress[0].fromEndpoints[0]` sub-Value on a rendered
/// [`crate::CILIUM_KIND_NETWORK_POLICY`]. Unlike the sibling
/// [`kube_metadata_labels`] which pre-navigates the outer
/// `metadata:` sub-block inside the helper (composing on
/// [`kube_metadata_map_field`] which itself pre-navigates
/// `metadata:`), this accessor takes the LabelSelector-shaped Value
/// directly — the outer navigation to the selector-Value is caller-
/// side because a K8s `LabelSelector` appears at many different
/// per-CR axes (`spec.endpointSelector` on `CiliumNetworkPolicy`,
/// `spec.ingress[].fromEndpoints[]` on the same, `spec.selector` on
/// every workload-shaped CR, `spec.parentRefs[]` on Gateway API
/// Routes, etc.) with no single pinned parent-axis to fold into the
/// helper.
///
/// The canonical shape 5 test-side per-CNP selector-value readback
/// sites across [`caixa-mesh`][mesh] previously carried inline as the
/// two-hop composition
///
/// ```ignore
/// <selector_value>
/// .get(KUBE_KEY_MATCH_LABELS)
/// .and_then(|m| m.as_mapping())
/// ```
///
/// or its `Option`-lifted `.and_then` variant threaded off the outer
/// selector-navigating chain. The lift collapses the two-hop chain —
/// the per-selector `.get(KUBE_KEY_MATCH_LABELS)` axis-key hop, the
/// trailing `.and_then(|m| m.as_mapping())` shape gate — onto one
/// accessor the caller reads as intent (`kube_match_labels(
/// <selector_value>)` — "read this selector's `matchLabels:` sub-
/// mapping") rather than as an axis-key-hop → shape-gate two-line
/// chain.
///
/// Every future selector-side `matchLabels:` readback (the future
/// per-`:politicas` `CiliumClusterwideEnvoyConfig` emitter's per-
/// policy `spec.selector` readback, MESH-COMPOSITION §III.2 #3; the
/// future `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
/// per-Aplicacao `spec.selector.matchLabels` reconciler, §III.2 #5;
/// the future per-tenant CNP filter's per-endpoint selector probe;
/// the M4 cross-cluster fan-out's per-cluster peer-selector readback)
/// reaches the same pinned accessor by construction, with no axis-
/// key argument drift and no re-inlined two-hop chain.
///
/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
#[must_use]
pub fn kube_match_labels(value: &serde_yaml::Value) -> Option<&serde_yaml::Mapping> {
kube_root_map_field(value, KUBE_KEY_MATCH_LABELS)
}
/// Read a single string-scalar label value at `matchLabels.<label>` on
/// a K8s `LabelSelector`-shaped YAML value (Cilium `EndpointSelector`,
/// `fromEndpoints[]`, Gateway API selectors, workload `spec.selector`)
/// as `Option<&str>` — the parametric scalar-arity accessor peer that
/// composes on top of the lifted sub-mapping-arity [`kube_match_labels`]
/// (63daa47) accessor to close the two-hop per-label readback the
/// caixa-mesh test-side per-selector per-label scalar-value probes
/// previously walked inline as `.and_then(kube_match_labels).<expect>
/// .get(<LABEL>).and_then(|v| v.as_str())`. Structural mirror on the
/// selector-side `matchLabels:` axis of the way sibling
/// [`kube_metadata_label`] (f3d9fcd) composes on the CR-side
/// [`kube_metadata_labels`] sub-mapping accessor — the two accessors
/// bracket both sides of the K8s API-machinery's `LabelSelector
/// .matchLabels ⊆ ObjectMeta.labels` selection contract at the per-label
/// scalar-readback arity, structural pair to the two accessors that
/// already bracket both sides at the sub-mapping-arity.
///
/// Returns `None` when either the enclosing `matchLabels:` sub-mapping
/// is absent (the same two-way vacuous-`None` short-circuit the parent
/// [`kube_match_labels`] closes — missing `matchLabels:` sub-block, non-
/// Mapping `matchLabels:` value), the requested `<label>` scalar-key is
/// absent under the selector (a legally-admitted arm on a selector that
/// carries other labels but not this one), or the `<label>` value is
/// present but carries a non-string YAML type (a schema-invalid
/// selector-label shape per the K8s API-machinery contract that pins
/// `matchLabels` values as string scalars, but tolerated here as `None`
/// so the readback stays a total function). The returned `&str` borrows
/// into the input `Value` — the caller decides whether to compare
/// (`==`), clone (`.to_string()`), or unwrap-then-panic. The three-hop
/// navigation happens in one method call the caller reads as intent
/// (`kube_match_label(<value>, <LABEL>)` — "read this `LabelSelector`'s
/// `matchLabels.<LABEL>` string-scalar") rather than three hand-spelled
/// positional artifacts (the outer `get(KUBE_KEY_MATCH_LABELS)` hop, the
/// per-label `and_then(|l| l.get(<LABEL>))` sub-hop, the trailing
/// `and_then(|v| v.as_str())` shape gate).
///
/// The `label` axis stays parametric (rather than pinned to a specific
/// label-key like [`LABEL_APLICACAO`] or [`LABEL_PROGRAM`] as separate
/// helpers) so the same lift closes every string-scalar selector-label
/// a caixa-mesh emitter selects on today ([`LABEL_APLICACAO`],
/// [`LABEL_PROGRAM`]) and every string-scalar selector-label a future
/// renderer surfaces (per-tenant selector-prefix filters, per-`:politicas`
/// per-policy selector labels, per-Aplicacao CR materializer selector
/// labels) — each new label reaches for the same helper with a new
/// [`crate::LABEL_*`] const argument, not a fresh per-label helper.
/// Structural mirror at the selector-side of the way sibling
/// [`kube_metadata_label`] stays parametric on the label axis at the
/// CR-side because both the K8s `metadata.labels` axis and the K8s
/// `LabelSelector.matchLabels` axis deliberately admit an open-ended
/// label-key surface, and pinning a specific label would foreclose reuse
/// across the label set on either side of the selection contract.
///
/// Unlike sibling [`kube_metadata_label`] which pre-navigates the outer
/// `metadata:` sub-block inside the helper (composing on
/// [`kube_metadata_labels`] which itself pre-navigates `metadata:`),
/// this accessor takes the LabelSelector-shaped `Value` directly — the
/// outer navigation to the selector-Value is caller-side because a K8s
/// `LabelSelector` appears at many different per-CR axes
/// (`spec.endpointSelector` on `CiliumNetworkPolicy`,
/// `spec.ingress[].fromEndpoints[]` on the same, `spec.selector` on
/// every workload-shaped CR, `spec.parentRefs[]` on Gateway API Routes,
/// etc.) with no single pinned parent-axis to fold into the helper —
/// same discipline the parent [`kube_match_labels`] applies to its
/// selector-Value input surface.
///
/// The canonical shape 4 caixa-mesh test-side per-selector per-label
/// scalar-value readback sites previously carried inline as the
/// three-token composition
///
/// ```ignore
/// <selector_source_value>
/// .and_then(kube_match_labels) // outer accessor
/// .<unwrap-or-expect> // total-function commit
/// .get(<LABEL>).and_then(|v| v.as_str()) // per-label + shape-gate
/// ```
///
/// around a one-token semantic payload (the `<LABEL>` axis-key —
/// [`LABEL_APLICACAO`] on the per-CNP fromEndpoints selector-value
/// pin, [`LABEL_PROGRAM`] on the per-CNP endpointSelector /
/// fromEndpoints program-name pins). The lift collapses the three-
/// token composition — the outer sub-mapping accessor, the commit,
/// the per-label sub-hop, the trailing shape gate — onto one
/// accessor the caller reads as intent (`kube_match_label(
/// <selector_source_value>, <LABEL>)`) rather than as a
/// `sub-map → commit → per-label + shape-gate` chain, folding the
/// outer commit off the routed sites' hot path so the outer selector-
/// Value can carry a legally-absent `matchLabels:` sub-block without
/// triggering a `.unwrap()` on the outer intermediate.
///
/// Every future selector-side per-label scalar readback (the future
/// per-`:politicas` `CiliumClusterwideEnvoyConfig` emitter's per-policy
/// `spec.selector.matchLabels.<LABEL>` readback, MESH-COMPOSITION §III.2
/// #3; the future `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
/// per-Aplicacao `spec.selector.matchLabels.<LABEL>` reconciler,
/// §III.2 #5; the future per-tenant CNP filter's per-endpoint per-
/// tenant-label probe; the M4 cross-cluster fan-out's per-cluster
/// peer-selector per-label readback) reaches the same pinned accessor
/// by construction, with no axis-key argument drift and no re-inlined
/// three-hop chain.
///
/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
#[must_use]
pub fn kube_match_label<'a>(value: &'a serde_yaml::Value, label: &str) -> Option<&'a str> {
kube_match_labels(value)
.and_then(|labels| labels.get(label))
.and_then(|v| v.as_str())
}
/// Predicate: does the K8s `LabelSelector`-shaped YAML value at `value`
/// carry `matchLabels.<label>` with the string-scalar value `expected`?
///
/// Composes on top of [`kube_match_label`] as
/// `kube_match_label(value, label) == Some(expected)` — the same one-
/// hop `readback → equality-wrap` shape the sibling CR-side per-label
/// predicate [`kube_metadata_label_is`] (662c4dc) carries on the
/// `metadata.labels.<label>` axis, mirrored onto the selector-side
/// `matchLabels.<label>` axis. Structural pair to sibling
/// [`kube_metadata_label_is`]: the pair closes the per-label equality-
/// predicate arity on both sides of the K8s API-machinery's
/// `LabelSelector.matchLabels ⊆ ObjectMeta.labels` selection contract,
/// so every future per-label equality-check (a `kubectl -l
/// <label>=<value>` selector match, an `app-operator`'s per-Aplicacao
/// selector-match reconciler, a per-`:politicas` policy-scoping label
/// predicate) reaches the same pinned predicate on whichever side of
/// the contract the caller is holding.
///
/// Returns `false` on any of the four vacuous-`None` short-circuits the
/// underlying [`kube_match_label`] accessor closes (missing
/// `matchLabels:` sub-block, non-Mapping `matchLabels:` value, requested
/// `<label>` key absent, or non-string label value): the predicate
/// treats the "label absent" arm and the "label present but non-string"
/// arm the same as the "label present but wrong value" arm — every
/// downstream selector-filter caller treats every non-match the same,
/// so the predicate's boolean shape matches the selector's boolean
/// verdict rather than exposing the underlying `Option`'s three-way
/// split.
///
/// Unlike sibling CR-side per-label predicates ([`kube_name_is`],
/// [`kube_namespace_is`], [`kube_kind_is`], [`kube_api_version_is`])
/// whose `field` axis is pinned inside the helper because the K8s API-
/// machinery pins those specific coordinates as the load-bearing per-CR
/// discriminators, this predicate stays parametric on the `label` axis-
/// key argument because the K8s `LabelSelector.matchLabels` contract
/// deliberately admits an open-ended per-selector label surface — same
/// discipline the sibling CR-side [`kube_metadata_label_is`] applies to
/// the `metadata.labels.<label>` axis.
///
/// The canonical shape 1 caixa-mesh test-side per-selector per-label
/// scalar-value equality-check site
/// (`cilium_policies_are_identity_based`'s
/// `assert_eq!(from.get(LABEL_APLICACAO).and_then(|v| v.as_str()),
/// Some("checkout"))` fromEndpoints aplicacao-scope pin) previously
/// carried inline as the three-token composition — the readback helper
/// call, the `== Some(...)` equality wrap, the string-scalar axis-value
/// pin — around a two-token semantic payload (the `<label>` axis-key +
/// the `<expected>` axis-value). The lift collapses the three-token
/// composition — the accessor call, the equality wrap, the
/// `Some(...)` constructor — onto one predicate the caller reads as
/// intent (`kube_match_label_is(<selector>, LABEL_X, "v")` — "does this
/// `LabelSelector` carry `matchLabels.LABEL_X` = `v`") rather than as a
/// `readback → wrap → compare` chain.
///
/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
#[must_use]
pub fn kube_match_label_is(value: &serde_yaml::Value, label: &str, expected: &str) -> bool {
kube_match_label(value, label) == Some(expected)
}
/// Read the first entry of the sub-`spec.<field>[]` YAML sequence on a
/// K8s custom resource YAML document as `Option<&serde_yaml::Value>` —
/// the composed first-entry-arity accessor peer that stands on the
/// composed sequence-arity [`kube_spec_seq_field`] (fc64ed7) accessor,
/// folding the trailing `.and_then(|s| s.first())` head-of-sequence
/// closure into the helper for callers that always want the first
/// entry (the canonical "there is exactly one" pin every emit-side
/// per-CR sequence-shaped body-sub-field carries — `spec.ingress[0]`
/// on the `CiliumNetworkPolicy` per-`:contratos` L4/L7 rule-set (each
/// emitted `CiliumNetworkPolicy` carries exactly one ingress rule
/// bracketing one `(from, toPorts)` pair — MESH-COMPOSITION §III.2),
/// `spec.parentRefs[0]` on the Gateway API `HTTPRoute` per-`:entrada`
/// parent-`Gateway` pin, `spec.listeners[0]` on the Gateway API
/// `Gateway` per-`:entrada` HTTP-listener pin, `spec.rules[0]` on the
/// Gateway API `HTTPRoute` per-rule fan-out first-rule pin,
/// `spec.healthChecks[0]` on the Flux v2 `Kustomization` first-health-
/// check pin). Structural mirror of the sibling composed-shape-gate
/// accessors on the same sub-`spec.<field>` axis ([`kube_spec_str_field`],
/// [`kube_spec_seq_field`], [`kube_spec_map_field`]) that fold a
/// trailing shape-gate closure onto [`kube_spec_field`]; where those
/// three close `.as_str()` / `.as_sequence()` / `.as_mapping()` shape
/// gates onto the two-hop `spec → sub-field` navigation, this closes
/// the sequence-head `.first()` head-selector onto the three-hop
/// `spec → sub-field → as_sequence` navigation. Stays parametric on
/// the per-`<field>` sub-field axis-key for the same reason the
/// sibling accessors do — the K8s API-machinery admits an open-ended
/// per-CR body-sub-field surface, and pinning a specific `<field>`
/// would foreclose reuse across the sub-field set on the per-CR body.
///
/// Returns `None` on any of the five short-circuit arms folded through
/// the underlying composition: the outer `spec:` block is absent (the
/// [`kube_spec`] outer-arm short-circuit), the `spec:` value is present
/// but carries a non-Mapping YAML type (the [`kube_spec`] shape-gate
/// short-circuit), the requested sub-field `<field>` axis-key is absent
/// from the `spec:` sub-mapping (the [`kube_spec_field`] trailing
/// `Mapping::get` none-arm), the sub-field value is present but
/// carries a non-sequence YAML type (the [`kube_spec_seq_field`]
/// trailing `.as_sequence()` shape-gate short-circuit — a schema-
/// invalid per-CR body-sub-field type per the K8s apiserver's
/// `OpenAPI` schema but tolerated here as `None` so the readback stays
/// a total function), or the sequence is present but empty (the
/// trailing `.first()` head-selector none-arm — a legally-emitted
/// empty sub-block per every routed sub-field's zero-cardinality arm,
/// but folded here to the same `None` verdict every caller downstream
/// treats it as). The returned `&Value` borrows into the input `Value`
/// — the caller decides whether to look up a nested key (`.get(<KEY>)`),
/// further-shape-gate (`.as_mapping()`, `.as_str()`), or commit
/// (`.expect(...)`).
///
/// The canonical shape 25 caixa-mesh + 1 caixa-flux test-side per-CR
/// first-entry readback sites previously carried inline as the three-
/// token composition
///
/// ```ignore
/// kube_spec_seq_field(<value>, <FIELD>)
/// .and_then(|s| s.first())
/// ...
/// ```
///
/// around a one-token semantic payload (the `<FIELD>` sub-field axis-
/// key — [`CILIUM_KEY_INGRESS`] on the per-CNP first-ingress-rule pin,
/// [`GATEWAY_API_KEY_PARENT_REFS`] on the per-HTTPRoute first-parent-
/// `Gateway` pin, [`GATEWAY_API_KEY_LISTENERS`] on the per-Gateway
/// first-listener pin, [`KUBE_KEY_RULES`] on the per-HTTPRoute first-
/// rule pin, `FLUX_KEY_HEALTH_CHECKS` on the per-`Kustomization` first-
/// health-check pin). After this lift, every routed consumer folds the
/// three-hop-plus-head-selector navigation onto
/// `kube_spec_seq_first(<value>, <FIELD>)` — the outer
/// `spec → as_mapping → get(<field>) → as_sequence → first` walk
/// happens once inside the helper, and the caller keeps its downstream
/// idiom (`.and_then(|i| i.get(<SUB_KEY>))`, `.and_then(kube_kind)`,
/// `.expect(...)`) unchanged — the lift closes the navigation surface,
/// not the per-site continuation posture.
///
/// Every future per-CR first-entry readback (the future per-`:politicas`
/// `CiliumClusterwideEnvoyConfig` emitter's per-policy first-rule pin,
/// MESH-COMPOSITION §III.2 #3; the `app-operator`'s per-Aplicacao
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's first-`membros`
/// / first-`contratos` pin, §III.2 #5; the future `caixa-otel` per-
/// Servico OpenTelemetry-Collector CR's first-`receivers` pin; every
/// future test-side `spec.<field>[0]` first-entry probe the M3.x + M4
/// renderer set adds) reaches this same helper by construction — no
/// per-consumer three-hop-plus-head-selector chain re-inline, no per-
/// consumer `KUBE_KEY_SPEC` axis-key drift, no coordinated rewrite
/// across every per-CR body-sub-field first-entry readback on a future
/// K8s API-machinery rebrand of the top-level `spec:` axis.
///
/// [flux]: https://github.com/pleme-io/caixa/tree/main/caixa-flux
/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
#[must_use]
pub fn kube_spec_seq_first<'a>(
value: &'a serde_yaml::Value,
field: &str,
) -> Option<&'a serde_yaml::Value> {
kube_spec_seq_field(value, field).and_then(|s| s.first())
}
/// Read the first entry of a sub-`<field>[]` YAML sequence nested one
/// hop under an arbitrary `&serde_yaml::Value` mapping receiver as
/// `Option<&serde_yaml::Value>` — the value-level three-hop navigation
/// primitive that folds `.get(<field>) → as_sequence → first` into a
/// single helper call. Peer of the spec-anchored
/// [`kube_spec_seq_first`] (b7babfe) at one altitude below: where the
/// spec-anchored accessor folds the outer `spec → as_mapping →
/// get(<field>)` prelude before the same sequence-head selector, this
/// accessor drops the outer three-hop and stays parametric on the
/// mapping receiver — so a caller already holding a nested `&Value`
/// (a `spec.ingress[0]` bracket returned from
/// [`kube_spec_seq_first`], a `spec.rules[0]` first-rule bracket from
/// the sibling per-`HTTPRoute` readback, any two-hop nested-mapping
/// bracket the M3 renderer set exposes) reaches for this accessor
/// directly instead of re-inlining the three-line
/// `.and_then(|v| v.get(<K>)).and_then(|v| v.as_sequence())
/// .and_then(|s| s.first())` block. Together with
/// [`kube_spec_seq_first`] the pair closes the head-selector arity on
/// both the outer sub-`spec.<field>[]` axis and the inner nested sub-
/// `<field>[]` axis every routed `spec.<outer>[0].<inner>[0]` readback
/// (the per-CNP `spec.ingress[0].toPorts[0]` L4-port bracket pair, the
/// per-CNP `spec.ingress[0].fromEndpoints[0]` source-endpoint
/// selector bracket, the per-`HTTPRoute` `spec.rules[0].backendRefs[0]`
/// first-backend pin, the per-`HTTPRoute` `spec.rules[0].matches[0]`
/// first-match pin, MESH-COMPOSITION §III.2) walks through — the outer
/// hop via the spec-anchored peer, the inner hop via this one.
///
/// Returns `None` on any of the four short-circuit arms folded through
/// the underlying three-hop composition: the receiver `value` carries a
/// YAML type without a `get(<field>)` navigation surface
/// ([`serde_yaml::Value::get`] returns `None` on scalar arms — string,
/// bool, number, null — that expose no per-key lookup), the requested
/// `<field>` axis-key is absent from the receiver's mapping
/// ([`serde_yaml::Value::get`] trailing miss), the sub-field value is
/// present but carries a non-sequence YAML type (the trailing
/// `.as_sequence()` shape-gate short-circuit — a schema-invalid nested-
/// sub-field type per the K8s apiserver's `OpenAPI` schema but tolerated
/// here as `None` so the readback stays a total function), or the
/// sequence is present but empty (the trailing `.first()` head-selector
/// none-arm — a legally-emitted zero-cardinality sub-block per every
/// routed inner sub-field's empty arm). The returned `&Value` borrows
/// into the input `Value` — the caller decides whether to further-
/// navigate (`.get(<KEY>)`, `.as_str()`, `.as_u64()`,
/// `.and_then(kube_kind)`) or unwrap.
///
/// The canonical shape 20 test-side per-`spec.<outer>[0].<inner>[0]`
/// readback sites in [`caixa-mesh`][mesh] previously carried inline as
/// the three-line composition
///
/// ```ignore
/// <value>
/// .and_then(|v| v.get(<FIELD>))
/// .and_then(|v| v.as_sequence())
/// .and_then(|s| s.first())
/// ...
/// ```
///
/// around a one-token semantic payload (the `<FIELD>` sub-field axis-
/// key — [`CILIUM_KEY_TO_PORTS`] / [`CILIUM_KEY_PORTS`] on the per-CNP
/// L4-tuple `spec.ingress[0].toPorts[0].ports[0]` port-tuple bracket
/// chain, [`CILIUM_KEY_FROM_ENDPOINTS`] on the per-CNP source-endpoint
/// selector bracket, [`CILIUM_KEY_HTTP`] on the per-CNP L7 HTTP-rule
/// bracket, [`GATEWAY_API_KEY_BACKEND_REFS`] on the per-`HTTPRoute`
/// first-backend bracket, [`GATEWAY_API_KEY_MATCHES`] on the per-
/// `HTTPRoute` first-match bracket). After this lift every routed
/// consumer folds the three-line navigation onto
/// `kube_seq_first(<value>, <FIELD>)` — the three-hop `get →
/// as_sequence → first` walk happens once inside the helper, and the
/// caller keeps its downstream continuation idiom (`.get(<KEY>)`,
/// `.as_u64()`, `.and_then(|v| v.as_str())`, `.and_then(kube_match_labels)`,
/// `.expect(...)`) unchanged — the lift closes the navigation surface,
/// not the per-site continuation posture.
///
/// Every future nested sub-`<field>[0]` first-entry readback (the
/// future per-`:politicas` `CiliumClusterwideEnvoyConfig` per-policy
/// nested bracket chain, the `app-operator`'s per-Aplicacao
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's nested
/// `spec.entrada.paths[0]` / `spec.placement.clusters[0]` brackets,
/// the future `caixa-otel` per-Servico OpenTelemetry-Collector CR's
/// per-`receivers.<name>.protocols` nested bracket, every future test-
/// side nested-first probe the M3.x + M4 renderer set adds) reaches
/// this same helper by construction — no per-consumer three-hop chain
/// re-inline, no coordinated rewrite across every nested-first bracket
/// on a future [`serde_yaml`] surface rebrand or a shift in the head-
/// selector semantics.
///
/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
#[must_use]
pub fn kube_seq_first<'a>(
value: &'a serde_yaml::Value,
field: &str,
) -> Option<&'a serde_yaml::Value> {
value
.get(field)
.and_then(|v| v.as_sequence())
.and_then(|s| s.first())
}
/// Read a sub-`<field>[]` YAML sequence nested one hop under an
/// arbitrary `&serde_yaml::Value` mapping receiver as
/// `Option<&serde_yaml::Sequence>` — the value-level two-hop navigation
/// primitive that folds `.get(<field>) → as_sequence` into a single
/// helper call. Sequence-arity peer of the value-level first-entry
/// accessor [`kube_seq_first`] (2d6cb54): the pair closes the
/// (sequence, head-selector) two-arity closure on the value-level
/// axis, mirroring the ([`kube_spec_seq_field`], [`kube_spec_seq_first`])
/// two-arity closure on the spec-anchored axis one altitude above.
/// Where [`kube_seq_first`] folds the trailing `.first()` head-selector
/// onto the two-hop `get → as_sequence` navigation, this stops at the
/// sequence itself — so a caller enumerating the tail (`.iter()`,
/// `.len()`, `.filter_map`, `.expect(...)` a full-sequence bind for a
/// downstream length assertion or per-entry walk) reaches for this
/// accessor directly instead of re-inlining the two-line
/// `.and_then(|v| v.get(<K>)).and_then(|v| v.as_sequence())` block.
/// Together with [`kube_seq_first`] the pair spans the value-level
/// sub-`<field>[]` axis at both arities: the whole sequence via this
/// accessor, the head entry via the sibling.
///
/// Returns `None` on any of the three short-circuit arms folded through
/// the underlying two-hop composition: the receiver `value` carries a
/// YAML type without a `get(<field>)` navigation surface
/// ([`serde_yaml::Value::get`] returns `None` on scalar arms — string,
/// bool, number, null — that expose no per-key lookup), the requested
/// `<field>` axis-key is absent from the receiver's mapping
/// ([`serde_yaml::Value::get`] trailing miss), or the sub-field value is
/// present but carries a non-sequence YAML type (the trailing
/// `.as_sequence()` shape-gate short-circuit — a schema-invalid nested-
/// sub-field type per the K8s apiserver's `OpenAPI` schema but tolerated
/// here as `None` so the readback stays a total function). The returned
/// `&Sequence` borrows into the input `Value` — the caller decides
/// whether to iterate (`.iter()`), enumerate for length (`.len()`),
/// check emptiness (`.is_empty()`), filter-map by shape
/// (`.iter().filter_map(|v| v.as_str())`), or commit (`.expect(...)`).
///
/// The canonical shape 11 test-side per-nested-`<field>[]` sequence
/// readback sites in [`caixa-mesh`][mesh] and [`caixa-flux`][flux]
/// previously carried inline as the two-line composition
///
/// ```ignore
/// <value>
/// .and_then(|v| v.get(<FIELD>))
/// .and_then(|v| v.as_sequence())
/// ...
/// ```
///
/// around a one-token semantic payload (the `<FIELD>` sub-field axis-
/// key — [`CILIUM_KEY_TO_PORTS`] on the per-CNP `ingress[0].toPorts[]`
/// L4-port bracket, [`CILIUM_KEY_FROM_ENDPOINTS`] on the per-CNP
/// `ingress[0].fromEndpoints[]` source-endpoint bracket,
/// [`CILIUM_KEY_HTTP`] on the per-CNP L7 HTTP-rule sequence,
/// [`GATEWAY_API_KEY_MATCHES`] on the per-`HTTPRoute` per-rule
/// `matches[]` bracket, `M2_KEY_UPGRADE_FROM` on the caixa-flux
/// per-programs-entry upgradeFrom sequence, `FLEET_PROGRAMS_KEY_PROGRAMS`
/// on the fleet-programs values.programs[] readback). After this lift
/// every routed consumer folds the two-line navigation onto
/// `kube_seq(<value>, <FIELD>)` — the two-hop `get → as_sequence` walk
/// happens once inside the helper, and the caller keeps its downstream
/// idiom (`.iter()`, `.len()`, `.expect(...)`, `.filter_map(...)`)
/// unchanged — the lift closes the navigation surface, not the per-site
/// downstream posture.
///
/// Every future nested sub-`<field>[]` sequence readback (the future
/// per-`:politicas` `CiliumClusterwideEnvoyConfig` per-policy nested
/// rule-set walk, the `app-operator`'s per-Aplicacao
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's nested
/// `spec.entrada.paths[]` / `spec.placement.clusters[]` sequences, the
/// future `caixa-otel` per-Servico OpenTelemetry-Collector CR's
/// per-`receivers.<name>.protocols[]` nested sequence, every future
/// test-side nested-sequence probe the M3.x + M4 renderer set adds)
/// reaches this same helper by construction — no per-consumer two-hop
/// chain re-inline, no coordinated rewrite across every nested-sequence
/// bracket on a future [`serde_yaml`] surface rebrand or a shift in
/// the shape-gate semantics.
///
/// [flux]: https://github.com/pleme-io/caixa/tree/main/caixa-flux
/// [mesh]: https://github.com/pleme-io/caixa/tree/main/caixa-mesh
#[must_use]
pub fn kube_seq<'a>(value: &'a serde_yaml::Value, field: &str) -> Option<&'a serde_yaml::Sequence> {
value.get(field).and_then(|v| v.as_sequence())
}
/// Upsert `new_entry` into a typed sequence of programs.yaml-shaped
/// entries by matching on `new_entry`'s `<name_key>` scalar — the
/// idempotent "replace-in-place if present, else append" contract
/// every writer-side aggregator overlay lands the same 11-line block
/// in front of. Returns `Ok(true)` when the entry was appended new,
/// `Ok(false)` when an existing entry with the same `<name_key>`
/// value was replaced in place (preserving position); returns
/// `on_missing_name()` when `new_entry` doesn't carry `<name_key>`
/// as a string scalar (the caller's own typed
/// [`crate::RenderError`]-shaped error surface, threaded through the
/// closure so this helper stays crate-agnostic).
///
/// Two identical-shape call sites collapse onto this helper — the
/// two [`caixa-flux`] writer-side upsert paths that both land a
/// programs.yaml entry into a `programs:` sequence differing only
/// on the outer navigation:
///
/// * [`caixa_flux::upsert_into_helmrelease_programs`][helm-up] —
/// the aggregator-HelmRelease shape, upserting into
/// `spec.values.programs[]` on a `HelmRelease` document;
/// * [`caixa_flux::upsert_into_programs_yaml`][yaml-up] — the
/// bare-values.yaml shape, upserting into `programs[]` at the
/// values.yaml root.
///
/// Until this lift landed both call sites re-inlined the same
/// verbatim 11-line block — extract-name-scalar-or-error, iterate
/// the sequence, replace-in-place-on-match else fall through to
/// push — with no compile-time link between the two: a rebrand on
/// either side (a per-entry match key rename beyond the currently-
/// lifted [`crate::FLEET_PROGRAMS_KEY_NAME`], the idempotency
/// contract's semantic reshaping — e.g. matching on
/// `(name, namespace)` for the M4 multi-namespace aggregator flow
/// once the `lareira-fleet-programs` chart admits per-entry
/// `namespace:` overrides, the return-value's `bool`-shape shift
/// once "replace" grows a merge-semantics axis) would silently
/// desynchronize the two writer-side paths — one path idempotently
/// upserts under the new contract while the other silently keeps
/// the old shape, and the failure surfaces at aggregator-apply
/// time as a duplicated / missing / mis-merged entry far from the
/// rebrand commit's source. Peer of the sibling render-side lifts
/// ([`single_field_overlay`], [`servico_m2_overlay`],
/// [`insert_first_seen`]) on the same "the same shape written
/// verbatim ≥ 2 times becomes a typed helper" trajectory THEORY.md
/// §I.3.5 promotes to a build-time concern.
///
/// The `name_key` axis stays parametric (rather than pinned to
/// [`crate::FLEET_PROGRAMS_KEY_NAME`] inside the helper) so a
/// future per-entry match on a different discriminator scalar (an
/// M4 `id:` axis promoted alongside `name:`, the future
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-entry
/// `spec.selector` upsert path) reaches for the same helper with a
/// different key rather than re-inlining the loop. The closure-
/// shaped error surface (rather than a bare `Result<bool,
/// &'static str>` or an added typed error variant in this crate)
/// keeps every caller's own error enum authoritative — the
/// diagnostic remediation for a missing-name-scalar in a programs-
/// yaml entry rightly names the caller's aggregator schema
/// (`spec.values.programs[].name` for the `HelmRelease` shape,
/// `programs[].name` for the bare values.yaml shape), not this
/// generic helper.
///
/// [helm-up]: ../../caixa_flux/fn.upsert_into_helmrelease_programs.html
/// [yaml-up]: ../../caixa_flux/fn.upsert_into_programs_yaml.html
///
/// # Errors
///
/// Returns `on_missing_name()` when `new_entry.get(name_key)` is
/// not a [`serde_yaml::Value::String`] — the closure surfaces the
/// caller's own typed error variant naming the offending schema
/// axis. On success returns `Ok(true)` for a newly-appended entry,
/// `Ok(false)` for an in-place replacement.
pub fn upsert_named_entry<E>(
arr: &mut Vec<serde_yaml::Value>,
new_entry: serde_yaml::Value,
name_key: &'static str,
on_missing_name: impl FnOnce() -> E,
) -> Result<bool, E> {
let new_name = match new_entry.get(name_key).and_then(|n| n.as_str()) {
Some(s) => s.to_string(),
None => return Err(on_missing_name()),
};
for slot in arr.iter_mut() {
if slot.get(name_key).and_then(|n| n.as_str()) == Some(&new_name) {
*slot = new_entry;
return Ok(false);
}
}
arr.push(new_entry);
Ok(true)
}
/// Render the M2 typed-slot YAML overlay for a Caixa: the camelCase
/// `(key, value)` fragments every per-Servico renderer
/// ([`caixa-helm`]'s values block, [`caixa-flux`]'s programs.yaml
/// entry) merges into its target with `or_insert` semantics so explicit
/// `spec.*` fields from the ComputeUnit YAML take precedence over the
/// manifest-derived overlay.
///
/// Keys (alphabetically ordered, since the return type is
/// [`BTreeMap`]) match the ComputeUnit / pleme-computeunit values
/// schema:
///
/// * [`M2_KEY_BEHAVIOR`] — present iff `caixa.behavior` is `Some`
/// and `BehaviorSpec::is_empty` returns `false`.
/// * [`M2_KEY_LIMITS`] — present iff `caixa.limits` is `Some` and
/// `LimitsSpec::is_empty` returns `false`.
/// * [`M2_KEY_UPGRADE_FROM`] — present iff `caixa.upgrade_from` is
/// non-empty.
///
/// An entirely empty M2 surface returns an empty map; the renderer
/// merges zero fragments and emits no extra keys (the per-renderer
/// "empty M2 slots do not appear" tests pin this invariant —
/// `caixa_helm::tests::empty_m2_slots_do_not_appear` and
/// `caixa_flux::tests::empty_m2_slots_do_not_appear_in_programs_yaml_entry`).
///
/// # Errors
///
/// Returns [`RenderError::Yaml`] if `serde_yaml::to_value` fails for
/// any of the typed M2 slot values. The prior inline block silently
/// substituted [`serde_yaml::Value::Null`] in this case, which renders
/// as e.g. `limits: null` — indistinguishable from "the author omitted
/// the slot" once it leaves the typed surface.
pub fn servico_m2_overlay(
caixa: &Caixa,
) -> Result<BTreeMap<&'static str, serde_yaml::Value>, RenderError> {
let mut out = BTreeMap::new();
if let Some(limits) = caixa.limits() {
if !limits.is_empty() {
let v = serde_yaml::to_value(limits).map_err(|source| RenderError::Yaml {
slot: M2_KEY_LIMITS,
source,
})?;
out.insert(M2_KEY_LIMITS, v);
}
}
if let Some(behavior) = caixa.behavior() {
if !behavior.is_empty() {
let v = serde_yaml::to_value(behavior).map_err(|source| RenderError::Yaml {
slot: M2_KEY_BEHAVIOR,
source,
})?;
out.insert(M2_KEY_BEHAVIOR, v);
}
}
if !caixa.upgrade_from().is_empty() {
let v = serde_yaml::to_value(caixa.upgrade_from()).map_err(|source| RenderError::Yaml {
slot: M2_KEY_UPGRADE_FROM,
source,
})?;
out.insert(M2_KEY_UPGRADE_FROM, v);
}
Ok(out)
}
/// Compose the canonical per-Servico value-block splice every per-Servico
/// renderer applies to the target values / entry mapping — the two-step
/// sequence [`caixa_helm::build_values_yaml`] and
/// [`caixa_flux::programs_yaml_entry`] both re-derived inline before this
/// lift:
///
/// 1. Splice every string-keyed entry from the `ComputeUnit` YAML's
/// `spec.*` sub-mapping (routed through [`string_keyed_entries`],
/// preserving the source Mapping's insertion order).
/// 2. Overlay the M2 typed slots (routed through
/// [`servico_m2_overlay`], `BTreeMap` key-ordered) at every M2 key
/// not already claimed by step 1 — the `or_insert` precedence rule
/// the two prior inline call sites shared, promoted here to a
/// filtered append so the returned `Vec` is drop-in for a target
/// mapping whose insertion order is load-bearing (caixa-flux's
/// `serde_yaml::Mapping` preserves it; caixa-helm's `BTreeMap`
/// re-sorts by key, so both consumer shapes stay byte-identical
/// to their prior inline blocks under this lift).
///
/// Returns a `Vec<(String, serde_yaml::Value)>` in insertion order —
/// spec.* entries first (original ordering preserved), then the M2 slots
/// that weren't claimed by spec.* (in [`servico_m2_overlay`]'s canonical
/// BTreeMap-key ordering: `behavior` → `limits` → `upgradeFrom`).
/// Callers extend their target mapping by iterating the `Vec` and
/// inserting each pair with their own map type's canonical insert.
///
/// Until this lift landed the two prior inline blocks each carried the
/// same three-shape composition: `for (k, v) in
/// caixa_core::string_keyed_entries(spec) { <insert>(k, v.clone()); }`
/// followed by `for (key, value) in caixa_core::servico_m2_overlay(caixa)?
/// { <entry-and-or-insert>(key, value); }`. A future change to the
/// per-Servico splice / overlay composition — the M4 typed per-edge
/// policy overlay slot addition (MESH-COMPOSITION §III.2 #3), a change
/// to the spec.* / M2 precedence rule (e.g. reversing to "M2 wins on
/// collision" once per-Aplicacao operator overrides land), a
/// canonicalization pass on the merged key set (e.g. rejecting empty
/// string keys, casing-normalization on DNS-1123 labels) — would have
/// to be threaded through both renderers in lockstep or one would
/// silently diverge from the other on which keys it emitted and in
/// what order. Peer with the lifted [`servico_m2_overlay`] on the
/// per-Servico M2-overlay axis (10bf310 / 0e84fb9 on the sibling
/// upsert-loop / test-side probe axes) — completes the
/// "one canonical splice / overlay composition per typed axis"
/// discipline the M2 overlay lift established, now on the composed
/// spec.*+M2 axis every per-Servico renderer entry-point navigates.
///
/// # Errors
///
/// Propagates [`RenderError::Yaml`] from [`servico_m2_overlay`] when
/// `serde_yaml::to_value` fails for any typed M2 slot value — the same
/// error surface [`servico_m2_overlay`]'s docstring names.
pub fn servico_spec_and_m2_overlay_entries(
caixa: &Caixa,
spec: &serde_yaml::Value,
) -> Result<Vec<(String, serde_yaml::Value)>, RenderError> {
let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
let mut out: Vec<(String, serde_yaml::Value)> = Vec::new();
for (k, v) in string_keyed_entries(spec) {
seen.insert(k.to_string());
out.push((k.to_string(), v.clone()));
}
for (key, value) in servico_m2_overlay(caixa)? {
if !seen.contains(key) {
out.push((key.to_string(), value));
}
}
Ok(out)
}
/// Bracket a typed `u32` axis with the "zero-floor + upper-cap" gate
/// pair every capped-`u32` `:politicas` / `:supervisor` / `:limits`
/// axis carries. Returns `on_zero()` when `value == 0`,
/// `on_cap_exceeded(value)` when `value > cap`, `Ok(())` otherwise.
///
/// The zero-floor arm strictly precedes the cap arm so a literal `0`
/// value surfaces the self-locating zero diagnostic (which every
/// per-axis error variant already documents an "omit the axis to
/// express no-bound" remediation for) rather than the misleading
/// `0 > cap` false-negative on the cap arm. Same ordering discipline
/// every existing per-axis inline `if value == 0 { … } if value > CAP
/// { … }` block already applies — this lift makes the ordering a
/// property of the helper, not a per-call-site convention six sites
/// re-derive.
///
/// Six identical-shape call sites collapse onto this helper:
///
/// * [`crate::AplicacaoSpec::validate_politicas`] on
/// `MeshPolicy::retries` (zero →
/// [`crate::AplicacaoError::PolicyRetriesZero`], cap →
/// [`crate::AplicacaoError::PolicyRetriesExceedsCap`],
/// cap = [`crate::POLICY_RETRIES_MAX`]),
/// `CircuitBreaker::max_failures` (zero →
/// [`crate::AplicacaoError::PolicyBreakerZeroFailures`], cap →
/// [`crate::AplicacaoError::PolicyBreakerMaxFailuresExceedsCap`],
/// cap = [`crate::POLICY_BREAKER_MAX_FAILURES_MAX`]), and
/// `RateLimit::rate` (zero →
/// [`crate::AplicacaoError::PolicyRateLimitZero`], cap →
/// [`crate::AplicacaoError::PolicyRateLimitExceedsCap`],
/// cap = [`crate::POLICY_RATE_LIMIT_MAX`]);
/// * [`crate::SupervisorSpec::validate`] on `max_restarts`
/// (zero → [`crate::SupervisorError::ZeroMaxRestarts`], cap →
/// [`crate::SupervisorError::MaxRestartsExceedsCap`],
/// cap = [`crate::SUPERVISOR_MAX_RESTARTS_MAX`]);
/// * [`crate::LimitsSpec::validate`] on `cpu`
/// (zero → [`crate::LimitsError::CpuZero`], cap →
/// [`crate::LimitsError::CpuExceedsCap`],
/// cap = [`crate::LIMITS_CPU_MILLICORES_MAX`]).
///
/// Peer to [`require_positive_bounded_u64`] on the `u64`-typed axes
/// ([`crate::LimitsSpec::fuel`]). Generic over the caller's error enum
/// so the same helper reaches every crate-level [`thiserror`] surface
/// — the six per-axis error variants remain the source of truth for
/// each axis's remediation prose; the helper only sequences the two
/// gate arms in canonical order and threads the value into the cap
/// arm's discriminator field.
///
/// # Errors
///
/// Returns `on_zero()` for `value == 0`; returns `on_cap_exceeded(value)`
/// for `value > cap`; returns `Ok(())` otherwise.
pub fn require_positive_bounded_u32<E>(
value: u32,
cap: u32,
on_zero: impl FnOnce() -> E,
on_cap_exceeded: impl FnOnce(u32) -> E,
) -> Result<(), E> {
if value == 0 {
return Err(on_zero());
}
if value > cap {
return Err(on_cap_exceeded(value));
}
Ok(())
}
/// Peer of [`require_positive_bounded_u32`] on the `u64`-typed axes.
/// Returns `on_zero()` when `value == 0`, `on_cap_exceeded(value)`
/// when `value > cap`, `Ok(())` otherwise. See
/// [`require_positive_bounded_u32`] for the ordering / lift rationale
/// (same "zero-floor arm strictly precedes cap arm so `0` surfaces
/// the self-locating diagnostic" discipline the peer helper documents).
///
/// The single existing call site is [`crate::LimitsSpec::validate`] on
/// `fuel` (zero → [`crate::LimitsError::FuelZero`], cap →
/// [`crate::LimitsError::FuelExceedsCap`], cap =
/// [`crate::LIMITS_FUEL_MAX`]). Lifted alongside its `u32` peer so
/// the two integer-typed axes on this discipline share one canonical
/// entry-point — a future `u64`-typed axis (a hypothetical
/// per-Aplicacao byte-budget cap, the M4 per-edge policy resolver's
/// byte-throughput axis) reaches for the same helper by construction.
///
/// # Errors
///
/// Returns `on_zero()` for `value == 0`; returns `on_cap_exceeded(value)`
/// for `value > cap`; returns `Ok(())` otherwise.
pub fn require_positive_bounded_u64<E>(
value: u64,
cap: u64,
on_zero: impl FnOnce() -> E,
on_cap_exceeded: impl FnOnce(u64) -> E,
) -> Result<(), E> {
if value == 0 {
return Err(on_zero());
}
if value > cap {
return Err(on_cap_exceeded(value));
}
Ok(())
}
/// Bracket a typed `u64` axis carrying a quantized value with the
/// "zero-floor + below-quantum floor + upper-cap + not-quantum-multiple"
/// four-arm gate every capped-and-quantized `u64` axis in the crate
/// carries. Returns `on_zero()` when `value == 0`,
/// `on_below_quantum(value)` when `value < quantum`,
/// `on_cap_exceeded(value)` when `value > cap`,
/// `on_not_quantum_multiple(value)` when `value % quantum != 0`,
/// `Ok(())` otherwise.
///
/// The four arms fire in canonical `zero → below-quantum → cap →
/// not-quantum-multiple` order, matching the discipline the pre-lift
/// inline block at [`crate::LimitsSpec::validate`]'s `:memory` axis
/// applied by hand across four sequential `if let Some(m) = self.memory()`
/// wrappers. Each arm strictly precedes the next: the zero-floor arm
/// precedes the below-quantum arm so `Some(0)` (a value the modulus arm
/// would silently accept because `0 % quantum == 0` and the below-quantum
/// arm would also flag because `0 < quantum` — two distinct diagnostics
/// for the same value) surfaces the self-locating zero diagnostic every
/// per-axis error variant already documents an "omit the axis to
/// express no-bound" remediation for; the below-quantum arm precedes
/// the cap arm so a sub-quantum value (which is *also* not a quantum
/// multiple by construction — the smallest positive quantum multiple
/// *is* `quantum`) surfaces the more actionable "raise to at least one
/// quantum" diagnostic first; the cap arm precedes the not-multiple
/// arm so a value that is both above-cap and sub-quantum-residue
/// surfaces the cap diagnostic first (the not-multiple remediation
/// would be misleading when the offending value already exceeds the
/// upper bracket — the canonical fix collapses both into "pin a
/// quantum-aligned value ≤ cap"), peer to the
/// [`require_positive_canonical_bounded_duration`] cap-precedes-not-
/// canonical ordering on the sibling typed-`Duration` axis.
///
/// One existing call site collapses onto this helper —
/// [`crate::LimitsSpec::validate`] on
/// [`crate::LimitsSpec::memory`] (zero →
/// [`crate::LimitsError::MemoryZero`], below-quantum →
/// [`crate::LimitsError::MemoryBelowWasm32Page`], cap →
/// [`crate::LimitsError::MemoryExceedsWasm32Cap`], not-multiple →
/// [`crate::LimitsError::MemoryNotPageMultiple`],
/// quantum = [`crate::LIMITS_MEMORY_WASM32_PAGE_BYTES`] (64 KiB),
/// cap = [`crate::LIMITS_MEMORY_WASM32_MAX_BYTES`] (4 GiB)) — the last
/// unlifted `:limits` axis on the four-axis `LimitsSpec::validate`
/// discipline. The three peer axes (`:fuel`, `:wall-clock`, `:cpu`)
/// each route through one substrate helper today
/// ([`require_positive_bounded_u64`],
/// [`require_positive_canonical_bounded_duration`],
/// [`require_positive_bounded_u32`]); after this lift `:memory` joins
/// them at the same altitude — every `LimitsSpec::validate` axis is
/// exactly one typed-helper dispatch, with the four-arm ordering
/// discipline promoted from per-site convention to structural contract
/// on the substrate primitive.
///
/// Peer to [`require_positive_bounded_u32`] /
/// [`require_positive_bounded_u64`] on the two-arm integer-typed
/// bracket axes and to [`require_positive_canonical_bounded_duration`]
/// on the three-arm typed-`Duration` bracket-and-quantize axis. Generic
/// over the caller's error enum so the same helper reaches every
/// crate-level [`thiserror`] surface — the four per-axis error variants
/// remain the source of truth for each axis's remediation prose; the
/// helper only sequences the four gate arms in canonical order and
/// threads the value into the below-quantum / cap / not-multiple arms'
/// discriminator fields.
///
/// PRIME DIRECTIVE promotion: the four-arm quantized-byte-cap cascade
/// is the natural u64 extension of the two-arm
/// [`require_positive_bounded_u64`] bracket the sibling `:fuel` axis
/// already routes through. Lifting it means a future quantized-byte-cap
/// axis reaching for the same discipline — a wasm64-target promotion
/// raising the wasm32 page and address-space bounds, a hypothetical
/// per-Aplicacao heap-max byte-cap, an operator-side page-aligned
/// byte-cap admitted by the M4 CR materializer's admission webhook —
/// lands as a thin four-closure wrapper rather than re-inlining the
/// same four-arm cascade with a fresh page-alignment convention.
///
/// # Errors
///
/// Returns `on_zero()` for `value == 0`; returns
/// `on_below_quantum(value)` for `value < quantum`; returns
/// `on_cap_exceeded(value)` for `value > cap`; returns
/// `on_not_quantum_multiple(value)` for `value % quantum != 0`;
/// returns `Ok(())` otherwise.
pub fn require_positive_quantum_multiple_bounded_u64<E>(
value: u64,
quantum: u64,
cap: u64,
on_zero: impl FnOnce() -> E,
on_below_quantum: impl FnOnce(u64) -> E,
on_cap_exceeded: impl FnOnce(u64) -> E,
on_not_quantum_multiple: impl FnOnce(u64) -> E,
) -> Result<(), E> {
if value == 0 {
return Err(on_zero());
}
if value < quantum {
return Err(on_below_quantum(value));
}
if value > cap {
return Err(on_cap_exceeded(value));
}
if !value.is_multiple_of(quantum) {
return Err(on_not_quantum_multiple(value));
}
Ok(())
}
/// Bracket a typed `Duration` axis with the "zero-floor +
/// canonical-form + upper-cap" three-arm gate every typed-`Duration`
/// slot in the crate carries. Returns `on_zero()` when `value` is
/// `Duration::ZERO`, `on_not_canonical(value)` when `value` carries
/// sub-millisecond residue the shared
/// [`crate::supervisor::duration_codec`] cannot round-trip losslessly,
/// `on_cap_exceeded(value)` when `value > cap`, `Ok(())` otherwise.
///
/// The three arms fire in canonical `zero → not-canonical → cap` order,
/// matching the discipline every existing per-axis inline block already
/// applied by hand: the zero-floor arm precedes the canonical-form arm
/// so `Duration::ZERO` (whose `subsec_nanos() == 0` makes it accepted
/// by the canonical-form predicate) surfaces the self-locating zero
/// diagnostic — every per-axis zero variant already documents an
/// "omit the axis to express no-bound" remediation — rather than the
/// misleading no-op the canonical arm would return; the canonical-form
/// arm then precedes the cap arm so a `Duration` that is *both*
/// sub-millisecond and above-cap surfaces the more fundamental
/// round-trip-shape diagnostic first (the cap's `1ms..=<cap>`
/// remediation would be misleading when no integer-ms form of the
/// offending value exists). Same ordering discipline the peer
/// [`require_positive_bounded_u32`] applies on its two arms — this
/// lift makes the three-arm ordering a property of the helper, not a
/// per-call-site convention four sites re-derived by hand.
///
/// Four identical-shape call sites collapse onto this helper — one for
/// each typed-`Duration` slot in the crate:
///
/// * [`crate::AplicacaoSpec::validate`] on
/// [`crate::MeshPolicy::timeout`] (zero →
/// [`crate::AplicacaoError::PolicyTimeoutZero`], not-canonical →
/// [`crate::AplicacaoError::PolicyTimeoutNotCanonical`], cap →
/// [`crate::AplicacaoError::PolicyTimeoutExceedsCap`],
/// cap = [`crate::POLICY_TIMEOUT_MAX`]) and
/// [`crate::CircuitBreaker::window`] (zero →
/// [`crate::AplicacaoError::PolicyBreakerZeroWindow`],
/// not-canonical →
/// [`crate::AplicacaoError::PolicyBreakerWindowNotCanonical`],
/// cap → [`crate::AplicacaoError::PolicyBreakerWindowExceedsCap`],
/// cap = [`crate::POLICY_BREAKER_WINDOW_MAX`]);
/// * [`crate::LimitsSpec::validate`] on
/// [`crate::LimitsSpec::wall_clock`] (zero →
/// [`crate::LimitsError::WallClockZero`], not-canonical →
/// [`crate::LimitsError::WallClockNotCanonical`], cap →
/// [`crate::LimitsError::WallClockExceedsCap`],
/// cap = [`crate::LIMITS_WALL_CLOCK_MAX`]);
/// * [`crate::SupervisorSpec::validate`] on
/// [`crate::SupervisorSpec::restart_window`] (zero →
/// [`crate::SupervisorError::RestartWindowZero`], not-canonical →
/// [`crate::SupervisorError::RestartWindowNotCanonical`], cap →
/// [`crate::SupervisorError::RestartWindowExceedsCap`],
/// cap = [`crate::SUPERVISOR_RESTART_WINDOW_MAX`]).
///
/// Peer to [`require_positive_bounded_u32`] /
/// [`require_positive_bounded_u64`] on the integer-typed capped axes;
/// the four typed-`Duration` axes and the four typed-integer axes now
/// route through one helper each, so a future axis reaching for the
/// same discipline lands in exactly one place. Generic over the
/// caller's error enum so the same helper reaches every crate-level
/// [`thiserror`] surface — the ten per-axis error variants remain the
/// source of truth for each axis's remediation prose; the helper only
/// sequences the three gate arms in canonical order and threads the
/// value into the not-canonical / cap arms' discriminator fields.
///
/// # Errors
///
/// Returns `on_zero()` for `value.is_zero()`; returns
/// `on_not_canonical(value)` when `value` carries sub-millisecond
/// residue (`value.subsec_nanos() % 1_000_000 != 0`); returns
/// `on_cap_exceeded(value)` for `value > cap`; returns `Ok(())`
/// otherwise.
pub fn require_positive_canonical_bounded_duration<E>(
value: std::time::Duration,
cap: std::time::Duration,
on_zero: impl FnOnce() -> E,
on_not_canonical: impl FnOnce(std::time::Duration) -> E,
on_cap_exceeded: impl FnOnce(std::time::Duration) -> E,
) -> Result<(), E> {
if value.is_zero() {
return Err(on_zero());
}
if !crate::supervisor::duration_codec::is_integer_millisecond_duration(value) {
return Err(on_not_canonical(value));
}
if value > cap {
return Err(on_cap_exceeded(value));
}
Ok(())
}
/// Bracket a `:versao` requirement-string axis with the shared
/// "empty-first, then [`crate::parse_requirement`]" gate pair every
/// dep-shaped `:versao` slot carries. Returns `on_empty()` when
/// `versao.is_empty()`, `on_invalid(reason)` when
/// [`crate::parse_requirement`] rejects the non-empty input, `Ok(())`
/// otherwise.
///
/// The empty-first arm strictly precedes the parse arm so a literal
/// `""` value surfaces the self-locating empty diagnostic every
/// per-axis error variant already documents an "omit the axis to
/// express any-version" remediation for, rather than the misleading
/// parse-side no-op — [`crate::parse_requirement("")`][crate::parse_requirement]
/// hits `semver::VersionReq::parse("")` which returns
/// `Ok(VersionReq { comparators: [] })` (semantically identical to
/// [`semver::VersionReq::STAR`]), so without the empty-first arm an
/// authored blank `:versao "" ` would silently round-trip as an
/// implicit `"*"` — the same "silent widening" footgun the peer
/// [`require_positive_bounded_u32`] closes on its zero-floor arm.
///
/// The three existing call sites — [`crate::dep::Dep::validate`] on
/// [`crate::dep::Dep::versao`] (empty → [`crate::DepError::VersaoEmpty`],
/// invalid → [`crate::DepError::VersaoInvalid`]),
/// [`crate::AplicacaoSpec::validate_membros`] on
/// [`crate::aplicacao::Membro::versao`] (empty →
/// [`crate::AplicacaoError::MembroVersaoEmpty`], invalid →
/// [`crate::AplicacaoError::MembroVersaoInvalid`]), and
/// [`crate::SupervisorSpec::validate`] on
/// [`crate::supervisor::ChildSpec::versao`] (empty →
/// [`crate::SupervisorError::EmptyChildVersion`], invalid →
/// [`crate::SupervisorError::ChildVersaoInvalid`]) — each formerly
/// inlined this two-arm cascade verbatim. Lifting to one canonical
/// entry-point closes the drift footgun structurally: a future
/// widening of the accepted requirement-shape (a hypothetical
/// git-tag-prefix leniency, a per-axis strictness override, or the
/// M4 typed-resolver's `constraint:` axis on
/// [`ABSORPTION-ROADMAP.md`]'s per-resolver-step trajectory) reaches
/// every dep-shaped `:versao` consumer by one edit at this helper,
/// not a coordinated rewrite across three modules.
///
/// Peer of [`require_positive_bounded_u32`] /
/// [`require_positive_bounded_u64`] on the same closure-based
/// caller-error-variant discipline — the caller owns the enum
/// variant + its self-locating discriminator fields
/// (`nome`/`caixa`, `versao`), this helper only sequences the two
/// gate arms in canonical order and threads the parser's
/// `semver`-shaped reason into the invalid arm's `reason:` field.
///
/// # Errors
///
/// Returns `on_empty()` for `versao.is_empty()`; returns
/// `on_invalid(reason)` when [`crate::parse_requirement`] rejects
/// the non-empty input (the parser's `to_string()` output threaded
/// through as the invalid arm's `reason:`); returns `Ok(())`
/// otherwise.
pub fn require_valid_versao_requirement<E>(
versao: &str,
on_empty: impl FnOnce() -> E,
on_invalid: impl FnOnce(String) -> E,
) -> Result<(), E> {
if versao.is_empty() {
return Err(on_empty());
}
if let Err(e) = crate::parse_requirement(versao) {
return Err(on_invalid(e.to_string()));
}
Ok(())
}
/// Bracket a K8s DNS-1123-label-shaped axis with the shared
/// "empty-first, then [`is_dns_1123_label`]" gate pair every Servico-
/// name reference slot carries. Returns `on_empty()` when
/// `value.is_empty()`, `on_invalid(reason)` when [`is_dns_1123_label`]
/// rejects the non-empty input, `Ok(())` otherwise.
///
/// The empty-first arm strictly precedes the shape arm so a literal
/// `""` value surfaces each per-axis error variant's narrower self-
/// locating `_Empty` diagnostic (`MembroCaixaEmpty`, `PlacementClusterEmpty`,
/// `EntradaParaEmpty`, `NomeEmpty`, `EmptyChildName`, `ModuleEmpty`, …)
/// rather than the shared predicate's generic "must not be empty" prose
/// — the same "misframed generic diagnostic" footgun the peer
/// [`require_valid_versao_requirement`] closes on its empty arm. The
/// invalid arm threads the predicate's parser-shaped reason verbatim
/// into the caller's `*Invalid { reason }` field so the author's
/// remediation prose (which specific violation — length / boundary /
/// character-class) flows through unchanged.
///
/// The eight existing call sites — [`crate::AplicacaoSpec`]'s five
/// name-shaped slots (`validate_membro_caixa` on `:membros :caixa`,
/// `validate_placement_cluster` on `:placement :clusters`,
/// `validate_placement_affinity` on `:placement :affinity`,
/// `validate_contrato_caixa` on `:contratos :de`/`:para`,
/// `validate_entrada_para` on `:entrada :para`),
/// [`crate::SupervisorSpec::validate`] on `:children :caixa`,
/// [`crate::manifest::Caixa::validate_nome`] on `:nome`, and
/// [`crate::upgrade::validate_module`] on `:upgrade-from :module` —
/// each formerly inlined this two-arm cascade verbatim. Lifting to one
/// canonical entry-point closes the drift footgun structurally: a
/// future widening of the accepted DNS-1123-label shape (a hypothetical
/// IDN-Punycode-accepting variant, a per-axis strictness override for
/// the M4 CR materializer's `spec.name` axes, or the future
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's admission
/// webhook floor) reaches every name-shaped consumer by one edit at
/// this helper, not a coordinated rewrite across three modules.
///
/// Peer of [`require_valid_versao_requirement`] on the same closure-
/// based caller-error-variant discipline — the caller owns the enum
/// variant + its self-locating discriminator fields (`caixa`, `cluster`,
/// `affinity`, `nome`, `slot`, `kind`, `module`, …), this helper only
/// sequences the two gate arms in canonical order and threads the
/// predicate's shape-shaped reason into the invalid arm's `reason:`
/// field.
///
/// # Errors
///
/// Returns `on_empty()` for `value.is_empty()`; returns
/// `on_invalid(reason)` when [`is_dns_1123_label`] rejects the
/// non-empty input (the predicate's parser-shaped reason threaded
/// through as the invalid arm's `reason:`); returns `Ok(())` otherwise.
pub fn require_valid_dns_1123_label<E>(
value: &str,
on_empty: impl FnOnce() -> E,
on_invalid: impl FnOnce(String) -> E,
) -> Result<(), E> {
if value.is_empty() {
return Err(on_empty());
}
if let Err(reason) = is_dns_1123_label(value) {
return Err(on_invalid(reason));
}
Ok(())
}
/// Bracket a sandboxed-relative `.lisp`-terminating path axis with the
/// shared "empty → absolute → parent-escape → non-`.lisp`-extension"
/// four-arm gate every author-supplied M2 tatara-lisp source-path slot
/// on the caixa surface carries. Delegates to
/// [`is_sandboxed_relative_path`] for the three structural arms and to
/// [`is_lisp_extension`] for the extension arm; returns each arm's
/// caller-owned error variant via the four `FnOnce` closures.
///
/// The arm ordering (`Empty → Absolute → ParentEscape → NonLisp`) is
/// canonical across every existing per-axis site — a path that is
/// *both* sandbox-escaping and non-`.lisp` surfaces the more
/// fundamental sandbox-shape diagnostic first (the `.lisp` remediation
/// would be misleading when the offending path can never resolve under
/// the caixa root anyway; the canonical fix collapses both into "pin a
/// relative `.lisp` path under the caixa root"). Same
/// smallest-scope-arm-fires-last posture the peer
/// [`require_positive_bounded_u32`] /
/// [`require_positive_canonical_bounded_duration`] chains follow on the
/// integer / duration axes, and the same posture every per-axis inline
/// pre-lift block already applied by hand
/// ([`crate::behavior::BehaviorError`]'s `EmptyPath` → `AbsolutePath`
/// → `ParentEscape` → `NonLispExtension` chain,
/// [`crate::upgrade::UpgradeError`]'s `EmptyScript` → `AbsoluteScript`
/// → `ParentEscapeScript` → `NonLispExtensionScript` chain).
///
/// Two identical-shape call sites collapse onto this helper — one for
/// each M2 typed path-slot the wasm-engine reads through
/// `tatara_lisp::read`:
///
/// * [`crate::behavior::BehaviorSpec::validate`] on
/// `:behavior :on-*` callback paths — every arm carries the slot
/// name verbatim through the closure's caller-side capture (empty
/// → [`crate::behavior::BehaviorError::EmptyPath`], absolute →
/// [`crate::behavior::BehaviorError::AbsolutePath`], parent-escape
/// → [`crate::behavior::BehaviorError::ParentEscape`], non-`.lisp`
/// → [`crate::behavior::BehaviorError::NonLispExtension`]);
/// * [`crate::upgrade::UpgradeInstruction::validate`]'s `StateChange`
/// arm on `:upgrade-from :state-change :script` (empty →
/// [`crate::upgrade::UpgradeError::EmptyScript`], absolute →
/// [`crate::upgrade::UpgradeError::AbsoluteScript`], parent-escape
/// → [`crate::upgrade::UpgradeError::ParentEscapeScript`],
/// non-`.lisp` →
/// [`crate::upgrade::UpgradeError::NonLispExtensionScript`]).
///
/// Peer of the sibling `require_positive_bounded_u32` /
/// `require_positive_bounded_u64` /
/// `require_positive_canonical_bounded_duration` /
/// `require_valid_versao_requirement` / `require_valid_dns_1123_label`
/// helpers on the same closure-based caller-error-variant discipline —
/// the caller owns the enum variant + its self-locating discriminator
/// fields (`slot`, `path`, `script`), this helper only sequences the
/// four gate arms in canonical order and invokes the caller's closure
/// on the offending arm.
///
/// PRIME DIRECTIVE promotion: the two-consumer duplication budget
/// (THEORY.md §I.3.5: "every recurring shape becomes a generator
/// before it becomes a pattern; every pattern becomes a library before
/// it becomes duplicated code. The duplication budget is zero.")
/// promotes the four-step cascade to a typed substrate-side helper on
/// the same trajectory the [`is_sandboxed_relative_path`] /
/// [`is_lisp_extension`] primitives already follow. A future third
/// consumer — the `:bibliotecas` per-entry tatara-lisp source-file
/// axis, the `:exe` `:kind Binario` entry-point axis, the M2.5
/// wasm-engine pre-warm hook axis, the future `mesh.pleme.io/v1alpha1/Caixa`
/// CR materializer's per-path validator — lands as a thin
/// four-closure wrapper rather than re-inlining the same four-arm
/// cascade.
///
/// # Errors
///
/// Returns `on_empty()` when `path` is empty; returns `on_absolute()`
/// when `path` is absolute; returns `on_parent_escape()` when `path`
/// carries a [`std::path::Component::ParentDir`] component anywhere;
/// returns `on_non_lisp()` when `path`'s terminating extension is not
/// exactly [`LISP_SOURCE_EXTENSION`]; returns `Ok(())` otherwise.
pub fn require_sandboxed_lisp_path<E>(
path: &Path,
on_empty: impl FnOnce() -> E,
on_absolute: impl FnOnce() -> E,
on_parent_escape: impl FnOnce() -> E,
on_non_lisp: impl FnOnce() -> E,
) -> Result<(), E> {
match is_sandboxed_relative_path(path) {
Ok(()) => {}
Err(PathShapeViolation::Empty) => return Err(on_empty()),
Err(PathShapeViolation::Absolute) => return Err(on_absolute()),
Err(PathShapeViolation::ParentEscape) => return Err(on_parent_escape()),
}
if !is_lisp_extension(path) {
return Err(on_non_lisp());
}
Ok(())
}
/// Bracket a per-list uniqueness gate with the shared "insert into
/// `seen`; caller-shaped `Err` on the second occurrence" gate every
/// declaration-order-preserving `Vec`-authored slot in caixa-core
/// carries. Delegates to [`std::collections::HashSet::insert`] verbatim
/// (which returns `true` on first insertion, `false` on repeat), then
/// invokes the caller's `on_duplicate` closure only on the duplicate
/// arm — keeping the hot path (the unique case) allocation-free.
///
/// The ten existing call sites — [`crate::AplicacaoSpec::validate`]'s
/// four per-list uniqueness gates (`:membros :caixa` →
/// [`crate::AplicacaoError::MembroDuplicate`], `:placement :clusters` →
/// [`crate::AplicacaoError::PlacementClusterDuplicate`],
/// `:entrada :paths` → [`crate::AplicacaoError::EntradaPathDuplicate`],
/// `:contratos` on the six-tuple typed-edge identity key →
/// [`crate::AplicacaoError::ContratoDuplicate`]),
/// [`crate::SupervisorSpec::validate`] on `:children :caixa`
/// ([`crate::SupervisorError::DuplicateChildCaixa`]),
/// [`crate::manifest::Caixa`]'s four per-list uniqueness gates
/// ([`crate::manifest::Caixa::validate_deps`] on `:deps` and `:deps-dev`
/// → [`crate::DepError::DuplicateNome`],
/// [`crate::manifest::Caixa::validate_code_paths`] on
/// `:bibliotecas`/`:exe`/`:servicos` →
/// [`crate::ManifestError::CodePathDuplicate`],
/// [`crate::manifest::Caixa::validate_etiquetas`] on `:etiquetas` →
/// [`crate::ManifestError::EtiquetaDuplicate`],
/// [`crate::manifest::Caixa::validate_autores`] on `:autores` →
/// [`crate::ManifestError::AutorDuplicate`]), and
/// [`crate::dep::Dep`]'s [`crate::DepError::CaracteristicaDuplicate`]
/// gate on `:caracteristicas` — each formerly inlined the same three-
/// line
/// ```ignore
/// if !seen.insert(key) {
/// return Err(<Variant> { … });
/// }
/// ```
/// shape by hand, differing only in the seen-set key type and the
/// caller's [`thiserror`] variant. Lifting to one canonical entry-point
/// closes the drift footgun structurally: a future tightening of the
/// per-list uniqueness discipline (a declaration-order pin on the
/// reported entry index, an instrumentation hook for the operator's
/// audit trail, the M4 CR materializer's admission-webhook per-list
/// invariant) reaches every consumer by one edit at this helper, not
/// a coordinated rewrite across every per-list gate in the crate. The
/// per-axis error variants remain the source of truth for each axis's
/// remediation prose — this helper only sequences the insert-and-check
/// pair.
///
/// Same set-not-multiset discipline every peer `Duplicate*` variant
/// documents. The typed key `K` is generic so both `&str`-shaped
/// callers (nine sites) and the tuple-shaped
/// [`crate::AplicacaoError::ContratoDuplicate`] typed-edge identity
/// carrier route through one helper; the caller owns the enum variant
/// + its self-locating discriminator fields, this helper only sequences
/// the insert-and-check pair in canonical `insert → on_duplicate` order.
/// Sibling to the peer `require_positive_bounded_*` /
/// `require_positive_canonical_bounded_duration` /
/// `require_valid_versao_requirement` / `require_valid_dns_1123_label`
/// helpers on the same closure-based caller-error-variant discipline.
///
/// # Errors
///
/// Returns `on_duplicate()` when `key` was already in `seen` (the
/// [`std::collections::HashSet::insert`] call returns `false`); returns
/// `Ok(())` otherwise.
pub fn insert_first_seen<K, E, S>(
seen: &mut std::collections::HashSet<K, S>,
key: K,
on_duplicate: impl FnOnce() -> E,
) -> Result<(), E>
where
K: std::hash::Hash + Eq,
S: std::hash::BuildHasher,
{
if seen.insert(key) {
Ok(())
} else {
Err(on_duplicate())
}
}
/// Test-side pin that asserts a renderer-crate `pub use caixa_core::X;`
/// re-export shares both the byte value *and* the `&'static str`
/// allocation of its canonical `caixa_core::X` declaration — the
/// stronger predicate than a plain `assert_eq!` byte-equality check.
///
/// The canonical drift footgun this closes: a renderer crate silently
/// carries a sibling `pub const X: &str = "…";` (or a copy-pasted
/// `pub const X: &str = caixa_core::X;` shape whose right-hand side
/// materializes a fresh promoted-static allocation with the same
/// bytes) instead of `pub use caixa_core::X;`. A byte-only `assert_eq!`
/// on the value would pass — the strings are equal — but the two
/// declarations point at two different `&'static` allocations, so a
/// future canonical-side rebrand (`caixa_core::X` migrates from
/// `"foo"` to `"foo-v2"`) silently drifts the two apart, with the
/// apply-time symptom (the cluster-side CRD schema drops the malformed
/// axis, the operator's dispatch loop misses the renamed key, the
/// Cilium data plane silently reroutes past the renamed L4/L7 rule)
/// far from the drift commit's source. Byte-equality misses this
/// class of drift; static-data identity via [`std::ptr::eq`] catches
/// it structurally.
///
/// Lifted from the seventy-five per-`_re_export_points_at_caixa_core_
/// canonical` test bodies formerly inlined verbatim across
/// [`caixa-mesh`][mesh] (49 tests), [`caixa-flux`][flux] (21 tests),
/// and [`caixa-helm`][helm] (5 tests) — each formerly carried the same
/// two-arm `assert_eq!(<LOCAL>, caixa_core::<LOCAL>);` + `assert!(std
/// ::ptr::eq(<LOCAL>.as_ptr(), caixa_core::<LOCAL>.as_ptr()), "…must
/// be a re-export of caixa_core::…, not a sibling `pub const`…");`
/// pair by hand, differing only in the local `<LOCAL>` identifier the
/// diagnostic names. The lifted helper puts the canonical two-arm
/// gate in exactly one place so the next per-renderer re-export pin
/// (the future [`caixa-otel`] telemetry-pipeline renderer's per-CR
/// axis re-exports, the M4 [`mesh.pleme.io/v1alpha1/Aplicacao`] CR
/// materializer's per-spec-axis re-exports, the future per-Supervisor
/// reconciler's per-`:children` axis re-exports) lands on this
/// helper by construction rather than by copying the boilerplate.
///
/// Same trajectory as the sibling [`require_kind`] /
/// [`require_single_servico`] cross-renderer-shared-gate lifts on the
/// production-side axis; this closes the peer test-side re-export-
/// identity-gate axis.
///
/// # Panics
///
/// Panics via [`assert_eq!`] when the two byte-strings differ; panics
/// via [`assert!`] on the [`std::ptr::eq`] arm when the two share
/// bytes but point at different `&'static str` allocations. The
/// `name` argument names the local re-export for the failure message
/// so the diagnostic reads `KUBE_KEY_SPEC must be a re-export of
/// caixa_core::KUBE_KEY_SPEC, …` — pointing at the offending
/// re-export site, not just at the assertion.
///
/// [mesh]: https://docs.rs/caixa-mesh
/// [flux]: https://docs.rs/caixa-flux
/// [helm]: https://docs.rs/caixa-helm
pub fn assert_str_reexport_identity(name: &str, local: &'static str, canonical: &'static str) {
assert_eq!(
local, canonical,
"{name} must byte-equal caixa_core::{name}"
);
assert!(
std::ptr::eq(local.as_ptr(), canonical.as_ptr()),
"{name} must be a re-export of caixa_core::{name}, \
not a sibling `pub const` that happens to carry the same string \
— drift between the two is the canonical footgun this lift closes"
);
}
/// Extension methods on [`serde_yaml::Mapping`] that lift the per-key
/// scalar-promotion boilerplate every K8s-artifact-emitter across
/// `caixa-mesh`, `caixa-flux`, `caixa-helm`, and `caixa-core::render`
/// carries: the canonical `mapping.insert(Value::String(key.into()),
/// value)` three-liner the schema-key axis of every emitted YAML
/// document tunnels a `&'static str` key axis-name through.
///
/// Five methods form the primitive quintuple — one per non-Null
/// primitive [`serde_yaml::Value`] variant the K8s-artifact-emit
/// surface actually reaches for as a leaf payload:
///
/// * [`Self::insert_str_key`] — insert with a `&str` key and any
/// fully-built [`serde_yaml::Value`]. The building block every
/// other renderer helper (`yaml_string_mapping`, `label_selector`,
/// `kube_resource_skeleton`, `single_field_overlay`) composes on
/// top of.
/// * [`Self::insert_string`] — insert with a `&str` key and an
/// `Into<String>` value that gets auto-promoted to
/// [`serde_yaml::Value::String`]. The string-scalar-valued-field
/// shape every schema-typed `apiVersion` / `kind` /
/// `metadata.namespace` / `port.protocol` / `hostname` /
/// `path.value` axis emission uses — collapses the two-step
/// `insert_str_key(K, Value::String(V.into()))` boilerplate onto
/// one direct call.
/// * [`Self::insert_number`] — insert with a `&str` key and an
/// `Into<serde_yaml::Number>` value that gets auto-promoted to
/// [`serde_yaml::Value::Number`]. The integer-scalar-valued-field
/// shape every schema-typed `port` / `targetPort` / `attempts` /
/// `maxFailures` / `hostPort` axis emission uses — collapses the
/// two-step `insert_str_key(K, Value::Number(N.into()))`
/// boilerplate onto one direct call.
/// * [`Self::insert_mapping`] — insert with a `&str` key and a
/// [`serde_yaml::Mapping`] value that gets auto-promoted to
/// [`serde_yaml::Value::Mapping`]. The nested-Mapping-valued-field
/// shape every schema-typed `metadata` / `spec` / `spec.rules[].path`
/// / `toPorts[].rules` sub-block emission uses — collapses the
/// two-step `insert_str_key(K, Value::Mapping(m))` boilerplate
/// onto one direct call.
/// * [`Self::insert_sequence`] — insert with a `&str` key and a
/// `Vec<serde_yaml::Value>` value that gets auto-promoted to
/// [`serde_yaml::Value::Sequence`]. The list-shape-valued-field
/// shape every schema-typed `spec.ingress[].fromEndpoints` /
/// `spec.ingress[].toPorts` / `spec.hostnames` / `spec.rules` list
/// emission uses — collapses the two-step
/// `insert_str_key(K, Value::Sequence(v))` boilerplate onto one
/// direct call.
///
/// A sibling method — [`Self::entry_str_key`] — closes the entry-API
/// twin of [`Self::insert_str_key`] on the same `&str → Value::String`
/// key-promotion axis: the [`serde_yaml::Mapping::entry`] method's
/// `Value` parameter demands the same `Value::String(<K>.into())`
/// wrapping every fresh-emit site's `insert_str_key` call closes, but
/// on the idempotent-upsert axis (where callers compose
/// `.or_insert(...)` / `.or_insert_with(...)` / `.and_modify(...)` /
/// `.or_default()` on the returned entry handle) rather than the
/// fresh-emit axis. Same key-promotion contract, different downstream
/// API surface — so a future rebrand of the promotion (e.g. to
/// [`serde_yaml::Value::Tagged`] under a K8s Server-Side-Apply typed-
/// field-ownership axis) reaches both fresh-emit and upsert sites
/// through one lift.
///
/// See each method's docstring for its compounding rationale.
pub trait MappingExt {
/// Insert `(key, value)` into `self` with `key` promoted to a
/// [`serde_yaml::Value::String`]. Returns the prior value at that
/// key, mirroring [`serde_yaml::Mapping::insert`].
///
/// The canonical shape ~48 call sites across the caixa-side
/// renderer surface (`caixa-mesh` per-`CiliumNetworkPolicy` /
/// `Gateway` / `HTTPRoute` construction, `caixa-flux` per-
/// `GitRepository` / `HelmRelease` / `Kustomization` construction,
/// `caixa-helm` per-`Chart.yaml` / `values.yaml` construction,
/// `caixa-core::render` per-skeleton construction) previously
/// carried inline as the three-line block
/// `mapping.insert(serde_yaml::Value::String(<KEY>.into()),
/// <VALUE>)` — three per-call boilerplate axes (`serde_yaml::` path
/// re-quote, `Value::String(_)` promotion, `.into()` `&str → String`
/// coercion) around a two-token semantic payload (`<KEY>`, `<VALUE>`).
///
/// Lifting collapses the boilerplate into one method call the
/// caller reads as intent (`mapping.insert_str_key(<KEY>, <VALUE>)`
/// — "insert this schema key with this rendered value") rather
/// than five hand-spelled positional artifacts. The next renderer
/// to land — the per-`:politicas` `CiliumClusterwideEnvoyConfig`
/// emitter (MESH-COMPOSITION §III.2 #3), the `app-operator`'s
/// typed `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer (§III.2
/// #5), the M4 cross-cluster fan-out's per-cluster `Service` /
/// `HTTPRoute backendRefs` emission, the future `caixa-otel`
/// OpenTelemetry-Collector pipeline emitter — gets the canonical
/// key-scalar-promotion for free with one method call, instead of
/// re-inlining the three-line block.
///
/// Peer to the sibling render-side helpers on the
/// [`serde_yaml::Value`]-construction surface:
/// [`yaml_string_mapping`] (string→string mapping), [`label_selector`]
/// (K8s `LabelSelector` shape), [`kube_resource_skeleton`] (K8s
/// `apiVersion`+`kind`+`metadata` skeleton), [`single_field_overlay`]
/// (`Option<T>` → single-key overlay). Each closes a distinct axis
/// of the K8s-artifact-emit surface's "same shape, written N times"
/// duplication; this one closes the per-key insert primitive the
/// other four all compose on top of.
fn insert_str_key(&mut self, key: &str, value: serde_yaml::Value) -> Option<serde_yaml::Value>;
/// Insert `(key, Value::String(value.into()))` into `self` — the
/// string-scalar-valued-field emission shape that combines
/// [`Self::insert_str_key`]'s `&str → Value::String` key promotion
/// with an automatic `Value::String` promotion of an `Into<String>`
/// value. Returns the prior value at that key, mirroring
/// [`serde_yaml::Mapping::insert`].
///
/// The canonical shape ~17 production call sites across the caixa-
/// side renderer surface previously carried inline as the three-
/// line block `mapping.insert_str_key(<KEY>,
/// serde_yaml::Value::String(<VALUE>.into() | .clone() |
/// .to_string()))` — the two-token semantic payload (`<KEY>`,
/// `<VALUE>`) buried under three boilerplate axes (`serde_yaml::`
/// path re-quote, `Value::String(_)` promotion, the
/// `.into() | .clone() | .to_string()` `→ String` coercion).
///
/// Sites lifted:
///
/// * caixa-mesh's `programs_for_aplicacao` per-`:membros` entry
/// (`FLEET_PROGRAMS_KEY_NAME` / `FLEET_PROGRAMS_KEY_VERSAO` /
/// `FLEET_PROGRAMS_KEY_APLICACAO`);
/// * caixa-mesh's `cilium_network_policies` per-`toPorts[]` port
/// entry (`KUBE_KEY_PORT` / `KUBE_KEY_PROTOCOL`) and per-HTTP-
/// rule `CILIUM_KEY_PATH` L7 predicate;
/// * caixa-mesh's `gateway_routes` per-`Gateway` listener block
/// (`GATEWAY_API_KEY_NAME` /
/// [`crate::GATEWAY_API_KEY_HOSTNAME`] / `GATEWAY_API_KEY_PROTOCOL`)
/// and `spec.gatewayClassName`;
/// * caixa-mesh's `gateway_routes` per-`HTTPRoute` `parentRefs[]`
/// name, per-rule `matches[].path.{type,value}` prefix-match, and
/// per-rule `backendRefs[].name` backend-target;
/// * caixa-flux's `programs_yaml_entry` per-entry `name` /
/// `namespace` axes;
/// * caixa-core `kube_resource_skeleton`'s `apiVersion` / `kind`
/// scalar heads (the two production emit sites the prior
/// `Value::String(_.to_string())` inline shape sat at).
///
/// Lifting collapses the boilerplate into one method call the
/// caller reads as intent (`mapping.insert_string(<KEY>, <VALUE>)`
/// — "insert a string-scalar-typed field named `KEY` with rendered
/// value `VALUE`") rather than four hand-spelled positional
/// artifacts. The next renderer to land — the per-`:politicas`
/// `CiliumClusterwideEnvoyConfig` emitter (whose per-policy string-
/// scalar axes are `name` / `namespace` / `defaultAction`), the
/// `app-operator`'s typed `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer (per-`spec.selectors[]` `name` / per-`spec.gates[]`
/// string-typed axes), the M4 cross-cluster fan-out's per-cluster
/// `Service.spec.ports[].name` / `HTTPRoute.spec.rules[].filters[].
/// requestHeaderModifier.set[].name` string-scalar emission, the
/// future `caixa-otel` OpenTelemetry-Collector `pipelines.traces.
/// receivers[].endpoint` string-scalar emission — gets the canonical
/// string-scalar-valued-field shape for free with one method call,
/// instead of re-inlining the three-token
/// `Value::String(_.into() | .clone() | .to_string())` block.
///
/// Peer to [`Self::insert_str_key`] on the sibling any-Value axis —
/// the two together form the "one method call per emission axis"
/// primitive pair the K8s-artifact-emit surface's "same shape,
/// written N times" duplication (THEORY.md §I.3.5) collapses onto.
fn insert_string<V: Into<String>>(&mut self, key: &str, value: V) -> Option<serde_yaml::Value>;
/// Insert `(key, Value::Number(value.into()))` into `self` — the
/// integer-scalar-valued-field emission shape that combines
/// [`Self::insert_str_key`]'s `&str → Value::String` key promotion
/// with an automatic [`serde_yaml::Value::Number`] promotion of an
/// `Into<serde_yaml::Number>` value. Returns the prior value at that
/// key, mirroring [`serde_yaml::Mapping::insert`].
///
/// The canonical shape 2 production call sites across `caixa-mesh`
/// previously carried inline as the three-token block
/// `mapping.insert_str_key(<KEY>, serde_yaml::Value::Number(<N>.into()))`
/// — the two-token semantic payload (`<KEY>`, `<N>`) buried under
/// three boilerplate axes (`serde_yaml::` path re-quote,
/// `Value::Number(_)` promotion, the `<N>.into()` typed-integer →
/// [`serde_yaml::Number`] coercion) around a numeric constant or
/// typed field the caller already carries as `u16` / `u32` / `u64`.
///
/// Sites lifted:
///
/// * caixa-mesh's `gateway_routes` per-`Gateway` `spec.listeners[].port`
/// external HTTP listener port (`KUBE_KEY_PORT` around the lifted
/// [`crate::GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT`] `u16` const,
/// cd60fde);
/// * caixa-mesh's `gateway_routes` per-`HTTPRoute.spec.rules[].backendRefs[].port`
/// backend-target Servico port (`KUBE_KEY_PORT` around the
/// [`crate::AplicacaoSpec`]-side `entrada.port` `u16` field the
/// `:entrada :port` typed slot flows through).
///
/// Lifting collapses the boilerplate into one method call the
/// caller reads as intent (`mapping.insert_number(<KEY>, <N>)` —
/// "insert a numeric-scalar-typed field named `KEY` with the typed
/// integer `N`") rather than three hand-spelled positional artifacts.
/// The next renderer to land — the per-`:politicas`
/// `CiliumClusterwideEnvoyConfig` emitter (whose per-policy
/// integer-scalar axes are the Envoy circuit-breaker
/// `maxRequests` / `maxPendingRequests` / `maxConnections` count
/// fields and the Cilium ratelimit `requestPerUnit` field,
/// MESH-COMPOSITION §III.2 #3), the `app-operator`'s typed
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer (per-`spec.
/// selectors[]` integer-scored `weight` fields, §III.2 #5), the
/// M4 cross-cluster fan-out's per-cluster
/// `Service.spec.ports[].{port, targetPort, nodePort}` /
/// `HTTPRoute.spec.rules[].backendRefs[].{port, weight}`
/// integer-scalar emission, the future `caixa-otel`
/// OpenTelemetry-Collector `service.pipelines.traces.receivers[].
/// grpc.max_recv_msg_size_mib` integer-scalar emission — gets the
/// canonical integer-scalar-valued-field shape for free with one
/// method call, instead of re-inlining the three-token
/// `Value::Number(_.into())` block.
///
/// The `Into<serde_yaml::Number>` bound accepts every numeric
/// primitive [`serde_yaml::Number`] declares `From` for
/// (`i8`..=`i64`, `u8`..=`u64`, `f32`, `f64`) — the same coverage
/// the two production sites reach through with their `u16` port
/// fields and the same coverage every future numeric-scalar
/// emission (the K8s `Service.spec.ports[].targetPort` `IntOrString`
/// integer arm, the `HTTPRoute.spec.rules[].backendRefs[].weight`
/// `int32` axis, the Envoy `maxRequests` `uint32` axis) reaches
/// through with matching typed integer fields.
///
/// Peer to [`Self::insert_string`] on the sibling string-scalar axis
/// and to [`Self::insert_mapping`] / [`Self::insert_sequence`] on
/// the sibling nested-Mapping / list-shape axes — the five together
/// with [`Self::insert_str_key`] form the "one method call per
/// emission axis" primitive quintuple the K8s-artifact-emit
/// surface's "same shape, written N times" duplication (THEORY.md
/// §I.3.5) collapses onto: `insert_str_key` for any-Value inserts,
/// `insert_string` for the string-scalar-valued-field shape,
/// `insert_number` for the integer-scalar-valued-field shape,
/// `insert_mapping` for the nested-Mapping-valued-field shape,
/// `insert_sequence` for the list-shape-valued-field shape.
fn insert_number<N: Into<serde_yaml::Number>>(
&mut self,
key: &str,
value: N,
) -> Option<serde_yaml::Value>;
/// Insert `(key, Value::Mapping(value))` into `self` — the
/// nested-Mapping-valued-field emission shape that combines
/// [`Self::insert_str_key`]'s `&str → Value::String` key promotion
/// with an automatic [`serde_yaml::Value::Mapping`] promotion of a
/// [`serde_yaml::Mapping`] value. Returns the prior value at that
/// key, mirroring [`serde_yaml::Mapping::insert`].
///
/// The canonical shape ~6 production call sites across the caixa-
/// side renderer surface previously carried inline as the three-
/// token block `mapping.insert_str_key(<KEY>,
/// serde_yaml::Value::Mapping(<INNER>))` — a two-token semantic
/// payload (`<KEY>`, `<INNER>`) buried under a two-axis boilerplate
/// (`serde_yaml::` path re-quote, `Value::Mapping(_)` promotion)
/// around a `Mapping` variable the caller already built.
///
/// Sites lifted:
///
/// * caixa-mesh's `cilium_network_policies` per-`toPorts[]`
/// `rules:` L7-introspection sub-block (`KUBE_KEY_RULES` around
/// the built `rules` Mapping);
/// * caixa-mesh's `cilium_network_policies` per-`CiliumNetworkPolicy`
/// `spec:` block (`KUBE_KEY_SPEC` around the built `policy_spec`
/// Mapping);
/// * caixa-mesh's `gateway_routes` per-`Gateway` `spec:` block
/// (`KUBE_KEY_SPEC` around the built `g_spec` Mapping);
/// * caixa-mesh's `gateway_routes` per-`HTTPRoute.spec.rules[]`
/// `matches[].path:` sub-block (`GATEWAY_API_KEY_PATH` around the
/// built `path_match` Mapping);
/// * caixa-mesh's `gateway_routes` per-`HTTPRoute` `spec:` block
/// (`KUBE_KEY_SPEC` around the built `r_spec` Mapping);
/// * caixa-core's `kube_resource_skeleton` per-CR
/// `metadata:` sub-block (`KUBE_KEY_METADATA` around the built
/// `metadata_map` Mapping).
///
/// Lifting collapses the boilerplate into one method call the
/// caller reads as intent (`mapping.insert_mapping(<KEY>, <INNER>)`
/// — "insert a nested-Mapping-typed sub-block named `KEY` with the
/// built inner `INNER`") rather than three hand-spelled positional
/// artifacts. The next renderer to land — the per-`:politicas`
/// `CiliumClusterwideEnvoyConfig` emitter (whose per-policy
/// nested-Mapping sub-blocks are `metadata:` / `spec:` /
/// `spec.resources[]`), the `app-operator`'s typed
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer
/// (per-`spec.selectors[]` and per-`spec.gates[]` sub-blocks), the
/// M4 cross-cluster fan-out's per-cluster `Service.spec` /
/// `HTTPRoute.spec` sub-block emission, the future `caixa-otel`
/// OpenTelemetry-Collector per-pipeline `receivers:` /
/// `processors:` / `exporters:` nested-Mapping emission — gets the
/// canonical nested-Mapping-valued-field shape for free with one
/// method call, instead of re-inlining the three-token
/// `Value::Mapping(_)` promotion.
///
/// Peer to [`Self::insert_string`] on the sibling scalar-value axis
/// and [`Self::insert_sequence`] on the sibling list-shape axis —
/// the four together with [`Self::insert_str_key`] form the "one
/// method call per emission axis" primitive quadruple the K8s-
/// artifact-emit surface's "same shape, written N times" duplication
/// (THEORY.md §I.3.5) collapses onto: `insert_str_key` for any-Value
/// inserts, `insert_string` for the string-scalar-valued-field
/// shape, `insert_mapping` for the nested-Mapping-valued-field
/// shape, `insert_sequence` for the list-shape-valued-field shape.
fn insert_mapping(
&mut self,
key: &str,
value: serde_yaml::Mapping,
) -> Option<serde_yaml::Value>;
/// Insert `(key, Value::Sequence(value))` into `self` — the
/// list-shape-valued-field emission shape that combines
/// [`Self::insert_str_key`]'s `&str → Value::String` key promotion
/// with an automatic [`serde_yaml::Value::Sequence`] promotion of a
/// pre-built `Vec<serde_yaml::Value>` value. Returns the prior
/// value at that key, mirroring [`serde_yaml::Mapping::insert`].
///
/// The canonical shape 4 production call sites across `caixa-mesh`
/// previously carried inline as the three-token block
/// `mapping.insert_str_key(<KEY>, serde_yaml::Value::Sequence(<VEC>))`
/// — a two-token semantic payload (`<KEY>`, `<VEC>`) buried under a
/// two-axis boilerplate (`serde_yaml::` path re-quote,
/// `Value::Sequence(_)` promotion) around a `Vec<Value>` variable
/// the caller already built.
///
/// Sites lifted:
///
/// * caixa-mesh's `cilium_network_policies` per-`CiliumNetworkPolicy`
/// `spec.ingress[].fromEndpoints:` singleton-list (`CILIUM_KEY_FROM_ENDPOINTS`
/// around a `vec![from_endpoint]` selector wrapper);
/// * caixa-mesh's `cilium_network_policies` per-`CiliumNetworkPolicy`
/// `spec.ingress[].toPorts:` list (`CILIUM_KEY_TO_PORTS` around the
/// built `to_ports_seq` per-edge port-and-L7-rule vec);
/// * caixa-mesh's `gateway_routes` per-`HTTPRoute` `spec.hostnames:`
/// singleton-list (`GATEWAY_API_KEY_HOSTNAMES` around a
/// `vec![Value::String(entrada.host…)]` host wrapper);
/// * caixa-mesh's `gateway_routes` per-`HTTPRoute` `spec.rules:`
/// list (`KUBE_KEY_RULES` around the built `rules` per-path
/// match+backend+overlay vec).
///
/// Lifting collapses the boilerplate into one method call the
/// caller reads as intent (`mapping.insert_sequence(<KEY>, <VEC>)`
/// — "insert a list-shape-typed sub-block named `KEY` with the built
/// inner `VEC`") rather than three hand-spelled positional
/// artifacts. The next renderer to land — the per-`:politicas`
/// `CiliumClusterwideEnvoyConfig` emitter (whose per-policy
/// list-shape sub-blocks are `spec.resources[]` / `spec.listeners[]`
/// / `spec.virtualHosts[]`, MESH-COMPOSITION §III.2 #3), the
/// `app-operator`'s typed `mesh.pleme.io/v1alpha1/Aplicacao` CR
/// materializer (per-`spec.selectors[]` and per-`spec.gates[]`
/// list-shape sub-blocks, §III.2 #5), the M4 cross-cluster fan-out's
/// per-cluster `Service.spec.ports[]` /
/// `HTTPRoute.spec.rules[].backendRefs[]` list emission, the future
/// `caixa-otel` OpenTelemetry-Collector per-pipeline `receivers[]`
/// / `processors[]` / `exporters[]` list emission — gets the
/// canonical list-shape-valued-field shape for free with one method
/// call, instead of re-inlining the three-token `Value::Sequence(_)`
/// promotion.
///
/// Peer to [`Self::insert_mapping`] on the sibling nested-Mapping
/// axis and [`Self::insert_string`] on the sibling scalar-value axis
/// — the four together with [`Self::insert_str_key`] form the "one
/// method call per emission axis" primitive quadruple the K8s-
/// artifact-emit surface's "same shape, written N times" duplication
/// (THEORY.md §I.3.5) collapses onto: `insert_str_key` for any-Value
/// inserts, `insert_string` for the string-scalar-valued-field
/// shape, `insert_mapping` for the nested-Mapping-valued-field
/// shape, `insert_sequence` for the list-shape-valued-field shape.
///
/// Complementary to [`singleton_mapping_sequence`] on the peer
/// singleton-list-shape axis: `singleton_mapping_sequence(m)` builds
/// the sole-Mapping-element `Value::Sequence` payload;
/// `insert_sequence(K, v)` inserts an already-built `Vec<Value>`
/// payload under a schema key. A caller composing the two through
/// [`Self::insert_singleton_mapping_sequence`] writes
/// `mapping.insert_singleton_mapping_sequence(K, m)` for the
/// singleton case (the sole element is a fresh Mapping); reach for
/// `mapping.insert_sequence(K, v)` for the multi-element or
/// non-Mapping-element case (the vec is built up per-iteration or
/// wraps a non-Mapping scalar).
fn insert_sequence(
&mut self,
key: &str,
value: Vec<serde_yaml::Value>,
) -> Option<serde_yaml::Value>;
/// Insert `(key, Value::Sequence(vec![Value::Mapping(value)]))` into
/// `self` — the singleton-Mapping-list-shape-valued-field emission
/// shape that composes [`Self::insert_str_key`]'s
/// `&str → Value::String` key promotion with the
/// [`singleton_mapping_sequence`] helper's singleton-list wrap of a
/// [`serde_yaml::Mapping`] payload. Returns the prior value at that
/// key, mirroring [`serde_yaml::Mapping::insert`].
///
/// The canonical shape 7 production call sites across `caixa-mesh`
/// previously carried inline as the two-token composition
/// `mapping.insert_str_key(<KEY>, singleton_mapping_sequence(<M>))`
/// — a two-token semantic payload (`<KEY>`, `<M>`) buried under a
/// two-symbol boilerplate (`insert_str_key(_, _)` +
/// `singleton_mapping_sequence(_)`) that fully covers the axis: every
/// site both wraps its per-call `Mapping` as the sole-element list
/// value and inserts it under a schema key on an outer `Mapping`. A
/// rebrand on either half — the outer key-scalar promotion axis
/// migrating to a per-key typed `Value` variant, the singleton-list
/// wrap migrating to a Server-Side-Apply-typed `Value::Tagged`
/// per-CRD-list shape once K8s per-field ownership annotations reach
/// the K8s Gateway API / Cilium NetworkPolicy CRD list schemas —
/// would silently desynchronize one site while leaving the other six
/// on the old shape.
///
/// Sites lifted:
///
/// * caixa-mesh's `cilium_network_policies` per-`toPorts[]` port
/// entry `ports:` singleton-list (`CILIUM_KEY_PORTS` around the
/// built `port_entry` Mapping);
/// * caixa-mesh's `cilium_network_policies` per-`toPorts[]` L7
/// `rules.http:` singleton-list (`CILIUM_KEY_HTTP` around the
/// built `http_rule` Mapping);
/// * caixa-mesh's `cilium_network_policies` per-`CiliumNetworkPolicy`
/// `spec.ingress:` singleton-list (`CILIUM_KEY_INGRESS` around the
/// built `ingress_rule` Mapping);
/// * caixa-mesh's `gateway_routes` per-`Gateway` `spec.listeners:`
/// singleton-list (`GATEWAY_API_KEY_LISTENERS` around the built
/// `listener` Mapping);
/// * caixa-mesh's `gateway_routes` per-`HTTPRoute.spec.rules[]`
/// `matches:` singleton-list (`GATEWAY_API_KEY_MATCHES` around the
/// built `match_entry` Mapping);
/// * caixa-mesh's `gateway_routes` per-`HTTPRoute.spec.rules[]`
/// `backendRefs:` singleton-list (`GATEWAY_API_KEY_BACKEND_REFS`
/// around the built `backend_ref` Mapping);
/// * caixa-mesh's `gateway_routes` per-`HTTPRoute`
/// `spec.parentRefs:` singleton-list (`GATEWAY_API_KEY_PARENT_REFS`
/// around the built `parent_ref` Mapping).
///
/// Lifting collapses the two-symbol composition into one method call
/// the caller reads as intent (`mapping.insert_singleton_mapping_sequence
/// (<KEY>, <M>)` — "insert a singleton-Mapping-list-shape sub-block
/// named `KEY` wrapping the built inner `M`") rather than two
/// nested calls. Peer to [`Self::insert_sequence`] on the sibling
/// multi-element or non-Mapping-element list-shape axis — the two
/// together partition the list-shape-valued-field emission surface:
/// [`Self::insert_singleton_mapping_sequence`] for the sole-Mapping-
/// element case, [`Self::insert_sequence`] for every other case.
///
/// The next renderer to land — the per-`:politicas`
/// `CiliumClusterwideEnvoyConfig` emitter (whose singleton
/// `spec.resources:[]` / `spec.listeners:[]` / `spec.virtualHosts:[]`
/// Mapping-element blocks, MESH-COMPOSITION §III.2 #3, are exactly the
/// singleton-Mapping-list shape), the `app-operator`'s typed
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer (per-single-
/// selector / per-single-gate emission, §III.2 #5), the M4 cross-
/// cluster fan-out's per-cluster singleton `Service.spec.ports[]` /
/// `HTTPRoute.spec.rules[].backendRefs[]` sole-element emission, the
/// future `caixa-otel` OpenTelemetry-Collector `pipelines.traces.
/// receivers[]` singleton-receiver emission — gets the canonical
/// singleton-Mapping-list-shape wrap+insert for free with one method
/// call, instead of re-inlining the two-symbol composition.
fn insert_singleton_mapping_sequence(
&mut self,
key: &str,
value: serde_yaml::Mapping,
) -> Option<serde_yaml::Value>;
/// Entry-API sibling of [`Self::insert_str_key`] — mint the
/// `Value::String(<KEY>.into())` key-promotion the underlying
/// [`serde_yaml::Mapping::entry`] method's `Value` parameter
/// demands, and return the entry-API's
/// [`serde_yaml::mapping::Entry`] handle the caller composes
/// `.or_insert(<V>)` / `.or_insert_with(<F>)` /
/// `.and_modify(<F>)` / `.or_default()` on.
///
/// The canonical shape 4 production call sites across `caixa-flux`
/// previously carried inline as the three-token composition
/// `mapping.entry(serde_yaml::Value::String(<KEY>.into()))` around
/// a one-token semantic payload (the schema key axis-name). Every
/// site immediately composes an `.or_insert(...)` on the returned
/// [`serde_yaml::mapping::Entry`] handle — the pattern is the
/// entry-API twin of the [`Self::insert_str_key`] pattern the
/// ~48 fresh-emit sites already collapsed onto (23506b3).
///
/// Sites lifted:
///
/// * caixa-flux's `programs_yaml_entry` per-`servico_m2_overlay`
/// key idempotent-upsert loop (`entry.entry(Value::String(
/// <key>.to_string())).or_insert(<value>)` — one
/// `.or_insert(...)` per `M2_KEY_LIMITS` / `M2_KEY_BEHAVIOR` /
/// `M2_KEY_UPGRADE_FROM` axis, iterating the
/// [`servico_m2_overlay`] `BTreeMap`);
/// * caixa-flux's `upsert_into_helmrelease_programs` per-
/// `HelmRelease.spec.values` upsert-if-absent (`FLUX_KEY_VALUES`
/// around a default fresh `Value::Mapping`);
/// * caixa-flux's `upsert_into_helmrelease_programs` per-
/// `HelmRelease.spec.values.programs` upsert-if-absent
/// (`FLEET_PROGRAMS_KEY_PROGRAMS` around a default fresh
/// `Value::Sequence`);
/// * caixa-flux's `upsert_into_programs_yaml` per-top-level
/// `programs:` upsert-if-absent (`FLEET_PROGRAMS_KEY_PROGRAMS`
/// around a default fresh `Value::Sequence` — the sibling of
/// the `upsert_into_helmrelease_programs` site on the same
/// key, one path deep in a HelmRelease `spec.values.` sub-tree,
/// one path at the values.yaml root).
///
/// Lifting collapses the three-token composition into one method
/// call the caller reads as intent
/// (`mapping.entry_str_key(<KEY>).or_insert(<DEFAULT>)` — "get the
/// entry handle for this schema key and default it if missing")
/// rather than four hand-spelled positional artifacts
/// (`serde_yaml::` path re-quote, `Value::String(_)` promotion,
/// the `.into() | .to_string()` `&str → String` coercion, plus the
/// `.entry(_)` call itself). The next renderer to land — the
/// per-`:politicas` `CiliumClusterwideEnvoyConfig` emitter (which
/// upserts singleton `spec.resources:[]` / `spec.listeners:[]`
/// blocks under an existing per-cluster overlay CR, MESH-COMPOSITION
/// §III.2 #3), the `app-operator`'s typed
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer (which
/// upserts `status.` sub-fields on partial reconciles, §III.2 #5),
/// the M4 cross-cluster fan-out's per-cluster idempotent
/// HelmRelease upsert — gets the canonical entry-API key-promotion
/// for free with one method call, instead of re-inlining the
/// three-token block.
///
/// Peer to [`Self::insert_str_key`] on the sibling fresh-emit
/// axis of the same `&str → Value::String` key-promotion — the
/// two together partition the `Mapping`-write surface: entry-API
/// for idempotent-upsert sites where the caller cares whether the
/// prior value was present (`or_insert` / `and_modify` /
/// `or_default` composition), insert-API for fresh-emit sites where
/// the caller unconditionally writes a value and either drops or
/// pattern-matches on the returned `Option<Value>` prior value.
fn entry_str_key(&mut self, key: &str) -> serde_yaml::mapping::Entry<'_>;
/// Arity-0-or-1 twin of [`Self::insert_str_key`] — insert
/// `(key, value.clone())` iff `value` is `Some`; leave `self`
/// untouched iff `value` is `None`. Returns the prior value at that
/// key when the insert fires (mirroring
/// [`serde_yaml::Mapping::insert`]), and `None` otherwise (no insert
/// happened, so no prior value can be surfaced).
///
/// The canonical shape 3 production call sites across `caixa-mesh`
/// previously carried inline as the three-line block
/// `if let Some(<x>) = &<overlay> { <mapping>.insert_str_key(<KEY>,
/// <x>.clone()); }` around a two-token semantic payload (the schema
/// key axis-name + the `Option<Value>` overlay slot). Every site
/// pairs a per-`:politicas` overlay [`single_field_overlay`] `Option
/// <Value>` output with the same conditional-insert conditional —
/// the arity-0-or-1 twin of [`Self::insert_str_key`]'s always-1
/// arity on the per-`(:de, :para)` axis.
///
/// Sites lifted:
///
/// * caixa-mesh's `cilium_network_policies` per-ingress-rule
/// `:politicas :mtls-required` mutual-auth overlay
/// ([`crate::CILIUM_KEY_AUTHENTICATION`] around the
/// `mtls_overlay` [`single_field_overlay`] output — the
/// tristate `{mode: required | disabled}` block or the
/// None-omit arm);
/// * caixa-mesh's `gateway_routes` per-HTTPRoute-rule
/// `:politicas :timeout` request-deadline overlay
/// ([`crate::GATEWAY_API_KEY_TIMEOUTS`] around the
/// `timeout_overlay` [`single_field_overlay`] output — the
/// `{request: "<duration>"}` block or the None-omit arm);
/// * caixa-mesh's `gateway_routes` per-HTTPRoute-rule
/// `:politicas :retries` retry-attempt-cap overlay
/// ([`crate::GATEWAY_API_KEY_RETRY`] around the
/// `retry_overlay` [`single_field_overlay`] output — the
/// `{attempts: <N>}` block or the None-omit arm).
///
/// Lifting collapses the three-line block into one method call the
/// caller reads as intent (`mapping.insert_str_key_if_some(<KEY>,
/// <overlay>.as_ref())` — "insert this schema key if the overlay
/// carried a value; else leave the key absent") rather than four
/// hand-spelled positional artifacts (the `if let Some(_) = &_`
/// destructure, the per-inner `.clone()`, the trailing brace, plus
/// the `.insert_str_key(_)` call itself). The absent-overlay arm —
/// which every [`MeshPolicy`] axis defaults to when the author
/// leaves the typed slot unset (the `None` arm of the
/// `Option<Value>` [`single_field_overlay`] output) — reads as the
/// method's own `Option::None` branch, not a per-call-site inverted
/// `if let Some` scaffold around a per-call-site clone.
///
/// The next renderer to land — the per-`:politicas`
/// `CiliumClusterwideEnvoyConfig` emitter (whose per-policy
/// `authentication:` / `rateLimit:` / `circuitBreaker:` Option
/// overlays, MESH-COMPOSITION §III.2 #3, thread through the same
/// [`single_field_overlay`] `Option<Value>` axis the three lifted
/// sites here already reach), the `app-operator`'s typed
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer (whose per-
/// selector `status.` sub-field overlays are the same arity-0-or-1
/// shape, §III.2 #5), the M4 cross-cluster fan-out's per-cluster
/// `HTTPRoute.spec.rules[].filters[]` per-filter Option overlays
/// (the same shape at the per-cluster axis) — gets the canonical
/// arity-0-or-1 conditional-insert for free with one method call,
/// instead of re-inlining the three-line `if let Some { clone;
/// insert_str_key }` block.
///
/// Peer to [`Self::insert_str_key`] on the always-1 arity axis
/// (fresh-emit sites where the caller unconditionally writes a
/// value) — the two together partition the fresh-emit surface
/// exactly on the arity axis: [`Self::insert_str_key`] for
/// unconditional writes, [`Self::insert_str_key_if_some`] for
/// conditional writes gated on an `Option<Value>` upstream
/// producer (the per-`:politicas` overlay
/// [`single_field_overlay`] axis, and every future arity-0-or-1
/// axis every future renderer's optional-slot machinery reaches
/// through).
///
/// The `Option<&Value>` shape (as opposed to an owned
/// `Option<Value>`) lets the caller pass `overlay.as_ref()` on an
/// owned `Option<Value>` the caller reuses across iterations of an
/// outer per-`(:de, :para)` or per-rule loop — every lifted site
/// consumes the overlay from a loop-outer binding into each of N
/// per-iteration `Mapping`s, so the clone happens iff the insert
/// fires (the None arm skips the clone entirely) and the outer
/// binding stays available for the next iteration.
fn insert_str_key_if_some(
&mut self,
key: &str,
value: Option<&serde_yaml::Value>,
) -> Option<serde_yaml::Value>;
/// Fetch a `&mut serde_yaml::Mapping` at `key`, defaulting an empty
/// [`serde_yaml::Mapping`] into place when the entry is absent.
/// Returns `Some(&mut inner)` on the absent-key (fresh empty
/// Mapping) and present-Mapping arms; `None` iff `key` holds a
/// different [`serde_yaml::Value`] variant — a structural
/// container-type mismatch the caller surfaces as its own
/// domain-specific error (`Error::MissingField("spec.values must
/// be a mapping")` for the caixa-flux Flux-HelmRelease overlay
/// walker).
///
/// The canonical shape 1 production call site in `caixa-flux`
/// (`upsert_into_helmrelease_programs`'s per-`HelmRelease.spec.values`
/// container-upsert on the way down to
/// `spec.values.programs[]`) previously carried inline as a
/// four-line block combining [`Self::entry_str_key`]'s entry-API
/// key promotion (68d035e), an
/// `.or_insert(Value::Mapping(Mapping::new()))` empty-Mapping
/// default, and a `let Value::Mapping(inner) = _ else { Err(...) }`
/// destructure — a two-token semantic payload (the schema key +
/// the domain-specific type-mismatch diagnostic) buried under
/// three boilerplate axes (`Value::Mapping(_)` variant promotion,
/// `Mapping::new()` empty-container construction, the outer
/// `let else` destructure). Peer to
/// [`Self::entry_or_default_sequence`] on the sibling `Vec<Value>`-
/// valued idempotent-container-upsert axis — the two together
/// partition the entry-API-container-upsert surface exactly on the
/// container-variant axis: [`Self::entry_or_default_mapping`] for
/// nested-Mapping sub-blocks, [`Self::entry_or_default_sequence`]
/// for list-shape sub-blocks.
///
/// Sites lifted:
///
/// * caixa-flux's `upsert_into_helmrelease_programs` per-
/// `HelmRelease.spec.values` container-upsert
/// (`FLUX_KEY_VALUES` around the default fresh
/// `Value::Mapping`, on the way down to the nested
/// `spec.values.programs[]` sequence).
///
/// Lifting collapses the four-line block into one method call the
/// caller reads as intent (`mapping.entry_or_default_mapping(<KEY>)
/// .ok_or(<ERR>)?` — "give me the nested Mapping at this schema
/// key, defaulting empty if absent, else surface my domain
/// error") rather than five hand-spelled positional artifacts
/// (`serde_yaml::` path re-quote, `Value::Mapping(_)` promotion,
/// `Mapping::new()` construction, the entry-API `.or_insert(...)`
/// call, plus the outer `let Value::Mapping(_) = _ else {}`
/// destructure). The next renderer to land — the per-`:politicas`
/// `CiliumClusterwideEnvoyConfig` emitter (whose per-cluster
/// upsert walks
/// `HelmRelease.spec.values.<library>.<:politicas-axis>`,
/// idempotent-upserting nested-Mapping sub-blocks under each
/// axis, MESH-COMPOSITION §III.2 #3), the `app-operator`'s typed
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer (which
/// upserts `status.<axis>` nested-Mapping sub-blocks on partial
/// reconciles, §III.2 #5), the M4 cross-cluster fan-out's
/// per-cluster idempotent `HelmRelease.spec.values.<library>`
/// container-upsert — gets the canonical entry-API-with-
/// container-type-check for free with one method call, instead
/// of re-inlining the four-line block.
///
/// The default-empty-Mapping construction fires only on the
/// absent-key arm (`.or_insert_with(...)` gates the closure on
/// vacancy) — the present-key arm reuses the existing Mapping
/// verbatim, so the caller's downstream writes on `&mut inner`
/// compose with any prior overlay writes from earlier passes
/// (the exact idempotent-upsert semantic the caixa-flux
/// per-cluster `feira app deploy` write path depends on to
/// preserve operator-pinned overlays across re-renders).
fn entry_or_default_mapping(&mut self, key: &str) -> Option<&mut serde_yaml::Mapping>;
/// Fetch a `&mut Vec<serde_yaml::Value>` at `key`, defaulting an
/// empty [`Vec<serde_yaml::Value>`] into place when the entry is
/// absent. Returns `Some(&mut inner)` on the absent-key (fresh
/// empty Sequence) and present-Sequence arms; `None` iff `key`
/// holds a different [`serde_yaml::Value`] variant — a structural
/// container-type mismatch the caller surfaces as its own
/// domain-specific error (`Error::MissingField("programs must be
/// a sequence")` for the caixa-flux fleet-programs upsert
/// walkers).
///
/// The canonical shape 2 production call sites in `caixa-flux`
/// (`upsert_into_helmrelease_programs`'s per-
/// `HelmRelease.spec.values.programs` container-upsert and
/// `upsert_into_programs_yaml`'s top-level `programs:` container-
/// upsert) previously carried inline as a four-line block
/// combining [`Self::entry_str_key`]'s entry-API key promotion
/// (68d035e), an `.or_insert(Value::Sequence(Vec::new()))`
/// empty-Sequence default, and a `match _ { Value::Sequence(seq)
/// => seq, _ => return Err(...) }` destructure — a two-token
/// semantic payload (the schema key + the domain-specific
/// type-mismatch diagnostic) buried under three boilerplate axes
/// (`Value::Sequence(_)` variant promotion, `Vec::new()`
/// empty-container construction, the outer `match` destructure).
/// Peer to [`Self::entry_or_default_mapping`] on the sibling
/// nested-Mapping-valued idempotent-container-upsert axis.
///
/// Sites lifted:
///
/// * caixa-flux's `upsert_into_helmrelease_programs` per-
/// `HelmRelease.spec.values.programs` list-container-upsert
/// (`FLEET_PROGRAMS_KEY_PROGRAMS` around the default fresh
/// `Value::Sequence`, one path deep in a `HelmRelease`
/// `spec.values.` sub-tree);
/// * caixa-flux's `upsert_into_programs_yaml` per-top-level
/// `programs:` list-container-upsert
/// (`FLEET_PROGRAMS_KEY_PROGRAMS` around the default fresh
/// `Value::Sequence` — the sibling of the
/// `upsert_into_helmrelease_programs` site on the same key,
/// one path at the values.yaml root).
///
/// Lifting collapses the four-line block into one method call the
/// caller reads as intent (`mapping.entry_or_default_sequence(<KEY>)
/// .ok_or(<ERR>)?` — "give me the list at this schema key,
/// defaulting empty if absent, else surface my domain error")
/// rather than five hand-spelled positional artifacts
/// (`serde_yaml::` path re-quote, `Value::Sequence(_)` promotion,
/// `Vec::new()` construction, the entry-API `.or_insert(...)`
/// call, plus the outer `match { Value::Sequence(_) => _, _ =>
/// return Err(_) }` destructure). The next renderer to land — the
/// per-`:politicas` `CiliumClusterwideEnvoyConfig` emitter
/// (whose per-cluster upsert walks nested list-shape sub-blocks
/// `spec.resources[]` / `spec.listeners[]` / `spec.virtualHosts[]`
/// under existing operator-pinned overlay CRs, MESH-COMPOSITION
/// §III.2 #3), the `app-operator`'s typed
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer (which
/// upserts `status.selectors[]` / `status.gates[]` list-shape
/// sub-blocks on partial reconciles, §III.2 #5), the M4 cross-
/// cluster fan-out's per-cluster idempotent
/// `HelmRelease.spec.values.programs` list-upsert — gets the
/// canonical entry-API-with-container-type-check for free with
/// one method call, instead of re-inlining the four-line block.
///
/// The default-empty-Sequence construction fires only on the
/// absent-key arm (`.or_insert_with(...)` gates the closure on
/// vacancy) — the present-key arm reuses the existing Vec
/// verbatim, so the caller's downstream `upsert_named_entry`
/// (10bf310) call on `&mut inner` composes with any prior
/// entries the emitter wrote on earlier passes (the exact
/// idempotent-upsert semantic the `feira app deploy` per-cluster
/// write path depends on to preserve prior `programs[]` entries
/// across per-Servico rewrites).
fn entry_or_default_sequence(&mut self, key: &str) -> Option<&mut Vec<serde_yaml::Value>>;
}
impl MappingExt for serde_yaml::Mapping {
#[inline]
fn insert_str_key(&mut self, key: &str, value: serde_yaml::Value) -> Option<serde_yaml::Value> {
self.insert(serde_yaml::Value::String(key.to_string()), value)
}
#[inline]
fn insert_string<V: Into<String>>(&mut self, key: &str, value: V) -> Option<serde_yaml::Value> {
self.insert_str_key(key, serde_yaml::Value::String(value.into()))
}
#[inline]
fn insert_number<N: Into<serde_yaml::Number>>(
&mut self,
key: &str,
value: N,
) -> Option<serde_yaml::Value> {
self.insert_str_key(key, serde_yaml::Value::Number(value.into()))
}
#[inline]
fn insert_mapping(
&mut self,
key: &str,
value: serde_yaml::Mapping,
) -> Option<serde_yaml::Value> {
self.insert_str_key(key, serde_yaml::Value::Mapping(value))
}
#[inline]
fn insert_sequence(
&mut self,
key: &str,
value: Vec<serde_yaml::Value>,
) -> Option<serde_yaml::Value> {
self.insert_str_key(key, serde_yaml::Value::Sequence(value))
}
#[inline]
fn insert_singleton_mapping_sequence(
&mut self,
key: &str,
value: serde_yaml::Mapping,
) -> Option<serde_yaml::Value> {
self.insert_str_key(key, singleton_mapping_sequence(value))
}
#[inline]
fn entry_str_key(&mut self, key: &str) -> serde_yaml::mapping::Entry<'_> {
self.entry(serde_yaml::Value::String(key.to_string()))
}
#[inline]
fn insert_str_key_if_some(
&mut self,
key: &str,
value: Option<&serde_yaml::Value>,
) -> Option<serde_yaml::Value> {
value.and_then(|v| self.insert_str_key(key, v.clone()))
}
#[inline]
fn entry_or_default_mapping(&mut self, key: &str) -> Option<&mut serde_yaml::Mapping> {
match self
.entry_str_key(key)
.or_insert_with(|| serde_yaml::Value::Mapping(serde_yaml::Mapping::new()))
{
serde_yaml::Value::Mapping(m) => Some(m),
_ => None,
}
}
#[inline]
fn entry_or_default_sequence(&mut self, key: &str) -> Option<&mut Vec<serde_yaml::Value>> {
match self
.entry_str_key(key)
.or_insert_with(|| serde_yaml::Value::Sequence(Vec::new()))
{
serde_yaml::Value::Sequence(s) => Some(s),
_ => None,
}
}
}
/// Extension methods for the [`Vec<serde_yaml::Value>`] emission
/// surface that the K8s-artifact-emit sites of `caixa-mesh` /
/// `caixa-flux` / `caixa-helm` / `caixa-core::render` build up as
/// `spec.ingress[]` / `spec.rules[]` / `spec.hostnames[]` / per-
/// programs.yaml-entry payloads before wrapping each vec as a
/// [`serde_yaml::Value::Sequence`] on an outer [`serde_yaml::Mapping`]
/// (via [`MappingExt::insert_sequence`]).
///
/// Peer to [`MappingExt`] on the sibling [`serde_yaml::Value`]-
/// construction surface: [`MappingExt`] closes the per-key-and-value
/// insert primitive every schema-key axis reaches through;
/// [`SequenceExt`] closes the per-list-element push primitive every
/// per-iteration append site reaches through when the built-up
/// [`serde_yaml::Value`] variant is uniform across a loop body (e.g.
/// every element is a fresh [`serde_yaml::Value::Mapping`], not a
/// heterogeneous mix of `Mapping` / `String` / `Sequence`).
///
/// Each method mints the same `Value::<Variant>(<payload>)` promotion
/// the caller would otherwise re-inline as
/// `vec.push(serde_yaml::Value::<Variant>(<payload>))` on every
/// iteration. Same variant-promotion contract as [`MappingExt`]'s
/// typed inserts, applied to the sequence-append axis instead of the
/// mapping-insert axis — so a future rebrand of the `Value` variant
/// wrapping (e.g. to a Server-Side-Apply-typed
/// [`serde_yaml::Value::Tagged`] per-list-element ownership axis)
/// reaches both `Mapping`-insert and `Vec<Value>`-push sites through
/// one lift.
pub trait SequenceExt {
/// Append `Value::Mapping(value)` to `self` — the per-iteration
/// append shape that combines a `Vec<serde_yaml::Value>::push`
/// with an automatic [`serde_yaml::Value::Mapping`] promotion of a
/// pre-built [`serde_yaml::Mapping`] element.
///
/// The canonical shape 4 production call sites across `caixa-mesh`
/// previously carried inline as the three-token block
/// `<vec>.push(serde_yaml::Value::Mapping(<M>))` — a one-token
/// semantic payload (the per-iteration `Mapping`) buried under a
/// two-axis boilerplate (`serde_yaml::` path re-quote,
/// `Value::Mapping(_)` promotion) around a `Mapping` variable the
/// caller already built.
///
/// Sites lifted:
///
/// * caixa-mesh's `programs_for_aplicacao` per-`:membros`
/// programs.yaml entry append (per-member entry `Mapping` →
/// the fan-out `Vec<Value>`);
/// * caixa-mesh's `cilium_network_policies` per-edge
/// `spec.ingress[].toPorts[]` L4-and-L7 port-and-rule append
/// (per-`(:de, :para)` group's per-edge `to_port` Mapping →
/// the `to_ports_seq` Vec);
/// * caixa-mesh's `cilium_network_policies` per-policy
/// top-level CNP-document append (per-`(:de, :para)` group's
/// built `policy` Mapping → the render-output `Vec<Value>`);
/// * caixa-mesh's `gateway_routes` per-HTTPRoute-rule
/// `spec.rules[]` append (per-path built `rule` Mapping → the
/// `rules` Vec).
///
/// Lifting collapses the three-token block into one method call
/// the caller reads as intent (`<vec>.push_mapping(<M>)` —
/// "append this built inner `M` as the next `Value::Mapping`
/// element") rather than three hand-spelled positional artifacts
/// (`serde_yaml::` path re-quote, `Value::Mapping(_)` promotion,
/// plus the `.push(_)` call itself). Peer to
/// [`MappingExt::insert_singleton_mapping_sequence`] on the
/// singleton-Mapping-list-shape axis: [`Self::push_mapping`]
/// builds up a multi-element `Vec<Value>` per iteration when the
/// caller then calls [`MappingExt::insert_sequence`] to route the
/// finished vec under a schema key;
/// [`MappingExt::insert_singleton_mapping_sequence`] fuses the
/// singleton wrap + the schema-key insert into one call when the
/// caller has exactly one Mapping element to emit under a schema
/// key.
///
/// The next renderer to land — the per-`:politicas`
/// `CiliumClusterwideEnvoyConfig` emitter (whose per-policy
/// `spec.resources[]` / `spec.listeners[]` / `spec.virtualHosts[]`
/// list-shape axes fan out multi-Mapping-element per iteration,
/// MESH-COMPOSITION §III.2 #3), the `app-operator`'s typed
/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer (per-
/// `spec.selectors[]` / per-`spec.gates[]` multi-element append,
/// §III.2 #5), the M4 cross-cluster fan-out's per-cluster
/// multi-entry `Service.spec.ports[]` /
/// `HTTPRoute.spec.rules[].backendRefs[]` list append, the future
/// `caixa-otel` OpenTelemetry-Collector per-pipeline
/// `receivers[]` / `processors[]` / `exporters[]` multi-element
/// append — gets the canonical `Value::Mapping`-promoted append
/// for free with one method call, instead of re-inlining the
/// three-token `Value::Mapping(_)` promotion.
fn push_mapping(&mut self, value: serde_yaml::Mapping);
}
impl SequenceExt for Vec<serde_yaml::Value> {
#[inline]
fn push_mapping(&mut self, value: serde_yaml::Mapping) {
self.push(serde_yaml::Value::Mapping(value));
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{BehaviorSpec, CaixaKind, LimitsSpec, UpgradeFromEntry, UpgradeInstruction};
use std::path::PathBuf;
use std::time::Duration;
fn bare_servico() -> Caixa {
Caixa {
nome: "hello-rio".into(),
versao: "0.1.0".into(),
kind: CaixaKind::Servico,
edicao: Some("2026".into()),
descricao: None,
repositorio: None,
licenca: None,
autores: vec![],
etiquetas: vec![],
deps: vec![],
deps_dev: vec![],
exe: vec![],
bibliotecas: vec![],
servicos: vec!["servicos/hello-rio.computeunit.yaml".into()],
limits: None,
behavior: None,
upgrade_from: vec![],
estrategia: None,
max_restarts: None,
restart_window: None,
children: vec![],
membros: vec![],
contratos: vec![],
politicas: None,
placement: None,
entrada: None,
ci: None,
}
}
#[test]
fn empty_caixa_returns_empty_overlay() {
let overlay = servico_m2_overlay(&bare_servico()).unwrap();
assert!(
overlay.is_empty(),
"a Caixa with no M2 slots emits zero overlay fragments"
);
}
#[test]
fn empty_typed_specs_are_skipped_like_unset_ones() {
// `Some(LimitsSpec::default())` (every axis None) and
// `Some(BehaviorSpec::default())` (every callback None) must
// round-trip identical to `None` — the is_empty()-skip
// invariant the renderers' "empty M2 slots do not appear"
// tests pinned inline before this lift.
let mut c = bare_servico();
c.limits = Some(LimitsSpec::default());
c.behavior = Some(BehaviorSpec::default());
let overlay = servico_m2_overlay(&c).unwrap();
assert!(overlay.is_empty());
}
#[test]
fn limits_slot_appears_under_camelcase_key() {
let mut c = bare_servico();
c.limits = Some(LimitsSpec {
memory: Some(64 * 1024 * 1024),
fuel: Some(1_000_000),
wall_clock: Some(Duration::from_secs(30)),
cpu: Some(500),
});
let overlay = servico_m2_overlay(&c).unwrap();
assert_eq!(overlay.len(), 1);
let limits = overlay.get(M2_KEY_LIMITS).expect("limits key present");
assert_eq!(
limits.get(M2_LIMITS_KEY_MEMORY).and_then(|m| m.as_str()),
Some("64MiB")
);
assert_eq!(
limits
.get(M2_LIMITS_KEY_WALL_CLOCK)
.and_then(|m| m.as_str()),
Some("30s")
);
}
#[test]
fn behavior_slot_appears_under_camelcase_key() {
let mut c = bare_servico();
c.behavior = Some(BehaviorSpec {
on_init: Some(PathBuf::from("lib/init.lisp")),
on_call: Some(PathBuf::from("lib/handlers.lisp")),
..Default::default()
});
let overlay = servico_m2_overlay(&c).unwrap();
let behavior = overlay.get(M2_KEY_BEHAVIOR).expect("behavior key present");
assert_eq!(
behavior
.get(M2_BEHAVIOR_KEY_ON_INIT)
.and_then(|v| v.as_str()),
Some("lib/init.lisp")
);
assert_eq!(
behavior
.get(M2_BEHAVIOR_KEY_ON_CALL)
.and_then(|v| v.as_str()),
Some("lib/handlers.lisp")
);
}
#[test]
fn upgrade_from_slot_appears_under_camelcase_key() {
let mut c = bare_servico();
c.upgrade_from = vec![UpgradeFromEntry {
from: "0.0.9".into(),
instructions: vec![UpgradeInstruction::LoadModule {
module: "hello-rio".into(),
}],
}];
let overlay = servico_m2_overlay(&c).unwrap();
let upgrade = overlay
.get(M2_KEY_UPGRADE_FROM)
.expect("upgradeFrom key present");
let arr = upgrade.as_sequence().expect("sequence");
assert_eq!(arr.len(), 1);
assert_eq!(
arr[0]
.get(M2_UPGRADE_FROM_KEY_FROM)
.and_then(|v| v.as_str()),
Some("0.0.9")
);
}
#[test]
fn all_three_slots_appear_in_alphabetical_iteration_order() {
// BTreeMap iteration is sorted by key — pin that the renderers
// can rely on a deterministic iteration order, which feeds
// into deterministic YAML output (the value-as-proof property
// THEORY.md §V.2.7 "render determinism" requires).
let mut c = bare_servico();
c.limits = Some(LimitsSpec {
memory: Some(64 * 1024 * 1024),
..Default::default()
});
c.behavior = Some(BehaviorSpec {
on_init: Some(PathBuf::from("lib/init.lisp")),
..Default::default()
});
c.upgrade_from = vec![UpgradeFromEntry {
from: "0.0.9".into(),
instructions: vec![UpgradeInstruction::LoadModule {
module: "hello-rio".into(),
}],
}];
let overlay = servico_m2_overlay(&c).unwrap();
let keys: Vec<_> = overlay.keys().copied().collect();
assert_eq!(
keys,
vec![M2_KEY_BEHAVIOR, M2_KEY_LIMITS, M2_KEY_UPGRADE_FROM]
);
}
// ── servico_spec_and_m2_overlay_entries — composed splice ────────────
//
// The compound peer of `servico_m2_overlay` on the ComputeUnit-YAML
// `spec.*` + M2-overlay axis: fuses the two prior inline for-loops
// caixa-flux::programs_yaml_entry and caixa-helm::build_values_yaml
// both carried around `string_keyed_entries` + `servico_m2_overlay`
// into one canonical composition. The pins below bracket the shape
// end-to-end (spec.* keys first + preserved-insertion-order, then M2
// slots in BTreeMap-key order at every M2 key not already claimed by
// spec.*).
fn cu_yaml_with_spec_fields(spec_yaml: &str) -> serde_yaml::Value {
serde_yaml::from_str(&format!(
"apiVersion: wasm.pleme.io/v1alpha1\nkind: ComputeUnit\nmetadata:\n name: hello-rio\nspec:\n{spec_yaml}"
))
.unwrap()
}
#[test]
fn servico_spec_and_m2_overlay_entries_empty_caixa_and_empty_spec_yields_empty() {
let cu = cu_yaml_with_spec_fields(" {}\n");
let spec = cu.get(KUBE_KEY_SPEC).unwrap();
let out = servico_spec_and_m2_overlay_entries(&bare_servico(), spec).unwrap();
assert!(
out.is_empty(),
"empty spec + empty M2 surface yields zero entries \
(both loops short-circuit vacuously)"
);
}
#[test]
fn servico_spec_and_m2_overlay_entries_splices_spec_fields_in_source_insertion_order() {
// The spec.* field-splice loop preserves the source YAML
// Mapping's insertion order — caixa-flux's `serde_yaml::Mapping`
// target reads this back verbatim, so a rebrand of the source
// ComputeUnit YAML's field ordering must not silently reorder
// the emitted programs.yaml entry.
let cu = cu_yaml_with_spec_fields(
" module:\n source: oci://ghcr.io/pleme-io/hello-rio:v0.1.0\n \
trigger:\n service: {port: 8080}\n capabilities:\n - env\n",
);
let spec = cu.get(KUBE_KEY_SPEC).unwrap();
let out = servico_spec_and_m2_overlay_entries(&bare_servico(), spec).unwrap();
let keys: Vec<&str> = out.iter().map(|(k, _)| k.as_str()).collect();
assert_eq!(
keys,
vec![
COMPUTEUNIT_SPEC_KEY_MODULE,
COMPUTEUNIT_SPEC_KEY_TRIGGER,
COMPUTEUNIT_SPEC_KEY_CAPABILITIES,
],
"spec.* keys must appear in source-Mapping insertion order",
);
}
#[test]
fn servico_spec_and_m2_overlay_entries_appends_m2_slots_after_spec_in_canonical_key_order() {
// Bracket the second-half of the composition — the M2 overlay
// walk lands after the spec.* splice, in BTreeMap-key ordering
// (behavior → limits → upgradeFrom).
let cu = cu_yaml_with_spec_fields(
" module:\n source: oci://ghcr.io/pleme-io/hello-rio:v0.1.0\n",
);
let spec = cu.get(KUBE_KEY_SPEC).unwrap();
let mut c = bare_servico();
c.limits = Some(LimitsSpec {
memory: Some(64 * 1024 * 1024),
..Default::default()
});
c.behavior = Some(BehaviorSpec {
on_init: Some(PathBuf::from("lib/init.lisp")),
..Default::default()
});
c.upgrade_from = vec![UpgradeFromEntry {
from: "0.0.9".into(),
instructions: vec![UpgradeInstruction::LoadModule {
module: "hello-rio".into(),
}],
}];
let out = servico_spec_and_m2_overlay_entries(&c, spec).unwrap();
let keys: Vec<&str> = out.iter().map(|(k, _)| k.as_str()).collect();
assert_eq!(
keys,
vec![
COMPUTEUNIT_SPEC_KEY_MODULE,
M2_KEY_BEHAVIOR,
M2_KEY_LIMITS,
M2_KEY_UPGRADE_FROM,
],
"M2 slots must land after the spec.* splice, in canonical \
BTreeMap key order",
);
}
#[test]
fn servico_spec_and_m2_overlay_entries_or_insert_precedence_spec_wins_on_collision() {
// The or_insert precedence rule the two prior inline blocks
// shared: when the ComputeUnit YAML's `spec.*` sub-mapping
// already carries the M2 slot's key (an author-authored
// ComputeUnit `spec.limits` overriding the manifest-derived
// `caixa.limits` overlay), the spec.* value stays and the M2
// overlay's value is skipped. Regression-guards against a
// future reversal ("M2 wins on collision") silently changing
// the composition without an explicit slot-precedence flip at
// the helper.
let cu = cu_yaml_with_spec_fields(
" limits:\n memory: from-spec\n module:\n source: oci://x\n",
);
let spec = cu.get(KUBE_KEY_SPEC).unwrap();
let mut c = bare_servico();
c.limits = Some(LimitsSpec {
memory: Some(64 * 1024 * 1024),
..Default::default()
});
let out = servico_spec_and_m2_overlay_entries(&c, spec).unwrap();
let limits_entries: Vec<&(String, serde_yaml::Value)> =
out.iter().filter(|(k, _)| k == M2_KEY_LIMITS).collect();
assert_eq!(
limits_entries.len(),
1,
"on collision the M2 overlay's `limits` entry must be \
filtered out — spec.* wins, and appears exactly once",
);
assert_eq!(
limits_entries[0]
.1
.get(M2_LIMITS_KEY_MEMORY)
.and_then(|v| v.as_str()),
Some("from-spec"),
"the surviving `limits` entry must carry the spec.* value, \
not the manifest-derived M2 overlay's value",
);
}
#[test]
fn servico_spec_and_m2_overlay_entries_short_circuits_on_non_mapping_spec() {
// Sibling `string_keyed_entries` docstring pins the
// non-Mapping short-circuit; extend it to the composed splice
// — a spec that isn't a Mapping yields zero spec.* entries,
// and only the M2 overlay contributes. Bracket-guard against a
// future refactor that swaps `string_keyed_entries` for a
// stricter parser silently dropping the M2 half too.
let non_mapping_spec = serde_yaml::Value::String("not-a-mapping".into());
let mut c = bare_servico();
c.limits = Some(LimitsSpec {
memory: Some(64 * 1024 * 1024),
..Default::default()
});
let out = servico_spec_and_m2_overlay_entries(&c, &non_mapping_spec).unwrap();
let keys: Vec<&str> = out.iter().map(|(k, _)| k.as_str()).collect();
assert_eq!(
keys,
vec![M2_KEY_LIMITS],
"non-Mapping spec short-circuits the spec.* splice; the M2 \
overlay still contributes its filled slots",
);
}
#[test]
fn servico_spec_and_m2_overlay_entries_matches_hand_written_composition() {
// Cross-check the lifted composition against the hand-written
// two-loop shape the two prior inline blocks carried. A drift
// between the helper and the inline composition would silently
// emit a different key set / ordering / precedence at every
// routed renderer — pin the equivalence so the helper stays a
// drop-in replacement for both.
let cu = cu_yaml_with_spec_fields(
" module:\n source: oci://ghcr.io/pleme-io/hello-rio:v0.1.0\n \
trigger:\n service: {port: 8080}\n",
);
let spec = cu.get(KUBE_KEY_SPEC).unwrap();
let mut c = bare_servico();
c.limits = Some(LimitsSpec {
memory: Some(32 * 1024 * 1024),
..Default::default()
});
c.behavior = Some(BehaviorSpec {
on_call: Some(PathBuf::from("lib/handlers.lisp")),
..Default::default()
});
let via_helper = servico_spec_and_m2_overlay_entries(&c, spec).unwrap();
let mut via_inline: Vec<(String, serde_yaml::Value)> = Vec::new();
let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
for (k, v) in string_keyed_entries(spec) {
seen.insert(k.to_string());
via_inline.push((k.to_string(), v.clone()));
}
for (key, value) in servico_m2_overlay(&c).unwrap() {
if !seen.contains(key) {
via_inline.push((key.to_string(), value));
}
}
assert_eq!(
via_helper, via_inline,
"servico_spec_and_m2_overlay_entries must byte-equal the \
hand-written two-loop composition (spec.* splice + M2 \
overlay with or_insert precedence) the two prior inline \
call sites carried",
);
}
#[test]
fn pleme_label_consts_share_canonical_prefix() {
// Single-source-of-truth invariant: every pleme-io label key
// is `<PLEME_LABEL_PREFIX>/<axis>`. A future label-namespace
// rebrand is a one-line PLEME_LABEL_PREFIX edit + this test
// pins the contract that no other label leaks past the lift.
for k in [LABEL_APLICACAO, LABEL_PROGRAM, LABEL_CONTRATO] {
assert!(
k.starts_with(PLEME_LABEL_PREFIX),
"label key {k:?} must share the {PLEME_LABEL_PREFIX:?} prefix"
);
// Each label is `<prefix>/<axis>` — the suffix is non-empty
// (the `/` separator is followed by the axis name).
let suffix = k.strip_prefix(PLEME_LABEL_PREFIX).unwrap();
assert!(suffix.starts_with('/'));
assert!(suffix.len() > 1, "axis name must be non-empty for {k:?}");
}
}
#[test]
fn pleme_label_consts_have_expected_canonical_values() {
// Pin the actual string values so a typo in the lift can't
// silently rebrand the whole pleme-io label namespace. These
// strings are part of the cluster-side contract with the
// lareira-fleet-programs chart + Cilium identity layer + Hubble
// flow attribution; changing any of them is a coordinated
// multi-repo migration, not an incidental edit.
assert_eq!(PLEME_LABEL_PREFIX, "pleme.pleme.io");
assert_eq!(LABEL_APLICACAO, "pleme.pleme.io/aplicacao");
assert_eq!(LABEL_PROGRAM, "pleme.pleme.io/program");
assert_eq!(LABEL_CONTRATO, "pleme.pleme.io/contrato");
}
#[test]
fn default_namespace_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the cluster-side namespace every renderer emits
// into. The string is part of the cluster-side contract with
// the lareira-fleet-programs aggregator chart, the per-cluster
// CiliumNetworkPolicy `endpointSelector` namespace scope, the
// Gateway / HTTPRoute apply namespace, and the future M4
// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's apply
// namespace; changing it is a coordinated multi-repo migration
// (the per-cluster k8s repo's namespaces, every
// lareira-fleet-programs HelmRelease's targetNamespace, every
// ComputeUnit's `metadata.namespace`), not an incidental edit.
// Peer to `pleme_label_consts_have_expected_canonical_values`
// on the canonical-string-value-pin axis for the
// `PLEME_LABEL_PREFIX` / `LABEL_*` constants.
assert_eq!(DEFAULT_NAMESPACE, "tatara-system");
}
#[test]
fn default_flux_system_namespace_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the FluxCD installation namespace the rendered
// `kustomization.yaml`'s `metadata.namespace` /
// `spec.sourceRef.name` axes consume. The string is part of the
// cluster-side contract with the `flux bootstrap` pipeline (the
// bootstrap convention names the `GitRepository` after the
// installation namespace, so both axes are the same load-bearing
// string), the `kustomize-controller` watch-window scope (a
// drifted value sits outside the controller's watch window and
// is never reconciled), and the per-cluster k8s repo's flux
// bootstrap manifests; changing it is a coordinated multi-repo
// migration, not an incidental edit. Peer to
// `default_namespace_pins_canonical_value` on the
// canonical-string-value-pin axis for the workload-side
// [`DEFAULT_NAMESPACE`] constant.
assert_eq!(DEFAULT_FLUX_SYSTEM_NAMESPACE, "flux-system");
}
#[test]
fn default_flux_system_namespace_is_a_valid_dns_1123_label() {
// Cross-axis invariant: the FluxCD installation namespace lands
// as `metadata.namespace` on every emitted `Kustomization`
// resource and as `spec.sourceRef.name` (a K8s resource name
// under the same DNS-1123 floor), and the K8s apiserver
// enforces the DNS-1123 label rule on both. Pinning this here
// means a future rebrand on the canonical lift can't silently
// land a value the apiserver refuses at the *first*
// `kustomization.yaml` apply against a cluster, far from the
// rebrand commit's source — the typed [`is_dns_1123_label`]
// floor rejects it at caixa-core build time on the canonical
// lift, before any renderer consumes the value. Same shape as
// `default_namespace_is_a_valid_dns_1123_label` on the
// workload-side [`DEFAULT_NAMESPACE`] axis.
assert!(
is_dns_1123_label(DEFAULT_FLUX_SYSTEM_NAMESPACE).is_ok(),
"DEFAULT_FLUX_SYSTEM_NAMESPACE {DEFAULT_FLUX_SYSTEM_NAMESPACE:?} must be a valid \
DNS-1123 label — every K8s apiserver-side schema enforces \
this rule on `metadata.namespace`"
);
}
#[test]
fn default_flux_reconcile_interval_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the substrate-side default Flux v2 reconcile-poll
// cadence duration scalar the substrate's per-caixa
// `cluster_bundle` renderer seeds into every emitted per-caixa
// Flux v2 CR (GitRepository / HelmRelease / Kustomization) at
// its `spec.interval` axis when the operator doesn't pin a per-
// caixa override. The string is part of the cluster-side
// contract with the Flux v2 source-controller / helm-controller
// / kustomize-controller trio: each controller's per-CR admission
// gate parses the value via `metav1.ParseDuration` before
// installing the per-CR watch, and the resulting cadence pins
// the per-CR reconcile-freshness / cluster-load tradeoff every
// substrate-side Flux v2 pipeline runs at. Changing this value
// is a coordinated substrate-side reconcile-cadence promotion
// (a `10m` → `5m` migration once lower-latency-poll optimizations
// ship, a `10m` → `15m` migration on cost-optimized clusters
// where per-CR source-controller poll cost outweighs the
// reconcile-freshness gain), not an incidental edit. Peer to
// `default_namespace_pins_canonical_value` and
// `default_gateway_class_name_pins_canonical_value` on the
// canonical-substrate-default-load-bearing-scalar pin surface.
assert_eq!(DEFAULT_FLUX_RECONCILE_INTERVAL, "10m");
}
#[test]
fn default_flux_reconcile_interval_is_a_valid_metav1_duration_scalar() {
// Cross-axis grammar invariant: the Flux v2 controller-side per-
// CR admission gate parses the reconcile-poll cadence scalar via
// `metav1.ParseDuration` before installing the per-CR watch. The
// Go-duration-format grammar is non-empty, ASCII, and structured
// as `<digits><unit>[<digits><unit>...]` where each unit is one
// of `{ns, us, µs, ms, s, m, h}`. Pin a floor that catches the
// canonical drift footguns — an empty scalar (`""` — admission
// gate rejects), a non-ASCII-alphanumeric byte (`"10 m"` — the
// whitespace defeats the parser), a missing-unit scalar (`"10"`
// — the parser rejects for lack of a unit suffix), or a leading-
// non-digit scalar (`"m10"` — the parser rejects for lack of a
// leading magnitude). A future rebrand on the canonical lift
// that lands a value outside the Go-duration-format grammar
// would surface here at caixa-core build time on the canonical
// lift, before any renderer consumes the value. Same shape as
// `default_namespace_is_a_valid_dns_1123_label` /
// `default_flux_system_namespace_is_a_valid_dns_1123_label` /
// `default_gateway_class_name_is_a_valid_dns_1123_label` on the
// peer canonical-substrate-default-grammar-floor surface.
let v = DEFAULT_FLUX_RECONCILE_INTERVAL;
assert!(
!v.is_empty(),
"DEFAULT_FLUX_RECONCILE_INTERVAL {v:?} must be non-empty \
per the Flux v2 controller-side `metav1.ParseDuration` \
admission gate"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"DEFAULT_FLUX_RECONCILE_INTERVAL {v:?} must be ASCII-\
alphanumeric throughout per the Go-duration-format grammar \
— no whitespace / separator bytes the `metav1.ParseDuration` \
admission gate would reject"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_digit(),
"DEFAULT_FLUX_RECONCILE_INTERVAL {v:?} first byte {first:?} \
must be an ASCII digit per the Go-duration-format grammar \
— the leading magnitude precedes the unit suffix; a leading \
non-digit defeats `metav1.ParseDuration`"
);
let last = v.chars().next_back().expect("non-empty");
assert!(
last.is_ascii_alphabetic() && last.is_ascii_lowercase(),
"DEFAULT_FLUX_RECONCILE_INTERVAL {v:?} last byte {last:?} \
must be an ASCII lowercase alphabetic unit suffix per the \
Go-duration-format grammar — the trailing unit follows the \
magnitude; an unterminated magnitude defeats \
`metav1.ParseDuration`"
);
}
#[test]
fn default_flux_chart_source_subpath_pins_canonical_value() {
// Pin the actual scalar so a typo in this lift can't silently
// rebrand the substrate-side default Flux v2
// `HelmRelease.spec.chart.spec.chart` chart-directory-in-
// GitRepository-source sub-path the substrate's per-caixa
// `cluster_bundle` renderer seeds into every emitted per-caixa
// `helmrelease.yaml` document. The value is part of the
// cluster-side contract with the Flux v2 helm-controller (the
// per-CR chart-open loop uses this to locate the
// `Chart.yaml` + `values.yaml` pair inside the paired
// GitRepository clone root); changing it is a coordinated
// substrate-side chart-directory-in-git-source promotion
// (a `"chart"` → `"charts"` migration on a per-caixa multi-chart
// layout landing, a `"chart"` → `"helm"` migration on a
// cross-language convention alignment, a `"chart"` → `"deploy"`
// migration on a per-caixa-deploy-directory naming migration),
// not an incidental edit. Peer to
// `default_flux_reconcile_interval_pins_canonical_value` +
// `flux_helmrelease_remediation_retries_default_pins_canonical_value`
// on the canonical-Flux-v2-per-CR-substrate-default-scalar pin
// surface.
assert_eq!(DEFAULT_FLUX_CHART_SOURCE_SUBPATH, "chart");
}
#[test]
fn default_flux_chart_source_subpath_is_a_valid_relative_directory_scalar() {
// Cross-axis grammar invariant: the Flux v2 source-controller
// resolves the per-CR `HelmRelease.spec.chart.spec.chart` scalar
// as a directory path relative to the paired `GitRepository`
// clone root. Pin a floor that catches the canonical drift
// footguns — an empty scalar (`""` — the source-controller-side
// per-CR chart-open loop rejects for lack of a target directory),
// a leading-separator scalar (`"/chart"` — the source-controller
// rejects for the absolute-path shape breaking the relative-path
// composition against the per-clone-root anchor), a non-ASCII
// byte (a UTF-8 multi-byte name defeating the per-clone-root
// filesystem name resolution on the source-controller pod's
// filesystem layer), or a leading whitespace / dot byte (`" chart"`
// / `".chart"` — surface as either a "directory not found" per-
// CR error or, worse, a silent match against a hidden dot-file
// sibling of the intended chart directory). A future rebrand on
// the canonical lift that lands a value outside the grammar
// would surface here at caixa-core build time on the canonical
// lift, before any renderer consumes the value. Same shape as
// `default_flux_reconcile_interval_is_a_valid_metav1_duration_scalar`
// on the peer canonical-substrate-default-grammar-floor surface.
let v = DEFAULT_FLUX_CHART_SOURCE_SUBPATH;
assert!(
!v.is_empty(),
"DEFAULT_FLUX_CHART_SOURCE_SUBPATH {v:?} must be non-empty \
per the Flux v2 source-controller-side per-CR chart-open \
loop's requirement of a target directory"
);
assert!(
v.is_ascii(),
"DEFAULT_FLUX_CHART_SOURCE_SUBPATH {v:?} must be ASCII \
throughout — a non-ASCII multi-byte name defeats the per-\
clone-root filesystem name resolution on the source-\
controller pod's filesystem layer"
);
let first = v.chars().next().expect("non-empty");
assert!(
!matches!(first, '/' | '.' | ' ' | '\t'),
"DEFAULT_FLUX_CHART_SOURCE_SUBPATH {v:?} first byte {first:?} \
must not be a leading separator (`/`), leading dot (`.`), or \
leading whitespace — a leading separator breaks the relative-\
path composition against the per-clone-root anchor, a leading \
dot risks silent matches against hidden dot-file siblings, and \
leading whitespace defeats the per-clone-root filesystem name \
resolution"
);
}
#[test]
fn flux_helmrelease_remediation_retries_default_pins_canonical_value() {
// Pin the actual scalar so a typo in this lift can't silently
// rebrand the substrate-side default Flux v2
// `HelmRelease.spec.{install,upgrade}.remediation.retries` retry-
// count ceiling the substrate's per-caixa `cluster_bundle`
// renderer seeds into every emitted per-caixa `helmrelease.yaml`
// document under both the install-path and the upgrade-path
// remediation blocks. The value is part of the cluster-side
// contract with the Flux v2 helm-controller (the per-CR
// remediation loop uses this as the ceiling on the number of
// Helm-install / Helm-upgrade re-attempts before the controller
// marks the `HelmRelease` `Ready: False` and stops retrying);
// changing it is a coordinated substrate-side retry-ceiling
// promotion (a `3` → `5` migration once per-caixa idempotency
// invariants tighten and higher-retry recovery from transient
// apiserver / registry / oci-source flakes becomes safe, a `3` →
// `1` migration on hardened per-caixa pipelines where a failed
// apply should escalate to operator-attention rather than mask
// under further retries), not an incidental edit. Peer to
// `default_flux_reconcile_interval_pins_canonical_value` on the
// canonical-Flux-v2-per-CR-substrate-default-scalar pin surface.
assert_eq!(FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT, 3);
}
#[test]
fn flux_helmrelease_remediation_retries_default_is_a_bounded_positive_scalar() {
// Cross-axis invariant: the Flux v2 `HelmRelease.spec.{install,
// upgrade}.remediation.retries` OpenAPI schema types the field
// as a signed 64-bit integer with a documented sentinel `-1`
// meaning "retry indefinitely". The substrate opts out of the
// unbounded-retry sentinel by declaring the canonical default as
// a positive `u32` — the type itself rules out `-1` at
// caixa-core build time, so a future rebrand on this lift cannot
// silently land the "retry forever" sentinel by construction
// (which would let a persistently-failing per-caixa chart apply
// consume Flux v2 helm-controller reconcile-loop cycles
// indefinitely, masking under further retries rather than
// surfacing at the `HelmRelease.status.conditions[]` axis the
// substrate's downstream reconciliation-topology consumer
// watches). Pin the positive-scalar floor + a substrate-side
// "sane retry ceiling" upper bound (the same 100-attempt hard
// cap the peer `POLICY_RETRIES_MAX` per-`:politicas :retries`
// axis carries; a substrate that seeds a per-CR default above
// that ceiling is structurally a footgun by the same
// "unbounded-retry masks the underlying failure" argument that
// motivates the mesh-policy retries cap). Same shape as
// `default_flux_reconcile_interval_is_a_valid_metav1_duration_scalar`
// on the peer canonical-substrate-default-grammar-floor surface.
let v = FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT;
assert!(
v > 0,
"FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT {v} must be strictly \
positive per the substrate's opt-out from the Flux v2 \
`retries: -1` unbounded-retry sentinel — the `u32` type rules \
out the sentinel, and a zero-retries default is structurally \
a `remediation:` sub-block that never fires the retry path it \
is declaring"
);
assert!(
v <= 100,
"FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT {v} must be within \
the substrate's canonical retry-ceiling upper bound (100) — a \
per-CR default above that ceiling silently masks the underlying \
chart-apply failure under further retries rather than surfacing \
it at the `HelmRelease.status.conditions[]` axis the substrate's \
downstream reconciliation-topology consumer watches, the same \
argument that motivates the peer `POLICY_RETRIES_MAX` per-\
`:politicas :retries` axis cap"
);
}
#[test]
fn flux_helmrelease_key_remediation_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the substrate-side Flux v2
// `HelmRelease.spec.{install,upgrade}.remediation` sub-container-
// axis key the substrate's per-caixa `cluster_bundle` renderer
// seeds into every emitted per-caixa `helmrelease.yaml` document
// at both the install-path + upgrade-path per-CR remediation
// sub-block-header positions. The string is part of the cluster-
// side contract with the Flux v2 helm-controller (the controller's
// per-CR remediation loop reaches the retry-cap scalar through
// this exact sub-container axis; a drifted sub-container-key
// silently strips the entire per-path remediation block from the
// emitted per-CR document, leaving the helm-controller to fall
// back to the Flux v2 upstream defaults for the whole remediation
// surface rather than the substrate's chosen ceiling, with no
// diagnostic naming the container-axis-key-drift root cause).
// Changing it is a coordinated Flux v3 CRD-schema-rebrand
// migration alongside the upstream `helm-controller` deprecation
// cycle (candidates like `recovery` / `retryPolicy` /
// `errorHandling` that upstream Flux v3 roadmap floats in the
// migration prose), not an incidental edit. Peer to
// `flux_helmrelease_remediation_retries_default_pins_canonical_value`
// on the sibling scalar-value half + the sibling
// [`FLUX_HELMRELEASE_KEY_RETRIES`] leaf-scalar-key half of the
// same per-path retry-cap declaration triple.
assert_eq!(FLUX_HELMRELEASE_KEY_REMEDIATION, "remediation");
}
#[test]
fn flux_helmrelease_key_remediation_is_a_valid_dns_1123_label() {
// Cross-axis invariant: every Flux v2 `HelmRelease` CRD-schema
// sub-block-header key resolves through the K8s apiserver's
// OpenAPI-schema-side identifier grammar, whose per-field key
// axis is a subset of the DNS-1123-label grammar (lowercase
// alphanumerics + hyphens, non-empty, ≤63 bytes). Pinning the
// canonical `remediation` value against the typed
// [`is_dns_1123_label`] floor rules out grammar drift on this
// lift at caixa-core build time — a future rebrand landing a
// value outside the DNS-1123-label subset (a leading digit, an
// underscore, an uppercase byte, a `.` byte, or empty) would
// surface here on the canonical lift, before any renderer
// consumes the value and before any per-caixa Flux v2 CR reaches
// the apiserver's OpenAPI-schema-side per-field admission gate.
// Same shape as `default_gateway_class_name_is_a_valid_dns_1123_label`
// on the peer canonical-CRD-schema-grammar-floor surface.
assert!(
is_dns_1123_label(FLUX_HELMRELEASE_KEY_REMEDIATION).is_ok(),
"FLUX_HELMRELEASE_KEY_REMEDIATION {FLUX_HELMRELEASE_KEY_REMEDIATION:?} \
must be a valid DNS-1123 label — every K8s apiserver-side \
OpenAPI-schema-per-field-key axis is a subset of that grammar, \
and the Flux v2 `HelmRelease` CRD schema is no exception"
);
}
#[test]
fn flux_helmrelease_key_install_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Flux v2 `HelmRelease.spec.install` per-CR helm-
// action-phase discriminator parent-container-axis-key the
// rendered `helmrelease.yaml` document mounts its per-CR first-
// time chart apply phase-block under. The string is part of the
// cluster-side contract with the upstream Flux v2 helm-
// controller — the helm-controller's per-CR phase-dispatch loop
// reaches the install-path phase block through this exact parent-
// container axis; a drifted parent-container-key silently strips
// the entire install-path phase block from the emitted per-CR
// document, leaving the helm-controller to fall back to the Flux
// v2 upstream defaults for the whole install-path phase surface
// rather than the substrate's chosen per-CR install-path knob-set
// (the `createNamespace` seeder never fires, the per-CR retry-cap
// ceiling silently drops off the emitted document), with no
// diagnostic naming the phase-discriminator-drift root cause.
// Changing it is a coordinated Flux v3 CRD-schema-rebrand
// migration alongside the upstream `helm-controller` deprecation
// cycle (candidates like `initialize` / `apply` / `create` /
// `first-run` that upstream Flux v3 roadmap floats in the
// migration prose), not an incidental edit. Peer to
// `flux_helmrelease_key_upgrade_pins_canonical_value` on the
// sibling per-CR upgrade-path phase-discriminator parent-
// container-axis-key half of the same per-CR helm-action-phase
// discriminator parent-container-axis-key pair + the sibling
// [`FLUX_HELMRELEASE_KEY_REMEDIATION`] sub-container-axis-key
// hosted beneath both parent-container-axis-keys.
assert_eq!(FLUX_HELMRELEASE_KEY_INSTALL, "install");
}
#[test]
fn flux_helmrelease_key_install_is_a_valid_dns_1123_label() {
// Cross-axis invariant: every Flux v2 `HelmRelease` CRD-schema
// sub-block-header key resolves through the K8s apiserver's
// OpenAPI-schema-side identifier grammar, whose per-field key
// axis is a subset of the DNS-1123-label grammar (lowercase
// alphanumerics + hyphens, non-empty, ≤63 bytes). Pinning the
// canonical `install` value against the typed
// [`is_dns_1123_label`] floor rules out grammar drift on this
// lift at caixa-core build time — a future rebrand landing a
// value outside the DNS-1123-label subset (a leading digit, an
// underscore, an uppercase byte, a `.` byte, or empty) would
// surface here on the canonical lift, before any renderer
// consumes the value and before any per-caixa Flux v2 CR reaches
// the apiserver's OpenAPI-schema-side per-field admission gate.
// Same shape as `flux_helmrelease_key_remediation_is_a_valid_
// dns_1123_label` on the sibling per-CR sub-container-axis-key
// grammar-floor surface.
assert!(
is_dns_1123_label(FLUX_HELMRELEASE_KEY_INSTALL).is_ok(),
"FLUX_HELMRELEASE_KEY_INSTALL {FLUX_HELMRELEASE_KEY_INSTALL:?} \
must be a valid DNS-1123 label — every K8s apiserver-side \
OpenAPI-schema-per-field-key axis is a subset of that grammar, \
and the Flux v2 `HelmRelease` CRD schema is no exception"
);
}
#[test]
fn flux_helmrelease_key_upgrade_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Flux v2 `HelmRelease.spec.upgrade` per-CR helm-
// action-phase discriminator parent-container-axis-key the
// rendered `helmrelease.yaml` document mounts its per-CR
// subsequent-per-version chart re-apply phase-block under. The
// string is part of the cluster-side contract with the upstream
// Flux v2 helm-controller — the helm-controller's per-CR phase-
// dispatch loop reaches the upgrade-path phase block through this
// exact parent-container axis on every per-version chart re-apply
// after the initial install-path phase completes; a drifted
// parent-container-key silently strips the entire upgrade-path
// phase block from the emitted per-CR document, leaving the
// helm-controller to fall back to the Flux v2 upstream defaults
// for the whole upgrade-path phase surface rather than the
// substrate's chosen per-CR upgrade-path knob-set (the
// `remediateLastFailure` toggle never fires, the per-CR retry-
// cap ceiling silently drops off the emitted document), with no
// diagnostic naming the phase-discriminator-drift root cause.
// Changing it is a coordinated Flux v3 CRD-schema-rebrand
// migration alongside the upstream `helm-controller` deprecation
// cycle (candidates like `reapply` / `reconcile` / `update` /
// `promote` that upstream Flux v3 roadmap floats in the
// migration prose), not an incidental edit. Peer to
// `flux_helmrelease_key_install_pins_canonical_value` on the
// sibling per-CR install-path phase-discriminator parent-
// container-axis-key half of the same per-CR helm-action-phase
// discriminator parent-container-axis-key pair.
assert_eq!(FLUX_HELMRELEASE_KEY_UPGRADE, "upgrade");
}
#[test]
fn flux_helmrelease_key_upgrade_is_a_valid_dns_1123_label() {
// Cross-axis invariant: every Flux v2 `HelmRelease` CRD-schema
// sub-block-header key resolves through the K8s apiserver's
// OpenAPI-schema-side identifier grammar, whose per-field key
// axis is a subset of the DNS-1123-label grammar. Pinning the
// canonical `upgrade` value against the typed
// [`is_dns_1123_label`] floor rules out grammar drift on this
// lift at caixa-core build time. Peer to
// `flux_helmrelease_key_install_is_a_valid_dns_1123_label` on
// the sibling install-path phase-discriminator grammar-floor
// surface + `flux_helmrelease_key_remediation_is_a_valid_dns_
// 1123_label` on the sibling per-CR sub-container-axis-key
// grammar-floor surface — same DNS-1123-label subset governs
// every apiserver-side per-field-key axis, so every peer per-CR
// sub-block-header lift carries the same grammar-floor pin.
assert!(
is_dns_1123_label(FLUX_HELMRELEASE_KEY_UPGRADE).is_ok(),
"FLUX_HELMRELEASE_KEY_UPGRADE {FLUX_HELMRELEASE_KEY_UPGRADE:?} \
must be a valid DNS-1123 label — every K8s apiserver-side \
OpenAPI-schema-per-field-key axis is a subset of that grammar, \
and the Flux v2 `HelmRelease` CRD schema is no exception"
);
}
#[test]
fn flux_helmrelease_key_install_and_upgrade_stay_independent_axes() {
// The two per-CR helm-action-phase discriminator parent-
// container-axis-keys name distinct helm-controller-side phases
// — install-path first-time chart apply vs upgrade-path per-
// version chart re-apply — even though both host the same
// sibling [`FLUX_HELMRELEASE_KEY_REMEDIATION`] sub-container-
// axis-key beneath them. Pin that the two consts carry distinct
// byte-sequences so a future rebrand on either arm can't
// silently coalesce onto the peer arm (a
// `FLUX_HELMRELEASE_KEY_INSTALL = "upgrade"` typo would flip
// every substrate-side per-CR first-time chart apply phase
// block onto the upgrade-path phase key silently — the install-
// path becomes the upgrade-path at every emit site, and the
// helm-controller reconciles both phase blocks under the same
// parent-container-axis-key, silently dropping either the
// install-path or the upgrade-path per-CR knob-set with no
// diagnostic naming the phase-discriminator-coalesce root
// cause). The per-CR helm-action-phase discriminator pair must
// always resolve to distinct emitted parent-container-keys.
assert_ne!(
FLUX_HELMRELEASE_KEY_INSTALL, FLUX_HELMRELEASE_KEY_UPGRADE,
"the per-CR install-path and upgrade-path helm-action-phase \
discriminator parent-container-axis-keys must remain byte-\
distinct — a coalesce onto one value silently drops either \
the install-path or the upgrade-path per-CR knob-set from \
every emitted `HelmRelease` document"
);
}
#[test]
fn flux_helmrelease_key_remediate_last_failure_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Flux v2 `HelmRelease.spec.upgrade.remediation
// .remediateLastFailure` upgrade-path-only per-CR remediation-
// toggle leaf-scalar-key the substrate's per-caixa `cluster_bundle`
// renderer seeds to `true` into every emitted per-caixa
// `helmrelease.yaml` document under the sibling
// [`FLUX_HELMRELEASE_KEY_UPGRADE`] per-CR upgrade-path phase-
// discriminator parent-container-axis-key's nested
// [`FLUX_HELMRELEASE_KEY_REMEDIATION`] sub-container-axis-key. The
// string is part of the cluster-side contract with the upstream
// Flux v2 helm-controller — the controller's per-CR upgrade-path
// remediation loop reaches the post-retry-exhaustion rollback
// toggle through this exact leaf; a drifted leaf-scalar-key
// silently strips the substrate's chosen post-retry-exhaustion
// rollback semantic from every emitted per-caixa `HelmRelease`
// document, leaving the helm-controller to leave every terminally-
// failed upgrade in the failed state without rolling back to the
// prior last-known-good release the substrate's "no chart apply
// leaves a per-caixa CR in a stalled, unremediated state"
// MESH-COMPOSITION.md §V guarantee mandates, with no diagnostic
// naming the remediation-toggle-drift root cause. Changing it is
// a coordinated Flux v3 CRD-schema-rebrand migration alongside
// the upstream `helm-controller` deprecation cycle (candidates
// like `rollbackOnFailure` / `remediateOnFailure` /
// `recoverLastFailure` that upstream Flux v3 roadmap floats in
// the migration prose), not an incidental edit. Peer to
// `flux_helmrelease_key_retries_pins_canonical_value` on the
// sibling per-CR retry-cap leaf-scalar-key half of the same
// upgrade-path per-CR remediation block leaf-scalar-key pair.
assert_eq!(
FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE,
"remediateLastFailure"
);
}
#[test]
fn flux_helmrelease_key_remediate_last_failure_stays_independent_of_retries() {
// The upgrade-path per-CR remediation block hosts two independent
// leaf-scalar-key axes under the shared sibling
// [`FLUX_HELMRELEASE_KEY_REMEDIATION`] sub-container-axis-key —
// the per-CR retry-cap [`FLUX_HELMRELEASE_KEY_RETRIES`] (that
// also sits under the install-path per-CR remediation block) and
// the upgrade-path-only per-CR remediation-toggle
// [`FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE`]. Pin that the
// two consts carry byte-distinct sequences so a future rebrand
// on either arm can't silently coalesce onto the peer arm (a
// `FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE = "retries"` typo
// would silently rebind the post-retry-exhaustion rollback
// toggle onto the retry-cap ceiling axis at every emit site —
// the helm-controller then reads the substrate's `true` seed as
// an integer retry-cap `1` on the retry-cap axis instead of the
// rollback-on-terminal-failure boolean, silently truncating the
// per-CR upgrade-path retry budget and dropping the rollback
// semantic entirely with no diagnostic naming the leaf-key-
// coalesce root cause). The upgrade-path per-CR remediation
// leaf-scalar-key pair must always resolve to distinct emitted
// leaf-keys.
assert_ne!(
FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE, FLUX_HELMRELEASE_KEY_RETRIES,
"the upgrade-path per-CR remediation retry-cap leaf-scalar-\
key and remediation-toggle leaf-scalar-key must remain \
byte-distinct — a coalesce onto one value silently rebinds \
the post-retry-exhaustion rollback semantic onto the retry-\
cap ceiling axis at every emit site"
);
}
#[test]
fn flux_helmrelease_key_create_namespace_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Flux v2 `HelmRelease.spec.install.createNamespace`
// install-path-only per-CR namespace-seeder-toggle leaf-scalar-key
// the substrate's per-caixa `cluster_bundle` renderer seeds to
// `true` into every emitted per-caixa `helmrelease.yaml` document
// under the sibling [`FLUX_HELMRELEASE_KEY_INSTALL`] per-CR
// install-path phase-discriminator parent-container-axis-key. The
// string is part of the cluster-side contract with the upstream
// Flux v2 helm-controller — the controller's per-CR install-path
// pre-apply loop reaches the target-namespace-seeder toggle
// through this exact leaf; a drifted leaf-scalar-key silently
// strips the substrate's chosen first-apply namespace-seeder
// semantic from every emitted per-caixa `HelmRelease` document,
// leaving the helm-controller to refuse every first-time per-caixa
// chart apply against a fresh cluster whose target namespace has
// not been pre-provisioned by an out-of-band pipeline the
// substrate's "no per-caixa Servico apply is blocked on manual
// namespace preprovisioning" MESH-COMPOSITION.md §V install-path-
// fluency guarantee mandates, with no diagnostic naming the
// seeder-toggle-drift root cause. Changing it is a coordinated
// Flux v3 CRD-schema-rebrand migration alongside the upstream
// `helm-controller` deprecation cycle (candidates like
// `createTargetNamespace` / `seedNamespace` / `provisionNamespace`
// that upstream Flux v3 roadmap floats in the migration prose),
// not an incidental edit. Peer to
// `flux_helmrelease_key_remediate_last_failure_pins_canonical_value`
// on the sibling mirror-symmetric upgrade-path-only per-CR
// remediation-toggle leaf-scalar-key half of the same install/
// upgrade per-CR phase-specific toggle leaf-scalar-key pair.
assert_eq!(FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE, "createNamespace");
}
#[test]
fn flux_helmrelease_key_create_namespace_stays_independent_of_remediate_last_failure() {
// The per-CR install/upgrade phase blocks host two mirror-symmetric
// phase-specific toggle leaf-scalar-key axes: the install-path-only
// per-CR namespace-seeder-toggle
// [`FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE`] under the sibling
// [`FLUX_HELMRELEASE_KEY_INSTALL`] parent-container-axis-key (this
// lift) and the upgrade-path-only per-CR remediation-toggle
// [`FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE`] (96581b7) under the
// sibling [`FLUX_HELMRELEASE_KEY_UPGRADE`] parent-container-axis-key.
// Pin that the two consts carry byte-distinct sequences so a future
// rebrand on either arm can't silently coalesce onto the peer arm
// (a `FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE = "remediateLastFailure"`
// typo would silently rebind the install-path namespace-seeder
// toggle onto the upgrade-path per-CR remediation-toggle leaf at
// every emit site — the helm-controller would then read the
// substrate's `true` seed as a post-retry-exhaustion rollback opt-
// in on the upgrade-path per-CR remediation axis instead of the
// pre-apply namespace-seeder toggle, silently dropping the first-
// apply namespace-seeder semantic entirely and misrouting the
// install-path opt-in onto an upgrade-path axis where it never
// fires with no diagnostic naming the leaf-key-coalesce root
// cause). The install/upgrade per-CR phase-specific toggle leaf-
// scalar-key pair must always resolve to distinct emitted leaf-
// keys under mirror-symmetric parent-container-axis-keys.
assert_ne!(
FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE, FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE,
"the install-path per-CR namespace-seeder-toggle leaf-scalar-\
key and the upgrade-path per-CR remediation-toggle leaf-\
scalar-key must remain byte-distinct — a coalesce onto one \
value silently rebinds one phase's opt-in toggle onto the \
peer phase's opt-in-toggle axis at every emit site, dropping \
the phase-specific pre-apply / post-retry-exhaustion semantic \
the substrate seeds on the coalesced arm"
);
}
#[test]
fn flux_kustomization_key_prune_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Flux v2 `Kustomization.spec.prune` per-CR garbage-
// collection-toggle leaf-scalar-key the substrate's per-caixa
// `cluster_bundle` renderer seeds to `true` into every emitted
// per-caixa `kustomization.yaml` document at the top-level `spec`
// position. The string is part of the cluster-side contract with
// the upstream Flux v2 kustomize-controller — the controller's
// per-CR reconcile loop reaches the sweep-what-you-removed toggle
// through this exact leaf; a drifted leaf-scalar-key silently
// strips the substrate's chosen sweep-what-you-removed semantic
// from every emitted per-caixa `Kustomization` document, leaving
// per-caixa resources the source manifest set previously
// reconciled but no longer carries dangling in the cluster the
// substrate's "the cluster's per-caixa live state converges to
// the caixa's tatara-lisp source-of-truth on every reconcile —
// resources the source no longer carries are swept by the
// kustomize-controller, not left dangling" CAIXA-SDLC.md §V
// author-to-live-convergence guarantee mandates, with no
// diagnostic naming the toggle-drift root cause. Changing it is
// a coordinated Flux v3 CRD-schema-rebrand migration alongside
// the upstream `kustomize-controller` deprecation cycle
// (candidates like `garbageCollect` / `sweep` / `pruneOrphaned`
// / `deleteOrphans` that upstream Flux v3 roadmap floats in the
// migration prose), not an incidental edit. Peer to
// `flux_helmrelease_key_create_namespace_pins_canonical_value`
// on the sibling co-resident per-caixa `HelmRelease` CR install-
// path per-CR namespace-seeder-toggle leaf-scalar-key half of
// the same per-caixa Flux-bundle per-CR-toggle leaf-scalar-key
// surface.
assert_eq!(FLUX_KUSTOMIZATION_KEY_PRUNE, "prune");
}
#[test]
fn flux_kustomization_key_prune_stays_independent_of_create_namespace() {
// The per-caixa Flux bundle hosts two co-resident per-CR-toggle
// leaf-scalar-key axes: the per-`Kustomization`-CR garbage-
// collection-toggle [`FLUX_KUSTOMIZATION_KEY_PRUNE`] at the
// top-level `spec` position (this lift) and the per-`HelmRelease`-
// CR install-path namespace-seeder-toggle
// [`FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE`] (ba9ab8b) under the
// sibling [`FLUX_HELMRELEASE_KEY_INSTALL`] parent-container-axis-
// key. Pin that the two consts carry byte-distinct sequences so
// a future rebrand on either arm can't silently coalesce onto
// the peer arm (a `FLUX_KUSTOMIZATION_KEY_PRUNE = "createNamespace"`
// typo would silently rebind the Kustomization-CR garbage-
// collection-toggle onto the HelmRelease-CR install-path
// namespace-seeder-toggle leaf at every emit site — the
// kustomize-controller would then read the substrate's `true`
// seed at the drifted leaf-key rather than the canonical `prune`
// axis, silently dropping the sweep-what-you-removed semantic
// entirely and leaving per-caixa resources removed from the
// source manifest set dangling in the cluster with no
// diagnostic naming the leaf-key-coalesce root cause). The
// per-`Kustomization`-CR garbage-collection-toggle and the
// per-`HelmRelease`-CR install-path namespace-seeder-toggle must
// always resolve to distinct emitted leaf-keys under their
// respective co-resident per-CR spec surfaces.
assert_ne!(
FLUX_KUSTOMIZATION_KEY_PRUNE, FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE,
"the per-`Kustomization`-CR garbage-collection-toggle leaf-\
scalar-key and the per-`HelmRelease`-CR install-path \
namespace-seeder-toggle leaf-scalar-key must remain byte-\
distinct — a coalesce onto one value silently rebinds one \
CR's opt-in toggle onto the peer CR's opt-in-toggle axis at \
every emit site, dropping the per-CR-specific sweep-what-\
you-removed / pre-apply-namespace-seeder semantic the \
substrate seeds on the coalesced arm"
);
}
#[test]
fn flux_kustomization_prune_default_pins_canonical_value() {
// Pin the actual boolean so a rebrand on this lift can't silently
// rebrand the Flux v2 `Kustomization.spec.prune` per-CR garbage-
// collection-toggle scalar-value seed the substrate's per-caixa
// `cluster_bundle` renderer threads into every emitted per-caixa
// `kustomization.yaml` document under the sibling
// [`FLUX_KUSTOMIZATION_KEY_PRUNE`] leaf-scalar-key axis. The
// scalar is part of the cluster-side contract with the upstream
// Flux v2 kustomize-controller — the controller's per-CR reconcile
// loop reads the scalar under the sibling leaf-scalar-key axis
// to decide whether to garbage-collect resources that were
// previously reconciled by the CR but no longer appear in the
// CR's current desired-state manifest set. Drift from the
// canonical `true` seed to `false` silently drops the substrate's
// chosen sweep-what-you-removed semantic from every emitted
// per-caixa `Kustomization` document, leaving per-caixa resources
// the source manifest set previously reconciled but no longer
// carries dangling in the cluster the substrate's "the cluster's
// per-caixa live state converges to the caixa's tatara-lisp
// source-of-truth on every reconcile — resources the source no
// longer carries are swept by the kustomize-controller, not left
// dangling" CAIXA-SDLC.md §V author-to-live-convergence guarantee
// mandates, with no diagnostic naming the toggle-drift root
// cause. Changing it is a substrate-side policy migration
// (candidates: `true` → `false` on a per-cluster class where a
// human is expected to prune orphaned resources by hand once
// per-cluster policy grows an operator-driven-cleanup mode; a
// per-caixa opt-out slot the ABSORPTION-ROADMAP.md M4 typed-slot
// trajectory adds once the substrate grows a `:kustomization
// :prune` author-side toggle), not an incidental edit. Peer to
// `flux_helmrelease_remediation_retries_default_pins_lifted_value`
// on the sibling per-path per-CR HelmRelease remediation retry-
// cap scalar-value default axis — that default names the per-
// path per-CR remediation retry ceiling, and this default names
// whether the per-CR reconcile loop sweeps orphaned resources at
// all. Both are substrate-side policy choices the operator
// inherits when the per-caixa `ClusterBundleOpts` doesn't pin an
// override.
assert!(FLUX_KUSTOMIZATION_PRUNE_DEFAULT);
}
#[test]
fn flux_kustomization_prune_default_pairs_with_lifted_leaf_key() {
// Sibling-pair pin: the `(leaf-scalar-key, scalar-value)` per-CR
// garbage-collection-toggle declaration lives at two lifted
// `pub const` declarations —
// [`FLUX_KUSTOMIZATION_KEY_PRUNE`] (8ec7917) on the key half
// and [`FLUX_KUSTOMIZATION_PRUNE_DEFAULT`] on the value half.
// Both halves must move together on any coordinated Flux v3
// migration (a `garbageCollect: false` rename that rebrands the
// leaf axis onto a new controller-side opt-in vs. the current
// opt-out default; a leaf coalesce onto a peer per-CR toggle
// that reroutes the substrate's canonical scalar seed onto an
// unrelated axis), so a rebrand on either half without a
// coordinated edit on the other would silently split the
// substrate's canonical sweep-what-you-removed declaration —
// the emit-site format-string would still thread the `{prune_key}`
// named-arg through the lifted leaf-scalar-key but pair it with
// a canonical `{prune_default}` that no longer reflects the
// substrate-side semantic the leaf axis names. Pin the pair here
// so a future edit that touches only the leaf-scalar-key half
// or only the scalar-value default half surfaces at build time
// rather than at reconcile time far from the source edit.
// Confirms both consts carry their canonical wire representations
// (`"prune"` byte-string on the leaf-scalar-key half; `true` on
// the scalar-value default half) — the pair as-a-unit reads as
// the substrate's chosen `prune: true` per-CR opt-in.
assert_eq!(FLUX_KUSTOMIZATION_KEY_PRUNE, "prune");
assert!(FLUX_KUSTOMIZATION_PRUNE_DEFAULT);
}
#[test]
fn flux_helmrelease_remediate_last_failure_default_pins_canonical_value() {
// Pin the actual boolean so a rebrand on this lift can't silently
// rebrand the Flux v2 `HelmRelease.spec.upgrade.remediation
// .remediateLastFailure` upgrade-path-only per-CR remediation-toggle
// scalar-value seed the substrate's per-caixa `cluster_bundle`
// renderer threads into every emitted per-caixa `helmrelease.yaml`
// document under the sibling
// [`FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE`] leaf-scalar-key
// axis. The scalar is part of the cluster-side contract with the
// upstream Flux v2 helm-controller — the controller's per-CR
// upgrade-path remediation loop reads the scalar under the sibling
// leaf-scalar-key axis to decide whether to trigger the prior-
// release rollback pipeline once the paired
// [`FLUX_HELMRELEASE_REMEDIATION_RETRIES_DEFAULT`] retry-cap ceiling
// has been exhausted. Drift from the canonical `true` seed to
// `false` silently drops the substrate's chosen post-retry-
// exhaustion rollback semantic from every emitted per-caixa
// `HelmRelease` document, leaving every terminally-failed upgrade
// parked at `Ready: False` without rolling back to the prior last-
// known-good release the substrate's "no chart apply leaves a
// per-caixa CR in a stalled, unremediated state" MESH-COMPOSITION
// .md §V guarantee mandates, with no diagnostic naming the
// remediation-toggle-drift root cause. Changing it is a substrate-
// side policy migration (candidates: `true` → `false` on a per-
// cluster class where terminally-failed upgrades must escalate to
// operator-attention rather than mask under an auto-rollback pipe-
// line; a per-caixa opt-out slot the ABSORPTION-ROADMAP.md M4
// typed-slot trajectory adds once the substrate grows a `:upgrade
// :remediate-last-failure` author-side toggle), not an incidental
// edit. Peer to `flux_kustomization_prune_default_pins_canonical_value`
// on the sibling per-`Kustomization`-CR garbage-collection-toggle
// scalar-value default axis — that default names whether the
// per-CR `Kustomization` reconcile loop sweeps orphaned resources
// at all, and this default names whether the per-CR `HelmRelease`
// upgrade-path remediation loop rolls back to the prior last-
// known-good release once the retry-cap ceiling is exhausted.
// Both are substrate-side policy choices the operator inherits
// when the per-caixa `ClusterBundleOpts` doesn't pin an override.
assert!(FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT);
}
#[test]
fn flux_helmrelease_remediate_last_failure_default_pairs_with_lifted_leaf_key() {
// Sibling-pair pin: the `(leaf-scalar-key, scalar-value)` per-CR
// upgrade-path per-CR post-retry-exhaustion-rollback-toggle
// declaration lives at two lifted `pub const` declarations —
// [`FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE`] (96581b7) on the
// key half and [`FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT`]
// on the value half. Both halves must move together on any
// coordinated Flux v3 migration (a `rollbackOnFailure: false`
// rename that rebrands the leaf axis onto a new controller-side
// opt-in vs. the current opt-in default; a leaf coalesce onto a
// peer per-CR toggle that reroutes the substrate's canonical
// scalar seed onto an unrelated axis), so a rebrand on either half
// without a coordinated edit on the other would silently split the
// substrate's canonical post-retry-exhaustion rollback declaration
// — the emit-site format-string would still thread the
// `{remediate_last_failure_key}` named-arg through the lifted
// leaf-scalar-key but pair it with a canonical
// `{remediate_last_failure_default}` that no longer reflects the
// substrate-side semantic the leaf axis names. Pin the pair here
// so a future edit that touches only the leaf-scalar-key half or
// only the scalar-value default half surfaces at build time rather
// than at reconcile time far from the source edit. Confirms both
// consts carry their canonical wire representations
// (`"remediateLastFailure"` byte-string on the leaf-scalar-key
// half; `true` on the scalar-value default half) — the pair as-a-
// unit reads as the substrate's chosen
// `remediateLastFailure: true` per-CR opt-in.
assert_eq!(
FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE,
"remediateLastFailure"
);
assert!(FLUX_HELMRELEASE_REMEDIATE_LAST_FAILURE_DEFAULT);
}
#[test]
fn flux_helmrelease_create_namespace_default_pins_canonical_value() {
// Pin the actual boolean so a rebrand on this lift can't silently
// rebrand the Flux v2 `HelmRelease.spec.install.createNamespace`
// install-path-only per-CR namespace-seeder-toggle scalar-value
// seed the substrate's per-caixa `cluster_bundle` renderer threads
// into every emitted per-caixa `helmrelease.yaml` document under
// the sibling [`FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE`] leaf-
// scalar-key axis. The scalar is part of the cluster-side contract
// with the upstream Flux v2 helm-controller — the controller's
// per-CR install-path pre-apply loop reads the scalar under the
// sibling leaf-scalar-key axis to decide whether to first material-
// ize the target namespace before the first-time chart apply.
// Drift from the canonical `true` seed to `false` silently drops
// the substrate's chosen first-apply namespace-seeder semantic
// from every emitted per-caixa `HelmRelease` document, leaving
// every first-time per-caixa chart apply against a fresh cluster
// refused by the helm-controller because the target namespace was
// not pre-provisioned by an out-of-band pipeline the substrate's
// "no per-caixa Servico apply is blocked on manual namespace
// preprovisioning" MESH-COMPOSITION.md §V install-path-fluency
// guarantee mandates, with no diagnostic naming the seeder-toggle-
// drift root cause. Changing it is a substrate-side policy
// migration (candidates: `true` → `false` on hardened per-cluster
// classes where namespace provisioning is an out-of-band operator
// gate; a per-caixa opt-out slot the ABSORPTION-ROADMAP.md M4
// typed-slot trajectory adds once the substrate grows a `:install
// :create-namespace` author-side toggle), not an incidental edit.
// Peer to `flux_helmrelease_remediate_last_failure_default_pins_canonical_value`
// on the sibling mirror-symmetric upgrade-path-only per-CR
// remediation-toggle scalar-value default axis — that default
// names whether the per-CR `HelmRelease` upgrade-path remediation
// loop rolls back to the prior last-known-good release once the
// retry-cap ceiling is exhausted, and this default names whether
// the per-CR `HelmRelease` install-path pre-apply loop materializes
// the target namespace before the first-time chart apply. Both
// are substrate-side policy choices the operator inherits when
// the per-caixa `ClusterBundleOpts` doesn't pin an override, and
// both close the mirror-symmetric install/upgrade per-CR phase-
// specific toggle scalar-value default pair the peer leaf-scalar-
// key pair [`FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE`] (ba9ab8b) /
// [`FLUX_HELMRELEASE_KEY_REMEDIATE_LAST_FAILURE`] (96581b7)
// already closed on the key half.
assert!(FLUX_HELMRELEASE_CREATE_NAMESPACE_DEFAULT);
}
#[test]
fn flux_helmrelease_create_namespace_default_pairs_with_lifted_leaf_key() {
// Sibling-pair pin: the `(leaf-scalar-key, scalar-value)` per-CR
// install-path per-CR namespace-seeder-toggle declaration lives
// at two lifted `pub const` declarations —
// [`FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE`] (ba9ab8b) on the key
// half and [`FLUX_HELMRELEASE_CREATE_NAMESPACE_DEFAULT`] on the
// value half. Both halves must move together on any coordinated
// Flux v3 migration (a `createTargetNamespace: false` rename that
// rebrands the leaf axis onto a new controller-side opt-in vs.
// the current opt-in default; a leaf coalesce onto a peer per-CR
// toggle that reroutes the substrate's canonical scalar seed onto
// an unrelated axis), so a rebrand on either half without a
// coordinated edit on the other would silently split the substrate's
// canonical first-apply namespace-seeder declaration — the emit-
// site format-string would still thread the
// `{create_namespace_key}` named-arg through the lifted leaf-
// scalar-key but pair it with a canonical `{create_namespace_default}`
// that no longer reflects the substrate-side semantic the leaf
// axis names. Pin the pair here so a future edit that touches
// only the leaf-scalar-key half or only the scalar-value default
// half surfaces at build time rather than at reconcile time far
// from the source edit. Confirms both consts carry their canonical
// wire representations (`"createNamespace"` byte-string on the
// leaf-scalar-key half; `true` on the scalar-value default half) —
// the pair as-a-unit reads as the substrate's chosen
// `createNamespace: true` per-CR opt-in.
assert_eq!(FLUX_HELMRELEASE_KEY_CREATE_NAMESPACE, "createNamespace");
assert!(FLUX_HELMRELEASE_CREATE_NAMESPACE_DEFAULT);
}
#[test]
fn cluster_bundle_lareira_enabled_default_pins_canonical_value() {
// Pin the actual boolean so a rebrand on this lift can't silently
// rebrand the substrate-side default for the
// `HelmRelease.spec.values.<library>.enabled` child-chart-
// enablement toggle scalar the substrate's per-caixa
// `cluster_bundle` renderer threads into every emitted per-caixa
// `helmrelease.yaml` document under the sibling
// [`HELM_VALUES_KEY_ENABLED`] leaf-scalar-key axis inside the
// per-`{library_name}` values-overlay wrap. The scalar is the
// substrate's chosen "force-on the child chart under the
// cluster_bundle composition path" default — semantically
// distinct from and inverse of the standalone
// [`caixa_helm::RenderOpts`]::`enabled_default = false` seed
// (which renders `enabled: false` in the per-caixa `values.yaml`
// so cluster operators must opt each caixa in per-cluster); the
// `cluster_bundle` composition path is the substrate-side
// opt-in path where the operator has already asserted per-caixa
// cluster-scoped ownership by materializing a per-caixa
// GitRepository + HelmRelease + Kustomization trio, so the
// overlay forces the child chart on by seeding `enabled: true`
// under the `values.<library>` wrap. Drift from the canonical
// `true` seed to `false` silently drops the substrate's chosen
// force-on-under-composition semantic from every emitted
// per-caixa `HelmRelease` document, leaving the paired
// [`DEFAULT_LIBRARY_NAME`] child chart's `enabled: false`
// per-chart default un-overridden — the Helm rendering pipeline
// then no-ops every per-caixa lareira child chart at the
// per-cluster `HelmRelease` apply step, with no diagnostic
// naming the toggle-drift root cause. Peer to the sibling
// `flux_helmrelease_create_namespace_default_pins_canonical_value`
// (be1904b) / `flux_helmrelease_remediate_last_failure_default_pins_canonical_value`
// (be1904b) / `flux_kustomization_prune_default_pins_canonical_value`
// (ea857d8) on the peer canonical-Flux-v2-per-CR-substrate-
// default surface — all four defaults are substrate-side policy
// choices the operator inherits when the per-caixa
// `ClusterBundleOpts` doesn't pin an override.
assert!(CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT);
}
#[test]
fn cluster_bundle_lareira_enabled_default_pairs_with_lifted_leaf_key() {
// Sibling-pair pin: the `(leaf-scalar-key, scalar-value)` per-
// values-overlay child-chart-enablement-toggle declaration lives
// at two lifted `pub const` declarations —
// [`HELM_VALUES_KEY_ENABLED`] on the key half and
// [`CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT`] on the value half.
// Both halves must move together on any coordinated Helm 4
// migration (an `on: true` rename that rebrands the leaf axis
// onto a new controller-side opt-in vs. the current opt-in
// default; a leaf coalesce onto a peer per-values-block toggle
// that reroutes the substrate's canonical scalar seed onto an
// unrelated axis), so a rebrand on either half without a
// coordinated edit on the other would silently split the
// substrate's canonical force-on-under-composition declaration —
// the emit-site format-string would still thread the
// `{enabled_key}` named-arg through the lifted leaf-scalar-key
// but pair it with a canonical `{lareira_enabled_default}` that
// no longer reflects the substrate-side semantic the leaf axis
// names. Pin the pair here so a future edit that touches only
// the leaf-scalar-key half or only the scalar-value default
// half surfaces at build time rather than at apply time far
// from the source edit. Confirms both consts carry their
// canonical wire representations (`"enabled"` byte-string on
// the leaf-scalar-key half; `true` on the scalar-value default
// half) — the pair as-a-unit reads as the substrate's chosen
// `enabled: true` per-values-overlay opt-in. Peer to
// `flux_kustomization_prune_default_pairs_with_lifted_leaf_key`
// (ea857d8) /
// `flux_helmrelease_create_namespace_default_pairs_with_lifted_leaf_key`
// (be1904b) /
// `flux_helmrelease_remediate_last_failure_default_pairs_with_lifted_leaf_key`
// (be1904b) on the sibling canonical-Flux-v2-per-CR-
// substrate-default paired-halves surfaces.
assert_eq!(HELM_VALUES_KEY_ENABLED, "enabled");
assert!(CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT);
}
#[test]
fn standalone_lareira_enabled_default_pins_canonical_value() {
// Pin the actual boolean so a rebrand on this lift can't silently
// rebrand the substrate-side default for the
// `values.<library>.enabled` child-chart-enablement toggle scalar
// the substrate's per-caixa `caixa_helm::render_chart_for_servico`
// renderer seeds into every emitted per-caixa `values.yaml`
// document under the sibling [`HELM_VALUES_KEY_ENABLED`]
// leaf-scalar-key axis inside the per-`{library_name}` wrap. The
// scalar is the substrate's chosen "leave the child chart opted
// out under the standalone per-chart path" default —
// semantically distinct from and inverse of the composition
// [`CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT`] seed (which renders
// `enabled: true` in the per-cluster `HelmRelease` values-overlay
// so the substrate force-ons the child chart at bundle
// materialization time); the standalone per-chart path is the
// substrate-side opt-out path where the operator has not yet
// asserted per-caixa cluster-scoped ownership by materializing a
// per-caixa GitRepository + HelmRelease + Kustomization trio, so
// the per-chart `values.yaml` seeds `enabled: false` under the
// `values.<library>` wrap and cluster operators must opt each
// caixa in per-cluster. Drift from the canonical `false` seed to
// `true` silently drops the substrate's chosen
// opt-out-under-standalone semantic from every emitted per-caixa
// `values.yaml` document, force-onning the paired
// [`DEFAULT_LIBRARY_NAME`] child chart against the operator's
// stated per-cluster opt-in convention — every rendered chart's
// library-chart-side workload would come up on `helm template` /
// `helm install` with no diagnostic naming the toggle-drift root
// cause. Peer to `cluster_bundle_lareira_enabled_default_pins_canonical_value`
// on the sibling composition-path `HelmRelease.spec.values.<library>.enabled`
// scalar-value default surface — both defaults are substrate-side
// policy choices the operator inherits when the per-caixa
// `RenderOpts` / `ClusterBundleOpts` doesn't pin an override, and
// together they close the mirror-symmetric standalone / composition
// per-values-block child-chart-enablement-toggle scalar-value
// default pair.
assert!(!STANDALONE_LAREIRA_ENABLED_DEFAULT);
}
#[test]
fn standalone_lareira_enabled_default_pairs_with_lifted_leaf_key() {
// Sibling-pair pin: the `(leaf-scalar-key, scalar-value)` per-
// values-block child-chart-enablement-toggle declaration on the
// standalone per-chart path lives at two lifted `pub const`
// declarations — [`HELM_VALUES_KEY_ENABLED`] on the key half and
// [`STANDALONE_LAREIRA_ENABLED_DEFAULT`] on the value half. Both
// halves must move together on any coordinated Helm 4 migration
// (an `on: false` rename that rebrands the leaf axis onto a new
// controller-side opt-in vs. the current opt-out default; a leaf
// coalesce onto a peer per-values-block toggle that reroutes the
// substrate's canonical scalar seed onto an unrelated axis), so a
// rebrand on either half without a coordinated edit on the other
// would silently split the substrate's canonical
// opt-out-under-standalone declaration — the emit-site block
// insertion would still thread [`HELM_VALUES_KEY_ENABLED`] as the
// key but pair it with a canonical `enabled_default` scalar-value
// seed that no longer reflects the substrate-side semantic the
// leaf axis names. Pin the pair here so a future edit that
// touches only the leaf-scalar-key half or only the scalar-value
// default half surfaces at build time rather than at apply time
// far from the source edit. Confirms both consts carry their
// canonical wire representations (`"enabled"` byte-string on the
// leaf-scalar-key half; `false` on the scalar-value default half)
// — the pair as-a-unit reads as the substrate's chosen
// `enabled: false` per-values-block opt-out. Peer to
// `cluster_bundle_lareira_enabled_default_pairs_with_lifted_leaf_key`
// on the sibling composition-path
// `HelmRelease.spec.values.<library>.enabled` scalar-value default
// paired-halves surface — both `(key, value)` pairs share the same
// [`HELM_VALUES_KEY_ENABLED`] leaf-scalar-key half but diverge on
// the scalar-value half, which is exactly the mirror-symmetric
// standalone / composition path-selection the two scalar-value
// defaults name.
assert_eq!(HELM_VALUES_KEY_ENABLED, "enabled");
assert!(!STANDALONE_LAREIRA_ENABLED_DEFAULT);
}
#[test]
fn standalone_and_cluster_bundle_lareira_enabled_defaults_are_inverse_by_construction() {
// Cross-const coherence pin: the two peer
// per-values-block child-chart-enablement-toggle scalar-value
// defaults on the standalone per-chart path
// ([`STANDALONE_LAREIRA_ENABLED_DEFAULT`]) and the composition
// per-cluster-`HelmRelease` values-overlay path
// ([`CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT`]) name mirror-symmetric
// inverse defaults on the same underlying
// `values.<library>.enabled` sub-block axis: the standalone-path
// default is `false` (opt-out — cluster operators must opt each
// caixa in per-cluster) while the composition-path default is
// `true` (opt-in — the substrate force-ons the child chart once
// the operator has asserted per-caixa cluster-scoped ownership by
// materializing a per-caixa GitRepository + HelmRelease +
// Kustomization trio). The inversion is the substrate's chosen
// author-to-live path-selection semantic — every consumer that
// reads either default inherits the per-path opt-out / opt-in
// decision by construction, so a future edit that accidentally
// aligned the two defaults (both `false` on a substrate-wide
// opt-out migration, both `true` on a substrate-wide opt-in
// migration) would silently collapse the substrate's chosen
// standalone-vs-composition path-selection semantic — the
// per-chart `values.yaml` default and the per-cluster
// `HelmRelease.spec.values.<library>.enabled` overlay default
// would agree on the same enablement seed, and either the
// standalone path would force-on the child chart against the
// operator's per-cluster opt-in convention (both `true`) or the
// composition path would leave the child chart opted-out against
// the operator's per-caixa cluster-scoped ownership assertion
// (both `false`). Pin the structural inversion here so a future
// edit that touches only one of the two defaults surfaces at
// caixa-core build time rather than at chart-apply time far from
// the constant-drift source. Confirms the two `bool`s carry
// distinct canonical wire representations — the pair as-a-unit
// reads as the substrate's chosen mirror-symmetric author-to-live
// path-selection semantic (standalone opt-out, composition
// opt-in). Peer to the sibling pairwise-distinctness pins the
// `M3_PLACEMENT_ESTRATEGIA_*` /
// `M2_UPGRADE_INSTRUCTION_KIND_*` closed-set typed-enum
// discriminator axes carry on the peer canonical-typed-enum-
// discriminator distinctness surface.
assert_ne!(
STANDALONE_LAREIRA_ENABLED_DEFAULT, CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT,
"STANDALONE_LAREIRA_ENABLED_DEFAULT and CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT \
must remain inverse `bool`s — the standalone per-chart path defaults to \
opt-out (`false`) and the composition per-cluster-HelmRelease values-overlay \
path defaults to opt-in (`true`); collapsing the inversion silently \
breaks the substrate's chosen mirror-symmetric author-to-live \
path-selection semantic at chart-apply time far from the constant-\
drift source."
);
}
#[test]
fn flux_kustomization_key_path_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Flux v2 `Kustomization.spec.path` per-CR source-
// sub-tree leaf-scalar-key the substrate's per-caixa
// `cluster_bundle` renderer seeds into every emitted per-caixa
// `kustomization.yaml` document at the top-level `spec`
// position. The string is part of the cluster-side contract
// with the upstream Flux v2 kustomize-controller — the
// controller's per-CR reconcile loop reaches the source-sub-
// tree pointer through this exact leaf; a drifted leaf-scalar-
// key silently unbinds every per-caixa `Kustomization` from
// its paired per-caixa sub-tree of the pleme-io k8s repository
// (the controller defaults to `./` when the CR omits the leaf,
// pulling every unrelated cluster's manifests through the
// wrong per-caixa `Kustomization`), with no diagnostic naming
// the leaf-drift root cause. Changing it is a coordinated Flux
// v3 CRD-schema-rebrand migration alongside the upstream
// `kustomize-controller` deprecation cycle (candidates like
// `sourcePath` / `manifestsPath` / `sourceRoot` upstream Flux
// v3 roadmap floats), not an incidental edit. Peer to
// `flux_kustomization_key_prune_pins_canonical_value` on the
// sibling co-resident per-`Kustomization`-CR `spec.prune`
// garbage-collection-toggle leaf-scalar-key half of the same
// per-`Kustomization`-CR-spec surface.
assert_eq!(FLUX_KUSTOMIZATION_KEY_PATH, "path");
}
#[test]
fn flux_kustomization_key_path_stays_independent_of_prune() {
// The per-`Kustomization`-CR top-level `spec` surface hosts two
// co-resident leaf-scalar-key axes: the per-CR source-sub-tree
// pointer [`FLUX_KUSTOMIZATION_KEY_PATH`] (this lift) and the
// per-CR garbage-collection-toggle [`FLUX_KUSTOMIZATION_KEY_PRUNE`]
// (8ec7917). Pin that the two consts carry byte-distinct
// sequences so a future rebrand on either arm can't silently
// coalesce onto the peer arm (a
// `FLUX_KUSTOMIZATION_KEY_PATH = "prune"` typo would silently
// rebind the substrate's per-cluster / per-caixa sub-tree path
// seed onto the garbage-collection-toggle leaf at every emit
// site — the kustomize-controller would then read the
// substrate's `./clusters/<cluster>/services/<name>` seed as a
// boolean opt-in toggle, silently unbinding the per-caixa
// `Kustomization` from its source-sub-tree entirely with no
// diagnostic naming the leaf-key-coalesce root cause). The
// per-`Kustomization`-CR source-sub-tree pointer and the per-
// `Kustomization`-CR garbage-collection-toggle must always
// resolve to distinct emitted leaf-keys under the same
// top-level `spec` position.
assert_ne!(
FLUX_KUSTOMIZATION_KEY_PATH, FLUX_KUSTOMIZATION_KEY_PRUNE,
"the per-`Kustomization`-CR source-sub-tree leaf-scalar-key \
and the per-`Kustomization`-CR garbage-collection-toggle \
leaf-scalar-key must remain byte-distinct — a coalesce \
onto one value silently rebinds one axis onto the peer \
axis at every emit site, dropping the source-sub-tree / \
sweep-what-you-removed semantic the substrate seeds on the \
coalesced arm"
);
}
#[test]
fn flux_kustomization_key_timeout_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Flux v2 `Kustomization.spec.timeout` per-CR
// reconcile wall-clock cap leaf-scalar-key the substrate's per-
// caixa `cluster_bundle` renderer seeds into every emitted per-
// caixa `kustomization.yaml` document at the top-level `spec`
// position. The string is part of the cluster-side contract
// with the upstream Flux v2 kustomize-controller — the
// controller's per-CR reconcile loop reaches the wall-clock cap
// through this exact leaf; a drifted leaf-scalar-key silently
// strips the substrate's chosen reconcile-ceiling from every
// emitted per-caixa `Kustomization` document, letting the
// controller fall back to the upstream Flux v2 controller-side
// default cap rather than the substrate's per-caixa
// idempotency-checkpoint-tuned ceiling, with no diagnostic
// naming the timeout-drift root cause. Changing it is a
// coordinated Flux v3 CRD-schema-rebrand migration alongside
// the upstream `kustomize-controller` deprecation cycle, not
// an incidental edit. Peer to
// `flux_kustomization_key_path_pins_canonical_value` and
// `flux_kustomization_key_prune_pins_canonical_value` on the
// sibling co-resident per-`Kustomization`-CR spec surface
// leaf-scalar-key axes.
assert_eq!(FLUX_KUSTOMIZATION_KEY_TIMEOUT, "timeout");
}
#[test]
fn flux_kustomization_key_timeout_stays_independent_of_path_and_prune() {
// The per-`Kustomization`-CR top-level `spec` surface hosts
// three co-resident leaf-scalar-key axes: the per-CR reconcile
// wall-clock cap [`FLUX_KUSTOMIZATION_KEY_TIMEOUT`] (this
// lift), the per-CR source-sub-tree pointer
// [`FLUX_KUSTOMIZATION_KEY_PATH`] (613d7ed), and the per-CR
// garbage-collection-toggle [`FLUX_KUSTOMIZATION_KEY_PRUNE`]
// (8ec7917). Pin that the three consts carry byte-distinct
// sequences so a future rebrand on any one arm can't silently
// coalesce onto a peer arm (a
// `FLUX_KUSTOMIZATION_KEY_TIMEOUT = "path"` typo would silently
// rebind the reconcile wall-clock cap onto the source-sub-tree
// pointer leaf at every emit site — the kustomize-controller
// would then parse the substrate's `./clusters/<c>/services/<n>`
// seed as a `metav1.Duration` scalar and reject the per-CR
// admission gate, with no diagnostic naming the leaf-key-
// coalesce root cause). The per-`Kustomization`-CR reconcile
// wall-clock cap, per-CR source-sub-tree pointer, and per-CR
// garbage-collection-toggle must always resolve to distinct
// emitted leaf-keys under the same top-level `spec` position.
assert_ne!(
FLUX_KUSTOMIZATION_KEY_TIMEOUT, FLUX_KUSTOMIZATION_KEY_PATH,
"the per-`Kustomization`-CR reconcile wall-clock cap leaf-\
scalar-key and the per-`Kustomization`-CR source-sub-tree \
leaf-scalar-key must remain byte-distinct — a coalesce onto \
one value silently rebinds one axis onto the peer axis at \
every emit site, dropping the reconcile-ceiling / source-\
sub-tree semantic the substrate seeds on the coalesced arm"
);
assert_ne!(
FLUX_KUSTOMIZATION_KEY_TIMEOUT, FLUX_KUSTOMIZATION_KEY_PRUNE,
"the per-`Kustomization`-CR reconcile wall-clock cap leaf-\
scalar-key and the per-`Kustomization`-CR garbage-\
collection-toggle leaf-scalar-key must remain byte-distinct \
— a coalesce onto one value silently rebinds one axis onto \
the peer axis at every emit site, dropping the reconcile-\
ceiling / sweep-what-you-removed semantic the substrate \
seeds on the coalesced arm"
);
}
#[test]
fn default_flux_kustomization_timeout_pins_canonical_value() {
// Pin the actual scalar so a typo in this lift can't silently
// rebrand the substrate-side default Flux v2
// `Kustomization.spec.timeout` reconcile wall-clock cap the
// substrate's per-caixa `cluster_bundle` renderer seeds into
// every emitted per-caixa `kustomization.yaml` document at the
// top-level `spec` position. The value is part of the cluster-
// side contract with the Flux v2 kustomize-controller (the
// per-CR reconcile loop uses this as the ceiling on the wall-
// clock time a single reconcile attempt is allowed to consume
// before the controller marks the `Kustomization`
// `Ready: False` and stops retrying); changing it is a
// coordinated substrate-side reconcile-ceiling promotion (a
// `5m` → `3m` migration on faster per-caixa idempotency-
// checkpoint cadence, a `5m` → `10m` migration on larger per-
// caixa manifest sets), not an incidental edit. Peer to
// `default_flux_reconcile_interval_pins_canonical_value` and
// `flux_helmrelease_remediation_retries_default_pins_canonical_value`
// on the canonical-Flux-v2-per-CR-substrate-default-scalar pin
// surface.
assert_eq!(DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT, "5m");
}
#[test]
fn default_flux_kustomization_timeout_is_a_valid_metav1_duration_scalar() {
// Cross-axis grammar invariant: the Flux v2 kustomize-
// controller-side per-CR admission gate parses the reconcile
// wall-clock cap scalar via `metav1.ParseDuration` before
// installing the per-CR watch. The Go-duration-format grammar
// is non-empty, ASCII, and structured as
// `<digits><unit>[<digits><unit>...]` where each unit is one of
// `{ns, us, µs, ms, s, m, h}`. Pin a floor that catches the
// canonical drift footguns — an empty scalar (`""` — admission
// gate rejects), a non-ASCII-alphanumeric byte (`"5 m"` — the
// whitespace defeats the parser), a missing-unit scalar (`"5"`
// — the parser rejects for lack of a unit suffix), or a
// leading-non-digit scalar (`"m5"` — the parser rejects for
// lack of a leading magnitude). A future rebrand on the
// canonical lift that lands a value outside the Go-duration-
// format grammar would surface here at caixa-core build time
// on the canonical lift, before any renderer consumes the
// value. Same shape as
// `default_flux_reconcile_interval_is_a_valid_metav1_duration_scalar`
// on the peer canonical-substrate-default-grammar-floor surface.
let v = DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT;
assert!(
!v.is_empty(),
"DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT {v:?} must be non-empty \
per the Flux v2 controller-side `metav1.ParseDuration` \
admission gate"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT {v:?} must be ASCII-\
alphanumeric throughout per the Go-duration-format grammar \
— no whitespace / separator bytes the `metav1.ParseDuration` \
admission gate would reject"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_digit(),
"DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT {v:?} first byte {first:?} \
must be an ASCII digit per the Go-duration-format grammar \
— the leading magnitude precedes the unit suffix; a leading \
non-digit defeats `metav1.ParseDuration`"
);
let last = v.chars().next_back().expect("non-empty");
assert!(
last.is_ascii_alphabetic() && last.is_ascii_lowercase(),
"DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT {v:?} last byte {last:?} \
must be an ASCII lowercase alphabetic unit suffix per the \
Go-duration-format grammar — the trailing unit follows the \
magnitude; an unterminated magnitude defeats \
`metav1.ParseDuration`"
);
}
#[test]
fn default_gateway_class_name_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the substrate's chosen K8s Gateway API controller the
// rendered `Gateway`'s `spec.gatewayClassName` axis binds to.
// The string is part of the cluster-side contract with the Cilium
// Gateway API implementation (the Cilium operator watches
// `GatewayClass` objects whose `spec.controllerName` names the
// Cilium reconciler; a drifted `spec.gatewayClassName` on the
// emitted `Gateway` refers to a `GatewayClass` no controller
// reconciles, and the `Gateway` sits at `Programmed: False`
// with every attached `HTTPRoute` unbound), the same eBPF-identity
// data plane the sibling `CiliumNetworkPolicy` renderer emits
// policies against (the mesh-composition "one identity layer,
// one data plane" invariant, MESH-COMPOSITION.md §V), and the
// per-cluster GatewayClass fixture the operator-side install
// pipeline provisions. Changing it is a coordinated multi-repo
// migration (a substrate-side Gateway controller migration to
// Envoy Gateway / Istio Gateway or any per-edition variant),
// not an incidental edit. Peer to
// `default_namespace_pins_canonical_value` and
// `default_flux_system_namespace_pins_canonical_value` on the
// canonical-substrate-default-resource-name-value-pin axis.
assert_eq!(DEFAULT_GATEWAY_CLASS_NAME, "cilium");
}
#[test]
fn default_gateway_class_name_is_a_valid_dns_1123_label() {
// Cross-axis invariant: the Gateway API `GatewayClass` is a
// cluster-scoped K8s resource, and the K8s apiserver enforces
// the DNS-1123 label rule on every cluster-scoped resource's
// `metadata.name`. The emitted `Gateway`'s
// `spec.gatewayClassName` axis references the `GatewayClass`
// resource by that name — a drift to a value the apiserver
// would refuse as a `GatewayClass.metadata.name` couldn't
// resolve at reconcile time either, and the `Gateway`
// Programmed condition never flips true. Pinning this here
// means a future rebrand on the canonical lift can't silently
// land a value the apiserver refuses at the *first* `Gateway`
// apply against a cluster, far from the rebrand commit's
// source — the typed [`is_dns_1123_label`] floor rejects it at
// caixa-core build time on the canonical lift, before any
// renderer consumes the value. Same shape as
// `default_namespace_is_a_valid_dns_1123_label` and
// `default_flux_system_namespace_is_a_valid_dns_1123_label` on
// the peer canonical-DNS-1123-label-floor axes.
assert!(
is_dns_1123_label(DEFAULT_GATEWAY_CLASS_NAME).is_ok(),
"DEFAULT_GATEWAY_CLASS_NAME {DEFAULT_GATEWAY_CLASS_NAME:?} must be a \
valid DNS-1123 label — every K8s apiserver-side schema enforces \
this rule on cluster-scoped `metadata.name` axes, and the \
`Gateway.spec.gatewayClassName` axis resolves by that same rule"
);
}
#[test]
fn flux_helmrelease_api_version_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Flux v2 `HelmRelease` CRD group/version the rendered
// `helmrelease.yaml` document declares + the rendered
// `kustomization.yaml` document's `healthChecks[].apiVersion`
// axis transitively references. The string is part of the
// cluster-side contract with the Flux v2 `helm-controller` (the
// controller watches the exact `helm.toolkit.fluxcd.io/v2`
// group/version; a drifted value to a stale v2beta1 / v2beta2
// lands the rendered `HelmRelease` outside the controller's
// `Watches` and fails at apply time with "no kind 'HelmRelease'
// is registered for version 'helm.toolkit.fluxcd.io/v2beta2'");
// changing it is a coordinated Flux v3 migration alongside the
// upstream `helm-controller` deprecation cycle, not an
// incidental edit. Peer to `default_flux_system_namespace_pins_canonical_value`
// on the canonical-Flux-CRD-axis-pin axis for the sibling
// [`DEFAULT_FLUX_SYSTEM_NAMESPACE`] constant.
assert_eq!(FLUX_HELMRELEASE_API_VERSION, "helm.toolkit.fluxcd.io/v2");
}
#[test]
fn flux_helmrelease_api_version_carries_group_and_version_segments() {
// Cross-axis invariant: a Kubernetes CRD `apiVersion` is a
// `<group>/<version>` pair separated by exactly one `/` byte.
// The group segment is a DNS-style multi-segment hostname
// (`helm.toolkit.fluxcd.io`) and the version segment is a
// Kubernetes API version label (`v2`, `v2beta1`, `v1alpha1` —
// peer with the K8s API versioning convention upstream
// documents). Pinning this here means a future rebrand on the
// canonical lift can't silently land a malformed apiVersion
// (no `/`, two `/`, empty group, empty version) that every
// downstream YAML-aware deserializer would reject far from the
// rebrand commit's source. The single-`/` invariant is the
// load-bearing K8s API typed-discovery contract: a value the
// apiserver's `RESTMapper` consults to resolve the CRD's
// `RESTKind`.
let v = FLUX_HELMRELEASE_API_VERSION;
let parts: Vec<&str> = v.split('/').collect();
assert_eq!(
parts.len(),
2,
"FLUX_HELMRELEASE_API_VERSION {v:?} must split into exactly two \
`/`-delimited segments (group/version) per the K8s CRD apiVersion \
grammar — every downstream YAML-aware deserializer enforces this \
shape"
);
assert!(
!parts[0].is_empty(),
"FLUX_HELMRELEASE_API_VERSION {v:?} group segment must be non-empty"
);
assert!(
!parts[1].is_empty(),
"FLUX_HELMRELEASE_API_VERSION {v:?} version segment must be non-empty"
);
assert!(
parts[0].contains('.'),
"FLUX_HELMRELEASE_API_VERSION {v:?} group segment {group:?} must be a \
DNS-style multi-segment hostname (the canonical CRD-group convention \
every K8s controller-runtime / kube-rs-aware client expects)",
group = parts[0]
);
}
#[test]
fn default_flux_helmrelease_api_version_matches_caixa_flux_test_fixtures() {
// Cross-file drift pin: the four caixa-flux occurrences of
// `helm.toolkit.fluxcd.io/v2` all consult the same canonical
// constant, but the two `upsert_into_helmrelease_programs` test
// fixtures (caixa-flux/src/lib.rs:928, 970) carry the value as
// a static raw-string literal inside a `serde_yaml::from_str`
// input (the YAML parser is the unit-under-test there, not the
// rendering — the literals are intentionally not threaded
// through the lift). This pin trips at caixa-core build time
// if the canonical constant ever drifts past the literal the
// caixa-flux test fixtures carry, so a future Flux v3 migration
// surfaces here on the canonical-string axis rather than at the
// first failing test fixture far from the rebrand commit. Peer
// to the [`default_flux_system_namespace_pins_canonical_value`]
// pin on the sibling Flux-namespace axis: both pin the canonical
// string at the lift site so a future rebrand lands the
// constant + every downstream reference + every test fixture in
// one coordinated edit.
assert_eq!(
FLUX_HELMRELEASE_API_VERSION, "helm.toolkit.fluxcd.io/v2",
"drift between FLUX_HELMRELEASE_API_VERSION and the \
caixa-flux/src/lib.rs:928,970 test fixtures' literal values; \
coordinate the migration across the const + every fixture in \
one edit"
);
}
#[test]
fn flux_gitrepository_api_version_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Flux v2 `GitRepository` CRD group/version the rendered
// `gitrepository.yaml` document declares. The string is part of the
// cluster-side contract with the Flux v2 `source-controller` (the
// controller watches the exact `source.toolkit.fluxcd.io/v1`
// group/version; a drifted value to a stale v1beta1 / v1beta2 lands
// the rendered `GitRepository` outside the controller's `Watches`
// and fails at apply time with "no kind 'GitRepository' is
// registered for version 'source.toolkit.fluxcd.io/v1beta2'");
// changing it is a coordinated Flux v3 migration alongside the
// upstream `source-controller` deprecation cycle, not an
// incidental edit. Peer to
// `flux_helmrelease_api_version_pins_canonical_value` on the
// canonical-Flux-CRD-axis-pin axis for the sibling
// [`FLUX_HELMRELEASE_API_VERSION`] constant.
assert_eq!(
FLUX_GITREPOSITORY_API_VERSION,
"source.toolkit.fluxcd.io/v1"
);
}
#[test]
fn flux_gitrepository_api_version_carries_group_and_version_segments() {
// Cross-axis invariant: a Kubernetes CRD `apiVersion` is a
// `<group>/<version>` pair separated by exactly one `/` byte.
// The group segment is a DNS-style multi-segment hostname
// (`source.toolkit.fluxcd.io`) and the version segment is a
// Kubernetes API version label (`v1`, `v1beta1`, `v1alpha1` — peer
// with the K8s API versioning convention upstream documents).
// Pinning this here means a future rebrand on the canonical lift
// can't silently land a malformed apiVersion (no `/`, two `/`,
// empty group, empty version) that every downstream YAML-aware
// deserializer would reject far from the rebrand commit's source.
// The single-`/` invariant is the load-bearing K8s API typed-
// discovery contract: a value the apiserver's `RESTMapper`
// consults to resolve the CRD's `RESTKind`. Peer to
// `flux_helmrelease_api_version_carries_group_and_version_segments`
// on the sibling Flux-CRD-axis.
let v = FLUX_GITREPOSITORY_API_VERSION;
let parts: Vec<&str> = v.split('/').collect();
assert_eq!(
parts.len(),
2,
"FLUX_GITREPOSITORY_API_VERSION {v:?} must split into exactly two \
`/`-delimited segments (group/version) per the K8s CRD apiVersion \
grammar — every downstream YAML-aware deserializer enforces this \
shape"
);
assert!(
!parts[0].is_empty(),
"FLUX_GITREPOSITORY_API_VERSION {v:?} group segment must be non-empty"
);
assert!(
!parts[1].is_empty(),
"FLUX_GITREPOSITORY_API_VERSION {v:?} version segment must be non-empty"
);
assert!(
parts[0].contains('.'),
"FLUX_GITREPOSITORY_API_VERSION {v:?} group segment {group:?} must be a \
DNS-style multi-segment hostname (the canonical CRD-group convention \
every K8s controller-runtime / kube-rs-aware client expects)",
group = parts[0]
);
}
#[test]
fn flux_gitrepository_and_helmrelease_api_versions_share_toolkit_fluxcd_io_root() {
// Cross-axis invariant: every Flux v2 CRD group ends in the canonical
// `.toolkit.fluxcd.io` root the upstream `fluxcd/flux2` project pins
// for the source-/helm-/kustomize-/notification-controller triplet.
// A future Flux v3 promotion that breaks the root suffix (forking
// `source-controller` out of the toolkit group, for example) would
// surface here as a coordinated cross-axis edit-point — both lifted
// constants must move together to preserve the controller-triple
// contract.
const ROOT: &str = ".toolkit.fluxcd.io";
let gr_group = FLUX_GITREPOSITORY_API_VERSION
.split('/')
.next()
.expect("FLUX_GITREPOSITORY_API_VERSION has a group segment");
let hr_group = FLUX_HELMRELEASE_API_VERSION
.split('/')
.next()
.expect("FLUX_HELMRELEASE_API_VERSION has a group segment");
assert!(
gr_group.ends_with(ROOT),
"FLUX_GITREPOSITORY_API_VERSION group {gr_group:?} must end with the \
canonical Flux v2 `{ROOT}` root every controller in the triplet shares"
);
assert!(
hr_group.ends_with(ROOT),
"FLUX_HELMRELEASE_API_VERSION group {hr_group:?} must end with the \
canonical Flux v2 `{ROOT}` root every controller in the triplet shares"
);
}
#[test]
fn flux_kustomization_api_version_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Flux v2 `Kustomization` CRD group/version the
// rendered `kustomization.yaml` document declares. The string
// is part of the cluster-side contract with the Flux v2
// `kustomize-controller` (the controller watches the exact
// `kustomize.toolkit.fluxcd.io/v1` group/version; a drifted
// value to a stale v1beta1 / v1beta2 lands the rendered
// `Kustomization` outside the controller's `Watches` and
// fails at apply time with "no kind 'Kustomization' is
// registered for version
// 'kustomize.toolkit.fluxcd.io/v1beta2'"); changing it is a
// coordinated Flux v3 migration alongside the upstream
// `kustomize-controller` deprecation cycle, not an
// incidental edit. Peer to
// `flux_helmrelease_api_version_pins_canonical_value` /
// `flux_gitrepository_api_version_pins_canonical_value` on
// the canonical-Flux-CRD-axis-pin axis for the sibling
// [`FLUX_HELMRELEASE_API_VERSION`] /
// [`FLUX_GITREPOSITORY_API_VERSION`] constants — completes
// the Flux v2 controller-triplet's per-CRD-axis pin set.
assert_eq!(
FLUX_KUSTOMIZATION_API_VERSION,
"kustomize.toolkit.fluxcd.io/v1"
);
}
#[test]
fn flux_kustomization_api_version_carries_group_and_version_segments() {
// Cross-axis invariant: a Kubernetes CRD `apiVersion` is a
// `<group>/<version>` pair separated by exactly one `/` byte.
// The group segment is a DNS-style multi-segment hostname
// (`kustomize.toolkit.fluxcd.io`) and the version segment is a
// Kubernetes API version label (`v1`, `v1beta1`, `v1alpha1` —
// peer with the K8s API versioning convention upstream
// documents). Pinning this here means a future rebrand on the
// canonical lift can't silently land a malformed apiVersion
// (no `/`, two `/`, empty group, empty version) that every
// downstream YAML-aware deserializer would reject far from the
// rebrand commit's source. The single-`/` invariant is the
// load-bearing K8s API typed-discovery contract: a value the
// apiserver's `RESTMapper` consults to resolve the CRD's
// `RESTKind`. Peer to
// `flux_helmrelease_api_version_carries_group_and_version_segments`
// / `flux_gitrepository_api_version_carries_group_and_version_segments`
// on the sibling Flux-CRD-axis.
let v = FLUX_KUSTOMIZATION_API_VERSION;
let parts: Vec<&str> = v.split('/').collect();
assert_eq!(
parts.len(),
2,
"FLUX_KUSTOMIZATION_API_VERSION {v:?} must split into exactly two \
`/`-delimited segments (group/version) per the K8s CRD apiVersion \
grammar — every downstream YAML-aware deserializer enforces this \
shape"
);
assert!(
!parts[0].is_empty(),
"FLUX_KUSTOMIZATION_API_VERSION {v:?} group segment must be non-empty"
);
assert!(
!parts[1].is_empty(),
"FLUX_KUSTOMIZATION_API_VERSION {v:?} version segment must be non-empty"
);
assert!(
parts[0].contains('.'),
"FLUX_KUSTOMIZATION_API_VERSION {v:?} group segment {group:?} must be a \
DNS-style multi-segment hostname (the canonical CRD-group convention \
every K8s controller-runtime / kube-rs-aware client expects)",
group = parts[0]
);
}
#[test]
fn flux_controller_triplet_api_versions_share_toolkit_fluxcd_io_root() {
// Cross-axis triplet invariant: the Flux v2 controller triplet
// (source-controller + helm-controller + kustomize-controller)
// upstream all share the canonical `.toolkit.fluxcd.io` root.
// The two-axis sibling pin
// [`flux_gitrepository_and_helmrelease_api_versions_share_toolkit_fluxcd_io_root`]
// enforces the invariant on the source-/helm- pair; this
// pin extends it onto the kustomize-controller axis so a
// future Flux v3 promotion that forks any single controller
// out of the toolkit group surfaces as a coordinated
// cross-axis edit-point across all three constants — the
// controller triplet's CRD group/versions move together
// upstream, and the lift discipline preserves that
// movement at the typed substrate-side `&'static str`
// surface.
const ROOT: &str = ".toolkit.fluxcd.io";
for (name, v) in [
(
"FLUX_GITREPOSITORY_API_VERSION",
FLUX_GITREPOSITORY_API_VERSION,
),
("FLUX_HELMRELEASE_API_VERSION", FLUX_HELMRELEASE_API_VERSION),
(
"FLUX_KUSTOMIZATION_API_VERSION",
FLUX_KUSTOMIZATION_API_VERSION,
),
] {
let group = v
.split('/')
.next()
.expect("Flux v2 CRD apiVersion has a group segment");
assert!(
group.ends_with(ROOT),
"{name} group {group:?} must end with the canonical Flux v2 \
`{ROOT}` root every controller in the source/helm/kustomize \
triplet shares"
);
}
}
#[test]
fn flux_kind_git_repository_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Flux v2 `GitRepository` CRD `kind` discriminator
// the rendered Flux bundle's three `GitRepository`-naming axes
// declare (gitrepository.yaml top-level kind, helmrelease.yaml
// spec.chart.spec.sourceRef.kind, kustomization.yaml
// spec.sourceRef.kind). The string is part of the cluster-side
// contract with the Flux v2 `source-controller` — the
// apiserver-side CRD resolution contract is the
// `(apiVersion, kind)` tuple keyed against the registered
// `CustomResourceDefinition`, so the kind half of the tuple is
// exactly as load-bearing as the sibling
// [`FLUX_GITREPOSITORY_API_VERSION`] apiVersion half. A drifted
// value (e.g. an upstream Flux v3 rename to `GitSource`) lands
// the rendered documents outside the source-controller's CRD
// registration; changing it is a coordinated Flux v3 migration
// alongside the upstream `source-controller` deprecation cycle,
// not an incidental edit. Peer to
// `flux_gitrepository_api_version_pins_canonical_value` on the
// sibling apiVersion half of the same CRD-lookup tuple.
assert_eq!(FLUX_KIND_GIT_REPOSITORY, "GitRepository");
}
#[test]
fn flux_kind_git_repository_carries_upper_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD `kind` discriminator is
// an UpperCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds —
// "Kinds are always UpperCamelCase"). Pinning the shape here
// means a future rebrand on the canonical lift can't silently
// land a malformed kind discriminator (snake_case, kebab-case,
// lowercase, empty) that every downstream YAML-aware
// deserializer would reject far from the rebrand commit's
// source. The first-byte uppercase / rest-ASCII-alphanumeric
// invariant is the load-bearing K8s API typed-discovery
// contract: a value the apiserver's `RESTMapper` consults to
// resolve the CRD's `RESTKind`. Peer to
// `flux_gitrepository_api_version_carries_group_and_version_segments`
// on the sibling apiVersion half of the same CRD-lookup tuple.
let v = FLUX_KIND_GIT_REPOSITORY;
assert!(
!v.is_empty(),
"FLUX_KIND_GIT_REPOSITORY {v:?} must be non-empty per the K8s API \
UpperCamelCase kind discriminator grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_uppercase(),
"FLUX_KIND_GIT_REPOSITORY {v:?} first byte {first:?} must be \
ASCII-uppercase per the K8s API UpperCamelCase kind discriminator \
grammar (Kinds are always UpperCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"FLUX_KIND_GIT_REPOSITORY {v:?} must be ASCII-alphanumeric \
throughout per the K8s API kind discriminator grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
RESTMapper would reject"
);
}
#[test]
fn flux_kind_helm_release_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Flux v2 `HelmRelease` CRD `kind` discriminator
// the rendered Flux bundle's two `HelmRelease`-naming axes
// declare (helmrelease.yaml top-level kind, kustomization.yaml
// spec.healthChecks[].kind). The string is part of the
// cluster-side contract with the Flux v2 `helm-controller` —
// the apiserver-side CRD resolution contract is the
// `(apiVersion, kind)` tuple keyed against the registered
// `CustomResourceDefinition`, so the kind half of the tuple is
// exactly as load-bearing as the sibling
// [`FLUX_HELMRELEASE_API_VERSION`] apiVersion half. A drifted
// value (e.g. an upstream Flux v3 rename to `ChartRelease`)
// lands the rendered documents outside the helm-controller's
// CRD registration; changing it is a coordinated Flux v3
// migration alongside the upstream `helm-controller`
// deprecation cycle, not an incidental edit. Peer to
// `flux_kind_git_repository_pins_canonical_value` on the
// sibling Flux v2 source-controller CRD-`kind` axis.
assert_eq!(FLUX_KIND_HELM_RELEASE, "HelmRelease");
}
#[test]
fn flux_kind_helm_release_carries_upper_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD `kind` discriminator is
// an UpperCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds —
// "Kinds are always UpperCamelCase"). Pinning the shape here
// means a future rebrand on the canonical lift can't silently
// land a malformed kind discriminator (snake_case, kebab-case,
// lowercase, empty) that every downstream YAML-aware
// deserializer would reject far from the rebrand commit's
// source. The first-byte uppercase / rest-ASCII-alphanumeric
// invariant is the load-bearing K8s API typed-discovery
// contract: a value the apiserver's `RESTMapper` consults to
// resolve the CRD's `RESTKind`. Peer to
// `flux_kind_git_repository_carries_upper_camel_case_shape`
// on the sibling Flux v2 source-controller CRD-`kind` axis.
let v = FLUX_KIND_HELM_RELEASE;
assert!(
!v.is_empty(),
"FLUX_KIND_HELM_RELEASE {v:?} must be non-empty per the K8s API \
UpperCamelCase kind discriminator grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_uppercase(),
"FLUX_KIND_HELM_RELEASE {v:?} first byte {first:?} must be \
ASCII-uppercase per the K8s API UpperCamelCase kind discriminator \
grammar (Kinds are always UpperCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"FLUX_KIND_HELM_RELEASE {v:?} must be ASCII-alphanumeric \
throughout per the K8s API kind discriminator grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
RESTMapper would reject"
);
}
#[test]
fn flux_kind_kustomization_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Flux v2 `Kustomization` CRD `kind` discriminator
// the rendered `kustomization.yaml`'s top-level `kind` axis
// declares. The string is part of the cluster-side contract
// with the Flux v2 `kustomize-controller` — the apiserver-side
// CRD resolution contract is the `(apiVersion, kind)` tuple
// keyed against the registered `CustomResourceDefinition`, so
// the kind half of the tuple is exactly as load-bearing as the
// sibling [`FLUX_KUSTOMIZATION_API_VERSION`] apiVersion half. A
// drifted value (e.g. an upstream Flux v3 rename to
// `KustomizationSet`) lands the rendered document outside the
// kustomize-controller's CRD registration; changing it is a
// coordinated Flux v3 migration alongside the upstream
// `kustomize-controller` deprecation cycle, not an incidental
// edit. Peer to
// `flux_kind_git_repository_pins_canonical_value` /
// `flux_kind_helm_release_pins_canonical_value` on the sibling
// Flux v2 controller-triplet `kind`-axis surface — completes
// the canonical-Flux-v2-CRD-kind-discriminator pin set across
// the source-controller + helm-controller + kustomize-controller
// triplet.
assert_eq!(FLUX_KIND_KUSTOMIZATION, "Kustomization");
}
#[test]
fn flux_key_source_ref_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Flux v2 per-`HelmRelease`/`Kustomization`
// source-reference container-axis key the rendered
// `helmrelease.yaml` (`spec.chart.spec.sourceRef`) +
// `kustomization.yaml` (`spec.sourceRef`) documents mount the
// per-CR `(kind, name, namespace)` reference triple under. The
// string is part of the cluster-side contract with every
// Flux-v2-conformant source-controller — the per-CR reconcile
// loop keys off this exact container axis to source the
// `(kind, name, namespace)` reference triple; a drifted value
// (`"source_ref"` / `"source"` / `"sourceReference"` /
// `"gitSourceRef"`) silently dangles both the HelmRelease's
// chart resolution + the parent Kustomization's source
// resolution at the Flux v2 source-controller's CRD
// registration. Changing this value is a coordinated Flux v3
// migration alongside the upstream `fluxcd/flux2` deprecation
// cycle, not an incidental edit. Peer to
// `flux_kind_git_repository_pins_canonical_value` /
// `flux_kind_helm_release_pins_canonical_value` /
// `flux_kind_kustomization_pins_canonical_value` on the sibling
// per-CRD `kind`-axis surface — extends the canonical-Flux-v2-
// load-bearing-string pin discipline from the per-CRD kind
// discriminators onto the sibling per-CR source-reference
// container-axis key both `cluster_bundle` renderers consume.
assert_eq!(FLUX_KEY_SOURCE_REF, "sourceRef");
}
#[test]
fn flux_key_source_ref_carries_lower_camel_case_shape() {
// Cross-axis invariant: the Flux v2 CRD field-naming convention
// (inherited from the upstream K8s API conventions) admits
// lowerCamelCase per-field keys — the source-reference
// container-axis conforms to this on the leading-lowercase
// `sourceRef` shape. Pinning the shape here means a future
// rebrand on the canonical lift can't silently land a malformed
// container-axis key (snake_case, kebab-case, UpperCamelCase,
// empty) that the Flux v2 source-controller's per-CR reconcile
// loop would reject at apply parse time far from the rebrand
// commit's source. Peer to the sibling K8s-CR-lowerCamelCase-
// per-field pin trajectory the sibling `KUBE_KEY_MATCH_LABELS`
// / `GATEWAY_API_KEY_BACKEND_REFS` / `CILIUM_KEY_FROM_ENDPOINTS`
// / `CILIUM_KEY_TO_PORTS` pins established on the sibling per-
// K8s-CR-schema-field-name axes.
let v = FLUX_KEY_SOURCE_REF;
assert!(
!v.is_empty(),
"FLUX_KEY_SOURCE_REF {v:?} must be non-empty per the Flux v2 \
CRD field-naming grammar"
);
let mut chars = v.chars();
assert!(
chars.next().is_some_and(|c| c.is_ascii_lowercase()),
"FLUX_KEY_SOURCE_REF {v:?} must lead with an ASCII-lowercase \
byte per the Flux v2 lowerCamelCase per-CR-field-key convention"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"FLUX_KEY_SOURCE_REF {v:?} must be ASCII-alphanumeric throughout \
per the Flux v2 lowerCamelCase per-CR-field-key convention — \
no `_` / `-` / `.` / whitespace bytes the Flux v2 source-\
controller's per-CR reconcile loop would reject"
);
}
#[test]
fn flux_key_values_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Flux v2 per-`HelmRelease` values-override block-
// body-axis key the rendered `helmrelease.yaml`'s `spec.values`
// block declares. The string is part of the cluster-side
// contract with the Flux v2 `helm-controller` — the per-CR
// reconcile loop merges the per-cluster override YAML nested
// under this exact block-body axis into the referenced chart's
// `values.yaml` at Helm-render time; a drifted value
// (`"Values"` / `"vals"` / `"chartValues"` / `"overrides"`)
// silently routes the per-cluster overrides nowhere at Helm
// render, and the workload comes up with the referenced
// chart's admission-time defaults. Changing this value is a
// coordinated Flux v3 migration alongside the upstream
// `fluxcd/flux2` deprecation cycle, not an incidental edit.
// Peer to `flux_key_source_ref_pins_canonical_value` on the
// sibling Flux v2 per-CR container-axis-key surface — extends
// the canonical-Flux-v2-load-bearing-string pin discipline from
// the per-CR source-reference container-axis onto the sibling
// per-`HelmRelease` values-override block-body-axis.
assert_eq!(FLUX_KEY_VALUES, "values");
}
#[test]
fn flux_key_values_carries_lower_camel_case_shape() {
// Cross-axis invariant: the Flux v2 CRD field-naming convention
// (inherited from the upstream K8s API conventions) admits
// lowerCamelCase per-field keys — the values-override block-
// body axis conforms to this on the leading-lowercase `values`
// shape (a single-word lowerCamelCase reduces to all-lowercase).
// Pinning the shape here means a future rebrand on the
// canonical lift can't silently land a malformed block-body-
// axis key (snake_case, kebab-case, UpperCamelCase, empty) that
// the Flux v2 helm-controller's per-CR reconcile loop would
// reject at apply parse time far from the rebrand commit's
// source. Peer to `flux_key_source_ref_carries_lower_camel_case_shape`
// on the sibling Flux v2 per-CR container-axis-key surface.
let v = FLUX_KEY_VALUES;
assert!(
!v.is_empty(),
"FLUX_KEY_VALUES {v:?} must be non-empty per the Flux v2 \
CRD field-naming grammar"
);
let mut chars = v.chars();
assert!(
chars.next().is_some_and(|c| c.is_ascii_lowercase()),
"FLUX_KEY_VALUES {v:?} must lead with an ASCII-lowercase \
byte per the Flux v2 lowerCamelCase per-CR-field-key convention"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"FLUX_KEY_VALUES {v:?} must be ASCII-alphanumeric throughout \
per the Flux v2 lowerCamelCase per-CR-field-key convention — \
no `_` / `-` / `.` / whitespace bytes the Flux v2 helm-\
controller's per-CR reconcile loop would reject"
);
}
#[test]
fn flux_key_chart_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Flux v2 per-`HelmRelease` inline-chart-template
// container-axis key the rendered `helmrelease.yaml`'s
// `spec.chart` block declares. The string is part of the
// cluster-side contract with the Flux v2 `helm-controller` —
// the per-CR reconcile loop reads the nested
// `HelmChartTemplate` sub-document (chart-name string,
// source-of-truth reference triple, and reconcile cadence)
// under this exact container axis to source the referenced
// chart at Helm-render time; a drifted value (`"Chart"` /
// `"chartTemplate"` / `"helmChart"` / `"chartRef"`) silently
// dangles the whole chart-template resolution at the helm-
// controller's CRD registration and the referenced chart
// never resolves. Changing this value is a coordinated Flux
// v3 migration alongside the upstream `fluxcd/flux2`
// deprecation cycle, not an incidental edit. Peer to
// `flux_key_source_ref_pins_canonical_value` /
// `flux_key_values_pins_canonical_value` on the sibling Flux
// v2 per-`HelmRelease` body-key surfaces — extends the
// canonical-Flux-v2-load-bearing-string pin discipline from
// the source-reference container-axis + values-override
// block-body-axis onto the sibling chart-template container-
// axis, completing the triplet of Flux v2 per-`HelmRelease`
// `spec.*` body-key pin tests.
assert_eq!(FLUX_KEY_CHART, "chart");
}
#[test]
fn flux_key_chart_carries_lower_camel_case_shape() {
// Cross-axis invariant: the Flux v2 CRD field-naming
// convention (inherited from the upstream K8s API
// conventions) admits lowerCamelCase per-field keys — the
// chart-template container-axis conforms to this on the
// leading-lowercase `chart` shape (a single-word
// lowerCamelCase reduces to all-lowercase). Pinning the shape
// here means a future rebrand on the canonical lift can't
// silently land a malformed container-axis key (snake_case,
// kebab-case, UpperCamelCase, empty) that the Flux v2 helm-
// controller's per-CR reconcile loop would reject at apply
// parse time far from the rebrand commit's source. Peer to
// `flux_key_source_ref_carries_lower_camel_case_shape` /
// `flux_key_values_carries_lower_camel_case_shape` on the
// sibling Flux v2 per-`HelmRelease` body-key surfaces.
let v = FLUX_KEY_CHART;
assert!(
!v.is_empty(),
"FLUX_KEY_CHART {v:?} must be non-empty per the Flux v2 \
CRD field-naming grammar"
);
let mut chars = v.chars();
assert!(
chars.next().is_some_and(|c| c.is_ascii_lowercase()),
"FLUX_KEY_CHART {v:?} must lead with an ASCII-lowercase \
byte per the Flux v2 lowerCamelCase per-CR-field-key convention"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"FLUX_KEY_CHART {v:?} must be ASCII-alphanumeric throughout \
per the Flux v2 lowerCamelCase per-CR-field-key convention — \
no `_` / `-` / `.` / whitespace bytes the Flux v2 helm-\
controller's per-CR reconcile loop would reject"
);
}
#[test]
fn flux_helmchart_template_key_chart_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Flux v2 `HelmChartTemplate.spec.chart` per-CR
// chart-NAME reference leaf-scalar-axis key every caixa-flux-
// emitted `HelmRelease` document nests inside the parent
// `spec.chart.spec` sub-document. The helm-controller's
// reconcile pipeline reads the chart-artifact name from this
// exact leaf on every reconcile — a drifted `spec.chart.spec.Chart`
// / `spec.chart.spec.chartRef` / `spec.chart.spec.chartName`
// at the emission-side leaf key would silently land as a well-
// formed but ignored `HelmChartTemplate.spec.*` extra property
// the apiserver's CRD OpenAPI schema permits (arbitrary spec
// extras) and the helm-controller would fail to resolve any
// chart-artifact through the sibling `sourceRef` triple's
// source at reconcile time — a non-self-locating "chart
// 'unknown' not found in <source>" error far from the rebrand
// commit's source `caixa.lisp` / the renderer's format-string
// template. Peer to `flux_key_chart_pins_canonical_value` on
// the sibling per-CR chart-template container-axis parent
// this leaf-scalar-axis lift extends by descending one level
// beneath, closing the substrate-side declaration the parent
// container-axis lift docstring explicitly named as future
// work.
assert_eq!(FLUX_HELMCHART_TEMPLATE_KEY_CHART, "chart");
}
#[test]
fn flux_helmchart_template_key_chart_carries_lower_camel_case_shape() {
// Cross-axis invariant: the Flux v2 CRD field-naming
// convention (inherited from the upstream K8s API conventions)
// admits lowerCamelCase per-field keys — the per-`HelmChartTemplate`
// chart-NAME reference leaf-scalar-axis conforms to this on the
// leading-lowercase `chart` shape (a single-word lowerCamelCase
// reduces to all-lowercase). Pinning the shape here means a
// future rebrand on the canonical lift can't silently land a
// malformed leaf-scalar-axis key (snake_case, kebab-case,
// UpperCamelCase, empty) that the Flux v2 helm-controller's
// per-CR reconcile loop would reject at apply parse time far
// from the rebrand commit's source. Peer to
// `flux_key_chart_carries_lower_camel_case_shape` on the
// sibling per-CR chart-template container-axis parent, and to
// the deliberate axis-independence discipline the sibling
// [`CILIUM_KEY_PATH`] / [`GATEWAY_API_KEY_PATH`] two-CRD-
// groups-sharing-a-string re-exports established (two consts
// spelling the same underlying string at distinct schema
// axes stay sibling constants at the rustc symbol-name axis).
let v = FLUX_HELMCHART_TEMPLATE_KEY_CHART;
assert!(
!v.is_empty(),
"FLUX_HELMCHART_TEMPLATE_KEY_CHART {v:?} must be non-empty per \
the Flux v2 CRD field-naming grammar"
);
let mut chars = v.chars();
assert!(
chars.next().is_some_and(|c| c.is_ascii_lowercase()),
"FLUX_HELMCHART_TEMPLATE_KEY_CHART {v:?} must lead with an \
ASCII-lowercase byte per the Flux v2 lowerCamelCase per-CR-\
field-key convention"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"FLUX_HELMCHART_TEMPLATE_KEY_CHART {v:?} must be ASCII-\
alphanumeric throughout per the Flux v2 lowerCamelCase per-CR-\
field-key convention — no `_` / `-` / `.` / whitespace bytes \
the Flux v2 helm-controller's per-CR reconcile loop would reject"
);
}
#[test]
fn flux_helmchart_template_key_chart_and_flux_key_chart_stay_independent_axes() {
// Cross-axis independence pin: both `FLUX_HELMCHART_TEMPLATE_KEY_CHART`
// (`spec.chart.spec.chart` chart-NAME reference leaf-scalar-axis)
// and the sibling `FLUX_KEY_CHART` (`spec.chart` per-CR chart-
// template container-axis parent) spell the same underlying
// `"chart"` string today but name distinct schema axes on the
// same Flux v2 `HelmRelease` CRD group (a container-axis parent
// vs a leaf-scalar grandchild inside it). Pin byte-equality of
// each half against its own canonical declaration so a future
// Flux v3 rebrand on either axis lands independently at the
// rustc symbol-name axis rather than coalescing onto one
// canonical declaration through a shared `&'static str`
// allocation Rust's string interner would otherwise fuse.
// Same axis-independence discipline the sibling
// [`CILIUM_KEY_PATH`] (ef6114f) / [`GATEWAY_API_KEY_PATH`]
// (9f45aa4) two-CRD-groups-sharing-a-string re-exports
// established on the peer canonical-axis-independence surface.
assert_eq!(FLUX_HELMCHART_TEMPLATE_KEY_CHART, "chart");
assert_eq!(FLUX_KEY_CHART, "chart");
assert_eq!(FLUX_HELMCHART_TEMPLATE_KEY_CHART, FLUX_KEY_CHART);
}
#[test]
fn flux_key_health_checks_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Flux v2 per-`Kustomization` health-gate reference-
// list container-axis key the rendered `kustomization.yaml`'s
// `spec.healthChecks` block declares. The string is part of the
// cluster-side contract with the Flux v2 `kustomize-controller`
// — the per-CR reconcile loop reads the nested
// `[]NamespacedObjectKindReference` list under this exact
// container axis to gate the parent `Kustomization`'s
// `Ready=True` transition on the referenced sibling
// `HelmRelease` reaching its `HelmReleaseReady=True` condition;
// a drifted value (`"HealthChecks"` / `"healthchecks"` /
// `"healthcheck"` / `"health_checks"` / `"probes"`) silently
// dangles the parent `Kustomization` at `Reconciling` forever
// at the kustomize-controller's health-gate evaluation, and the
// dependent per-cluster fleet-programs upsert chain never sees
// `Ready=True`. Changing this value is a coordinated Flux v3
// migration alongside the upstream `fluxcd/flux2` deprecation
// cycle, not an incidental edit. Peer to
// `flux_key_source_ref_pins_canonical_value` /
// `flux_key_chart_pins_canonical_value` /
// `flux_key_values_pins_canonical_value` on the sibling Flux v2
// body-key surfaces — extends the canonical-Flux-v2-load-bearing-
// string pin discipline from the per-`HelmRelease` triplet
// (`spec.chart` + `spec.chart.spec.sourceRef` + `spec.values`)
// onto the sibling per-`Kustomization` `spec.healthChecks`
// reference-list container-axis, completing the quartet of Flux
// v2 `spec.*` body-key pin tests.
assert_eq!(FLUX_KEY_HEALTH_CHECKS, "healthChecks");
}
#[test]
fn flux_key_health_checks_carries_lower_camel_case_shape() {
// Cross-axis invariant: the Flux v2 CRD field-naming convention
// (inherited from the upstream K8s API conventions) admits
// lowerCamelCase per-field keys — the per-`Kustomization`
// health-gate reference-list container-axis conforms to this on
// the leading-lowercase `healthChecks` shape. Pinning the shape
// here means a future rebrand on the canonical lift can't
// silently land a malformed container-axis key (snake_case,
// kebab-case, UpperCamelCase, empty) that the Flux v2 kustomize-
// controller's per-CR reconcile loop would reject at apply
// parse time far from the rebrand commit's source. Peer to
// `flux_key_source_ref_carries_lower_camel_case_shape` /
// `flux_key_chart_carries_lower_camel_case_shape` /
// `flux_key_values_carries_lower_camel_case_shape` on the
// sibling Flux v2 body-key surfaces.
let v = FLUX_KEY_HEALTH_CHECKS;
assert!(
!v.is_empty(),
"FLUX_KEY_HEALTH_CHECKS {v:?} must be non-empty per the Flux \
v2 CRD field-naming grammar"
);
let mut chars = v.chars();
assert!(
chars.next().is_some_and(|c| c.is_ascii_lowercase()),
"FLUX_KEY_HEALTH_CHECKS {v:?} must lead with an ASCII-\
lowercase byte per the Flux v2 lowerCamelCase per-CR-field-\
key convention"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"FLUX_KEY_HEALTH_CHECKS {v:?} must be ASCII-alphanumeric \
throughout per the Flux v2 lowerCamelCase per-CR-field-key \
convention — no `_` / `-` / `.` / whitespace bytes the Flux \
v2 kustomize-controller's per-CR reconcile loop would reject"
);
}
#[test]
fn flux_key_interval_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Flux v2 per-CR reconcile-poll cadence scalar-axis
// key the rendered Flux bundle's three `spec.interval` scalars
// declare — the shared axis-key the source-controller, helm-
// controller, and kustomize-controller each read to schedule
// their per-CR poll cycles off the sibling per-CR `apiVersion` +
// `kind` registration. A drifted value (`"Interval"` / `"period"`
// / `"cadence"` / `"pollInterval"` / `"reconcileInterval"`)
// silently drops the per-CR reconcile schedule from all three
// Flux controllers' per-CR watch registrations simultaneously —
// the referenced Git source never re-polls / the referenced
// chart never re-templates / the parent Kustomization never
// re-applies at upstream drift, freezing the whole cluster's
// per-`caixa` per-cluster bundle at the last-applied snapshot.
// Changing this value is a coordinated Flux v3 migration
// alongside the upstream `fluxcd/flux2` deprecation cycle, not
// an incidental edit. Peer to
// `flux_key_source_ref_pins_canonical_value` /
// `flux_key_chart_pins_canonical_value` /
// `flux_key_values_pins_canonical_value` /
// `flux_key_health_checks_pins_canonical_value` on the sibling
// Flux v2 per-CR body-key surfaces — extends the canonical-Flux-
// v2-load-bearing-string pin discipline from the per-CR body-key
// quartet onto the sibling cross-CR-shared reconcile-poll
// cadence scalar-axis every Flux v2 controller reads.
assert_eq!(FLUX_KEY_INTERVAL, "interval");
}
#[test]
fn flux_key_interval_carries_lower_camel_case_shape() {
// Cross-axis invariant: the Flux v2 CRD field-naming convention
// (inherited from the upstream K8s API conventions) admits
// lowerCamelCase per-field keys — the per-CR reconcile-poll
// cadence scalar-axis conforms to this on the leading-lowercase
// `interval` shape. Pinning the shape here means a future rebrand
// on the canonical lift can't silently land a malformed scalar-
// axis key (snake_case, kebab-case, UpperCamelCase, empty) that
// any of the three Flux v2 controllers' per-CR reconcile loops
// would reject at apply parse time far from the rebrand commit's
// source. Peer to `flux_key_source_ref_carries_lower_camel_case_shape`
// / `flux_key_chart_carries_lower_camel_case_shape` /
// `flux_key_values_carries_lower_camel_case_shape` /
// `flux_key_health_checks_carries_lower_camel_case_shape` on the
// sibling Flux v2 per-CR body-key surfaces.
let v = FLUX_KEY_INTERVAL;
assert!(
!v.is_empty(),
"FLUX_KEY_INTERVAL {v:?} must be non-empty per the Flux \
v2 CRD field-naming grammar"
);
let mut chars = v.chars();
assert!(
chars.next().is_some_and(|c| c.is_ascii_lowercase()),
"FLUX_KEY_INTERVAL {v:?} must lead with an ASCII-\
lowercase byte per the Flux v2 lowerCamelCase per-CR-field-\
key convention"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"FLUX_KEY_INTERVAL {v:?} must be ASCII-alphanumeric \
throughout per the Flux v2 lowerCamelCase per-CR-field-key \
convention — no `_` / `-` / `.` / whitespace bytes any of \
the three Flux v2 controllers' per-CR reconcile loops would \
reject"
);
}
#[test]
fn flux_gitrepository_ref_key_tag_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Flux v2 per-`GitRepository` `spec.ref.tag`
// git-tag-selector scalar-axis key the rendered
// `gitrepository.yaml` document declares on the tag-arm of the
// FluxCD source-controller `spec.ref` discriminated-union axis.
// A drifted value (`"Tag"` / `"gitTag"` / `"tagName"`) silently
// dangles the tag-arm sub-block at the FluxCD source-controller's
// CRD registration; the per-Servico clone never resolves at
// reconcile time. Peer to
// `flux_gitrepository_ref_key_branch_pins_canonical_value` /
// `flux_gitrepository_ref_key_commit_pins_canonical_value` on
// the sibling per-shape arms of the same discriminated-union
// axis — closes the three-arm sub-selector-key trio the
// FluxCD source-controller reads to bind the per-CR git-source
// clone refspec.
assert_eq!(FLUX_GITREPOSITORY_REF_KEY_TAG, "tag");
}
#[test]
fn flux_gitrepository_ref_key_branch_pins_canonical_value() {
// Peer of `flux_gitrepository_ref_key_tag_pins_canonical_value`
// on the branch-arm of the FluxCD source-controller
// `GitRepository.spec.ref` discriminated-union axis.
assert_eq!(FLUX_GITREPOSITORY_REF_KEY_BRANCH, "branch");
}
#[test]
fn flux_gitrepository_ref_key_commit_pins_canonical_value() {
// Peer of `flux_gitrepository_ref_key_tag_pins_canonical_value`
// on the commit-arm of the FluxCD source-controller
// `GitRepository.spec.ref` discriminated-union axis.
assert_eq!(FLUX_GITREPOSITORY_REF_KEY_COMMIT, "commit");
}
#[test]
fn flux_gitrepository_key_ref_pins_canonical_value() {
// Bridge-arm pin: [`FLUX_GITREPOSITORY_KEY_REF`] resolves to
// the canonical `"ref"` byte today — the exact YAML key the
// FluxCD `source-controller` reads on every rendered
// `GitRepository` document's `spec.ref` container-axis to
// source the per-CR git-clone refspec discriminated-union
// arm (`{tag, branch, commit}`). Pin the literal here (peer
// with the sibling
// [`flux_gitrepository_ref_key_tag_pins_canonical_value`] /
// [`flux_gitrepository_ref_key_branch_pins_canonical_value`] /
// [`flux_gitrepository_ref_key_commit_pins_canonical_value`]
// per-shape arm sub-selector pins on the same `spec.ref`
// sub-schema) so a future Flux v3 sub-schema rebrand on the
// parent container-axis surfaces here as a coordinated edit-
// point at the definition site rather than a silent apply-
// time split between the writer-side template composer and
// the aggregator's per-CR `RESTMapper` reader.
assert_eq!(FLUX_GITREPOSITORY_KEY_REF, "ref");
}
#[test]
fn flux_gitrepository_key_url_pins_canonical_value() {
// Bridge-arm pin: [`FLUX_GITREPOSITORY_KEY_URL`] resolves to
// the canonical `"url"` byte today — the exact YAML key the
// FluxCD `source-controller` reads on every rendered
// `GitRepository` document's `spec.url` leaf-scalar-axis to
// source the per-CR git-remote clone target. Pin the literal
// here (peer with the sibling
// [`flux_gitrepository_key_ref_pins_canonical_value`] on the
// per-CR `spec.ref` container-axis surface) so a future Flux
// v3 sub-schema rebrand on the URL axis (e.g. an upstream
// `fluxcd/flux2` rename of `spec.url` to `spec.gitUrl` /
// `spec.repository`) surfaces here as a coordinated edit-
// point at the definition site rather than a silent apply-
// time split between the writer-side template composer and
// the source-controller's per-CR `RESTMapper` reader.
assert_eq!(FLUX_GITREPOSITORY_KEY_URL, "url");
}
#[test]
fn flux_gitrepository_key_url_stays_independent_of_ref_and_api_version() {
// Cross-axis peer-independence pin: the per-`GitRepository`-CRD
// canonical-load-bearing-string surface carries three distinct
// axes on the same CRD — `apiVersion`
// ([`FLUX_GITREPOSITORY_API_VERSION`], the CRD-group/version
// half of the `(apiVersion, kind)` apiserver-side CRD-lookup
// tuple), `spec.ref`
// ([`FLUX_GITREPOSITORY_KEY_REF`], the per-CR ref-selection
// container-axis), and `spec.url`
// ([`FLUX_GITREPOSITORY_KEY_URL`], the per-CR remote-repo-URL
// leaf-scalar-axis). These three constants spell mutually
// distinct schema axes on the same Flux v2 `source-controller`
// CRD; pinning distinctness here means a future rebrand on
// any one axis (a Flux v3 CRD-version bump, a `spec.ref`
// container-axis rename, or a `spec.url` schema promotion)
// surfaces as an edit on the corresponding canonical const
// alone, without silently collapsing the three axes into one
// edit-point at the rustc symbol-name axis.
assert_ne!(FLUX_GITREPOSITORY_KEY_URL, FLUX_GITREPOSITORY_KEY_REF);
assert_ne!(FLUX_GITREPOSITORY_KEY_URL, FLUX_GITREPOSITORY_API_VERSION);
}
#[test]
fn flux_gitrepository_ref_keys_all_carry_lower_camel_case_shape() {
// Cross-axis invariant on all three arms of the FluxCD
// source-controller `GitRepository.spec.ref` discriminated-union
// axis: the Flux v2 CRD field-naming convention (inherited from
// the upstream K8s API conventions) admits lowerCamelCase
// per-field keys — `tag` / `branch` / `commit` all conform.
// Pinning the shape here means a future rebrand on any of the
// three canonical lifts can't silently land a malformed
// sub-selector key (snake_case, kebab-case, UpperCamelCase,
// empty) that the Flux v2 source-controller's per-CR reconcile
// loop would reject at apply parse time. Peer to
// `flux_key_interval_carries_lower_camel_case_shape` on the
// sibling per-CR reconcile-poll-cadence scalar-axis key surface.
for v in [
FLUX_GITREPOSITORY_REF_KEY_TAG,
FLUX_GITREPOSITORY_REF_KEY_BRANCH,
FLUX_GITREPOSITORY_REF_KEY_COMMIT,
] {
assert!(
!v.is_empty(),
"FLUX_GITREPOSITORY_REF_KEY_* {v:?} must be non-empty \
per the Flux v2 CRD field-naming grammar"
);
let mut chars = v.chars();
assert!(
chars.next().is_some_and(|c| c.is_ascii_lowercase()),
"FLUX_GITREPOSITORY_REF_KEY_* {v:?} must lead with an \
ASCII-lowercase byte per the Flux v2 lowerCamelCase \
per-CR-field-key convention"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"FLUX_GITREPOSITORY_REF_KEY_* {v:?} must be ASCII-\
alphanumeric throughout per the Flux v2 lowerCamelCase \
per-CR-field-key convention — no `_` / `-` / `.` / \
whitespace bytes the Flux v2 source-controller's per-CR \
reconcile loop would reject"
);
}
}
#[test]
fn flux_gitrepository_ref_keys_are_pairwise_distinct() {
// The three arms of the FluxCD source-controller
// `GitRepository.spec.ref` discriminated-union axis must remain
// pairwise distinct — a hypothetical drift that collapsed two
// sub-selector keys onto the same byte-string (e.g. an
// accidental copy-paste making TAG and BRANCH both spell
// `"tag"`) would silently reroute the per-shape emit at
// `caixa_flux::GitRefSpec::ref_field_name` dispatch time and
// dangle one arm's rendered `spec.ref` sub-block at cluster-
// apply time. Pin the pairwise-distinctness here so the drift
// fires at test time, not at cluster-apply time far from the
// drift site.
let keys = [
FLUX_GITREPOSITORY_REF_KEY_TAG,
FLUX_GITREPOSITORY_REF_KEY_BRANCH,
FLUX_GITREPOSITORY_REF_KEY_COMMIT,
];
for (i, a) in keys.iter().enumerate() {
for b in keys.iter().skip(i + 1) {
assert_ne!(
a, b,
"FLUX_GITREPOSITORY_REF_KEY_* arms must be pairwise \
distinct (got a duplicate: {a:?})"
);
}
}
}
#[test]
fn flux_kind_kustomization_carries_upper_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD `kind` discriminator is
// an UpperCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds —
// "Kinds are always UpperCamelCase"). Pinning the shape here
// means a future rebrand on the canonical lift can't silently
// land a malformed kind discriminator (snake_case, kebab-case,
// lowercase, empty) that every downstream YAML-aware
// deserializer would reject far from the rebrand commit's
// source. The first-byte uppercase / rest-ASCII-alphanumeric
// invariant is the load-bearing K8s API typed-discovery
// contract: a value the apiserver's `RESTMapper` consults to
// resolve the CRD's `RESTKind`. Peer to
// `flux_kind_git_repository_carries_upper_camel_case_shape` /
// `flux_kind_helm_release_carries_upper_camel_case_shape` on
// the sibling Flux v2 controller-triplet `kind`-axis surface.
let v = FLUX_KIND_KUSTOMIZATION;
assert!(
!v.is_empty(),
"FLUX_KIND_KUSTOMIZATION {v:?} must be non-empty per the K8s API \
UpperCamelCase kind discriminator grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_uppercase(),
"FLUX_KIND_KUSTOMIZATION {v:?} first byte {first:?} must be \
ASCII-uppercase per the K8s API UpperCamelCase kind discriminator \
grammar (Kinds are always UpperCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"FLUX_KIND_KUSTOMIZATION {v:?} must be ASCII-alphanumeric \
throughout per the K8s API kind discriminator grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
RESTMapper would reject"
);
}
#[test]
fn gateway_api_api_version_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the K8s SIG-Network Gateway API CRD group/version
// the rendered `Gateway` / `HTTPRoute` documents declare. The
// string is part of the cluster-side contract with the
// upstream Gateway-API-conformant gateway implementation
// (Cilium, Istio, Envoy Gateway, NGINX, et al.): the
// apiserver-side CRD-version registration watches the exact
// `gateway.networking.k8s.io/v1` group/version; a drifted
// value to a stale v1beta1 / v1alpha2 lands the rendered
// `Gateway` / `HTTPRoute` outside the registration and fails
// at apply time with "no kind 'Gateway' is registered for
// version 'gateway.networking.k8s.io/v1beta1'"; changing it
// is a coordinated Gateway API GA promotion alongside the
// upstream SIG-Network deprecation cycle, not an incidental
// edit. Peer to `flux_kustomization_api_version_pins_canonical_value`
// / `flux_helmrelease_api_version_pins_canonical_value` /
// `flux_gitrepository_api_version_pins_canonical_value` on
// the canonical-K8s-CRD-axis-pin axis for the sibling
// Flux v2 controller-triplet constants — extends the
// canonical-string-pin discipline from the cluster-side
// Flux v2 reconcile contract onto the cluster-side K8s
// Gateway API ingress contract.
assert_eq!(GATEWAY_API_API_VERSION, "gateway.networking.k8s.io/v1");
}
#[test]
fn gateway_api_api_version_carries_group_and_version_segments() {
// Cross-axis invariant: a Kubernetes CRD `apiVersion` is a
// `<group>/<version>` pair separated by exactly one `/` byte.
// The group segment is a DNS-style multi-segment hostname
// (`gateway.networking.k8s.io`) and the version segment is a
// Kubernetes API version label (`v1`, `v1beta1`, `v1alpha2` —
// peer with the K8s API versioning convention upstream
// documents). Pinning this here means a future rebrand on the
// canonical lift can't silently land a malformed apiVersion
// (no `/`, two `/`, empty group, empty version) that every
// downstream YAML-aware deserializer would reject far from the
// rebrand commit's source. The single-`/` invariant is the
// load-bearing K8s API typed-discovery contract: a value the
// apiserver's `RESTMapper` consults to resolve the CRD's
// `RESTKind`. Peer to
// `flux_kustomization_api_version_carries_group_and_version_segments`
// / `flux_helmrelease_api_version_carries_group_and_version_segments`
// / `flux_gitrepository_api_version_carries_group_and_version_segments`
// on the sibling Flux v2 controller-triplet CRD-axes.
let v = GATEWAY_API_API_VERSION;
let parts: Vec<&str> = v.split('/').collect();
assert_eq!(
parts.len(),
2,
"GATEWAY_API_API_VERSION {v:?} must split into exactly two \
`/`-delimited segments (group/version) per the K8s CRD apiVersion \
grammar — every downstream YAML-aware deserializer enforces this \
shape"
);
assert!(
!parts[0].is_empty(),
"GATEWAY_API_API_VERSION {v:?} group segment must be non-empty"
);
assert!(
!parts[1].is_empty(),
"GATEWAY_API_API_VERSION {v:?} version segment must be non-empty"
);
assert!(
parts[0].contains('.'),
"GATEWAY_API_API_VERSION {v:?} group segment {group:?} must be a \
DNS-style multi-segment hostname (the canonical CRD-group convention \
every K8s controller-runtime / kube-rs-aware client expects)",
group = parts[0]
);
}
#[test]
fn cilium_api_version_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Cilium CRD group/version the rendered
// `CiliumNetworkPolicy` document declares. The string is part
// of the cluster-side contract with the upstream Cilium
// operator: the Cilium-operator-side CRD-version registration
// watches the exact `cilium.io/v2` group/version; a drifted
// value to a stale `v2alpha1` lands the rendered
// `CiliumNetworkPolicy` outside the registration and fails at
// apply time with "no kind 'CiliumNetworkPolicy' is registered
// for version 'cilium.io/v2alpha1'"; changing it is a
// coordinated Cilium-CRD promotion alongside the upstream
// Cilium deprecation cycle, not an incidental edit. Peer to
// `gateway_api_api_version_pins_canonical_value` /
// `flux_kustomization_api_version_pins_canonical_value` /
// `flux_helmrelease_api_version_pins_canonical_value` /
// `flux_gitrepository_api_version_pins_canonical_value` on
// the canonical-K8s-CRD-axis-pin axis for the sibling
// K8s Gateway API + Flux v2 controller-triplet constants —
// extends the canonical-string-pin discipline from the
// cluster-side K8s Gateway API ingress + Flux v2 reconcile
// contracts onto the cluster-side Cilium identity-based mesh
// contract.
assert_eq!(CILIUM_API_VERSION, "cilium.io/v2");
}
#[test]
fn cilium_api_version_carries_group_and_version_segments() {
// Cross-axis invariant: a Kubernetes CRD `apiVersion` is a
// `<group>/<version>` pair separated by exactly one `/` byte.
// The group segment is a DNS-style hostname (`cilium.io`) and
// the version segment is a Kubernetes API version label (`v2`,
// `v2alpha1` — peer with the K8s API versioning convention
// upstream documents). Pinning this here means a future rebrand
// on the canonical lift can't silently land a malformed
// apiVersion (no `/`, two `/`, empty group, empty version) that
// every downstream YAML-aware deserializer would reject far
// from the rebrand commit's source. The single-`/` invariant
// is the load-bearing K8s API typed-discovery contract: a value
// the apiserver's `RESTMapper` consults to resolve the CRD's
// `RESTKind`. Peer to
// `gateway_api_api_version_carries_group_and_version_segments`
// / `flux_kustomization_api_version_carries_group_and_version_segments`
// / `flux_helmrelease_api_version_carries_group_and_version_segments`
// / `flux_gitrepository_api_version_carries_group_and_version_segments`
// on the sibling K8s Gateway API + Flux v2 controller-triplet
// CRD-axes.
let v = CILIUM_API_VERSION;
let parts: Vec<&str> = v.split('/').collect();
assert_eq!(
parts.len(),
2,
"CILIUM_API_VERSION {v:?} must split into exactly two \
`/`-delimited segments (group/version) per the K8s CRD apiVersion \
grammar — every downstream YAML-aware deserializer enforces this \
shape"
);
assert!(
!parts[0].is_empty(),
"CILIUM_API_VERSION {v:?} group segment must be non-empty"
);
assert!(
!parts[1].is_empty(),
"CILIUM_API_VERSION {v:?} version segment must be non-empty"
);
assert!(
parts[0].contains('.'),
"CILIUM_API_VERSION {v:?} group segment {group:?} must be a \
DNS-style hostname (the canonical CRD-group convention \
every K8s controller-runtime / kube-rs-aware client expects)",
group = parts[0]
);
}
#[test]
fn cilium_kind_network_policy_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Cilium-operator-side `CiliumNetworkPolicy` CRD
// `kind` discriminator the rendered CNP document's top-level
// `kind` axis declares. The string is part of the cluster-side
// contract with the upstream Cilium operator — the apiserver-side
// CRD resolution contract is the `(apiVersion, kind)` tuple
// keyed against the registered `CustomResourceDefinition`, so
// the kind half of the tuple is exactly as load-bearing as the
// sibling [`CILIUM_API_VERSION`] apiVersion half. A drifted
// value (e.g. an upstream rename to `CiliumNetworkPolicyV2`)
// lands the rendered document outside the Cilium operator's
// CRD registration; changing it is a coordinated Cilium-CRD
// promotion alongside the upstream Cilium deprecation cycle,
// not an incidental edit. Peer to
// `flux_kind_kustomization_pins_canonical_value` /
// `flux_kind_helm_release_pins_canonical_value` /
// `flux_kind_git_repository_pins_canonical_value` on the
// sibling cluster-side-CRD-`kind`-discriminator pin set —
// extends the canonical-string-pin discipline from the Flux v2
// controller-triplet `kind`-axis surface onto the Cilium-CRD
// `kind`-axis surface, completing the per-Cilium-CRD
// kind+apiVersion canonical-pin pair the M3 Aplicacao mesh
// renderer's eBPF data-plane contract rests on.
assert_eq!(CILIUM_KIND_NETWORK_POLICY, "CiliumNetworkPolicy");
}
#[test]
fn cilium_kind_network_policy_carries_upper_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD `kind` discriminator is
// an UpperCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds —
// "Kinds are always UpperCamelCase"). Pinning the shape here
// means a future rebrand on the canonical lift can't silently
// land a malformed kind discriminator (snake_case, kebab-case,
// lowercase, empty) that every downstream YAML-aware
// deserializer would reject far from the rebrand commit's
// source. The first-byte uppercase / rest-ASCII-alphanumeric
// invariant is the load-bearing K8s API typed-discovery
// contract: a value the apiserver's `RESTMapper` consults to
// resolve the CRD's `RESTKind`. Peer to
// `flux_kind_kustomization_carries_upper_camel_case_shape` /
// `flux_kind_helm_release_carries_upper_camel_case_shape` /
// `flux_kind_git_repository_carries_upper_camel_case_shape` on
// the sibling cluster-side-CRD-`kind`-discriminator surface.
let v = CILIUM_KIND_NETWORK_POLICY;
assert!(
!v.is_empty(),
"CILIUM_KIND_NETWORK_POLICY {v:?} must be non-empty per the K8s API \
UpperCamelCase kind discriminator grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_uppercase(),
"CILIUM_KIND_NETWORK_POLICY {v:?} first byte {first:?} must be \
ASCII-uppercase per the K8s API UpperCamelCase kind discriminator \
grammar (Kinds are always UpperCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"CILIUM_KIND_NETWORK_POLICY {v:?} must be ASCII-alphanumeric \
throughout per the K8s API kind discriminator grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
RESTMapper would reject"
);
}
#[test]
fn cilium_key_to_ports_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Cilium CNP `spec.ingress[].toPorts[]` per-ingress-
// rule port-set-container-axis key the rendered CNP document
// mounts its per-port-set `{ports: […], rules: {…}}` list under.
// The string is part of the cluster-side contract with the
// upstream Cilium operator — the Cilium-operator-side per-CNP
// L4/L7-dispatch pass keys off this axis to route the per-port
// set through the eBPF data-plane's L4-allow (via `ports`) /
// L7-dispatch (via nested `rules`) branches; a drifted value
// (`"toport"` / `"toPort"` / `"targetPorts"`) at either the
// production emitter or a downstream renderer's per-ingress-rule
// port-set upsert silently emits a per-ingress-rule entry whose
// port-set container the Cilium CRD schema validator drops as
// unknown, and every intra-mesh `:contratos` flow the affected
// CNP was authored to allow drops at the eBPF data-plane's
// default-deny gate. Changing this value is a coordinated
// Cilium-CRD promotion alongside the upstream Cilium project's
// CRD schema-migration cycle, not an incidental edit. Peer to
// `kube_key_rules_pins_canonical_value` (the nested
// `spec.ingress[].toPorts[].rules` axis-key pin the L7-dispatch
// container nests inside this port-set container's each entry)
// on the sibling per-CNP-dispatch-axis pin set — completes the
// per-CNP L4/L7-dispatch-container `(toPorts, rules)` pin pair
// the M3 Aplicacao mesh renderer's eBPF data-plane contract
// rests on.
assert_eq!(CILIUM_KEY_TO_PORTS, "toPorts");
}
#[test]
fn cilium_key_to_ports_carries_lower_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD schema field name is a
// lowerCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
// "Field names should be lowercase camelCase") — first byte
// ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
// kebab-case or whitespace. Pinning the shape here means a
// future rebrand on the canonical lift can't silently land a
// malformed field-name discriminator (snake_case, kebab-case,
// UpperCamelCase, empty) that the apiserver-side CRD schema
// validator would reject far from the rebrand commit's source.
// The first-byte lowercase / rest-ASCII-alphanumeric invariant
// is the load-bearing K8s API typed-schema contract: a value
// the apiserver-side OpenAPI schema validator consults to
// resolve each CR-field's typed slot. Peer to the sibling
// per-CNP `kind`-axis
// `cilium_kind_network_policy_carries_upper_camel_case_shape`
// pin — the UpperCamelCase K8s discriminator grammar governs
// the top-level `kind` axis, the lowerCamelCase K8s field-name
// grammar governs every nested schema-field axis (including
// this per-ingress-rule port-set-container-axis key), same
// convention distinct grammars.
let v = CILIUM_KEY_TO_PORTS;
assert!(
!v.is_empty(),
"CILIUM_KEY_TO_PORTS {v:?} must be non-empty per the K8s API \
lowerCamelCase field-name grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_lowercase(),
"CILIUM_KEY_TO_PORTS {v:?} first byte {first:?} must be \
ASCII-lowercase per the K8s API lowerCamelCase field-name \
grammar (field names are always lowerCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"CILIUM_KEY_TO_PORTS {v:?} must be ASCII-alphanumeric \
throughout per the K8s API field-name grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
OpenAPI schema validator would reject"
);
}
#[test]
fn cilium_key_endpoint_selector_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Cilium CNP `spec.endpointSelector` destination-
// identity-axis key the rendered CNP document mounts its
// L3-target `LabelSelector` under. The string is part of the
// cluster-side contract with the upstream Cilium operator —
// the Cilium-operator-side per-CNP identity-resolution pass
// keys off this axis to bind the emitted policy against its
// destination workload identity via the K8s LabelSelector
// schema; a drifted value (`"endpointselector"` /
// `"endpointSelectors"` / `"endpoints"`) at either the
// production emitter or a downstream renderer's per-CNP
// destination-identity upsert silently emits a CNP whose
// destination-identity axis the Cilium CRD schema validator
// drops as unknown, and the policy binds against no
// destination pods — every intra-mesh `:contratos` flow the
// affected CNP was authored to allow drops at the eBPF
// data-plane's default-deny gate. Changing this value is a
// coordinated Cilium-CRD promotion alongside the upstream
// Cilium project's CRD schema-migration cycle, not an
// incidental edit. Peer to `cilium_key_to_ports_pins_\
// canonical_value` (the per-ingress-rule port-set container
// axis-key pin the L3-target selector pairs with under the
// shared per-CNP-body schema) on the sibling per-CNP-body-axis
// pin set — completes the per-CNP L3/L4/L7-triad
// `(endpointSelector, ingress → toPorts → rules)` pin set the
// M3 Aplicacao mesh renderer's eBPF data-plane contract rests
// on.
assert_eq!(CILIUM_KEY_ENDPOINT_SELECTOR, "endpointSelector");
}
#[test]
fn cilium_key_endpoint_selector_carries_lower_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD schema field name is a
// lowerCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
// "Field names should be lowercase camelCase") — first byte
// ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
// kebab-case or whitespace. Pinning the shape here means a
// future rebrand on the canonical lift can't silently land a
// malformed field-name discriminator (snake_case, kebab-case,
// UpperCamelCase, empty) that the apiserver-side CRD schema
// validator would reject far from the rebrand commit's source.
// Peer to `cilium_key_to_ports_carries_lower_camel_case_shape`
// on the sibling per-CNP-body-axis grammar-pin set — the
// lowerCamelCase K8s field-name grammar governs every nested
// schema-field axis (including this per-CNP destination-
// identity-axis key), same convention.
let v = CILIUM_KEY_ENDPOINT_SELECTOR;
assert!(
!v.is_empty(),
"CILIUM_KEY_ENDPOINT_SELECTOR {v:?} must be non-empty per the K8s API \
lowerCamelCase field-name grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_lowercase(),
"CILIUM_KEY_ENDPOINT_SELECTOR {v:?} first byte {first:?} must be \
ASCII-lowercase per the K8s API lowerCamelCase field-name \
grammar (field names are always lowerCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"CILIUM_KEY_ENDPOINT_SELECTOR {v:?} must be ASCII-alphanumeric \
throughout per the K8s API field-name grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
OpenAPI schema validator would reject"
);
}
#[test]
fn cilium_key_ingress_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Cilium CNP `spec.ingress[]` traffic-direction
// container-axis key the rendered CNP document mounts its
// permitted per-`(:de, :para)` inbound-ingress-rule list under.
// The string is part of the cluster-side contract with the
// upstream Cilium operator — the Cilium-operator-side per-CNP
// L4/L7-dispatch pass keys off this axis to route the per-CNP
// ingress-rule list through the eBPF data-plane's inbound-
// traffic dispatch branch; a drifted value (`"Ingress"` /
// `"ingressRules"` / `"inbound"`) at either the production
// emitter or a downstream renderer's per-CNP traffic-direction
// upsert silently emits a CNP whose ingress-rule list the
// Cilium CRD schema validator drops as unknown, and every
// intra-mesh `:contratos` flow the affected CNP was authored to
// allow drops at the eBPF data-plane's default-deny gate.
// Changing this value is a coordinated Cilium-CRD promotion
// alongside the upstream Cilium project's CRD schema-migration
// cycle, not an incidental edit. Peer to
// `cilium_key_endpoint_selector_pins_canonical_value` (the
// destination-identity axis-key pin the traffic-direction
// container axis-key sits alongside under the shared per-CNP-
// body schema) + `cilium_key_to_ports_pins_canonical_value`
// (the per-ingress-rule port-set container axis-key pin the
// traffic-direction axis nests) on the sibling per-CNP-body-
// axis pin set — completes the per-CNP L3/L4/L7-triad
// `(endpointSelector, ingress → toPorts → rules)` pin set the
// M3 Aplicacao mesh renderer's eBPF data-plane contract rests
// on.
assert_eq!(CILIUM_KEY_INGRESS, "ingress");
}
#[test]
fn cilium_key_ingress_carries_lower_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD schema field name is a
// lowerCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
// "Field names should be lowercase camelCase") — first byte
// ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
// kebab-case or whitespace. Pinning the shape here means a
// future rebrand on the canonical lift can't silently land a
// malformed field-name discriminator (snake_case, kebab-case,
// UpperCamelCase, empty) that the apiserver-side CRD schema
// validator would reject far from the rebrand commit's source.
// Peer to `cilium_key_endpoint_selector_carries_lower_camel_\
// case_shape` / `cilium_key_to_ports_carries_lower_camel_case_\
// shape` on the sibling per-CNP-body-axis grammar-pin set — the
// lowerCamelCase K8s field-name grammar governs every nested
// schema-field axis (including this per-CNP traffic-direction-
// axis key), same convention.
let v = CILIUM_KEY_INGRESS;
assert!(
!v.is_empty(),
"CILIUM_KEY_INGRESS {v:?} must be non-empty per the K8s API \
lowerCamelCase field-name grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_lowercase(),
"CILIUM_KEY_INGRESS {v:?} first byte {first:?} must be \
ASCII-lowercase per the K8s API lowerCamelCase field-name \
grammar (field names are always lowerCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"CILIUM_KEY_INGRESS {v:?} must be ASCII-alphanumeric \
throughout per the K8s API field-name grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
OpenAPI schema validator would reject"
);
}
#[test]
fn cilium_key_from_endpoints_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Cilium CNP `spec.ingress[].fromEndpoints[]`
// identity-source selector-list-axis key the rendered CNP
// document mounts its permitted-source `LabelSelector` list
// under. The string is part of the cluster-side contract with
// the upstream Cilium operator — the Cilium-operator-side per-
// CNP identity-resolution pass keys off this axis to bind the
// emitted ingress rule against the admitted source workload
// identities via the K8s LabelSelector schema; a drifted value
// (`"fromendpoints"` / `"fromEndPoint"` / `"sourceEndpoints"`)
// at either the production emitter or a downstream renderer's
// per-ingress-rule identity-source upsert silently emits a CNP
// whose per-ingress-rule identity-source axis the Cilium CRD
// schema validator drops as unknown, and the ingress rule
// admits no source pods — every intra-mesh `:contratos` flow
// the affected CNP was authored to allow drops at the eBPF
// data-plane's default-deny gate. Changing this value is a
// coordinated Cilium-CRD promotion alongside the upstream
// Cilium project's CRD schema-migration cycle, not an
// incidental edit. Peer to
// `cilium_key_endpoint_selector_pins_canonical_value` (the
// destination-identity axis-key pin the identity-source axis
// structurally pairs with under the SPIFFE-identity-bound per-
// CNP access-control contract) on the sibling per-CNP identity-
// pair pin set — completes the per-CNP identity-pair
// `(endpointSelector, fromEndpoints)` pin set the M3 Aplicacao
// mesh renderer's eBPF data-plane contract rests on.
assert_eq!(CILIUM_KEY_FROM_ENDPOINTS, "fromEndpoints");
}
#[test]
fn cilium_key_from_endpoints_carries_lower_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD schema field name is a
// lowerCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
// "Field names should be lowercase camelCase") — first byte
// ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
// kebab-case or whitespace. Pinning the shape here means a
// future rebrand on the canonical lift can't silently land a
// malformed field-name discriminator (snake_case, kebab-case,
// UpperCamelCase, empty) that the apiserver-side CRD schema
// validator would reject far from the rebrand commit's source.
// Peer to `cilium_key_endpoint_selector_carries_lower_camel_\
// case_shape` / `cilium_key_ingress_carries_lower_camel_case_\
// shape` / `cilium_key_to_ports_carries_lower_camel_case_shape`
// on the sibling per-CNP-body-axis grammar-pin set — the
// lowerCamelCase K8s field-name grammar governs every nested
// schema-field axis (including this per-ingress-rule identity-
// source-axis key), same convention.
let v = CILIUM_KEY_FROM_ENDPOINTS;
assert!(
!v.is_empty(),
"CILIUM_KEY_FROM_ENDPOINTS {v:?} must be non-empty per the K8s API \
lowerCamelCase field-name grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_lowercase(),
"CILIUM_KEY_FROM_ENDPOINTS {v:?} first byte {first:?} must be \
ASCII-lowercase per the K8s API lowerCamelCase field-name \
grammar (field names are always lowerCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"CILIUM_KEY_FROM_ENDPOINTS {v:?} must be ASCII-alphanumeric \
throughout per the K8s API field-name grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
OpenAPI schema validator would reject"
);
}
#[test]
fn cilium_key_ports_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Cilium CNP `spec.ingress[].toPorts[].ports[]`
// per-`toPorts[]`-entry L4-port-tuple-list-container-axis key
// the rendered CNP document mounts its per-port-set
// `[{port, protocol}]` list under. The string is part of the
// cluster-side contract with the upstream Cilium operator —
// the Cilium-operator-side per-CNP L4-allow eBPF-program-
// generation pass keys off this axis to source the per-port-set
// `(port, protocol)` tuples the emitted ingress rule admits; a
// drifted value (`"port"` / `"portList"` / `"L4Ports"`) at
// either the production emitter or a downstream renderer's
// per-`toPorts[]`-entry L4-port-tuple-list upsert silently
// emits a per-`toPorts[]` entry whose L4-port-tuple-list-
// container axis the Cilium CRD schema validator drops as
// unknown, and the port-set admits no `(port, protocol)`
// tuple — every intra-mesh `:contratos` flow the affected CNP
// was authored to allow drops at the eBPF data-plane's
// default-deny gate. Changing this value is a coordinated
// Cilium-CRD promotion alongside the upstream Cilium project's
// CRD schema-migration cycle, not an incidental edit. Peer to
// `cilium_key_to_ports_pins_canonical_value` (the outer per-
// ingress-rule port-set-container axis-key pin the L4 port-
// tuple-list-container axis nests inside) on the sibling per-
// CNP-dispatch-axis pin set — completes the per-CNP L4-half
// `(toPorts, ports)` container-pair pin the M3 Aplicacao mesh
// renderer's eBPF data-plane L4-allow contract rests on.
assert_eq!(CILIUM_KEY_PORTS, "ports");
}
#[test]
fn cilium_key_ports_carries_lower_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD schema field name is a
// lowerCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
// "Field names should be lowercase camelCase") — first byte
// ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
// kebab-case or whitespace. Pinning the shape here means a
// future rebrand on the canonical lift can't silently land a
// malformed field-name discriminator (snake_case, kebab-case,
// UpperCamelCase, empty) that the apiserver-side CRD schema
// validator would reject far from the rebrand commit's source.
// Peer to `cilium_key_endpoint_selector_carries_lower_camel_\
// case_shape` / `cilium_key_ingress_carries_lower_camel_case_\
// shape` / `cilium_key_to_ports_carries_lower_camel_case_shape`
// / `cilium_key_from_endpoints_carries_lower_camel_case_shape`
// on the sibling per-CNP-body-axis grammar-pin set — the
// lowerCamelCase K8s field-name grammar governs every nested
// schema-field axis (including this per-`toPorts[]`-entry L4-
// port-tuple-list-container-axis key), same convention.
let v = CILIUM_KEY_PORTS;
assert!(
!v.is_empty(),
"CILIUM_KEY_PORTS {v:?} must be non-empty per the K8s API \
lowerCamelCase field-name grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_lowercase(),
"CILIUM_KEY_PORTS {v:?} first byte {first:?} must be \
ASCII-lowercase per the K8s API lowerCamelCase field-name \
grammar (field names are always lowerCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"CILIUM_KEY_PORTS {v:?} must be ASCII-alphanumeric \
throughout per the K8s API field-name grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
OpenAPI schema validator would reject"
);
}
#[test]
fn cilium_key_authentication_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Cilium CNP `spec.ingress[].authentication`
// per-ingress-rule mutual-auth-policy body-axis key the
// rendered CNP document mounts its per-rule mTLS enforcement
// block under. The string is part of the cluster-side
// contract with the upstream Cilium operator — the Cilium-
// operator-side per-CNP mutual-auth SPIFFE-handshake pipeline
// keys off this axis to source the per-rule mTLS enforcement
// mode (`required` vs `disabled`); a drifted value (`"auth"`
// / `"mutualAuth"` / `"mtls"` / `"authPolicy"`) at either
// the production emitter or a downstream renderer's per-
// ingress-rule mutual-auth upsert silently emits a per-
// `ingress[]` entry whose mutual-auth-axis the Cilium CRD
// schema validator drops as unknown, and the ingress rule
// falls back to the cluster-default authentication mode
// (typically `"disabled"` — no mutual-auth enforcement)
// silently bypassing the SPIFFE-identity-bound mTLS handshake
// every intra-mesh `:contratos` flow the CNP was authored to
// protect. Changing this value is a coordinated Cilium-CRD
// promotion alongside the upstream Cilium project's CRD
// schema-migration cycle, not an incidental edit. Peer to
// `cilium_key_from_endpoints_pins_canonical_value` /
// `cilium_key_to_ports_pins_canonical_value` (the sibling
// per-ingress-rule-body-axis pins the mutual-auth axis pairs
// with at the per-rule triple
// `(fromEndpoints, toPorts, authentication)`) on the sibling
// per-CNP-dispatch-axis pin set — completes the per-CNP per-
// ingress-rule-body triple the M3 Aplicacao mesh renderer's
// SPIFFE-identity-bound per-edge mTLS contract rests on.
assert_eq!(CILIUM_KEY_AUTHENTICATION, "authentication");
}
#[test]
fn cilium_key_authentication_carries_lower_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD schema field name is a
// lowerCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
// "Field names should be lowercase camelCase") — first byte
// ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
// kebab-case or whitespace. Pinning the shape here means a
// future rebrand on the canonical lift can't silently land a
// malformed field-name discriminator (snake_case, kebab-case,
// UpperCamelCase, empty) that the apiserver-side CRD schema
// validator would reject far from the rebrand commit's source.
// Peer to `cilium_key_endpoint_selector_carries_lower_camel_\
// case_shape` / `cilium_key_ingress_carries_lower_camel_case_\
// shape` / `cilium_key_to_ports_carries_lower_camel_case_shape`
// / `cilium_key_from_endpoints_carries_lower_camel_case_shape`
// / `cilium_key_ports_carries_lower_camel_case_shape` on the
// sibling per-CNP-body-axis grammar-pin set — the
// lowerCamelCase K8s field-name grammar governs every nested
// schema-field axis (including this per-`ingress[]`-entry
// mutual-auth-policy body-axis key), same convention.
let v = CILIUM_KEY_AUTHENTICATION;
assert!(
!v.is_empty(),
"CILIUM_KEY_AUTHENTICATION {v:?} must be non-empty per the K8s API \
lowerCamelCase field-name grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_lowercase(),
"CILIUM_KEY_AUTHENTICATION {v:?} first byte {first:?} must be \
ASCII-lowercase per the K8s API lowerCamelCase field-name \
grammar (field names are always lowerCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"CILIUM_KEY_AUTHENTICATION {v:?} must be ASCII-alphanumeric \
throughout per the K8s API field-name grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
OpenAPI schema validator would reject"
);
}
#[test]
fn cilium_key_mode_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Cilium CNP `spec.ingress[].authentication.mode`
// per-ingress-rule mutual-auth-mode-discriminator leaf-scalar-
// axis key the rendered CNP document mounts its per-rule mTLS
// enforcement mode value under. The string is part of the
// cluster-side contract with the upstream Cilium operator —
// the Cilium-operator-side per-CNP mutual-auth SPIFFE-handshake
// pipeline reads this leaf axis to source the per-rule mTLS
// enforcement mode value (`"required"` vs `"disabled"`); a
// drifted key (`"policy"` / `"authMode"` / `"handshakeMode"`)
// at either the production emitter or a downstream renderer's
// per-ingress-rule mutual-auth-mode-leaf upsert silently emits
// a per-`ingress[]` entry whose mutual-auth block's mode-
// discriminator leaf-axis the Cilium CRD schema validator
// drops as unknown, and the ingress rule falls back to the
// cluster-default authentication mode (typically `"disabled"`
// — no mutual-auth enforcement) silently bypassing the SPIFFE-
// identity-bound mTLS handshake every intra-mesh `:contratos`
// flow the CNP was authored to protect. Changing this value is
// a coordinated Cilium-CRD promotion alongside the upstream
// Cilium project's CRD schema-migration cycle, not an
// incidental edit. Peer to
// `cilium_key_authentication_pins_canonical_value` on the
// sibling per-ingress-rule mutual-auth body-axis pin set —
// completes the per-rule mutual-auth
// `(authentication → mode)` body/leaf axis pin pair the M3
// Aplicacao mesh renderer's SPIFFE-identity-bound per-edge
// mTLS enforcement contract rests on. Byte-identical to the
// sibling `:politicas :circuit-breaker (:window)` /
// `:placement :estrategia` overlay mode-like axes today, but
// semantically distinct: this const names the Cilium CRD's
// per-authentication-block mode-discriminator leaf-axis key
// (spelled per the Cilium project's CRD schema), so a future
// rebrand on the Cilium CRD's per-authentication-block mode-
// leaf axis lands at its own canonical const without coupling
// the Cilium schema to any peer surface that happens to carry
// the same byte.
assert_eq!(CILIUM_KEY_MODE, "mode");
}
#[test]
fn cilium_key_mode_carries_lower_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD schema field name is a
// lowerCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
// "Field names should be lowercase camelCase") — first byte
// ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
// kebab-case or whitespace. Pinning the shape here means a
// future rebrand on the canonical lift can't silently land a
// malformed field-name discriminator (snake_case, kebab-case,
// UpperCamelCase, empty) that the apiserver-side CRD schema
// validator would reject far from the rebrand commit's source.
// Peer to `cilium_key_authentication_carries_lower_camel_case_\
// shape` on the sibling per-ingress-rule mutual-auth-body-axis
// grammar-pin — the lowerCamelCase K8s field-name grammar
// governs every nested schema-field axis (including this
// per-authentication-block mode-discriminator leaf-axis key),
// same convention.
let v = CILIUM_KEY_MODE;
assert!(
!v.is_empty(),
"CILIUM_KEY_MODE {v:?} must be non-empty per the K8s API \
lowerCamelCase field-name grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_lowercase(),
"CILIUM_KEY_MODE {v:?} first byte {first:?} must be \
ASCII-lowercase per the K8s API lowerCamelCase field-name \
grammar (field names are always lowerCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"CILIUM_KEY_MODE {v:?} must be ASCII-alphanumeric \
throughout per the K8s API field-name grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
OpenAPI schema validator would reject"
);
}
#[test]
fn cilium_key_http_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Cilium CNP `spec.ingress[].toPorts[].rules.http`
// per-`toPorts[]` L7-HTTP-rule-list-discriminator container-axis
// key the rendered CNP document mounts its per-`toPorts[]` L7
// URL-path-prefix predicate list under. The string is part of the
// cluster-side contract with the upstream Cilium operator — the
// Cilium-operator-side per-CNP L7 dispatch pipeline reads this
// container axis to source the per-`toPorts[]` L7 URL-path-prefix
// predicate list the ingress rule was authored to filter each
// HTTP-shaped `:contratos` flow through; a drifted key (`"HTTP"` /
// `"Http"` / `"httpRules"` / `"httpMatch"`) at either the
// production emitter or a downstream renderer's per-`toPorts[]`
// L7-rule-list-discriminator upsert silently emits a per-
// `toPorts[]` entry whose L7-HTTP-rule-list-discriminator key the
// Cilium CRD schema validator drops as unknown, and the per-
// `toPorts[]` entry falls back to L4-only enforcement — no L7
// URL-path predicate is applied — silently admitting every HTTP-
// method / URL-path combination the ingress rule was authored to
// filter to the exact path prefix set the typed `:contratos`
// graph names at the L7 introspection axis. Changing this value
// is a coordinated Cilium-CRD promotion alongside the upstream
// Cilium project's CRD schema-migration cycle, not an incidental
// edit. Peer to `cilium_key_mode_pins_canonical_value` /
// `cilium_key_authentication_pins_canonical_value` on the
// sibling per-ingress-rule mutual-auth body/leaf axis pin pair —
// completes the per-`toPorts[]` L7-introspection
// `(rules → http)` container/protocol-discriminator axis pin
// pair the M3 Aplicacao mesh renderer's HTTP-shaped-`:contratos`
// URL-path-prefix-filtering L7-enforcement contract rests on.
// Byte-identical to the sibling `Gateway.spec.listeners[].name`
// arbitrary-author-chosen listener-name today (`"http"` — the
// author-chosen name for the substrate's V0 HTTP listener), but
// semantically distinct: this const names the Cilium CRD's per-
// `toPorts[]` L7-HTTP-rule-list-discriminator container-axis key
// (spelled per the Cilium project's CRD schema), so a future
// rebrand on the Cilium CRD's L7-HTTP-rule-list-discriminator
// axis lands at its own canonical const without coupling the
// Cilium schema to any peer surface that happens to carry the
// same byte.
assert_eq!(CILIUM_KEY_HTTP, "http");
}
#[test]
fn cilium_key_http_carries_lower_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD schema field name is a
// lowerCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
// "Field names should be lowercase camelCase") — first byte
// ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
// kebab-case or whitespace. Pinning the shape here means a
// future rebrand on the canonical lift can't silently land a
// malformed field-name discriminator (snake_case, kebab-case,
// UpperCamelCase, empty) that the apiserver-side CRD schema
// validator would reject far from the rebrand commit's source.
// Peer to `cilium_key_mode_carries_lower_camel_case_shape` /
// `cilium_key_authentication_carries_lower_camel_case_shape` on
// the sibling per-ingress-rule mutual-auth-body/leaf-axis
// grammar-pin set — the lowerCamelCase K8s field-name grammar
// governs every nested schema-field axis (including this per-
// `toPorts[]` L7-HTTP-rule-list-discriminator container-axis
// key), same convention.
let v = CILIUM_KEY_HTTP;
assert!(
!v.is_empty(),
"CILIUM_KEY_HTTP {v:?} must be non-empty per the K8s API \
lowerCamelCase field-name grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_lowercase(),
"CILIUM_KEY_HTTP {v:?} first byte {first:?} must be \
ASCII-lowercase per the K8s API lowerCamelCase field-name \
grammar (field names are always lowerCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"CILIUM_KEY_HTTP {v:?} must be ASCII-alphanumeric \
throughout per the K8s API field-name grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
OpenAPI schema validator would reject"
);
}
#[test]
fn kube_key_type_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the K8s discriminated-union `type` scalar-discriminator
// container-axis key every rendered CR mounts its per-position
// discriminated-union type-value under. The string is part of the
// cluster-side contract with every K8s apiserver-side OpenAPI
// schema validator — the Gateway API v1 gateway-class-controller's
// per-`HTTPRouteMatch` path-selection-predicate dispatch pass
// reads this scalar-key to source the path-match-strategy
// discriminator (the closed `PathMatchType` OpenAPI schema enum's
// `{Exact, PathPrefix, RegularExpression}` set) the per-rule L7
// URL-path-filtering was authored to bind — a drifted key
// (`"Type"` / `"kind"` / `"discriminator"` / `"predicate"`) at
// either the production emitter or a downstream renderer's per-
// `HTTPRouteMatch` path-selection-predicate discriminator upsert
// silently emits a per-match entry whose discriminator scalar-key
// the Gateway API v1 `HTTPPathMatch` OpenAPI schema validator
// drops as unknown, and the per-match entry falls back to the
// schema-side default path-match-strategy — silently admitting
// every URL-path prefix the ingress rule was authored to filter
// to the exact predicate the typed `:entrada :paths` slot names
// at the request-path-selection axis. Changing this value is a
// coordinated K8s-API-conventions promotion alongside the
// upstream sig-architecture per-version deprecation cycle, not
// an incidental edit. Peer to
// `cilium_key_http_pins_canonical_value` /
// `cilium_key_mode_pins_canonical_value` /
// `cilium_key_authentication_pins_canonical_value` on the
// sibling per-CRD-body-axis pin set — extends the canonical-
// string-pin discipline from the per-CRD-body-axis surfaces
// onto the load-bearing nested K8s-discriminated-union-type-
// scalar-discriminator axis every downstream apiserver-side
// OpenAPI-schema-validator / gateway-class-controller consumer
// of the rendered mesh bundle keys off before it can commit to
// a per-match request-path-selection predicate.
assert_eq!(KUBE_KEY_TYPE, "type");
}
#[test]
fn kube_key_type_carries_lower_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD schema field name is a
// lowerCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
// "Field names should be lowercase camelCase") — first byte
// ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
// kebab-case or whitespace. Pinning the shape here means a
// future rebrand on the canonical lift can't silently land a
// malformed field-name discriminator (snake_case, kebab-case,
// UpperCamelCase, empty) that the apiserver-side CRD schema
// validator would reject far from the rebrand commit's source.
// Peer to `cilium_key_http_carries_lower_camel_case_shape` /
// `cilium_key_mode_carries_lower_camel_case_shape` /
// `cilium_key_authentication_carries_lower_camel_case_shape` on
// the sibling per-CRD-body-axis grammar-pin set — the
// lowerCamelCase K8s field-name grammar governs every nested
// schema-field axis (including this K8s-discriminated-union-
// type-scalar-discriminator axis), same convention.
let v = KUBE_KEY_TYPE;
assert!(
!v.is_empty(),
"KUBE_KEY_TYPE {v:?} must be non-empty per the K8s API \
lowerCamelCase field-name grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_lowercase(),
"KUBE_KEY_TYPE {v:?} first byte {first:?} must be \
ASCII-lowercase per the K8s API lowerCamelCase field-name \
grammar (field names are always lowerCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"KUBE_KEY_TYPE {v:?} must be ASCII-alphanumeric \
throughout per the K8s API field-name grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
OpenAPI schema validator would reject"
);
}
#[test]
fn gateway_api_kind_gateway_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Gateway-API-conformant `Gateway` CRD `kind`
// discriminator the rendered Gateway document's top-level
// `kind` axis declares. The string is part of the cluster-side
// contract with every Gateway-API-conformant gateway
// implementation (Cilium, Istio, Envoy Gateway, NGINX) — the
// apiserver-side CRD resolution contract is the
// `(apiVersion, kind)` tuple keyed against the registered
// `CustomResourceDefinition`, so the kind half of the tuple is
// exactly as load-bearing as the sibling
// [`GATEWAY_API_API_VERSION`] apiVersion half. A drifted value
// (e.g. an upstream Gateway-API rebrand to `GatewayV1`) lands
// the rendered document outside the apiserver-side CRD
// registration; changing it is a coordinated Gateway-API
// promotion alongside the upstream SIG-Network deprecation
// cycle, not an incidental edit. Peer to
// `cilium_kind_network_policy_pins_canonical_value` /
// `flux_kind_kustomization_pins_canonical_value` /
// `flux_kind_helm_release_pins_canonical_value` /
// `flux_kind_git_repository_pins_canonical_value` on the
// sibling cluster-side-CRD-`kind`-discriminator pin set —
// extends the canonical-string-pin discipline from the
// Cilium-CRD + Flux v2 controller-triplet `kind`-axis surfaces
// onto the Gateway-API-CRD `kind`-axis surface, beginning the
// per-Gateway-API-CRD kind+apiVersion canonical-pin pair the
// M3 Aplicacao mesh renderer's external `:entrada` ingress
// contract rests on.
assert_eq!(GATEWAY_API_KIND_GATEWAY, "Gateway");
}
#[test]
fn gateway_api_kind_gateway_carries_upper_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD `kind` discriminator is
// an UpperCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds —
// "Kinds are always UpperCamelCase"). Pinning the shape here
// means a future rebrand on the canonical lift can't silently
// land a malformed kind discriminator (snake_case, kebab-case,
// lowercase, empty) that every downstream YAML-aware
// deserializer would reject far from the rebrand commit's
// source. The first-byte uppercase / rest-ASCII-alphanumeric
// invariant is the load-bearing K8s API typed-discovery
// contract: a value the apiserver's `RESTMapper` consults to
// resolve the CRD's `RESTKind`. Peer to
// `cilium_kind_network_policy_carries_upper_camel_case_shape` /
// `flux_kind_kustomization_carries_upper_camel_case_shape` /
// `flux_kind_helm_release_carries_upper_camel_case_shape` /
// `flux_kind_git_repository_carries_upper_camel_case_shape` on
// the sibling cluster-side-CRD-`kind`-discriminator surface.
let v = GATEWAY_API_KIND_GATEWAY;
assert!(
!v.is_empty(),
"GATEWAY_API_KIND_GATEWAY {v:?} must be non-empty per the K8s API \
UpperCamelCase kind discriminator grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_uppercase(),
"GATEWAY_API_KIND_GATEWAY {v:?} first byte {first:?} must be \
ASCII-uppercase per the K8s API UpperCamelCase kind discriminator \
grammar (Kinds are always UpperCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"GATEWAY_API_KIND_GATEWAY {v:?} must be ASCII-alphanumeric \
throughout per the K8s API kind discriminator grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
RESTMapper would reject"
);
}
#[test]
fn gateway_api_kind_http_route_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Gateway-API-conformant `HTTPRoute` CRD `kind`
// discriminator the rendered HTTPRoute document's top-level
// `kind` axis declares. The string is part of the cluster-side
// contract with every Gateway-API-conformant gateway
// implementation (Cilium, Istio, Envoy Gateway, NGINX) — the
// apiserver-side CRD resolution contract is the
// `(apiVersion, kind)` tuple keyed against the registered
// `CustomResourceDefinition`, so the kind half of the tuple is
// exactly as load-bearing as the sibling
// [`GATEWAY_API_API_VERSION`] apiVersion half. A drifted value
// (e.g. an upstream Gateway-API rebrand to `HTTPRouteV1`) lands
// the rendered document outside the apiserver-side CRD
// registration; changing it is a coordinated Gateway-API
// promotion alongside the upstream SIG-Network deprecation
// cycle, not an incidental edit. Peer to
// `gateway_api_kind_gateway_pins_canonical_value` /
// `cilium_kind_network_policy_pins_canonical_value` /
// `flux_kind_kustomization_pins_canonical_value` /
// `flux_kind_helm_release_pins_canonical_value` /
// `flux_kind_git_repository_pins_canonical_value` on the
// sibling cluster-side-CRD-`kind`-discriminator pin set —
// completes the per-Gateway-API-CRD `kind`-axis canonical-pin
// pair across the `(Gateway, HTTPRoute)` pair the renderer's
// `gateway_routes` external `:entrada` ingress contract emits
// together.
assert_eq!(GATEWAY_API_KIND_HTTP_ROUTE, "HTTPRoute");
}
#[test]
fn gateway_api_kind_http_route_carries_upper_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD `kind` discriminator is
// an UpperCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds —
// "Kinds are always UpperCamelCase"). Acronyms like HTTP stay
// ASCII-uppercase across the prefix per the same convention
// (the K8s API Kinds for `HTTPRoute`, `TCPRoute`, `TLSRoute`,
// `GRPCRoute` carry the full-uppercase protocol acronym).
// Pinning the shape here means a future rebrand on the
// canonical lift can't silently land a malformed kind
// discriminator (snake_case, kebab-case, lowercase, empty)
// that every downstream YAML-aware deserializer would reject
// far from the rebrand commit's source. The first-byte
// uppercase / rest-ASCII-alphanumeric invariant is the
// load-bearing K8s API typed-discovery contract: a value the
// apiserver's `RESTMapper` consults to resolve the CRD's
// `RESTKind`. Peer to
// `gateway_api_kind_gateway_carries_upper_camel_case_shape` /
// `cilium_kind_network_policy_carries_upper_camel_case_shape` /
// `flux_kind_kustomization_carries_upper_camel_case_shape` /
// `flux_kind_helm_release_carries_upper_camel_case_shape` /
// `flux_kind_git_repository_carries_upper_camel_case_shape` on
// the sibling cluster-side-CRD-`kind`-discriminator surface.
let v = GATEWAY_API_KIND_HTTP_ROUTE;
assert!(
!v.is_empty(),
"GATEWAY_API_KIND_HTTP_ROUTE {v:?} must be non-empty per the K8s API \
UpperCamelCase kind discriminator grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_uppercase(),
"GATEWAY_API_KIND_HTTP_ROUTE {v:?} first byte {first:?} must be \
ASCII-uppercase per the K8s API UpperCamelCase kind discriminator \
grammar (Kinds are always UpperCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"GATEWAY_API_KIND_HTTP_ROUTE {v:?} must be ASCII-alphanumeric \
throughout per the K8s API kind discriminator grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
RESTMapper would reject"
);
}
#[test]
fn gateway_api_protocol_http_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Gateway API v1 `ProtocolType` OpenAPI schema enum's
// canonical `HTTP` listener-protocol value the rendered
// `Gateway.spec.listeners[].protocol` scalar declares. The value
// is part of the cluster-side contract with every Gateway-API-
// conformant gateway implementation (Cilium, Istio, Envoy
// Gateway, NGINX) — the gateway-class-controller's per-listener
// bind loop keys off this exact byte-sequence to select the L7
// parser + TLS termination strategy; the Gateway API v1
// `ProtocolType` OpenAPI schema enum admits the closed set
// `{"HTTP", "HTTPS", "TCP", "TLS", "UDP"}` verbatim, so a
// drifted value (`"http"` / `"Http"` / `"HTTP/1.1"` / `"http/1.1"`)
// lands the rendered `Gateway` outside the `ProtocolType` enum's
// admitted set and every external `:entrada` HTTP flow drops at
// the gateway-class-controller's admission gate. Changing this
// value is a coordinated Gateway API `ProtocolType` promotion
// alongside the upstream SIG-Network deprecation cycle, not an
// incidental edit. Peer to
// `gateway_api_kind_gateway_pins_canonical_value` /
// `gateway_api_kind_http_route_pins_canonical_value` /
// `default_gateway_class_name_pins_canonical_value` on the
// sibling Gateway-API-CRD-`kind`-discriminator + Gateway-
// controller-binding-scalar-value pin set — extends the pair
// of `kind`-axis canonical-value pins across the
// `(Gateway, HTTPRoute)` pair onto the sibling per-Gateway
// `spec.listeners[].protocol` listener-protocol-scalar-value axis
// the same `gateway_routes` external `:entrada` ingress emitter
// carries.
assert_eq!(GATEWAY_API_PROTOCOL_HTTP, "HTTP");
}
#[test]
fn gateway_api_protocol_http_carries_upper_case_shape() {
// Cross-axis invariant: the Gateway API v1 `ProtocolType` OpenAPI
// schema enum admits the closed set
// `{"HTTP", "HTTPS", "TCP", "TLS", "UDP"}` — every admitted value
// is ASCII-uppercase throughout per the upstream SIG-Network
// Gateway API convention (see
// https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.ProtocolType
// — the admitted values are the transport / application-layer
// protocol acronyms in their canonical uppercase form). Pinning
// the shape here means a future rebrand on the canonical lift
// can't silently land a malformed listener-protocol scalar
// (lowercase `"http"`, mixed-case `"Http"`, dotted `"HTTP/1.1"`,
// empty) that the K8s Gateway API v1 `ProtocolType` OpenAPI
// schema enum would reject at admission time far from the
// rebrand commit's source. The all-ASCII-uppercase invariant is
// the load-bearing Gateway-API-implementation-side typed
// listener-parser-selection contract: a value the gateway-
// class-controller's per-listener bind loop selects the L7
// parser + TLS termination strategy from.
let v = GATEWAY_API_PROTOCOL_HTTP;
assert!(
!v.is_empty(),
"GATEWAY_API_PROTOCOL_HTTP {v:?} must be non-empty per the \
Gateway API v1 `ProtocolType` OpenAPI schema enum grammar"
);
assert!(
v.chars().all(|c| c.is_ascii_uppercase()),
"GATEWAY_API_PROTOCOL_HTTP {v:?} must be ASCII-uppercase \
throughout per the Gateway API v1 `ProtocolType` OpenAPI \
schema enum convention — no lowercase, mixed-case, dotted, \
or whitespace bytes the gateway-class-controller's per-\
listener bind loop would reject"
);
}
#[test]
fn gateway_api_path_match_type_path_prefix_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Gateway API v1 `PathMatchType` OpenAPI schema
// enum's canonical `PathPrefix` per-`HTTPRouteMatch` path-
// selection-predicate discriminator value the rendered
// `HTTPRoute.spec.rules[].matches[].path.type` scalar declares.
// The value is part of the cluster-side contract with every
// Gateway-API-conformant gateway implementation (Cilium, Istio,
// Envoy Gateway, NGINX) — the gateway-class-controller's
// per-rule L7 dispatch loop keys off this exact byte-sequence
// to select the request-path-selection predicate; the Gateway
// API v1 `PathMatchType` OpenAPI schema enum admits the closed
// set `{"Exact", "PathPrefix", "RegularExpression"}` verbatim,
// so a drifted value (`"pathPrefix"` / `"path_prefix"` /
// `"Prefix"` / `"path-prefix"`) lands the rendered `HTTPRoute`
// outside the `PathMatchType` enum's admitted set and every
// external `:entrada` path-filtered flow drops at the gateway-
// class-controller's admission gate. Changing this value is a
// coordinated Gateway API `PathMatchType` promotion alongside
// the upstream SIG-Network deprecation cycle, not an incidental
// edit. Peer to
// `gateway_api_protocol_http_pins_canonical_value` /
// `gateway_api_kind_gateway_pins_canonical_value` /
// `gateway_api_kind_http_route_pins_canonical_value` /
// `default_gateway_class_name_pins_canonical_value` on the
// sibling Gateway-API-v1-OpenAPI-schema-enum-value +
// Gateway-API-CRD-`kind`-discriminator + Gateway-controller-
// binding-scalar-value pin set — extends the canonical-
// Gateway-API-v1-OpenAPI-schema-enum-value single-sourcing
// discipline the `ProtocolType.HTTP` pin established onto the
// sibling `PathMatchType.PathPrefix` per-`HTTPRouteMatch`
// path-selection-predicate discriminator the same
// `gateway_routes` external `:entrada` ingress emitter carries
// under the shared `HTTPRoute` body.
assert_eq!(GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX, "PathPrefix");
}
#[test]
fn gateway_api_path_match_type_path_prefix_carries_upper_camel_case_shape() {
// Cross-axis invariant: the Gateway API v1 `PathMatchType`
// OpenAPI schema enum admits the closed set
// `{"Exact", "PathPrefix", "RegularExpression"}` — every
// admitted value is UpperCamelCase per the upstream SIG-Network
// Gateway API convention (see
// https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.PathMatchType
// — the admitted values are the request-path-selection
// predicate names in their canonical UpperCamelCase form,
// matching the K8s API `Kinds are always UpperCamelCase`
// convention the sibling `GATEWAY_API_KIND_*` discriminators
// carry on the CRD-`kind`-axis surface). Pinning the shape
// here means a future rebrand on the canonical lift can't
// silently land a malformed path-match-type scalar (lowercase
// `"pathprefix"`, snake_case `"path_prefix"`, kebab-case
// `"path-prefix"`, empty) that the K8s Gateway API v1
// `PathMatchType` OpenAPI schema enum would reject at
// admission time far from the rebrand commit's source. The
// first-byte uppercase / rest-ASCII-alphanumeric invariant is
// the load-bearing Gateway-API-implementation-side typed
// per-match request-path-selection-predicate-selection
// contract: a value the gateway-class-controller's per-rule
// L7 dispatch loop selects the request-path-predicate
// evaluator from. Peer to
// `gateway_api_kind_gateway_carries_upper_camel_case_shape` /
// `gateway_api_kind_http_route_carries_upper_camel_case_shape`
// on the sibling cluster-side-CRD-`kind`-discriminator
// UpperCamelCase pin set — extends the canonical-K8s-API-
// UpperCamelCase-typed-discriminator pin discipline the
// `Kind` axis carries onto the sibling Gateway API v1
// `PathMatchType` OpenAPI schema enum's per-value
// UpperCamelCase surface (distinct from the sibling
// Gateway API v1 `ProtocolType` OpenAPI schema enum's all-
// ASCII-uppercase per-value convention the
// `gateway_api_protocol_http_carries_upper_case_shape` pin
// carries — the two peer Gateway-API-v1 OpenAPI schema
// enum-value conventions do not collapse).
let v = GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX;
assert!(
!v.is_empty(),
"GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX {v:?} must be non-empty per \
the Gateway API v1 `PathMatchType` OpenAPI schema enum grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_uppercase(),
"GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX {v:?} first byte {first:?} \
must be ASCII-uppercase per the Gateway API v1 `PathMatchType` \
OpenAPI schema enum UpperCamelCase convention"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"GATEWAY_API_PATH_MATCH_TYPE_PATH_PREFIX {v:?} must be ASCII-\
alphanumeric throughout per the Gateway API v1 `PathMatchType` \
OpenAPI schema enum UpperCamelCase convention — no snake_case, \
kebab-case, or whitespace bytes the gateway-class-controller's \
per-rule L7 dispatch loop would reject"
);
}
#[test]
fn kube_protocol_tcp_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the K8s core `Protocol` OpenAPI schema enum's
// canonical `TCP` L4-transport-protocol scalar value the
// rendered `CiliumNetworkPolicy.spec.ingress[].toPorts[].ports[]
// .protocol` scalar declares. The value is part of the cluster-
// side contract with every K8s-core-`Protocol`-conformant CNI
// + kube-proxy + eBPF-data-plane implementation (Cilium,
// Calico, kube-proxy iptables/ipvs) — the CNI's per-CNP L4
// dispatch pass keys off this exact byte-sequence to select
// the per-tuple L4-transport-protocol predicate; the K8s core
// `Protocol` OpenAPI schema enum admits the closed set
// `{"TCP", "UDP", "SCTP"}` verbatim (see
// https://kubernetes.io/docs/reference/generated/kubernetes-api/v1/#protocol-v1-core),
// so a drifted value (`"tcp"` / `"Tcp"` / `"TCP/IP"` /
// `"transport-tcp"`) lands the rendered `CiliumNetworkPolicy`
// outside the `Protocol` enum's admitted set and every intra-
// mesh `:contratos` L4-tuple-gated flow drops at the Cilium
// operator's admission gate. Changing this value is a
// coordinated K8s core `Protocol` promotion alongside the
// upstream SIG-Network deprecation cycle, not an incidental
// edit. Peer to
// `gateway_api_protocol_http_pins_canonical_value` /
// `gateway_api_path_match_type_path_prefix_pins_canonical_value`
// on the sibling Gateway-API-v1-OpenAPI-schema-enum-value pin
// set — extends the canonical-cluster-side-OpenAPI-schema-enum-
// value single-sourcing discipline the Gateway-API v1
// `ProtocolType.HTTP` / `PathMatchType.PathPrefix` pins
// established onto the sibling K8s-core `Protocol.TCP` per-port-
// tuple L4-transport-protocol-discriminator the
// `cilium_network_policies` intra-mesh L4-tuple-gating emitter
// carries under the shared `CiliumNetworkPolicy` body.
assert_eq!(KUBE_PROTOCOL_TCP, "TCP");
}
#[test]
fn kube_protocol_tcp_carries_upper_case_shape() {
// Cross-axis invariant: the K8s core `Protocol` OpenAPI schema
// enum admits the closed set `{"TCP", "UDP", "SCTP"}` — every
// admitted value is ASCII-uppercase throughout per the upstream
// SIG-Network convention (the admitted values are the L4-
// transport-protocol acronyms in their canonical uppercase form,
// matching the sibling Gateway-API v1 `ProtocolType` OpenAPI
// schema enum's `{"HTTP", "HTTPS", "TCP", "TLS", "UDP"}` all-
// ASCII-uppercase convention the
// `gateway_api_protocol_http_carries_upper_case_shape` pin
// carries on the peer per-listener L7-parser-selection scalar
// axis). Pinning the shape here means a future rebrand on the
// canonical lift can't silently land a malformed L4-transport-
// protocol scalar (lowercase `"tcp"`, mixed-case `"Tcp"`,
// dotted `"TCP/IP"`, empty) that the K8s core `Protocol`
// OpenAPI schema enum would reject at admission time far from
// the rebrand commit's source. The all-ASCII-uppercase
// invariant is the load-bearing K8s-core-`Protocol`-enum-side
// typed L4-transport-selection contract: a value the CNI's per-
// CNP L4 dispatch pass selects the per-tuple L4-transport-
// protocol predicate from. Peer to
// `gateway_api_protocol_http_carries_upper_case_shape` on the
// sibling Gateway-API v1 `ProtocolType` OpenAPI schema enum's
// all-ASCII-uppercase per-value convention pin set — the two
// peer canonical-cluster-side-OpenAPI-schema-enum-value
// uppercase conventions collapse on the shared `TCP` transport-
// protocol acronym both `Protocol` enums admit at the closed-
// set intersection.
let v = KUBE_PROTOCOL_TCP;
assert!(
!v.is_empty(),
"KUBE_PROTOCOL_TCP {v:?} must be non-empty per the K8s core \
`Protocol` OpenAPI schema enum grammar"
);
assert!(
v.chars().all(|c| c.is_ascii_uppercase()),
"KUBE_PROTOCOL_TCP {v:?} must be ASCII-uppercase throughout \
per the K8s core `Protocol` OpenAPI schema enum convention \
— no lowercase, mixed-case, dotted, or whitespace bytes the \
CNI's per-CNP L4 dispatch pass would reject"
);
}
#[test]
fn cilium_auth_mode_required_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Cilium `CiliumNetworkPolicy` `MutualAuthenticationMode`
// OpenAPI schema enum's `required` mTLS-mandatory scalar-value the
// rendered CNP's `spec.ingress[].authentication.mode` leaf declares
// under the `:mtls-required t` affirmative arm of the typed
// `:politicas :mtls-required` tristate. The value is part of the
// cluster-side contract with the Cilium-agent-side per-rule mutual-
// auth-block schema validator — the agent's per-rule dispatch loop
// keys off this exact byte-sequence to select the SPIFFE-identity-
// handshake-mandatory enforcement policy; the Cilium CNP
// `MutualAuthenticationMode` OpenAPI schema enum admits the closed
// set `{"required", "disabled", "test-always-fail"}` verbatim (the
// `test-always-fail` arm is a Cilium-side debugging surface, not
// author-reachable), so a drifted value (`"Required"` /
// `"REQUIRED"` / `"mandatory"` / `"mtls-required"`) lands the
// rendered `CiliumNetworkPolicy` outside the
// `MutualAuthenticationMode` enum's admitted set and every intra-
// mesh `:contratos` flow the CNP was authored to protect with per-
// edge SPIFFE-identity-bound mutual-auth silently bypasses the
// handshake at the Cilium data-plane's default-authentication mode
// (typically also "disabled" today, but environment-divergent —
// take effect) with no field naming the mTLS-mandatory-scalar-value-
// drift root cause. Changing this value is a coordinated Cilium
// CNP `MutualAuthenticationMode` promotion alongside the Cilium
// project's periodic CRD schema-migration passes, not an
// incidental edit. Peer to
// `gateway_api_protocol_http_pins_canonical_value` /
// `gateway_api_path_match_type_path_prefix_pins_canonical_value` /
// `kube_protocol_tcp_pins_canonical_value` on the sibling
// canonical-cluster-side-OpenAPI-schema-enum-value pin set —
// extends the canonical-cluster-side-OpenAPI-schema-enum-value
// single-sourcing discipline the Gateway-API v1 `ProtocolType.HTTP`
// / `PathMatchType.PathPrefix` / K8s-core `Protocol.TCP` pins
// established onto the sibling Cilium-CNP-side
// `MutualAuthenticationMode.required` per-rule mTLS-mandatory
// scalar-value the `cilium_network_policies` per-edge SPIFFE-
// identity-bound mutual-auth emitter carries under the shared
// `CiliumNetworkPolicy` body.
assert_eq!(CILIUM_AUTH_MODE_REQUIRED, "required");
}
#[test]
fn cilium_auth_mode_disabled_pins_canonical_value() {
// Peer to `cilium_auth_mode_required_pins_canonical_value` on the
// `Some(false)` opt-out arm of the same
// `MutualAuthenticationMode` OpenAPI schema enum: pin the actual
// string so a typo can't silently rebrand the Cilium `disabled`
// mTLS-skipped scalar-value the rendered CNP's per-rule authn-
// block declares under the explicit `:mtls-required nil` opt-out
// (distinct from the `None` slot-absent arm the renderer maps to
// omit-the-block-entirely). A drifted value (`"Disabled"` /
// `"DISABLED"` / `"off"` / `"skip"`) lands outside the
// `MutualAuthenticationMode` OpenAPI schema enum's admitted set;
// the author's explicit-opt-out intent silently collapses onto the
// cluster-default authentication mode with no field naming the
// mTLS-skipped-scalar-value-drift root cause. Peer to
// `cilium_auth_mode_required_pins_canonical_value` on the
// affirmative arm of the same enum — completes the per-authn-block
// `(mode → {required, disabled})` author-reachable-scalar-value-
// pair single-sourcing the M3 Aplicacao mesh renderer's SPIFFE-
// identity-bound per-edge mTLS enforcement + explicit-opt-out
// contract rests on across the two arms of the `:politicas
// :mtls-required` tristate.
assert_eq!(CILIUM_AUTH_MODE_DISABLED, "disabled");
}
#[test]
fn cilium_auth_modes_carry_lower_case_shape() {
// Cross-axis invariant: the Cilium CNP `MutualAuthenticationMode`
// OpenAPI schema enum admits the closed set `{"required",
// "disabled", "test-always-fail"}` — every admitted value is
// ASCII-lowercase throughout per the Cilium-project convention
// (distinct from the sibling K8s-core `Protocol.TCP` /
// Gateway-API-v1 `ProtocolType.HTTP` all-ASCII-uppercase
// convention the `kube_protocol_tcp_carries_upper_case_shape` /
// `gateway_api_protocol_http_carries_upper_case_shape` pins carry
// on the sibling per-listener L7-parser-selection scalar axis, and
// distinct from the sibling Gateway-API-v1
// `PathMatchType.PathPrefix` UpperCamelCase convention the
// `gateway_api_path_match_type_path_prefix_carries_upper_camel_case_shape`
// pin carries on the sibling per-match request-path-selection
// scalar axis — the Cilium CNP `MutualAuthenticationMode` enum
// grammar does not collapse with either sibling cluster-side
// OpenAPI schema enum's per-value casing convention). Pinning the
// shape here means a future rebrand on either lifted value can't
// silently land a malformed mode-discriminator scalar (uppercase
// `"REQUIRED"` / `"DISABLED"`, UpperCamelCase `"Required"` /
// `"Disabled"`, mixed-case, whitespace) that the Cilium CNP
// `MutualAuthenticationMode` OpenAPI schema enum would reject at
// admission time far from the rebrand commit's source.
for v in [CILIUM_AUTH_MODE_REQUIRED, CILIUM_AUTH_MODE_DISABLED] {
assert!(
!v.is_empty(),
"{v:?} must be non-empty per the Cilium CNP \
`MutualAuthenticationMode` OpenAPI schema enum grammar"
);
assert!(
v.chars().all(|c| c.is_ascii_lowercase()),
"{v:?} must be ASCII-lowercase throughout per the Cilium \
CNP `MutualAuthenticationMode` OpenAPI schema enum \
convention — no uppercase, UpperCamelCase, or whitespace \
bytes the Cilium-agent-side per-rule mutual-auth-block \
schema validator would reject"
);
}
}
#[test]
fn cilium_auth_modes_are_distinct() {
// Pin the `MutualAuthenticationMode` enum's per-arm distinctness
// at type-check time: the two author-reachable arms of the typed
// `:politicas :mtls-required` tristate must not collapse onto the
// same scalar-value byte-sequence. A future rebrand that landed
// both lifted constants on the same string (e.g. both `"required"`
// through a copy-paste typo, or both aliased through a shared
// helper) would silently erase the tristate's affirmative /
// explicit-opt-out distinction at the emit boundary — the
// renderer would emit the same scalar under both the `Some(true)`
// and `Some(false)` arms of the closure the
// `single_field_overlay(spec.politicas.mtls_required,
// CILIUM_KEY_MODE, |required| …)` call site carries, collapsing
// the two author intents onto a single Cilium-side enforcement
// policy with no field naming the collapse root cause. Peer to
// the two `cilium_auth_mode_{required,disabled}_pins_canonical_
// value` per-arm pins — completes the per-arm distinctness pin
// set on the closed author-reachable subset of the enum.
assert_ne!(
CILIUM_AUTH_MODE_REQUIRED, CILIUM_AUTH_MODE_DISABLED,
"the two author-reachable arms of the `:mtls-required` \
tristate must land distinct `MutualAuthenticationMode` \
scalar-values"
);
}
#[test]
fn cilium_auth_mode_bijection_dispatches_tristate_arms_onto_scalar_values() {
// Pin the `bool → &'static str` projection every consumer of the
// Cilium `MutualAuthenticationMode` closed-set enum's author-
// reachable scalar-value pair reaches through: `true` (the
// `Some(true)` mTLS-mandatory arm of the typed `:politicas
// :mtls-required` tristate) maps to [`CILIUM_AUTH_MODE_REQUIRED`],
// `false` (the `Some(false)` explicit-opt-out arm) maps to
// [`CILIUM_AUTH_MODE_DISABLED`]. One projection body, both arms of
// the tristate's non-`None` value-space, so a future per-arm
// reassignment (e.g. an upstream Cilium v3 schema swap of the
// `required` ↔ `disabled` scalars, or a per-arm renaming of the
// mTLS-mandatory scalar from `required` to `enforced` / `strict`
// / `mandatory`) lands at the two consts + this projection body
// — not at the caixa-mesh production emitter's closure body and
// the caixa-core `single_field_overlay_threads_typed_value_
// through_closure` generic-helper pin's closure body independently.
// Pin the per-arm round-trip so a future refactor that inverts
// the bool → arm mapping (or collapses one arm) surfaces here
// rather than silently letting a Cilium data-plane pod either
// enforce mTLS where the author asked for skip or skip it where
// the author asked for enforce.
assert_eq!(cilium_auth_mode(true), CILIUM_AUTH_MODE_REQUIRED);
assert_eq!(cilium_auth_mode(false), CILIUM_AUTH_MODE_DISABLED);
// The two arms cover distinct value-space entries — a regression
// that collapses them onto the same scalar surfaces here. Peer
// to `cilium_auth_modes_are_distinct` (the per-arm distinctness
// pin at the const-declaration axis) — this test extends the
// pin onto the projection body axis, so both the raw consts and
// the projection's per-arm dispatch preserve the tristate's
// author-intent distinction end-to-end.
assert_ne!(
cilium_auth_mode(true),
cilium_auth_mode(false),
"cilium_auth_mode must project the two tristate arms onto \
distinct `MutualAuthenticationMode` value-space entries — \
a collapsed-arm regression would silently render both \
`:mtls-required t` and `:mtls-required nil` identically at \
the cluster artifact",
);
}
#[test]
fn gateway_api_key_parent_refs_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Gateway API `HTTPRoute` parent-Gateway-binding
// container-axis key the rendered HTTPRoute document mounts its
// per-route `[{name}]` parent-Gateway attachment list under. The
// string is part of the cluster-side contract with every
// Gateway-API-conformant gateway implementation (Cilium, Istio,
// Envoy Gateway, NGINX) — the Gateway-API-implementation-side
// per-HTTPRoute reconcile loop keys off this axis to source the
// per-route parent-Gateway attachment list the route is bound
// to; a drifted value (`"parentRef"` / `"parents"` /
// `"parentGateways"`) at either the production emitter or a
// downstream renderer's per-HTTPRoute parent-Gateway-binding
// upsert silently emits an `HTTPRoute` whose parent-Gateway-
// binding axis the Gateway API CRD schema validator drops as
// unknown — the route lands unattached to any Gateway, and
// every external `:entrada` flow the HTTPRoute was authored to
// accept drops at the Gateway API implementation's per-Gateway
// HTTP-listener fan-in with no field naming the parent-Gateway-
// binding-drift root cause. Changing this value is a
// coordinated Gateway API promotion alongside the upstream
// SIG-Network Gateway API deprecation cycle, not an incidental
// edit. Peer to `cilium_key_ports_pins_canonical_value` /
// `cilium_key_from_endpoints_pins_canonical_value` /
// `cilium_key_endpoint_selector_pins_canonical_value` /
// `cilium_key_ingress_pins_canonical_value` /
// `cilium_key_to_ports_pins_canonical_value` on the sibling
// per-CNP-body-axis pin set — begins the per-Gateway-API-
// HTTPRoute-body-axis canonical-string-pin set (`parentRefs`,
// future `hostnames`) the M3 Aplicacao mesh renderer's external
// `:entrada` ingress contract rests on across the Gateway API
// HTTPRoute-side per-route body-shape.
assert_eq!(GATEWAY_API_KEY_PARENT_REFS, "parentRefs");
}
#[test]
fn gateway_api_key_parent_refs_carries_lower_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD schema field name is a
// lowerCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
// "Field names should be lowercase camelCase") — first byte
// ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
// kebab-case or whitespace. Pinning the shape here means a
// future rebrand on the canonical lift can't silently land a
// malformed field-name discriminator (snake_case, kebab-case,
// UpperCamelCase, empty) that the apiserver-side CRD schema
// validator would reject far from the rebrand commit's source.
// Peer to `cilium_key_ports_carries_lower_camel_case_shape` /
// `cilium_key_from_endpoints_carries_lower_camel_case_shape` /
// `cilium_key_endpoint_selector_carries_lower_camel_case_shape`
// / `cilium_key_ingress_carries_lower_camel_case_shape` /
// `cilium_key_to_ports_carries_lower_camel_case_shape` on the
// sibling per-CNP-body-axis grammar-pin set — the lowerCamelCase
// K8s field-name grammar governs every nested schema-field axis
// (including this per-HTTPRoute parent-Gateway-binding-
// container-axis key), same convention.
let v = GATEWAY_API_KEY_PARENT_REFS;
assert!(
!v.is_empty(),
"GATEWAY_API_KEY_PARENT_REFS {v:?} must be non-empty per the K8s API \
lowerCamelCase field-name grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_lowercase(),
"GATEWAY_API_KEY_PARENT_REFS {v:?} first byte {first:?} must be \
ASCII-lowercase per the K8s API lowerCamelCase field-name \
grammar (field names are always lowerCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"GATEWAY_API_KEY_PARENT_REFS {v:?} must be ASCII-alphanumeric \
throughout per the K8s API field-name grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
OpenAPI schema validator would reject"
);
}
#[test]
fn gateway_api_key_backend_refs_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Gateway API `HTTPRoute` per-rule backend-destination
// container-axis key the rendered HTTPRoute document mounts its
// per-rule `[{name, port}]` backend fan-out list under. The
// string is part of the cluster-side contract with every
// Gateway-API-conformant gateway implementation (Cilium, Istio,
// Envoy Gateway, NGINX) — the Gateway-API-implementation-side
// per-rule L7 dispatch loop keys off this axis to source the
// per-rule backend list the request is forwarded to; a drifted
// value (`"backendRef"` / `"backends"` / `"forwardTo"`) at
// either the production emitter or a downstream renderer's
// per-rule backend-destination upsert silently emits an
// `HTTPRoute` whose per-rule backend fan-out axis the Gateway
// API CRD schema validator drops as unknown — no backend is
// picked at the per-rule L7 dispatch, and every external
// `:entrada` request the rule was authored to route drops at
// the gateway-class-controller's per-rule reconcile with no
// field naming the backend-destination-drift root cause.
// Changing this value is a coordinated Gateway API promotion
// alongside the upstream SIG-Network Gateway API deprecation
// cycle, not an incidental edit. Peer to
// `gateway_api_key_parent_refs_pins_canonical_value` on the
// sibling per-HTTPRoute-body-axis canonical-string-pin surface
// — extends the per-Gateway-API-HTTPRoute-body-axis pin set
// (`parentRefs`, `backendRefs`, future `hostnames`) the M3
// Aplicacao mesh renderer's external `:entrada` ingress
// contract rests on across the Gateway API HTTPRoute-side per-
// route body-shape.
assert_eq!(GATEWAY_API_KEY_BACKEND_REFS, "backendRefs");
}
#[test]
fn gateway_api_key_backend_refs_carries_lower_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD schema field name is a
// lowerCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
// "Field names should be lowercase camelCase") — first byte
// ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
// kebab-case or whitespace. Pinning the shape here means a
// future rebrand on the canonical lift can't silently land a
// malformed field-name discriminator (snake_case, kebab-case,
// UpperCamelCase, empty) that the apiserver-side CRD schema
// validator would reject far from the rebrand commit's source.
// Peer to `gateway_api_key_parent_refs_carries_lower_camel_case_shape`
// on the sibling per-HTTPRoute-body-axis grammar-pin surface —
// the lowerCamelCase K8s field-name grammar governs every
// nested schema-field axis (including this per-rule backend-
// destination-container-axis key), same convention.
let v = GATEWAY_API_KEY_BACKEND_REFS;
assert!(
!v.is_empty(),
"GATEWAY_API_KEY_BACKEND_REFS {v:?} must be non-empty per the K8s API \
lowerCamelCase field-name grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_lowercase(),
"GATEWAY_API_KEY_BACKEND_REFS {v:?} first byte {first:?} must be \
ASCII-lowercase per the K8s API lowerCamelCase field-name \
grammar (field names are always lowerCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"GATEWAY_API_KEY_BACKEND_REFS {v:?} must be ASCII-alphanumeric \
throughout per the K8s API field-name grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
OpenAPI schema validator would reject"
);
}
#[test]
fn gateway_api_key_matches_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Gateway API `HTTPRoute` per-rule route-match
// container-axis key the rendered HTTPRoute document mounts
// its per-rule `[{path: {type, value}}]` route-match fan-out
// list under. The string is part of the cluster-side contract
// with every Gateway-API-conformant gateway implementation
// (Cilium, Istio, Envoy Gateway, NGINX) — the Gateway-API-
// implementation-side per-rule L7 dispatch loop keys off this
// axis to source the per-rule request-selection predicate the
// incoming request line + headers + query must satisfy for
// the rule's backend fan-out to apply; a drifted value
// (`"match"` / `"routeMatches"` / `"predicates"`) at either
// the production emitter or a downstream renderer's per-rule
// route-match upsert silently emits an `HTTPRoute` whose per-
// rule request-selection axis the Gateway API CRD schema
// validator drops as unknown — the per-rule predicate
// degrades to the wildcard match at the gateway-class-
// controller's per-rule reconcile, the rule matches every
// request unconditionally, and every external `:entrada` path
// filter the rule was authored to enforce drops with no field
// naming the route-match-drift root cause. Changing this
// value is a coordinated Gateway API promotion alongside the
// upstream SIG-Network Gateway API deprecation cycle, not an
// incidental edit. Peer to
// `gateway_api_key_backend_refs_pins_canonical_value` /
// `gateway_api_key_parent_refs_pins_canonical_value` on the
// sibling per-HTTPRoute-body-axis canonical-string-pin
// surface — completes the per-rule top-level-axis pin set
// (`matches`, `backendRefs`, `timeouts`, `retry`) the M3
// Aplicacao mesh renderer's external `:entrada` ingress
// contract rests on across the Gateway API HTTPRoute per-rule
// body-shape.
assert_eq!(GATEWAY_API_KEY_MATCHES, "matches");
}
#[test]
fn gateway_api_key_matches_carries_lower_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD schema field name is a
// lowerCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
// "Field names should be lowercase camelCase") — first byte
// ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
// kebab-case or whitespace. Pinning the shape here means a
// future rebrand on the canonical lift can't silently land a
// malformed field-name discriminator (snake_case, kebab-case,
// UpperCamelCase, empty) that the apiserver-side CRD schema
// validator would reject far from the rebrand commit's source.
// Peer to `gateway_api_key_backend_refs_carries_lower_camel_case_shape`
// / `gateway_api_key_parent_refs_carries_lower_camel_case_shape`
// on the sibling per-HTTPRoute-body-axis grammar-pin surface —
// the lowerCamelCase K8s field-name grammar governs every
// nested schema-field axis (including this per-rule route-
// match-container-axis key), same convention.
let v = GATEWAY_API_KEY_MATCHES;
assert!(
!v.is_empty(),
"GATEWAY_API_KEY_MATCHES {v:?} must be non-empty per the K8s API \
lowerCamelCase field-name grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_lowercase(),
"GATEWAY_API_KEY_MATCHES {v:?} first byte {first:?} must be \
ASCII-lowercase per the K8s API lowerCamelCase field-name \
grammar (field names are always lowerCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"GATEWAY_API_KEY_MATCHES {v:?} must be ASCII-alphanumeric \
throughout per the K8s API field-name grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
OpenAPI schema validator would reject"
);
}
#[test]
fn gateway_api_key_gateway_class_name_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Gateway API `Gateway` per-Gateway controller-
// binding scalar-axis key the rendered Gateway document
// mounts its per-Gateway `GatewayClass.metadata.name`
// reference under. The string is part of the cluster-side
// contract with every Gateway-API-conformant gateway
// implementation (Cilium, Istio, Envoy Gateway, NGINX) —
// the Gateway-API-implementation-side per-Gateway reconcile
// loop keys off this axis to source the `GatewayClass`
// reference the per-Gateway controller-name-lookup dispatch
// resolves; a drifted value (`"gatewayClass"` /
// `"className"` / `"gatewayClassRef"`) at the production
// emitter silently emits a `Gateway` whose controller-binding
// scalar-axis the Gateway API CRD schema validator drops as
// unknown — no `GatewayClass` is resolved, no `controllerName`
// is looked up, and every external `:entrada` flow the
// Gateway was authored to accept drops at the gateway-class-
// controller's per-Gateway reconcile with no field naming
// the controller-binding-drift root cause. Changing this
// value is a coordinated Gateway API promotion alongside
// the upstream SIG-Network Gateway API deprecation cycle,
// not an incidental edit. Peer to
// `gateway_api_key_listeners_pins_canonical_value` /
// `gateway_api_key_hostname_pins_canonical_value` on the
// sibling per-Gateway-body-axis canonical-string-pin
// surface — completes the per-Gateway-body-axis top-level-
// axis pin set (`gatewayClassName`, `listeners`) the M3
// Aplicacao mesh renderer's external `:entrada` ingress
// contract rests on. Sibling of the peer
// `default_gateway_class_name_pins_canonical_value` on the
// canonical-Gateway-API-`(key, value)`-pair-lift surface
// this lift closes the KEY half of.
assert_eq!(GATEWAY_API_KEY_GATEWAY_CLASS_NAME, "gatewayClassName");
}
#[test]
fn gateway_api_key_gateway_class_name_carries_lower_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD schema field name is a
// lowerCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
// "Field names should be lowercase camelCase") — first byte
// ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
// kebab-case or whitespace. Pinning the shape here means a
// future rebrand on the canonical lift can't silently land a
// malformed field-name discriminator (snake_case, kebab-case,
// UpperCamelCase, empty) that the apiserver-side CRD schema
// validator would reject far from the rebrand commit's source.
// Peer to `gateway_api_key_listeners_carries_lower_camel_case_shape`
// / `gateway_api_key_matches_carries_lower_camel_case_shape`
// on the sibling per-Gateway / per-HTTPRoute-body-axis
// grammar-pin surface — the lowerCamelCase K8s field-name
// grammar governs every nested schema-field axis (including
// this per-Gateway controller-binding scalar-axis key), same
// convention.
let v = GATEWAY_API_KEY_GATEWAY_CLASS_NAME;
assert!(
!v.is_empty(),
"GATEWAY_API_KEY_GATEWAY_CLASS_NAME {v:?} must be non-empty per the K8s API \
lowerCamelCase field-name grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_lowercase(),
"GATEWAY_API_KEY_GATEWAY_CLASS_NAME {v:?} first byte {first:?} must be \
ASCII-lowercase per the K8s API lowerCamelCase field-name \
grammar (field names are always lowerCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"GATEWAY_API_KEY_GATEWAY_CLASS_NAME {v:?} must be ASCII-alphanumeric \
throughout per the K8s API field-name grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
OpenAPI schema validator would reject"
);
}
#[test]
fn gateway_api_key_path_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Gateway API `HTTPRoute` per-`HTTPRouteMatch`
// path-matcher container-axis key the rendered HTTPRoute
// document mounts its per-match `{type, value}` path-selection
// predicate under. The string is part of the cluster-side
// contract with every Gateway-API-conformant gateway
// implementation (Cilium, Istio, Envoy Gateway, NGINX) — the
// Gateway-API-implementation-side per-rule L7 dispatch loop
// keys off this axis to source the per-match request-path-
// selection predicate the incoming request line's `:path`
// pseudo-header must satisfy under a `type` discriminator of
// `Exact | PathPrefix | RegularExpression`; a drifted value
// (`"pathMatch"` / `"prefix"` / `"url"`) at the production
// emitter silently emits an `HTTPRoute` whose per-match path-
// selection axis the Gateway API CRD schema validator drops
// as unknown — the per-match path predicate degrades to the
// wildcard match at the gateway-class-controller's per-rule
// reconcile, the rule matches every request path
// unconditionally, and every external `:entrada` path filter
// the rule was authored to enforce drops with no field
// naming the path-matcher-drift root cause. Changing this
// value is a coordinated Gateway API promotion alongside the
// upstream SIG-Network Gateway API deprecation cycle, not an
// incidental edit. Peer to
// `gateway_api_key_matches_pins_canonical_value` /
// `gateway_api_key_backend_refs_pins_canonical_value` on the
// sibling per-HTTPRoute-body-axis canonical-string-pin
// surface — nests the per-Gateway-API-HTTPRoute-per-rule-
// body-axis pin set (`matches`, `backendRefs`, `timeouts`,
// `retry`) one level deeper onto the per-`HTTPRouteMatch`
// body-axis surface the M3 Aplicacao mesh renderer's external
// `:entrada` ingress contract rests on.
assert_eq!(GATEWAY_API_KEY_PATH, "path");
}
#[test]
fn gateway_api_key_path_carries_lower_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD schema field name is a
// lowerCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
// "Field names should be lowercase camelCase") — first byte
// ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
// kebab-case or whitespace. Pinning the shape here means a
// future rebrand on the canonical lift can't silently land a
// malformed field-name discriminator (snake_case, kebab-case,
// UpperCamelCase, empty) that the apiserver-side CRD schema
// validator would reject far from the rebrand commit's source.
// Peer to `gateway_api_key_matches_carries_lower_camel_case_shape`
// / `gateway_api_key_backend_refs_carries_lower_camel_case_shape`
// on the sibling per-HTTPRoute-body-axis grammar-pin surface —
// the lowerCamelCase K8s field-name grammar governs every
// nested schema-field axis (including this per-`HTTPRouteMatch`
// path-matcher-container-axis key), same convention.
let v = GATEWAY_API_KEY_PATH;
assert!(
!v.is_empty(),
"GATEWAY_API_KEY_PATH {v:?} must be non-empty per the K8s API \
lowerCamelCase field-name grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_lowercase(),
"GATEWAY_API_KEY_PATH {v:?} first byte {first:?} must be \
ASCII-lowercase per the K8s API lowerCamelCase field-name \
grammar (field names are always lowerCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"GATEWAY_API_KEY_PATH {v:?} must be ASCII-alphanumeric \
throughout per the K8s API field-name grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
OpenAPI schema validator would reject"
);
}
#[test]
fn gateway_api_key_value_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Gateway API `HTTPPathMatch` scalar-payload axis
// key the rendered `HTTPRoute` document mounts its per-match
// request-path-selection scalar payload under. The string is
// part of the cluster-side contract with every Gateway-API-
// conformant gateway implementation (Cilium, Istio, Envoy
// Gateway, NGINX) — the Gateway-API-implementation-side per-
// rule L7 dispatch loop keys off this axis to source the
// per-match request-path string that the sibling `type`
// discriminator (Exact | PathPrefix | RegularExpression) is
// applied against; a drifted value (`"path"` / `"prefix"` /
// `"pattern"` / `"expression"`) at the production emitter
// silently emits an `HTTPRoute` whose per-match request-path
// scalar the Gateway API CRD schema validator drops as
// unknown — the per-match path predicate degrades to the
// wildcard match at the gateway-class-controller's per-rule
// reconcile, the rule matches every request path
// unconditionally, and every external `:entrada` path filter
// the rule was authored to enforce drops with no field
// naming the `HTTPPathMatch`-scalar-payload-drift root cause.
// Changing this value is a coordinated Gateway API promotion
// alongside the upstream SIG-Network Gateway API deprecation
// cycle, not an incidental edit. Peer to
// `gateway_api_key_path_pins_canonical_value` on the sibling
// per-`HTTPRouteMatch`-body-axis canonical-string-pin surface
// — nests the per-Gateway-API-HTTPRoute-per-match-body-axis
// pin set (`path` container-axis, `value` scalar-payload key)
// one level deeper onto the per-`HTTPPathMatch` body-axis
// surface the M3 Aplicacao mesh renderer's external `:entrada`
// ingress contract rests on.
assert_eq!(GATEWAY_API_KEY_VALUE, "value");
}
#[test]
fn gateway_api_key_value_carries_lower_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD schema field name is
// a lowerCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
// "Field names should be lowercase camelCase") — first byte
// ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
// kebab-case or whitespace. Pinning the shape here means a
// future rebrand on the canonical lift can't silently land a
// malformed field-name discriminator (snake_case, kebab-case,
// UpperCamelCase, empty) that the apiserver-side CRD schema
// validator would reject far from the rebrand commit's source.
// Peer to `gateway_api_key_path_carries_lower_camel_case_shape`
// on the sibling per-`HTTPRouteMatch`-body-axis grammar-pin
// surface — the lowerCamelCase K8s field-name grammar governs
// every nested schema-field axis (including this per-
// `HTTPPathMatch` scalar-payload-axis key), same convention.
let v = GATEWAY_API_KEY_VALUE;
assert!(
!v.is_empty(),
"GATEWAY_API_KEY_VALUE {v:?} must be non-empty per the K8s API \
lowerCamelCase field-name grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_lowercase(),
"GATEWAY_API_KEY_VALUE {v:?} first byte {first:?} must be \
ASCII-lowercase per the K8s API lowerCamelCase field-name \
grammar (field names are always lowerCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"GATEWAY_API_KEY_VALUE {v:?} must be ASCII-alphanumeric \
throughout per the K8s API field-name grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
OpenAPI schema validator would reject"
);
}
#[test]
fn gateway_api_key_value_distinct_from_gateway_api_key_path() {
// Cross-axis invariant: the `HTTPPathMatch` scalar-payload key
// (`value`) and its parent-container-axis key (`path`) name
// *distinct* Gateway-API-side schema fields — the parent is a
// container that hangs off the per-`HTTPRouteMatch`
// `matches[]` entry, the child is the scalar payload that
// rides inside the parent's `{type, value}` two-axis body.
// Under the sibling K8s API conventions grammar
// (`gateway_api_key_value_carries_lower_camel_case_shape` /
// `gateway_api_key_path_carries_lower_camel_case_shape`) both
// are ASCII-lowerCamelCase identifiers, so a same-shape
// grammar-pin alone doesn't prevent a future rebrand from
// silently collapsing the two axes onto the same string —
// pinning inequality here surfaces that footgun at exactly
// this build-time lift instead of at apply time as an
// `HTTPRoute` whose per-match `path` container-body is
// structurally malformed (`{path: <str>, path: <str>}` — the
// apiserver's OpenAPI schema validator drops the whole match
// block, the per-match path predicate degrades to the
// wildcard match at the gateway-class-controller's per-rule
// reconcile, the rule matches every request path
// unconditionally, and every external `:entrada` path filter
// the rule was authored to enforce drops with no field
// naming the container/scalar-collapse root cause).
assert_ne!(
GATEWAY_API_KEY_VALUE, GATEWAY_API_KEY_PATH,
"GATEWAY_API_KEY_VALUE ({GATEWAY_API_KEY_VALUE:?}) must not \
collapse onto GATEWAY_API_KEY_PATH ({GATEWAY_API_KEY_PATH:?}) \
— the two name distinct Gateway API `HTTPPathMatch` axes \
(parent container vs. inner scalar payload) that must \
remain independently addressable in the emitted \
`HTTPRoute` per-match body"
);
}
#[test]
fn gateway_api_key_listeners_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Gateway API `Gateway` per-listener-set container-
// axis key the rendered Gateway document mounts its per-Gateway
// `[{name, port, protocol, hostname}]` L7-listener fan-out list
// under. The string is part of the cluster-side contract with
// every Gateway-API-conformant gateway implementation (Cilium,
// Istio, Envoy Gateway, NGINX) — the Gateway-API-implementation-
// side per-Gateway reconcile loop keys off this axis to source
// the per-Gateway L7-listener fan-out the external `:entrada`
// flow the Gateway was authored to accept lands on; a drifted
// value (`"listener"` / `"listen"` / `"servers"`) at either the
// production emitter or a downstream renderer's per-Gateway L7-
// listener-set upsert silently emits a `Gateway` whose L7-
// listener-set axis the Gateway API CRD schema validator drops
// as unknown — no listener is opened, and every external
// `:entrada` flow drops at the gateway-class-controller's per-
// Gateway reconcile with no field naming the L7-listener-set-
// drift root cause. Changing this value is a coordinated
// Gateway API promotion alongside the upstream SIG-Network
// Gateway API deprecation cycle, not an incidental edit. Peer
// to `gateway_api_key_parent_refs_pins_canonical_value` /
// `gateway_api_key_backend_refs_pins_canonical_value` on the
// sibling per-Gateway-API-CRD-body-axis canonical-string-pin
// surface — extends the per-Gateway-API-CRD-body-axis pin set
// (`parentRefs`, `backendRefs`, `listeners`, future
// `hostnames`) the M3 Aplicacao mesh renderer's external
// `:entrada` ingress contract rests on across the Gateway API
// CRD-side body-shape.
assert_eq!(GATEWAY_API_KEY_LISTENERS, "listeners");
}
#[test]
fn gateway_api_key_listeners_carries_lower_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD schema field name is a
// lowerCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
// "Field names should be lowercase camelCase") — first byte
// ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
// kebab-case or whitespace. Pinning the shape here means a
// future rebrand on the canonical lift can't silently land a
// malformed field-name discriminator (snake_case, kebab-case,
// UpperCamelCase, empty) that the apiserver-side CRD schema
// validator would reject far from the rebrand commit's source.
// Peer to `gateway_api_key_parent_refs_carries_lower_camel_case_shape`
// / `gateway_api_key_backend_refs_carries_lower_camel_case_shape`
// on the sibling per-Gateway-API-CRD-body-axis grammar-pin
// surface — the lowerCamelCase K8s field-name grammar governs
// every nested schema-field axis (including this per-Gateway
// L7-listener-set-container-axis key), same convention.
let v = GATEWAY_API_KEY_LISTENERS;
assert!(
!v.is_empty(),
"GATEWAY_API_KEY_LISTENERS {v:?} must be non-empty per the K8s API \
lowerCamelCase field-name grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_lowercase(),
"GATEWAY_API_KEY_LISTENERS {v:?} first byte {first:?} must be \
ASCII-lowercase per the K8s API lowerCamelCase field-name \
grammar (field names are always lowerCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"GATEWAY_API_KEY_LISTENERS {v:?} must be ASCII-alphanumeric \
throughout per the K8s API field-name grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
OpenAPI schema validator would reject"
);
}
#[test]
fn gateway_api_key_hostname_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Gateway API `Gateway` per-listener DNS-host-
// discriminator axis key the rendered Gateway document mounts
// each listener's virtual-host filter under. The string is part
// of the cluster-side contract with every Gateway-API-conformant
// gateway implementation (Cilium, Istio, Envoy Gateway, NGINX) —
// the Gateway-API-implementation-side per-listener SNI /
// `Host:`-header dispatch loop keys off this axis to source the
// per-listener virtual-host filter each listener's inbound
// traffic is scoped against; a drifted value (`"host"` /
// `"vhost"` / `"serverName"`) at either the production emitter
// or a downstream renderer's per-listener DNS-host-discriminator
// upsert silently emits a `Gateway` whose per-listener virtual-
// host filter axis the Gateway API CRD schema validator drops as
// unknown — the listener accepts traffic on the wildcard host
// rather than the typed `:entrada :host` the Aplicacao author
// declared, and every external `:entrada` flow the listener was
// authored to accept lands on the wrong virtual-host filter with
// no field naming the DNS-host-discriminator-drift root cause.
// Changing this value is a coordinated Gateway API promotion
// alongside the upstream SIG-Network Gateway API deprecation
// cycle, not an incidental edit. Peer to
// `gateway_api_key_listeners_pins_canonical_value` /
// `gateway_api_key_parent_refs_pins_canonical_value` /
// `gateway_api_key_backend_refs_pins_canonical_value` on the
// sibling per-Gateway-API-CRD-body-axis canonical-string-pin
// surface — nests the per-Gateway-API-CRD-body-axis pin
// discipline one level deeper onto the sibling per-listener
// body-axis surface, extending the per-Gateway-API-CRD-body-
// axis pin set (`parentRefs`, `backendRefs`, `listeners`,
// `hostname`, future `hostnames`) the M3 Aplicacao mesh
// renderer's external `:entrada` ingress contract rests on
// across the Gateway API CRD-side body-shape.
assert_eq!(GATEWAY_API_KEY_HOSTNAME, "hostname");
}
#[test]
fn gateway_api_key_hostname_carries_lower_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD schema field name is a
// lowerCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
// "Field names should be lowercase camelCase") — first byte
// ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
// kebab-case or whitespace. Pinning the shape here means a
// future rebrand on the canonical lift can't silently land a
// malformed field-name discriminator (snake_case, kebab-case,
// UpperCamelCase, empty) that the apiserver-side CRD schema
// validator would reject far from the rebrand commit's source.
// Peer to `gateway_api_key_listeners_carries_lower_camel_case_shape`
// / `gateway_api_key_parent_refs_carries_lower_camel_case_shape`
// / `gateway_api_key_backend_refs_carries_lower_camel_case_shape`
// on the sibling per-Gateway-API-CRD-body-axis grammar-pin
// surface — the lowerCamelCase K8s field-name grammar governs
// every nested schema-field axis (including this per-listener
// DNS-host-discriminator-axis key), same convention.
let v = GATEWAY_API_KEY_HOSTNAME;
assert!(
!v.is_empty(),
"GATEWAY_API_KEY_HOSTNAME {v:?} must be non-empty per the K8s API \
lowerCamelCase field-name grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_lowercase(),
"GATEWAY_API_KEY_HOSTNAME {v:?} first byte {first:?} must be \
ASCII-lowercase per the K8s API lowerCamelCase field-name \
grammar (field names are always lowerCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"GATEWAY_API_KEY_HOSTNAME {v:?} must be ASCII-alphanumeric \
throughout per the K8s API field-name grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
OpenAPI schema validator would reject"
);
}
#[test]
fn gateway_api_key_hostnames_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Gateway API `HTTPRoute` spec-level DNS-host-filter
// axis key the rendered HTTPRoute document mounts each route's
// per-route virtual-host filter list under. The string is part
// of the cluster-side contract with every Gateway-API-conformant
// gateway implementation (Cilium, Istio, Envoy Gateway, NGINX) —
// the Gateway-API-implementation-side per-route SNI /
// `Host:`-header dispatch loop keys off this axis to source the
// per-route virtual-host filter list each route's inbound
// traffic is scoped against; a drifted value (`"hosts"` /
// `"vhosts"` / `"serverNames"`) at either the production emitter
// or a downstream renderer's per-route DNS-host-filter upsert
// silently emits an `HTTPRoute` whose per-route virtual-host
// filter axis the Gateway API CRD schema validator drops as
// unknown — the route accepts traffic on every host the parent
// Gateway's listener accepts rather than the typed `:entrada
// :host` the Aplicacao author declared, and every external
// `:entrada` flow the route was authored to accept lands on the
// wildcard virtual-host filter with no field naming the DNS-
// host-filter-drift root cause. Changing this value is a
// coordinated Gateway API promotion alongside the upstream
// SIG-Network Gateway API deprecation cycle, not an incidental
// edit. Peer to
// `gateway_api_key_hostname_pins_canonical_value` /
// `gateway_api_key_listeners_pins_canonical_value` /
// `gateway_api_key_parent_refs_pins_canonical_value` /
// `gateway_api_key_backend_refs_pins_canonical_value` on the
// sibling per-Gateway-API-CRD-body-axis canonical-string-pin
// surface — closes the per-Gateway-API-CRD `HTTPRoute` per-route
// body-axis pin pair across the singular / plural DNS-host
// discriminator surface (`hostname` at the parent-Gateway per-
// listener discriminator + `hostnames` at the child HTTPRoute
// per-route filter list), so both halves of the DNS-host-
// discriminator convention across the `(Gateway, HTTPRoute)`
// pair the M3 Aplicacao mesh renderer's external `:entrada`
// ingress contract emits together now carry one lifted
// canonical-string pin apiece.
assert_eq!(GATEWAY_API_KEY_HOSTNAMES, "hostnames");
}
#[test]
fn gateway_api_key_hostnames_carries_lower_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD schema field name is a
// lowerCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
// "Field names should be lowercase camelCase") — first byte
// ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
// kebab-case or whitespace. Pinning the shape here means a
// future rebrand on the canonical lift can't silently land a
// malformed field-name discriminator (snake_case, kebab-case,
// UpperCamelCase, empty) that the apiserver-side CRD schema
// validator would reject far from the rebrand commit's source.
// Peer to `gateway_api_key_hostname_carries_lower_camel_case_shape`
// / `gateway_api_key_listeners_carries_lower_camel_case_shape`
// / `gateway_api_key_parent_refs_carries_lower_camel_case_shape`
// / `gateway_api_key_backend_refs_carries_lower_camel_case_shape`
// on the sibling per-Gateway-API-CRD-body-axis grammar-pin
// surface — the lowerCamelCase K8s field-name grammar governs
// every nested schema-field axis (including this per-route DNS-
// host-filter-axis key), same convention.
let v = GATEWAY_API_KEY_HOSTNAMES;
assert!(
!v.is_empty(),
"GATEWAY_API_KEY_HOSTNAMES {v:?} must be non-empty per the K8s API \
lowerCamelCase field-name grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_lowercase(),
"GATEWAY_API_KEY_HOSTNAMES {v:?} first byte {first:?} must be \
ASCII-lowercase per the K8s API lowerCamelCase field-name \
grammar (field names are always lowerCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"GATEWAY_API_KEY_HOSTNAMES {v:?} must be ASCII-alphanumeric \
throughout per the K8s API field-name grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
OpenAPI schema validator would reject"
);
}
#[test]
fn gateway_api_key_timeouts_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Gateway API `HTTPRoute` per-rule request-timeout-
// policy body-axis key the rendered HTTPRoute document mounts
// each rule's per-rule `:politicas :timeout` overlay under. The
// string is part of the cluster-side contract with every
// Gateway-API-conformant gateway implementation (Cilium, Istio,
// Envoy Gateway, NGINX) — the Gateway-API-implementation-side
// per-rule request-dispatch loop keys off this axis to source
// the per-rule wall-clock deadline each accepted request is
// bounded against; a drifted value (`"timeout"` (singular) /
// `"timeoutPolicy"` / `"deadlines"`) at either the production
// emitter or a downstream renderer's per-rule timeout-policy
// upsert silently emits an `HTTPRoute` whose per-rule request-
// timeout policy axis the Gateway API CRD schema validator
// drops as unknown — the route accepts every inbound request
// with no per-rule wall-clock deadline (the "no infinite
// blocking" guarantee MESH-COMPOSITION.md §V mandates for every
// rendered per-`:politicas` mesh-composition edge silently
// regresses to the pre-overlay unbounded-request semantic), and
// every external `:entrada` flow the route was authored to
// bound by the typed `:politicas :timeout` slot runs to
// whatever backend deadline the resolved backend's downstream
// infrastructure picks with no field naming the per-rule-
// timeout-policy-drift root cause. Changing this value is a
// coordinated Gateway API promotion alongside the upstream
// SIG-Network Gateway API deprecation cycle, not an incidental
// edit. Peer to
// `gateway_api_key_hostnames_pins_canonical_value` /
// `gateway_api_key_hostname_pins_canonical_value` /
// `gateway_api_key_listeners_pins_canonical_value` /
// `gateway_api_key_parent_refs_pins_canonical_value` /
// `gateway_api_key_backend_refs_pins_canonical_value` on the
// sibling per-Gateway-API-CRD-body-axis canonical-string-pin
// surface — extends the per-Gateway-API-`HTTPRoute` per-rule
// body-axis pin set (`backendRefs`, future per-rule sibling
// axes) onto the load-bearing per-rule request-timeout-policy
// axis the M3 Aplicacao mesh renderer's per-`:politicas
// :timeout` overlay lands under.
assert_eq!(GATEWAY_API_KEY_TIMEOUTS, "timeouts");
}
#[test]
fn gateway_api_key_timeouts_carries_lower_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD schema field name is a
// lowerCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
// "Field names should be lowercase camelCase") — first byte
// ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
// kebab-case or whitespace. Pinning the shape here means a
// future rebrand on the canonical lift can't silently land a
// malformed field-name discriminator (snake_case, kebab-case,
// UpperCamelCase, empty) that the apiserver-side CRD schema
// validator would reject far from the rebrand commit's source.
// Peer to `gateway_api_key_hostnames_carries_lower_camel_case_shape`
// / `gateway_api_key_hostname_carries_lower_camel_case_shape`
// / `gateway_api_key_listeners_carries_lower_camel_case_shape`
// / `gateway_api_key_parent_refs_carries_lower_camel_case_shape`
// / `gateway_api_key_backend_refs_carries_lower_camel_case_shape`
// on the sibling per-Gateway-API-CRD-body-axis grammar-pin
// surface — the lowerCamelCase K8s field-name grammar governs
// every nested schema-field axis (including this per-rule
// request-timeout-policy-axis key), same convention.
let v = GATEWAY_API_KEY_TIMEOUTS;
assert!(
!v.is_empty(),
"GATEWAY_API_KEY_TIMEOUTS {v:?} must be non-empty per the K8s API \
lowerCamelCase field-name grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_lowercase(),
"GATEWAY_API_KEY_TIMEOUTS {v:?} first byte {first:?} must be \
ASCII-lowercase per the K8s API lowerCamelCase field-name \
grammar (field names are always lowerCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"GATEWAY_API_KEY_TIMEOUTS {v:?} must be ASCII-alphanumeric \
throughout per the K8s API field-name grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
OpenAPI schema validator would reject"
);
}
#[test]
fn gateway_api_key_retry_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Gateway API `HTTPRoute` per-rule retry-policy
// body-axis key the rendered HTTPRoute document mounts each
// rule's per-rule `:politicas :retries` overlay under. The
// string is part of the cluster-side contract with every
// Gateway-API-conformant gateway implementation (Cilium, Istio,
// Envoy Gateway, NGINX) — the Gateway-API-implementation-side
// per-rule request-dispatch loop keys off this axis to source
// the per-rule retry budget each failed backend attempt count
// is bounded against; a drifted value (`"retries"` (plural) /
// `"retryPolicy"` / `"budget"`) at either the production
// emitter or a downstream renderer's per-rule retry-policy
// upsert silently emits an `HTTPRoute` whose per-rule retry-
// budget axis the Gateway API CRD schema validator drops as
// unknown — the route accepts every inbound request with no
// per-rule retry budget (the "no infinite retrying without
// bound" guarantee MESH-COMPOSITION.md §V mandates for every
// rendered per-`:politicas` mesh-composition edge silently
// regresses to the pre-overlay unbounded-retry semantic), and
// every external `:entrada` flow the route was authored to cap
// by the typed `:politicas :retries` slot runs to whatever
// retry policy the resolved backend's downstream infrastructure
// picks with no field naming the per-rule-retry-policy-drift
// root cause. Changing this value is a coordinated Gateway API
// promotion alongside the upstream SIG-Network Gateway API
// deprecation cycle, not an incidental edit. Peer to
// `gateway_api_key_timeouts_pins_canonical_value` /
// `gateway_api_key_hostnames_pins_canonical_value` /
// `gateway_api_key_hostname_pins_canonical_value` /
// `gateway_api_key_listeners_pins_canonical_value` /
// `gateway_api_key_parent_refs_pins_canonical_value` /
// `gateway_api_key_backend_refs_pins_canonical_value` on the
// sibling per-Gateway-API-CRD-body-axis canonical-string-pin
// surface — closes the per-Gateway-API-`HTTPRoute`-per-rule
// `:politicas` overlay axis pair (`timeouts` for `:politicas
// :timeout`, `retry` for `:politicas :retries`) both
// MESH-COMPOSITION.md §V "no infinite blocking / no infinite
// retrying" guarantees rest on.
assert_eq!(GATEWAY_API_KEY_RETRY, "retry");
}
#[test]
fn gateway_api_key_retry_carries_lower_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD schema field name is a
// lowerCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
// "Field names should be lowercase camelCase") — first byte
// ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
// kebab-case or whitespace. Pinning the shape here means a
// future rebrand on the canonical lift can't silently land a
// malformed field-name discriminator (snake_case, kebab-case,
// UpperCamelCase, empty) that the apiserver-side CRD schema
// validator would reject far from the rebrand commit's source.
// Peer to `gateway_api_key_timeouts_carries_lower_camel_case_shape`
// / `gateway_api_key_hostnames_carries_lower_camel_case_shape`
// / `gateway_api_key_hostname_carries_lower_camel_case_shape`
// / `gateway_api_key_listeners_carries_lower_camel_case_shape`
// / `gateway_api_key_parent_refs_carries_lower_camel_case_shape`
// / `gateway_api_key_backend_refs_carries_lower_camel_case_shape`
// on the sibling per-Gateway-API-CRD-body-axis grammar-pin
// surface — the lowerCamelCase K8s field-name grammar governs
// every nested schema-field axis (including this per-rule
// retry-policy-axis key), same convention.
let v = GATEWAY_API_KEY_RETRY;
assert!(
!v.is_empty(),
"GATEWAY_API_KEY_RETRY {v:?} must be non-empty per the K8s API \
lowerCamelCase field-name grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_lowercase(),
"GATEWAY_API_KEY_RETRY {v:?} first byte {first:?} must be \
ASCII-lowercase per the K8s API lowerCamelCase field-name \
grammar (field names are always lowerCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"GATEWAY_API_KEY_RETRY {v:?} must be ASCII-alphanumeric \
throughout per the K8s API field-name grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
OpenAPI schema validator would reject"
);
}
#[test]
fn gateway_api_key_attempts_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Gateway API `HTTPRoute` per-rule retry-policy
// `attempts` leaf scalar-key the rendered HTTPRoute document
// mounts each rule's per-rule `:politicas :retries` typed `u32`
// attempt count under. The string is part of the cluster-side
// contract with every Gateway-API-conformant gateway
// implementation (Cilium, Istio, Envoy Gateway, NGINX) — the
// Gateway-API-implementation-side per-rule request-dispatch
// loop keys off this leaf to source the per-rule retry attempt
// budget each failed backend attempt count is bounded against;
// a drifted value (`"attempt"` (singular) / `"count"` /
// `"tries"` / `"maxAttempts"`) at either the production
// emitter or a downstream renderer's per-rule retry-attempts
// upsert silently emits an `HTTPRoute` whose per-rule retry-
// attempts leaf the Gateway API CRD schema validator drops as
// unknown — the retry sub-shape parses as an empty
// `HTTPRouteRetry` with the typed `u32` attempt count silently
// discarded, the route accepts every inbound request with no
// per-rule retry budget (the "no infinite retrying without
// bound" guarantee MESH-COMPOSITION.md §V mandates for every
// rendered per-`:politicas` mesh-composition edge silently
// regresses to the pre-overlay unbounded-retry semantic), and
// every external `:entrada` flow the route was authored to cap
// by the typed `:politicas :retries` slot runs to whatever
// retry policy the resolved backend's downstream infrastructure
// picks with no field naming the per-rule-retry-attempts-leaf-
// key-drift root cause. Changing this value is a coordinated
// Gateway API promotion alongside the upstream SIG-Network
// Gateway API deprecation cycle, not an incidental edit. Peer
// to `gateway_api_key_retry_pins_canonical_value` /
// `gateway_api_key_timeouts_pins_canonical_value` /
// `gateway_api_key_hostnames_pins_canonical_value` /
// `gateway_api_key_hostname_pins_canonical_value` /
// `gateway_api_key_listeners_pins_canonical_value` /
// `gateway_api_key_parent_refs_pins_canonical_value` /
// `gateway_api_key_backend_refs_pins_canonical_value` on the
// sibling per-Gateway-API-CRD-body-axis canonical-string-pin
// surface — closes the parent-leaf axis pair (`retry`
// container + `attempts` leaf) both MESH-COMPOSITION.md §V
// "no infinite retrying" guarantees rest on, one nesting
// level deeper than the parent per-rule retry-policy
// container axis (`retry`).
assert_eq!(GATEWAY_API_KEY_ATTEMPTS, "attempts");
}
#[test]
fn gateway_api_key_attempts_carries_lower_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD schema field name is a
// lowerCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
// "Field names should be lowercase camelCase") — first byte
// ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
// kebab-case or whitespace. Pinning the shape here means a
// future rebrand on the canonical lift can't silently land a
// malformed field-name discriminator (snake_case, kebab-case,
// UpperCamelCase, empty) that the apiserver-side CRD schema
// validator would reject far from the rebrand commit's source.
// Peer to `gateway_api_key_retry_carries_lower_camel_case_shape`
// / `gateway_api_key_timeouts_carries_lower_camel_case_shape`
// / `gateway_api_key_hostnames_carries_lower_camel_case_shape`
// / `gateway_api_key_hostname_carries_lower_camel_case_shape`
// / `gateway_api_key_listeners_carries_lower_camel_case_shape`
// / `gateway_api_key_parent_refs_carries_lower_camel_case_shape`
// / `gateway_api_key_backend_refs_carries_lower_camel_case_shape`
// on the sibling per-Gateway-API-CRD-body-axis grammar-pin
// surface — the lowerCamelCase K8s field-name grammar governs
// every nested schema-field axis (including this per-rule
// retry-attempts-leaf-key), same convention.
let v = GATEWAY_API_KEY_ATTEMPTS;
assert!(
!v.is_empty(),
"GATEWAY_API_KEY_ATTEMPTS {v:?} must be non-empty per the K8s API \
lowerCamelCase field-name grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_lowercase(),
"GATEWAY_API_KEY_ATTEMPTS {v:?} first byte {first:?} must be \
ASCII-lowercase per the K8s API lowerCamelCase field-name \
grammar (field names are always lowerCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"GATEWAY_API_KEY_ATTEMPTS {v:?} must be ASCII-alphanumeric \
throughout per the K8s API field-name grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
OpenAPI schema validator would reject"
);
}
#[test]
fn gateway_api_key_request_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Gateway API `HTTPRoute` per-rule request-timeout-
// policy `request` leaf scalar-key the rendered HTTPRoute
// document mounts each rule's per-rule `:politicas :timeout`
// typed K8s-duration string under. The string is part of the
// cluster-side contract with every Gateway-API-conformant
// gateway implementation (Cilium, Istio, Envoy Gateway, NGINX)
// — the Gateway-API-implementation-side per-rule request-
// dispatch loop keys off this leaf to source the per-rule
// request wall-clock deadline each inbound request is bounded
// against; a drifted value (`"deadline"` / `"requestTimeout"`
// / `"timeout"` / `"upstreamRequest"`) at either the production
// emitter or a downstream renderer's per-rule request-deadline
// upsert silently emits an `HTTPRoute` whose per-rule request-
// deadline leaf the Gateway API CRD schema validator drops as
// unknown — the timeouts sub-shape parses as an empty
// `HTTPRouteTimeouts` with the typed duration silently
// discarded, the route accepts every inbound request with no
// per-rule request deadline (the "no infinite blocking"
// guarantee MESH-COMPOSITION.md §V mandates for every rendered
// per-`:politicas` mesh-composition edge silently regresses to
// the pre-overlay unbounded-blocking semantic), and every
// external `:entrada` flow the route was authored to cap by
// the typed `:politicas :timeout` slot runs to whatever
// request-deadline the resolved backend's downstream
// infrastructure picks with no field naming the per-rule-
// request-deadline-leaf-key-drift root cause. Changing this
// value is a coordinated Gateway API promotion alongside the
// upstream SIG-Network Gateway API deprecation cycle, not an
// incidental edit. Peer to
// `gateway_api_key_attempts_pins_canonical_value` /
// `gateway_api_key_retry_pins_canonical_value` /
// `gateway_api_key_timeouts_pins_canonical_value` /
// `gateway_api_key_hostnames_pins_canonical_value` /
// `gateway_api_key_hostname_pins_canonical_value` /
// `gateway_api_key_listeners_pins_canonical_value` /
// `gateway_api_key_parent_refs_pins_canonical_value` /
// `gateway_api_key_backend_refs_pins_canonical_value` on the
// sibling per-Gateway-API-CRD-body-axis canonical-string-pin
// surface — closes the second parent-leaf axis pair
// (`timeouts` container + `request` leaf) both
// MESH-COMPOSITION.md §V "no infinite blocking / no infinite
// retrying" guarantees rest on, sibling to the parent-leaf
// pair (`retry` container + `attempts` leaf) closed in
// e2e136b.
assert_eq!(GATEWAY_API_KEY_REQUEST, "request");
}
#[test]
fn gateway_api_key_request_carries_lower_camel_case_shape() {
// Cross-axis invariant: a Kubernetes CRD schema field name is a
// lowerCamelCase identifier per the K8s API conventions
// (https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-conventions —
// "Field names should be lowercase camelCase") — first byte
// ASCII-lowercase, rest ASCII-alphanumeric, no snake_case or
// kebab-case or whitespace. Pinning the shape here means a
// future rebrand on the canonical lift can't silently land a
// malformed field-name discriminator (snake_case, kebab-case,
// UpperCamelCase, empty) that the apiserver-side CRD schema
// validator would reject far from the rebrand commit's source.
// Peer to `gateway_api_key_attempts_carries_lower_camel_case_shape`
// / `gateway_api_key_retry_carries_lower_camel_case_shape`
// / `gateway_api_key_timeouts_carries_lower_camel_case_shape`
// / `gateway_api_key_hostnames_carries_lower_camel_case_shape`
// / `gateway_api_key_hostname_carries_lower_camel_case_shape`
// / `gateway_api_key_listeners_carries_lower_camel_case_shape`
// / `gateway_api_key_parent_refs_carries_lower_camel_case_shape`
// / `gateway_api_key_backend_refs_carries_lower_camel_case_shape`
// on the sibling per-Gateway-API-CRD-body-axis grammar-pin
// surface — the lowerCamelCase K8s field-name grammar governs
// every nested schema-field axis (including this per-rule
// request-deadline-leaf-key), same convention.
let v = GATEWAY_API_KEY_REQUEST;
assert!(
!v.is_empty(),
"GATEWAY_API_KEY_REQUEST {v:?} must be non-empty per the K8s API \
lowerCamelCase field-name grammar"
);
let first = v.chars().next().expect("non-empty");
assert!(
first.is_ascii_lowercase(),
"GATEWAY_API_KEY_REQUEST {v:?} first byte {first:?} must be \
ASCII-lowercase per the K8s API lowerCamelCase field-name \
grammar (field names are always lowerCamelCase)"
);
assert!(
v.chars().all(|c| c.is_ascii_alphanumeric()),
"GATEWAY_API_KEY_REQUEST {v:?} must be ASCII-alphanumeric \
throughout per the K8s API field-name grammar — no \
snake_case, kebab-case, or whitespace bytes the apiserver-side \
OpenAPI schema validator would reject"
);
}
#[test]
fn default_namespace_is_a_valid_dns_1123_label() {
// Cross-axis invariant: the default namespace lands as
// `metadata.namespace` on every emitted K8s object across every
// renderer, and the K8s apiserver enforces the DNS-1123 label
// rule on every `metadata.namespace`. Pinning this here means
// a future rebrand on the canonical `DEFAULT_NAMESPACE`
// declaration can't silently land a value the apiserver
// refuses at the *first* renderer to apply against a cluster,
// far from the rebrand commit's source — the typed
// [`is_dns_1123_label`] floor rejects it at caixa-core build
// time on the canonical lift, before any renderer consumes
// the value. Same trajectory as `:membros :caixa` /
// `:placement :clusters` / `:contratos :de`/`:para` /
// `:entrada :para` / `:placement :affinity` (dfd4902 — the
// five typed-identifier axes on the Aplicacao surface that
// already land on this same `is_dns_1123_label` floor at
// their respective validate gates), now extended onto the
// canonical-namespace-default axis the renderers share.
assert!(
is_dns_1123_label(DEFAULT_NAMESPACE).is_ok(),
"DEFAULT_NAMESPACE {DEFAULT_NAMESPACE:?} must be a valid \
DNS-1123 label — every K8s apiserver-side schema enforces \
this rule on `metadata.namespace`"
);
}
#[test]
fn helm_chart_api_version_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Helm 3 chart-schema apiVersion the rendered
// `lareira-<nome>` `Chart.yaml` document declares at its
// top-level `apiVersion` axis. The string is part of the
// Helm-side contract with the Helm 3 chart-schema parser:
// `helm dependency build` / `helm lint` / `helm template`
// all resolve the chart under the Helm 3 v2 schema (permitting
// top-level `dependencies:`); a drifted value to the legacy
// Helm 2 `"v1"` schema (the pre-Helm-3 chart schema every
// upstream Helm-3-migration doc names) silently reroutes the
// rendered Chart.yaml through the Helm 2 parser, where the
// top-level `dependencies:` block is unknown and the chart's
// dep on the `pleme-computeunit` library chart never resolves
// — `helm dependency build` reports "no requirements found"
// and every `helm template` / `helm install` emits an empty
// release (no ComputeUnit / Service / ScaledObject resources
// land) far from the source caixa.lisp / the renderer's
// `build_chart_yaml` call site. Changing it is a coordinated
// Helm 4 chart-schema migration alongside the upstream Helm
// chart-schema deprecation cycle, not an incidental edit.
// Peer to `flux_helmrelease_api_version_pins_canonical_value`
// / `flux_gitrepository_api_version_pins_canonical_value` /
// `flux_kustomization_api_version_pins_canonical_value` /
// `gateway_api_api_version_pins_canonical_value` /
// `cilium_api_version_pins_canonical_value` on the sibling
// cluster-side-CRD-apiVersion-pin set — those pin the K8s
// apiserver-side `(apiVersion, kind)` `RESTMapper` contract,
// this one pins the Helm-side chart-schema-parser contract
// that gates every rendered `lareira-<nome>` chart's
// dependency resolution before any K8s resource lands.
assert_eq!(HELM_CHART_API_VERSION, "v2");
}
#[test]
fn helm_chart_api_version_carries_helm_3_chart_schema_shape() {
// Cross-axis invariant: the Helm 3 chart-schema apiVersion is
// a bare `v<digit>` version label (unlike the K8s CRD
// apiVersion — `<group>/<version>` — the sibling
// FLUX_HELMRELEASE_API_VERSION / GATEWAY_API_API_VERSION /
// CILIUM_API_VERSION lifts pin). The Helm-side chart-schema
// grammar carries no group prefix at all — the value is
// parsed as a plain schema-version discriminator against the
// Helm binary's built-in schema table (Helm 2 recognizes
// `"v1"`, Helm 3 recognizes both `"v1"` for legacy compat
// and `"v2"` for its native schema). Pinning the shape here
// means a future rebrand on the canonical lift can't silently
// land a K8s-CRD-shaped `group/version` value (e.g. an
// accidental copy-paste from the sibling FLUX / GATEWAY /
// CILIUM constants) that the Helm chart-schema parser would
// fail to recognize at `helm dependency build` /
// `helm lint` / `helm template` time. The `v<digit>+`
// invariant is the load-bearing Helm-side chart-schema
// typed-discovery contract: a value the Helm binary's
// chart-schema resolver consults to select the schema
// parser that reads the rest of the document. Peer to
// `flux_kind_helm_release_carries_upper_camel_case_shape`
// (which pins the K8s `RESTMapper` kind-grammar shape) —
// both close the "the shape of the lifted schema-version
// discriminator is grammatical, not just a byte-equal string"
// discipline at the lift site.
let v = HELM_CHART_API_VERSION;
assert!(
!v.is_empty(),
"HELM_CHART_API_VERSION {v:?} must be non-empty per the Helm \
chart-schema apiVersion grammar"
);
assert!(
!v.contains('/'),
"HELM_CHART_API_VERSION {v:?} must not contain `/` — the Helm-side \
chart-schema apiVersion is a bare `v<digit>` label with no group \
prefix, unlike the K8s CRD `<group>/<version>` shape the sibling \
FLUX_HELMRELEASE_API_VERSION / GATEWAY_API_API_VERSION / \
CILIUM_API_VERSION lifts carry"
);
let bytes = v.as_bytes();
assert_eq!(
bytes[0], b'v',
"HELM_CHART_API_VERSION {v:?} must start with `v` per the Helm \
chart-schema apiVersion grammar (`v1` for the legacy schema, \
`v2` for the Helm 3 schema — every accepted value the Helm \
binary's chart-schema resolver knows carries the `v` prefix)"
);
assert!(
bytes.len() >= 2,
"HELM_CHART_API_VERSION {v:?} must be at least 2 bytes (`v` + \
at least one digit) per the Helm chart-schema apiVersion \
grammar"
);
assert!(
bytes[1..].iter().all(u8::is_ascii_digit),
"HELM_CHART_API_VERSION {v:?} bytes after the leading `v` must be \
ASCII digits per the Helm chart-schema apiVersion grammar — \
no dots, no hyphens, no whitespace, no non-digit bytes the \
Helm binary's chart-schema resolver would reject"
);
}
#[test]
fn helm_chart_type_application_pins_canonical_value() {
// Pin the actual string so a typo in this lift can't silently
// rebrand the Helm 3 chart-schema `type` field's canonical
// `application` per-chart-kind discriminator scalar-value the
// rendered `lareira-<nome>` chart's Chart.yaml `type:` axis
// declares. The value is part of the cluster-side contract with
// Helm's per-release install-shape dispatch loop — the Helm
// chart-schema pins the per-chart-kind axis to the closed set
// `{"application", "library"}` (see
// https://helm.sh/docs/topics/charts/#chart-types), so a drifted
// value (`"Application"` / `"APPLICATION"` / `"app"` /
// `"workload"`) lands the rendered `lareira-<nome>` chart outside
// the schema's admitted set, and Helm's chart-schema parser
// silently treats the unrecognized value as the default
// `application` shape (masking the schema violation with no
// process-log drift-signal); worse, an accidental collapse onto
// the sibling `"library"` shape lands `lareira-<nome>` in the
// dependency-only install-shape Helm refuses to install directly
// ("Error: library charts cannot be installed"), dropping every
// per-Servico `helm install` / `helm upgrade` release cycle with
// no field naming the chart-kind-drift root cause. Changing this
// value is a coordinated Helm chart-schema promotion alongside
// the upstream Helm project's per-schema deprecation cycle, not
// an incidental edit. Peer to
// `helm_chart_api_version_pins_canonical_value` /
// `kube_protocol_tcp_pins_canonical_value` /
// `gateway_api_protocol_http_pins_canonical_value` /
// `cilium_auth_mode_required_pins_canonical_value` on the
// sibling canonical-Helm-chart-schema-axis + canonical-cluster-
// side-OpenAPI-schema-enum-value pin sets — pivots the
// canonical-enum-value single-sourcing discipline from the K8s-
// CR-side surfaces onto the Helm-chart-schema-enum-value axis
// every rendered Chart.yaml carries at its per-chart-kind
// discriminator field.
assert_eq!(HELM_CHART_TYPE_APPLICATION, "application");
}
#[test]
fn helm_chart_type_application_carries_lowercase_shape() {
// Cross-axis invariant: the Helm 3 chart-schema `type` field
// admits the closed set `{"application", "library"}` — every
// admitted value is all-ASCII-lowercase throughout per the
// upstream Helm project's per-enum-value naming convention
// (distinct from the sibling K8s-core `Protocol` OpenAPI schema
// enum's all-ASCII-uppercase per-value convention the
// `kube_protocol_tcp_carries_upper_case_shape` pin carries, and
// distinct from the sibling Gateway-API v1 `PathMatchType`
// OpenAPI schema enum's UpperCamelCase per-value convention the
// `gateway_api_path_match_type_path_prefix_carries_upper_camel_case_shape`
// pin carries — the three peer canonical-cluster-side-schema-
// enum-value conventions do not collapse). Same all-ASCII-
// lowercase shape as the sibling Cilium `MutualAuthenticationMode`
// enum-values the peer `cilium_auth_mode_required_carries_lowercase_shape`
// / `cilium_auth_mode_disabled_carries_lowercase_shape` pins
// enshrine — the two peer canonical-cluster-side-schema-enum-
// value all-lowercase conventions collapse on the shared byte-
// shape convention Helm and Cilium happen to share (independent
// upstream projects, coincidental convention agreement).
//
// Pinning the shape here means a future rebrand on the canonical
// lift can't silently land a malformed per-chart-kind scalar
// (uppercase `"APPLICATION"`, mixed-case `"Application"`, empty)
// that the Helm chart-schema parser would silently treat as the
// default `application` shape (masking the drift with no
// process-log signal).
let v = HELM_CHART_TYPE_APPLICATION;
assert!(
!v.is_empty(),
"HELM_CHART_TYPE_APPLICATION {v:?} must be non-empty per the \
Helm 3 chart-schema `type` field grammar"
);
assert!(
v.chars().all(|c| c.is_ascii_lowercase()),
"HELM_CHART_TYPE_APPLICATION {v:?} must be ASCII-lowercase \
throughout per the Helm 3 chart-schema per-chart-kind \
discriminator naming convention — no uppercase, mixed-case, \
or whitespace bytes the Helm chart-schema parser would \
silently treat as the default `application` shape (masking \
the drift with no process-log signal)"
);
}
#[test]
fn helm_chart_type_library_pins_canonical_value() {
// Pin the sibling closed-set arm of the Helm 3 chart-schema
// `type` field's admitted set `{"application", "library"}` (see
// https://helm.sh/docs/topics/charts/#chart-types). A drift on
// this const's value (an `"Library"` / `"LIBRARY"` /
// `"library-chart"` / `"lib"` typo, an accidental collapse onto
// the sibling [`HELM_CHART_TYPE_APPLICATION`] shape) would land
// a future per-Aplicacao library-chart emitter — the trajectory
// item the [`HELM_CHART_TYPE_APPLICATION`] docstring names as
// the natural next consumer of this const — outside the Helm
// chart-schema's admitted set, with the same silent-collapse-
// onto-`application`-default failure mode the peer
// [`HELM_CHART_TYPE_APPLICATION`] pin's docstring enumerates on
// the sibling closed-set arm (Helm's chart-schema parser
// silently treats an unrecognized `type:` value as the default
// `application` shape, so the misdeclared library chart installs
// as an application chart instead of surfacing the schema
// violation). Peer of
// `helm_chart_type_application_pins_canonical_value` on the
// sibling closed-set arm — the two pins together enshrine the
// full closed set at the substrate-side canonical surface, and
// the paired
// `helm_chart_type_application_and_library_are_distinct` pin
// (below) enforces the two arms never accidentally converge on
// the same byte-shape.
assert_eq!(HELM_CHART_TYPE_LIBRARY, "library");
}
#[test]
fn helm_chart_type_library_carries_lowercase_shape() {
// Cross-axis invariant: the Helm 3 chart-schema `type` field
// admits the closed set `{"application", "library"}` — every
// admitted value is all-ASCII-lowercase throughout per the
// upstream Helm project's per-enum-value naming convention.
// Same all-ASCII-lowercase shape the peer
// `helm_chart_type_application_carries_lowercase_shape` pin
// enshrines on the sibling closed-set arm — the two pins
// together enforce the shape-convention across the full
// canonical-Helm-chart-schema-per-chart-kind-discriminator
// closed set.
//
// Pinning the shape here means a future rebrand on the canonical
// lift can't silently land a malformed per-chart-kind scalar
// (uppercase `"LIBRARY"`, mixed-case `"Library"`, empty) that
// the Helm chart-schema parser would silently treat as the
// default `application` shape (masking the drift with no
// process-log signal, and installing the misdeclared library
// chart as an application chart instead of surfacing the
// schema violation at chart-consumption time).
let v = HELM_CHART_TYPE_LIBRARY;
assert!(
!v.is_empty(),
"HELM_CHART_TYPE_LIBRARY {v:?} must be non-empty per the \
Helm 3 chart-schema `type` field grammar"
);
assert!(
v.chars().all(|c| c.is_ascii_lowercase()),
"HELM_CHART_TYPE_LIBRARY {v:?} must be ASCII-lowercase \
throughout per the Helm 3 chart-schema per-chart-kind \
discriminator naming convention — no uppercase, mixed-case, \
or whitespace bytes the Helm chart-schema parser would \
silently treat as the default `application` shape (masking \
the drift with no process-log signal)"
);
}
#[test]
fn helm_chart_type_application_and_library_are_distinct() {
// Structural distinctness invariant on the closed-set pair the
// Helm 3 chart-schema `type` field admits (`{"application",
// "library"}`). The two arms name distinct per-chart-kind
// install shapes at the substrate-side Helm dispatch — an
// `application`-typed chart installs into a namespace as a
// workload while a `library`-typed chart is dependency-only
// and Helm refuses to install it directly ("Error: library
// charts cannot be installed") — so a future rebrand that
// accidentally collapsed the two consts onto the same
// byte-shape would land every consumer of one arm on the
// sibling's install semantic by construction: a rendered
// `lareira-<nome>` (application) chart that silently emitted
// `type: library` would drop every per-Servico
// `helm install` / `helm upgrade` release cycle with no field
// naming the chart-kind-drift root cause, and (symmetrically)
// a future per-Aplicacao library chart emitting
// `type: application` would be install-able as a workload
// when the substrate's install-shape dispatch expects it to
// fail with the library-charts-cannot-be-installed diagnostic.
// Pinning the distinctness here means a hypothetical future
// edit that accidentally converges the two arms (a copy-paste
// rebrand at one lift that stops at the peer const declaration,
// a substrate-wide vocabulary shift that lands one arm without
// its paired peer) surfaces at caixa-core build time rather
// than as a chart-install-shape drift far from the source
// commit. Same "closed-set arms are byte-distinct by
// construction" discipline the peer
// [`crate::CILIUM_AUTH_MODE_REQUIRED`] /
// [`crate::CILIUM_AUTH_MODE_DISABLED`] pair carries on the
// sibling two-arm Cilium `MutualAuthenticationMode` OpenAPI
// enum closed set.
assert_ne!(
HELM_CHART_TYPE_APPLICATION, HELM_CHART_TYPE_LIBRARY,
"HELM_CHART_TYPE_APPLICATION ({HELM_CHART_TYPE_APPLICATION:?}) and \
HELM_CHART_TYPE_LIBRARY ({HELM_CHART_TYPE_LIBRARY:?}) must remain \
byte-distinct — the two arms name the two install shapes of the \
Helm 3 chart-schema `type` field's closed set {{\"application\", \
\"library\"}} and every substrate-side consumer that dispatches \
on the per-chart-kind axis relies on the two byte-shapes \
distinguishing the workload-install-shape arm from the \
dependency-only-install-shape arm"
);
}
#[test]
fn helm_chart_key_api_version_pins_canonical_value() {
// Pin the actual byte-string so a typo in this lift can't
// silently rebrand the Helm 3 `Chart.yaml` top-level chart-
// schema-apiVersion YAML axis-key the rendered `lareira-<nome>`
// chart declares. The string is part of the substrate-side
// contract with Helm's chart-schema parser at
// `helm dependency build` / `helm lint` / `helm template` /
// `helm install` time: the parser looks up the per-chart
// chart-schema-apiVersion scalar under exactly this top-level
// YAML key (Helm's chart-schema treats a missing `apiVersion:`
// top-level scalar as an "apiVersion is required" hard error,
// and Helm 3's chart-schema-version-router silently defaults
// an unrecognized top-level apiVersion-carrier key to Helm 2
// parsing shape). A drift on this const's value (an accidental
// collapse onto `"ApiVersion"` / `"apiversion"` /
// `"schemaVersion"` / the empty string) would silently reroute
// the rendered `Chart.yaml` through the wrong chart-schema
// parser at `helm dependency build` / `helm lint` /
// `helm template` time. Peer to
// `helm_chart_api_version_pins_canonical_value` on the sibling
// axis-value canonical pin — completes the per-Chart.yaml
// chart-schema-apiVersion axis's `(key, value)` canonical-pin
// pair at the substrate.
assert_eq!(HELM_CHART_KEY_API_VERSION, "apiVersion");
}
#[test]
fn helm_chart_key_api_version_matches_kube_key_api_version() {
// Load-bearing byte-shape coincidence between the Helm 3
// `Chart.yaml` top-level chart-schema-apiVersion YAML axis-key
// ([`HELM_CHART_KEY_API_VERSION`]) and the K8s-CR top-level
// per-CR schema-apiVersion YAML axis-key ([`KUBE_KEY_API_VERSION`])
// — Helm inherits the K8s CR top-level shape verbatim (see
// https://helm.sh/docs/topics/charts/#the-chartyaml-file), so
// every consumer that navigates a Chart.yaml top-level mapping
// by the schema-apiVersion key and every consumer that
// navigates a K8s CR top-level mapping by the schema-apiVersion
// key both read the byte-identical `"apiVersion"` key. The two
// axes are structurally-independent schema surfaces (the Helm 3
// chart-schema top-level shape vs. the K8s apiserver-side CR
// top-level shape), so the substrate carries two distinct
// `pub const` symbols; this pin makes the byte-shape
// coincidence load-bearing rather than accidental so a future
// K8s-side rebrand at [`KUBE_KEY_API_VERSION`] (or a Helm-side
// rebrand at [`HELM_CHART_KEY_API_VERSION`]) that dropped the
// byte-identity would fail the pin at substrate-build time
// rather than as a silent Helm-chart-schema-parser rejection
// at `helm lint` / `helm template` time far from the drift
// site. Complementary to the sibling
// [`helm_chart_key_type_is_byte_distinct_from_kube_key_kind`]
// pin — that peer asserts the per-chart-kind discriminator key
// pair is byte-distinct across the two schema surfaces (the
// Chart.yaml `type:` axis vs. the K8s CR `kind:` axis), and
// this pin asserts the per-schema-apiVersion axis-key pair is
// byte-identical across the two schema surfaces; together the
// two pins cover the full independence-map of the top-level
// discriminator axes at the two schema surfaces.
assert_eq!(
HELM_CHART_KEY_API_VERSION, KUBE_KEY_API_VERSION,
"HELM_CHART_KEY_API_VERSION ({HELM_CHART_KEY_API_VERSION:?}) \
must remain byte-identical to KUBE_KEY_API_VERSION \
({KUBE_KEY_API_VERSION:?}) — Helm 3 inherits the K8s CR \
top-level schema-apiVersion YAML-axis-key byte-shape \
verbatim, and every downstream consumer that navigates a \
`Chart.yaml` / K8s CR top-level mapping by the schema-\
apiVersion key reads the byte-identical `\"apiVersion\"` \
key; a drift on either side silently reroutes the \
consumer through a schema-parser rejection far from the \
drift site"
);
}
#[test]
fn helm_chart_key_type_pins_canonical_value() {
// Pin the actual byte-string so a typo in this lift can't silently
// rebrand the Helm 3 `Chart.yaml` top-level per-chart-kind
// discriminator YAML axis-key the rendered `lareira-<nome>` chart
// declares. The string is part of the substrate-side contract with
// Helm's chart-schema parser at `helm dependency build` /
// `helm lint` / `helm template` / `helm install` time: the parser
// looks up the per-chart-kind discriminator scalar under exactly
// this top-level YAML key, and a drift on this const's value
// (an accidental collapse onto `"Type"` / `"chartType"` /
// `"kind"`, or the empty string) would silently reroute the
// rendered `Chart.yaml` through the schema-shape-defaulting arm
// of Helm's parser (unknown top-level keys default the
// per-chart-kind axis to `application` with no process-log
// signal). Peer to
// `helm_chart_type_application_pins_canonical_value` /
// `helm_chart_type_library_pins_canonical_value` on the sibling
// axis-value canonical pin pair — completes the per-Chart.yaml
// per-chart-kind discriminator axis's `(key, value-set)`
// canonical-pin trio at the substrate.
assert_eq!(HELM_CHART_KEY_TYPE, "type");
}
#[test]
fn helm_chart_key_type_is_byte_distinct_from_kube_key_kind() {
// Structural distinctness invariant: the Helm 3 `Chart.yaml`
// top-level per-chart-kind YAML axis-key
// ([`HELM_CHART_KEY_TYPE`]) and the K8s CR top-level per-CRD
// kind-discriminator YAML axis-key ([`KUBE_KEY_KIND`]) name
// two structurally-independent axes at two structurally-
// independent schema surfaces — the Helm-side chart-schema
// top-level shape and the K8s-apiserver-side CR top-level
// shape — and every substrate-side renderer that emits or
// navigates a `Chart.yaml` vs. a K8s CR YAML relies on the
// two byte-shapes distinguishing the two schema-surfaces at
// its top-level mapping-key resolution. A hypothetical future
// rebrand that accidentally aliased [`HELM_CHART_KEY_TYPE`]
// at [`KUBE_KEY_KIND`]'s canonical would collapse the
// per-Chart.yaml per-chart-kind discriminator axis onto the
// K8s-CR per-CRD kind-discriminator axis at every consumer,
// and Helm's chart-schema parser would silently drop the
// rebranded key (top-level `kind:` is not part of the Helm 3
// chart-schema's admitted set — the parser silently ignores
// it, defaulting the per-chart-kind axis to `application`
// with no process-log signal). Same "byte-distinct axis-keys
// at structurally-independent schema surfaces" discipline the
// peer [`CILIUM_KEY_PATH`] / [`GATEWAY_API_KEY_PATH`]
// (ef6114f / 9f45aa4) pair carries on the sibling Cilium-CRD-
// vs.-Gateway-API-per-HTTPRouteMatch path-matcher axis
// independence — extends the discipline from the two K8s-CR-
// side path-matcher schemas onto the Helm-side vs. K8s-side
// top-level discriminator-key axis pair.
assert_ne!(
HELM_CHART_KEY_TYPE, KUBE_KEY_KIND,
"HELM_CHART_KEY_TYPE ({HELM_CHART_KEY_TYPE:?}) and \
KUBE_KEY_KIND ({KUBE_KEY_KIND:?}) name the top-level \
discriminator keys of two structurally-independent schema \
surfaces (the Helm 3 chart-schema and the K8s apiserver-side \
CR schema) and must remain byte-distinct — a collapse \
silently reroutes the per-Chart.yaml per-chart-kind axis \
through the K8s-CR-shape-defaulting arm of Helm's parser"
);
}
#[test]
fn helm_chart_key_app_version_pins_canonical_value() {
// Pin the actual byte-string so a typo in this lift can't silently
// rebrand the Helm 3 `Chart.yaml` top-level per-chart-app-version
// YAML axis-key the rendered `lareira-<nome>` chart declares.
// The string is part of the substrate-side contract with Helm's
// chart-schema parser + every downstream chart-consumer that
// routes the underlying-application-version display onto the
// rendered chart's per-app-version field (Artifact Hub's per-
// chart-search index, `helm search` / `helm show chart` operator
// surfaces, the OCI-artifact-labels emitter every chart-publish
// pipeline exports). A drift on this const's value (`"AppVersion"`
// / `"applicationVersion"` / `"appversion"` / the empty string)
// would silently drop the underlying-application-version field
// from the parsed chart-metadata shape at every downstream
// consumer, with no process-log signal at the substrate-side
// emitter site. The `appVersion:` camelCase byte-shape is the
// load-bearing Helm chart-schema per-app-version YAML axis-key
// grammar the upstream Helm project pins. Peer to
// `helm_chart_key_type_pins_canonical_value` on the sibling
// per-Chart.yaml top-level YAML axis-key canonical pin surface —
// completes the per-Chart.yaml top-level YAML axis-key
// canonical-pin trio at the substrate for the three serde-
// rename-literal-only axes on [`caixa_helm::ChartYaml`] (the
// third top-level axis-key `apiVersion` lands under the peer
// [`HELM_CHART_KEY_API_VERSION`] pin whose byte-shape coincides
// with [`KUBE_KEY_API_VERSION`] by Helm's design decision to
// inherit the K8s CR top-level shape verbatim — the paired
// `helm_chart_key_api_version_matches_kube_key_api_version`
// pin makes the coincidence load-bearing rather than
// accidental).
assert_eq!(HELM_CHART_KEY_APP_VERSION, "appVersion");
}
#[test]
fn helm_chart_key_app_version_is_byte_distinct_from_helm_chart_key_version() {
// Structural distinctness invariant on the per-Chart.yaml top-
// level version-axis-key pair. The Helm 3 chart-schema pins two
// structurally-distinct version YAML axis-keys at the top-level
// of every `Chart.yaml`:
//
// - `version:` — the chart's own SemVer (incremented per
// release of the chart itself)
// - `appVersion:` — the underlying application's version
// (the version the containerized workload the chart
// installs advertises)
//
// At the caixa-helm renderer both YAML axes today draw from the
// caixa's `:versao` at `build_chart_yaml` (a caixa's per-caixa
// BLAKE3-closure identity binds chart + wasm-binary at exactly
// one release axis), but the Helm 3 chart-schema pins the two
// top-level YAML keys distinctly regardless — every downstream
// Helm-consumer (Artifact Hub's per-chart index, `helm search` /
// `helm show chart` surfaces) routes the two version-axis
// scalars onto distinct display fields. A hypothetical future
// rebrand that accidentally aliased [`HELM_CHART_KEY_APP_VERSION`]
// at the sibling per-Chart.yaml top-level `version:` key
// (`"version"`) would collapse the two YAML axes at the
// renderer's ChartYaml serialization, and Helm's chart-schema
// parser would silently read the app-version scalar under the
// chart-own-SemVer axis (the last `version:` key wins in
// `serde_yaml`'s emitted mapping under this drift), overwriting
// the chart's own SemVer at every downstream chart-consumer.
// Same "byte-distinct version-axis keys at the same schema
// surface" discipline the peer [`FLEET_PROGRAMS_KEY_VERSAO`] /
// [`FLEET_PROGRAMS_KEY_NAME`] pair carries on the sibling
// per-fleet-programs-entry axis pair — extends the discipline
// from the per-fleet-programs-entry key-pair onto the per-
// Chart.yaml top-level version-axis-key pair.
assert_ne!(
HELM_CHART_KEY_APP_VERSION, "version",
"HELM_CHART_KEY_APP_VERSION ({HELM_CHART_KEY_APP_VERSION:?}) \
must remain byte-distinct from the sibling per-Chart.yaml \
top-level chart-own-SemVer `version:` key — a collapse \
silently overwrites the chart's own SemVer at every \
downstream Helm chart-consumer"
);
}
#[test]
fn helm_chart_key_dependencies_pins_canonical_value() {
// Pin the actual byte-string so a typo in this lift can't silently
// rebrand the Helm 3 `Chart.yaml` top-level per-chart dependency-
// list YAML axis-key the rendered `lareira-<nome>` chart declares.
// The string is part of the substrate-side contract with Helm's
// chart-schema parser — every rendered chart's `dependencies:`
// list-container mounts under this exact byte-shape, and Helm's
// per-dep resolver at `helm dependency build` / `helm dependency
// update` time consumes the per-entry sub-mapping tetrad only if
// the top-level list-container key matches this canonical shape.
// A drift on this const's value (`"Dependencies"` / `"deps"` /
// `"chartDependencies"` / `"depends"` / the empty string) would
// silently drop the entire per-chart dep list from the parsed
// chart-metadata shape, and every rendered `lareira-<nome>`
// chart's install would fail with `template: no template ...
// associated with template ...` far from the drift site with
// no field naming the top-level-list-key-drift root cause. Peer
// to [`helm_chart_key_type_pins_canonical_value`] /
// [`helm_chart_key_app_version_pins_canonical_value`] /
// [`helm_chart_key_api_version_pins_canonical_value`] on the
// sibling per-Chart.yaml top-level YAML axis-key canonical-pin
// surface — extends the per-Chart.yaml top-level YAML axis-key
// canonical-pin trio those pins established onto the fourth
// top-level axis-key at the substrate, the parent list-container
// whose already-lifted per-`dependencies[]`-entry sub-mapping
// tetrad ([`HELM_CHART_DEPENDENCY_KEY_NAME`] /
// [`HELM_CHART_DEPENDENCY_KEY_VERSION`] /
// [`HELM_CHART_DEPENDENCY_KEY_REPOSITORY`] /
// [`HELM_CHART_DEPENDENCY_KEY_ALIAS`]) mounts one level down.
assert_eq!(HELM_CHART_KEY_DEPENDENCIES, "dependencies");
}
#[test]
fn helm_chart_key_dependencies_is_byte_distinct_from_per_dep_sub_mapping_tetrad() {
// Structural distinctness invariant on the per-Chart.yaml
// `dependencies:` parent list-container axis-key vs. the four
// already-lifted per-entry sub-mapping keys mounted one level
// down. The parent+children pair spans two schema-nested YAML
// levels — the top-level `dependencies:` list-container and
// the per-entry sub-mapping `{name, version, repository,
// alias}` — and Helm's chart-schema parser navigates them as
// two structurally-independent axes: a collapse of the parent
// axis-key onto any child (e.g. an accidental future rebrand
// that renamed the [`HELM_CHART_KEY_DEPENDENCIES`] value to
// `"name"` or `"version"`) would either drop the entire per-
// chart dep list at the top-level parse (the child scalar
// silently masks the parent list-container the schema expects)
// or read the top-level list under a scalar-shaped axis-key
// and reject the chart at `helm lint` with a shape mismatch
// far from the drift site. Same "parent list-container
// axis-key must remain byte-distinct from every child sub-
// mapping axis-key" discipline the peer
// [`SUPERVISOR_KEY_CHILDREN`] parent axis-key already carries
// against the sibling [`SUPERVISOR_CHILD_KEY_CAIXA`] /
// [`SUPERVISOR_CHILD_KEY_VERSAO`] / [`SUPERVISOR_CHILD_KEY_RESTART`]
// per-entry sub-mapping triad on the M2 typed
// `:supervisor :children` surface — extends the discipline onto
// the Helm 3 `Chart.yaml` per-chart-dependency-list surface.
for child in [
HELM_CHART_DEPENDENCY_KEY_NAME,
HELM_CHART_DEPENDENCY_KEY_VERSION,
HELM_CHART_DEPENDENCY_KEY_REPOSITORY,
HELM_CHART_DEPENDENCY_KEY_ALIAS,
] {
assert_ne!(
HELM_CHART_KEY_DEPENDENCIES, child,
"HELM_CHART_KEY_DEPENDENCIES \
({HELM_CHART_KEY_DEPENDENCIES:?}) must remain \
byte-distinct from every per-`dependencies[]`-entry \
sub-mapping key ({child:?}) — a collapse silently \
orphans the parent list-container at `helm lint` / \
`helm dependency build` time"
);
}
}
#[test]
fn helm_chart_dependency_key_tetrad_pins_canonical_values() {
// Byte-string pin on the per-`dependencies[]`-entry sub-mapping
// YAML axis-key tetrad the Helm 3 chart-schema pins for every
// per-dep entry the substrate emits under the top-level
// `dependencies:` list at every rendered `lareira-<nome>`
// Chart.yaml. The four axis-keys name the four load-bearing
// per-dep sub-mapping fields Helm's per-dep resolver consumes
// at `helm dependency build` / `helm dependency update` time:
// `name` (the Helm-registry chart name), `version` (the SemVer-
// range constraint), `repository` (the registry URL to fetch
// from), and `alias` (the per-dep values wrap-key override).
// A drift on any const's value (a typo on this lift, a case
// flip to `"Name"` / `"Version"` / `"Repository"` / `"Alias"`,
// an accidental collapse onto a sibling axis-key) would
// silently rebrand the wire key at the `caixa_helm::ChartYaml`
// emitter site — Helm's chart-schema parser silently drops
// the drifted per-dep sub-mapping field, and the per-dep
// resolver falls back to the parsed-shape defaults
// (`""` / wildcard `*` / "no repository defined") at
// `helm dependency build` time far from the drift site. Peer
// to [`supervisor_child_key_tetrad_pins_canonical_values`] on
// the sibling per-`:children` sub-mapping tetrad (ef912df) and
// [`entrada_key_tetrad_pins_canonical_values`] on the sibling
// per-`:entrada` sub-mapping tetrad (a3d6162).
assert_eq!(HELM_CHART_DEPENDENCY_KEY_NAME, "name");
assert_eq!(HELM_CHART_DEPENDENCY_KEY_VERSION, "version");
assert_eq!(HELM_CHART_DEPENDENCY_KEY_REPOSITORY, "repository");
assert_eq!(HELM_CHART_DEPENDENCY_KEY_ALIAS, "alias");
}
#[test]
fn helm_chart_dependency_key_name_matches_kube_key_name() {
// Load-bearing byte-shape coincidence between the Helm 3
// Chart.yaml per-`dependencies[]`-entry sub-mapping name key
// ([`HELM_CHART_DEPENDENCY_KEY_NAME`]) and the K8s CR
// per-`metadata` sub-mapping name key ([`KUBE_KEY_NAME`]) —
// Helm inherits the K8s CR body-key vocabulary at every schema
// surface it consumes (chart-metadata top-level, per-CR
// install-payload, per-dep dependency-list). The two axes are
// structurally-independent schema surfaces (the Helm 3
// chart-schema per-dep entry vs. the K8s apiserver-side CR
// metadata block) whose byte-shapes happen to coincide today;
// this pin makes the byte-shape coincidence load-bearing
// rather than accidental so a future K8s-side rebrand at
// [`KUBE_KEY_NAME`] (or a Helm-side rebrand at
// [`HELM_CHART_DEPENDENCY_KEY_NAME`]) that dropped the
// byte-identity would fail the pin at substrate-build time
// rather than as a silent Helm-per-dep-resolver drop at
// `helm dependency build` time far from the drift site. Same
// discipline as the peer
// [`helm_chart_key_api_version_matches_kube_key_api_version`]
// pin on the sibling top-level chart-schema-apiVersion axis
// (cc44e4b) — extends the axis-key byte-identity coincidence
// discipline from the per-Chart.yaml top-level shape onto the
// per-`dependencies[]`-entry sub-mapping shape.
assert_eq!(
HELM_CHART_DEPENDENCY_KEY_NAME, KUBE_KEY_NAME,
"HELM_CHART_DEPENDENCY_KEY_NAME ({HELM_CHART_DEPENDENCY_KEY_NAME:?}) \
must remain byte-identical to KUBE_KEY_NAME ({KUBE_KEY_NAME:?}) — \
Helm 3 inherits the K8s CR body-key vocabulary at every schema \
surface, and every downstream consumer that navigates a per-dep \
sub-mapping / a K8s CR metadata block by the `name` key reads the \
byte-identical `\"name\"` key; a drift on either side silently \
reroutes the consumer through a schema-parser drop far from the \
drift site"
);
}
#[test]
fn helm_chart_readme_filename_pins_canonical_value() {
// Pin the actual byte-string so a typo on the canonical lift
// can't silently rebrand the third leg of the per-`lareira-<nome>`
// chart-directory `{Chart.yaml, values.yaml, README.md}`
// canonical-per-chart-directory-filename axis triple. Peer to
// the sibling
// [`HELM_CHART_YAML_FILENAME`] / [`HELM_VALUES_YAML_FILENAME`]
// canonical filename axes — the two schema-load-bearing halves
// of the triple the sibling
// [`HELM_VALUES_YAML_FILENAME`] docstring's closing paragraph
// explicitly names as the pair that needed the third-leg
// (`README.md`) filename half to close the discipline across
// every `ChartFile` the [`caixa_helm::render_chart_for_servico`]
// emitter's `ChartDir::files` vec carries. A drifted per-chart
// readme filename value would surface downstream as GitHub /
// Artifact Hub / any per-chart README-surfacing UI silently
// falling back to "no README available" for the rendered
// `lareira-<nome>` chart — the chart lists with no per-chart
// elevator pitch or install instructions far from the drift
// commit's source, with no field naming the readme-filename-
// drift root cause. Same pin discipline as the peer
// canonical-Helm-per-chart-directory-filename axes.
assert_eq!(HELM_CHART_README_FILENAME, "README.md");
}
#[test]
fn helm_chart_readme_filename_carries_readme_dot_md_shape() {
// Cross-axis invariant: the per-`lareira-<nome>`-chart-directory
// human-facing readme filename carries the `.md` Markdown
// extension the [`caixa_helm::build_readme`] emitter's Markdown-
// shaped body targets — a drift to `.txt` / `.rst` /
// extensionless / a per-fork rename would silently reroute the
// rendered readme through a downstream tool that reads by
// extension for its Markdown renderer (GitHub's per-repo README
// surfacer, Artifact Hub's per-chart README surfacer, every
// per-chart-directory `find . -name README.md` navigator any
// downstream tooling might use). Peer to the sibling
// [`HELM_CHART_YAML_FILENAME`] / [`HELM_VALUES_YAML_FILENAME`]
// schema-load-bearing filename halves — the two YAML halves
// carry the `.yaml` extension per Helm's per-chart-schema
// convention; the readme half carries the `.md` extension per
// the substrate's per-chart human-facing convention. Distinct
// per-half schema conventions do not collapse on the shared
// `<name>.<ext>` shape gate.
let v = HELM_CHART_README_FILENAME;
assert!(
!v.is_empty(),
"HELM_CHART_README_FILENAME {v:?} must be non-empty per the \
per-`lareira-<nome>`-chart-directory readme-file axis"
);
assert!(
v.ends_with(".md"),
"HELM_CHART_README_FILENAME {v:?} must carry the `.md` \
Markdown extension per the substrate's per-chart human-\
facing readme convention — a drifted extension (`.txt` / \
`.rst` / extensionless) would silently reroute downstream \
tooling's Markdown renderer (GitHub's per-repo README \
surfacer, Artifact Hub's per-chart README surfacer) to a \
non-Markdown fallback path"
);
}
// ── lareira-<nome> chart-name prefix lift ──────────────────────
//
// The lift pins the substrate-wide `lareira-` chart-name prefix
// as the single source of truth every per-Servico renderer
// (caixa-helm, caixa-flux, caixa-tatara) reaches for, peer to the
// [`DEFAULT_NAMESPACE`] (a085b26) lift on the canonical-namespace
// axis. Pinning the prefix value, the helper's
// construction-shape, and the DNS-1123-label round-trip for the
// canonical-fixture input forms the structural floor every future
// renderer consumer inherits by construction.
#[test]
fn lareira_chart_name_prefix_pins_canonical_value() {
// Pin the actual string value so a typo on the canonical lift
// can't silently rebrand the substrate's per-Servico Helm chart
// namespace. The string is part of the contract with the OCI
// chart-publishing pipeline (`oci://<registry>/lareira-<nome>`),
// the per-cluster HelmRelease `chart:` field (which Flux
// resolves through the OCI ref), and the historical
// `pleme-io/helmworks/charts/lareira-<name>/` source tree
// layout (caixa-helm/src/lib.rs:7); changing it is a
// coordinated multi-repo migration, not an incidental edit.
// Peer to `default_namespace_pins_canonical_value` on the
// canonical-string-value-pin axis for the
// `DEFAULT_NAMESPACE` constant.
assert_eq!(LAREIRA_CHART_NAME_PREFIX, "lareira-");
}
#[test]
fn lareira_chart_name_composes_prefix_and_nome() {
// Pin the helper's construction shape — the chart name is the
// prefix concatenated with the caixa's `:nome` verbatim, with
// no intermediate hyphen, no path separator, no trimming. Pin
// the canonical hello-rio fixture (the in-tree
// `caixa-helm` test fixture at caixa-helm/src/lib.rs:431
// already asserts `dir.name == "lareira-hello-rio"`, which
// this helper now derives) and a peer fixture
// (`checkout-aplicacao` member) to sweep the typical author
// surface.
assert_eq!(lareira_chart_name("hello-rio"), "lareira-hello-rio");
assert_eq!(lareira_chart_name("cart"), "lareira-cart");
assert_eq!(lareira_chart_name("worker"), "lareira-worker");
}
#[test]
fn lareira_chart_name_starts_with_prefix() {
// Cross-axis invariant: every output of the helper begins with
// the lifted prefix verbatim — a future refactor that
// accidentally introduced a different prefix-application
// shape (e.g. `format!("{nome}-lareira")` transposition, or a
// `to_uppercase()` case fold) would surface here. The
// structural pin holds for the empty `:nome` shape too
// (a value `validate_nome` rejects upstream, but the helper
// itself imposes no shape on the input).
for nome in ["hello-rio", "cart", "worker", "a", ""] {
let chart = lareira_chart_name(nome);
assert!(
chart.starts_with(LAREIRA_CHART_NAME_PREFIX),
"lareira_chart_name({nome:?}) = {chart:?} must start with the lifted prefix \
{LAREIRA_CHART_NAME_PREFIX:?}"
);
}
}
#[test]
fn lareira_chart_name_round_trips_through_dns_1123_for_validated_nome() {
// Cross-axis invariant: every `:nome` past
// [`Caixa::validate_nome`] (6c992f8) is a valid DNS-1123 label,
// and the prepended `lareira-` segment is itself a valid
// DNS-1123 label prefix (lowercase ASCII + hyphen with a
// terminating-hyphen continuation). The composition therefore
// round-trips through [`is_dns_1123_label`] for every
// `:nome` whose joint length with the prefix stays ≤ 63 bytes
// (the DNS-1123 label cap). The canonical author surface sits
// far below that cap (the in-tree fixtures range from
// `"a"` = 9-byte chart name to `"checkout"` = 16 bytes, with
// the cap admitting up to 55-byte `:nome` values). Pin the
// round-trip for the canonical-fixture set so a future renderer
// that lands the helper's output verbatim as a K8s
// `metadata.name` (caixa-helm's `ChartDir.name`,
// caixa-flux's HelmRelease `chart:` field, caixa-tatara's
// `release_name`) inherits the apiserver-valid floor by
// construction.
for nome in ["hello-rio", "cart", "worker", "checkout", "a"] {
let chart = lareira_chart_name(nome);
assert!(
is_dns_1123_label(&chart).is_ok(),
"lareira_chart_name({nome:?}) = {chart:?} must be a valid DNS-1123 label"
);
}
}
#[test]
fn lareira_chart_name_prefix_is_a_valid_dns_1123_segment_continuation() {
// The lifted prefix is one substring of the rendered chart
// name; pin its grammar so a future rebrand can't land a
// value that would invalidate the joint DNS-1123 label
// structurally. The prefix must:
// - be lowercase ASCII alphanumeric + hyphen (the DNS-1123
// accepted set), so its bytes don't widen the joint
// accepted set;
// - end with a hyphen (so the concatenation slot doesn't
// accidentally merge with the leading character of the
// `:nome` it precedes).
assert!(
LAREIRA_CHART_NAME_PREFIX
.bytes()
.all(|b| b.is_ascii_lowercase() || b.is_ascii_digit() || b == b'-'),
"LAREIRA_CHART_NAME_PREFIX {LAREIRA_CHART_NAME_PREFIX:?} must use only DNS-1123-label \
bytes (lowercase ASCII alphanumeric + hyphen)"
);
assert!(
LAREIRA_CHART_NAME_PREFIX.ends_with('-'),
"LAREIRA_CHART_NAME_PREFIX {LAREIRA_CHART_NAME_PREFIX:?} must end with `-` so \
concatenation with the caixa's `:nome` produces a hyphenated joint label"
);
}
// ── is_lareira_chart_name_shape — joint-length budget on `:nome` ─────
//
// The canonical [`lareira_chart_name`] helper's own doc comment
// (f7320d7) explicitly defers: "the M4 admission webhook will pin
// the joint-length invariant when it lands". These tests land it
// at the manifest-validate layer instead — the predicate consults
// [`lareira_chart_name`] + [`is_dns_1123_label`] (no third primitive)
// so a future rebrand of either axis re-derives the budget
// mechanically and the test suite re-pins through the same lifts.
#[test]
fn lareira_chart_name_nome_max_len_pins_arithmetic() {
// Pin the arithmetic so a future shift in either input axis
// surfaces here. The const is mechanically derived from
// [`DNS_1123_LABEL_MAX_LEN`] (63 — the K8s apiserver cap every
// chart-name-derived `metadata.name` inherits) minus
// [`LAREIRA_CHART_NAME_PREFIX`].len() (8 — the canonical
// chart-name prefix the lift f7320d7 made structural). The
// landing value: 55 bytes the caixa's `:nome` may itself
// occupy under the joint chart-name cap.
assert_eq!(LAREIRA_CHART_NAME_NOME_MAX_LEN, 55);
assert_eq!(
LAREIRA_CHART_NAME_NOME_MAX_LEN,
DNS_1123_LABEL_MAX_LEN - LAREIRA_CHART_NAME_PREFIX.len()
);
}
#[test]
fn is_lareira_chart_name_shape_accepts_canonical_fixtures() {
// Positive control: every in-tree fixture `:nome` (caixa-helm,
// caixa-flux, caixa-mesh, caixa-tatara tests, the
// checkout-aplicacao example) sits far below the cap. The
// predicate must not regress this baseline shape.
for nome in [
"hello-rio",
"cart",
"worker",
"checkout",
"a",
"akeyless-attest",
] {
is_lareira_chart_name_shape(nome).unwrap_or_else(|e| {
panic!("canonical :nome {nome:?} must pass chart-name budget, got {e:?}")
});
}
}
#[test]
fn is_lareira_chart_name_shape_accepts_nome_at_budget() {
// Boundary-accepting case at the 55-byte cap — the joint
// chart name is exactly 63 bytes, the DNS-1123 label cap.
let at_cap = "a".repeat(LAREIRA_CHART_NAME_NOME_MAX_LEN);
assert_eq!(at_cap.len(), LAREIRA_CHART_NAME_NOME_MAX_LEN);
is_lareira_chart_name_shape(&at_cap).unwrap();
assert_eq!(lareira_chart_name(&at_cap).len(), DNS_1123_LABEL_MAX_LEN);
}
#[test]
fn is_lareira_chart_name_shape_rejects_nome_one_over_budget() {
// Fail-before-pass-after pin: 56 bytes is the smallest `:nome`
// length that overflows the joint chart-name cap. The inner
// [`is_dns_1123_label`] check accepts it (56 ≤ 63), so prior
// to this gate it silently passed `Caixa::validate_nome` and
// surfaced as a `helm lint` / apiserver rejection on the
// rendered chart name far from the source caixa.lisp.
let over = "a".repeat(LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
let err = is_lareira_chart_name_shape(&over).unwrap_err();
assert!(
err.contains("63") && err.contains("64") && err.contains("55"),
"diagnostic must name the DNS-1123 cap (63), the actual chart-name length (64), \
and the per-`:nome` budget (55), got {err:?}"
);
assert!(
err.contains("lareira-"),
"diagnostic must name the canonical prefix verbatim, got {err:?}"
);
}
#[test]
fn is_lareira_chart_name_shape_diagnostic_carries_offending_chart_name() {
// The rendered chart name appears verbatim in the diagnostic
// so the author sees exactly the string the apiserver would
// have rejected — no re-derivation required to grep the source.
let over = "x".repeat(LAREIRA_CHART_NAME_NOME_MAX_LEN + 5);
let err = is_lareira_chart_name_shape(&over).unwrap_err();
let expected_chart = lareira_chart_name(&over);
assert!(
err.contains(&expected_chart),
"diagnostic must carry the rendered chart name {expected_chart:?} verbatim, \
got {err:?}"
);
}
#[test]
fn is_lareira_chart_name_shape_composes_through_canonical_helper() {
// Cross-axis invariant: the predicate is defined exactly as
// `is_dns_1123_label(lareira_chart_name(nome))` for the length
// arm — no inline `format!("lareira-{nome}")` shape duplicating
// the canonical lift. Pinning this composition closes the
// drift footgun where a future predicate refactor re-inlines
// the prefix-and-`:nome` concatenation and diverges from the
// canonical helper. Sweep across the boundary so both sides
// (accept + reject) consult the same helper.
for delta in 0..=2usize {
let nome = "z".repeat(LAREIRA_CHART_NAME_NOME_MAX_LEN.saturating_sub(delta));
let predicate_ok = is_lareira_chart_name_shape(&nome).is_ok();
let canonical_ok = is_dns_1123_label(&lareira_chart_name(&nome)).is_ok();
assert_eq!(
predicate_ok,
canonical_ok,
"predicate / canonical-composition divergence for :nome of len {} \
(predicate_ok = {predicate_ok}, canonical_ok = {canonical_ok})",
nome.len()
);
}
}
// ── OCI chart-ref composer — `oci://<registry>/lareira-<nome>` ───────
//
// Peer to the `lareira_chart_name` composer above on the sibling
// OCI-artifact-reference axis. Until this lift landed the
// `caixa-tatara`'s `derive_chart_ref` carried an inline
// `format!("oci://{registry}/{chart}")` — a 2-axis composition
// (the `oci://` scheme prefix + the `lareira-<nome>` chart name)
// whose byte-shape had no compile-time link to the historical doc
// comments across `caixa-core`, `caixa-flux`, `caixa-helm`, and
// `caixa-tatara` promising the same shape. Pin the const, the
// composition equation, and the byte-shape against the prior
// inline `format!` so a future composer-internal drift fires at
// test time.
#[test]
fn oci_scheme_prefix_pins_canonical_value() {
// Pin the actual string value so a typo on the canonical lift
// can't silently rebrand the substrate's OCI-artifact-reference
// scheme. The string is part of the contract with the Helm 3
// OCI storage protocol (`helm push chart.tgz oci://…`,
// `helm registry login <registry>`, `helm install release
// oci://…`) and the FluxCD `HelmRepository` `type: oci` source
// (Flux source-controller keys off this literal on the OCI
// path); changing it is a coordinated multi-repo migration,
// not an incidental edit. Peer to
// [`lareira_chart_name_prefix_pins_canonical_value`] on the
// sibling canonical-string-value-pin axis.
assert_eq!(OCI_SCHEME_PREFIX, "oci://");
}
#[test]
fn oci_chart_ref_pins_byte_shape_against_prior_inline_format() {
// Byte-shape pin against the prior inline
// `format!("oci://{registry}/{chart}")` at
// caixa-tatara/src/lib.rs:202 (where `chart` was itself
// `lareira_chart_name(caixa.nome.as_str())`). Any future
// composer-internal drift on either axis (the `oci://` scheme
// prefix, the `/` scheme-authority separator, the composition
// with `lareira_chart_name`) surfaces here as a byte-shape
// regression rather than at cluster-apply time far from the
// drift site.
assert_eq!(
oci_chart_ref("ghcr.io/pleme-io/charts", "akeyless-attest"),
"oci://ghcr.io/pleme-io/charts/lareira-akeyless-attest"
);
assert_eq!(
oci_chart_ref("ghcr.io/pleme-io", "hello-rio"),
"oci://ghcr.io/pleme-io/lareira-hello-rio"
);
}
#[test]
fn oci_chart_ref_composes_through_canonical_helpers() {
// Structural composition equation: the OCI chart-ref is
// exactly `{OCI_SCHEME_PREFIX}{registry}/{lareira_chart_name(nome)}`
// — no inline `"oci://"` scheme literal, no inline
// `format!("lareira-{}", nome)` prefix duplication. Pinning
// this composition closes the drift footgun where a future
// composer refactor re-inlines either axis and diverges from
// its canonical source of truth. Sweep across the canonical
// fixture set so the composition holds for the same `:nome`
// values every peer per-Servico renderer consults.
for (registry, nome) in [
("ghcr.io/pleme-io/charts", "hello-rio"),
("ghcr.io/pleme-io", "cart"),
("registry.example.com", "worker"),
("localhost:5000", "checkout"),
] {
let composed = oci_chart_ref(registry, nome);
let expected = format!("{OCI_SCHEME_PREFIX}{registry}/{}", lareira_chart_name(nome));
assert_eq!(
composed, expected,
"oci_chart_ref({registry:?}, {nome:?}) must equal the canonical composition \
through OCI_SCHEME_PREFIX + lareira_chart_name"
);
}
}
#[test]
fn oci_chart_ref_starts_with_scheme_prefix() {
// Cross-axis invariant: every output of the composer begins
// with the lifted scheme prefix verbatim — a future refactor
// that accidentally introduced a different scheme (e.g. a
// `https://` transposition, or a scheme-authority separator
// drift) would surface here. Peer to
// [`lareira_chart_name_starts_with_prefix`] on the sibling
// per-composer prefix-anchoring axis.
for (registry, nome) in [
("ghcr.io/pleme-io/charts", "hello-rio"),
("ghcr.io/pleme-io", "cart"),
("localhost:5000", "a"),
] {
let composed = oci_chart_ref(registry, nome);
assert!(
composed.starts_with(OCI_SCHEME_PREFIX),
"oci_chart_ref({registry:?}, {nome:?}) = {composed:?} must start with the lifted \
prefix {OCI_SCHEME_PREFIX:?}"
);
}
}
#[test]
fn oci_chart_ref_contains_lareira_chart_name_verbatim() {
// Cross-axis invariant: every output of the composer contains
// the canonical `lareira_chart_name(nome)` output verbatim as
// its trailing segment — a future refactor that accidentally
// introduced a case fold, a hyphen-collapse, or a different
// prefix-application shape would surface here. Structurally
// pins that the OCI chart-ref path and the peer per-Servico
// renderer chart-name path (caixa-helm's `ChartDir.name`,
// caixa-flux's `HelmRelease` `chart:` field) both reach for
// the same canonical `lareira_chart_name` helper's output.
for (registry, nome) in [
("ghcr.io/pleme-io/charts", "hello-rio"),
("ghcr.io/pleme-io", "cart"),
] {
let composed = oci_chart_ref(registry, nome);
let chart = lareira_chart_name(nome);
assert!(
composed.ends_with(&chart),
"oci_chart_ref({registry:?}, {nome:?}) = {composed:?} must end with the canonical \
lareira_chart_name({nome:?}) = {chart:?}"
);
}
}
// ── Flux Kustomization source-sub-tree composer ───────────────────────
//
// Peer to the `oci_chart_ref` / `cilium_network_policy_name` /
// `gateway_api_http_route_name` composers above on the sibling
// canonical-load-bearing-scalar-that-consumers-key-off axis. Until
// this lift landed the two-axis composition
// (`./clusters/<cluster>/services/<nome>`) sat as an inline
// `format!` template at the sole `caixa-flux::cluster_bundle`
// `kustomization.yaml` production emit site plus a mirror-symmetric
// inline `format!` at its paired test-fixture navigation site — no
// compile-time link between the two sites and no compile-time link
// ahead of the second production-emit occurrence the M4
// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Aplicacao
// `Kustomization` synthesis will surface. Pin the byte-shape, the
// composition equation, and the sub-tree-scope invariants against
// the prior inline `format!` so a future composer-internal drift
// fires at test time.
#[test]
fn flux_kustomization_source_subtree_pins_byte_shape_against_prior_inline_format() {
// Byte-shape pin against the prior inline
// `format!("./clusters/{cluster}/services/{name}")` at
// caixa-flux/src/lib.rs (both the `cluster_bundle`
// `kustomization.yaml` `spec.path` production emit site and the
// paired `cluster_bundle_kustomization_path_pins_lifted_sub_tree`
// test-fixture navigation site). Any future composer-internal
// drift on either axis (the `./clusters/` per-cluster prefix,
// the `/services/` per-caixa infix, the trailing per-caixa
// suffix, the composition order) surfaces here as a byte-shape
// regression rather than at cluster-apply time far from the
// drift site.
assert_eq!(
flux_kustomization_source_subtree("rio", "hello-rio"),
"./clusters/rio/services/hello-rio"
);
assert_eq!(
flux_kustomization_source_subtree("paris", "cart"),
"./clusters/paris/services/cart"
);
assert_eq!(
flux_kustomization_source_subtree("tokyo", "checkout"),
"./clusters/tokyo/services/checkout"
);
}
#[test]
fn flux_kustomization_source_subtree_starts_with_relative_clusters_prefix() {
// Structural invariant: every output starts with the canonical
// `./clusters/` per-cluster-prefix half of the sub-tree seed.
// The leading `./` scopes the emit to the GitRepository root
// (the kustomize-controller keys the per-CR reconcile loop off
// the GitRepository the paired `sourceRef` names, so the sub-
// tree seed must resolve relative to the GitRepository root,
// not an absolute filesystem path). The `clusters/` component
// scopes the emit to the paired cluster's manifest set under
// the pleme-io k8s repository's canonical directory-tree
// layout.
for (cluster, nome) in [
("rio", "hello-rio"),
("paris", "cart"),
("tokyo", "checkout"),
] {
let sub = flux_kustomization_source_subtree(cluster, nome);
assert!(
sub.starts_with("./clusters/"),
"flux_kustomization_source_subtree({cluster:?}, {nome:?}) = {sub:?} must start \
with the canonical `./clusters/` GitRepository-root-relative per-cluster prefix"
);
}
}
#[test]
fn flux_kustomization_source_subtree_contains_paired_cluster_and_nome() {
// Cross-axis invariant: every output contains the paired
// `<cluster>` and `<nome>` scalars verbatim, at their canonical
// per-cluster / per-caixa sub-tree positions. A future
// composer-internal drift that accidentally case-folded, hyphen-
// collapsed, or transposed either axis (`./clusters/rio/services/hello-rio`
// → `./clusters/hello-rio/services/rio` under a swapped
// composition, `./clusters/Rio/services/HelloRio` under an
// accidental case fold) would surface here as a structural
// regression rather than at cluster-apply time far from the
// drift site.
for (cluster, nome) in [
("rio", "hello-rio"),
("paris", "cart"),
("tokyo", "checkout"),
("us-east-1", "worker"),
] {
let sub = flux_kustomization_source_subtree(cluster, nome);
assert!(
sub.contains(&format!("/clusters/{cluster}/")),
"flux_kustomization_source_subtree({cluster:?}, {nome:?}) = {sub:?} must carry \
the paired `<cluster>` scalar under its canonical per-cluster sub-tree position"
);
assert!(
sub.ends_with(&format!("/services/{nome}")),
"flux_kustomization_source_subtree({cluster:?}, {nome:?}) = {sub:?} must end with \
the paired `/services/<nome>` per-caixa sub-tree suffix"
);
}
}
#[test]
fn flux_kustomization_source_subtree_distinct_across_clusters_and_nomes() {
// Uniqueness invariant: two distinct `(cluster, nome)` inputs
// resolve to two distinct `spec.path` scalars. A composer-
// internal drift that accidentally coalesced either axis onto
// a constant (dropping `<cluster>` or `<nome>` from the emit)
// would silently collapse two per-cluster / per-caixa
// `Kustomization` CRs onto the same reconcile-target sub-tree,
// routing two distinct manifest sets through the same apply
// loop with no diagnostic naming the coalesce root cause.
let a = flux_kustomization_source_subtree("rio", "hello-rio");
let b = flux_kustomization_source_subtree("paris", "hello-rio");
let c = flux_kustomization_source_subtree("rio", "cart");
assert_ne!(
a, b,
"distinct clusters (`rio` vs `paris`) hosting the same per-caixa Servico \
must resolve to distinct `spec.path` scalars — coalesce would silently route \
two per-cluster reconcile loops through the same manifest sub-tree"
);
assert_ne!(
a, c,
"distinct per-caixa Servicos (`hello-rio` vs `cart`) co-resident under the \
same cluster must resolve to distinct `spec.path` scalars — coalesce would \
silently route two per-caixa reconcile loops through the same manifest sub-tree"
);
}
#[test]
fn pleme_program_selector_carries_only_program() {
let sel = pleme_program_selector("cart");
assert_eq!(sel.len(), 1);
assert_eq!(sel.get(LABEL_PROGRAM).map(String::as_str), Some("cart"));
assert!(sel.get(LABEL_APLICACAO).is_none());
}
#[test]
fn pleme_program_in_aplicacao_selector_carries_both_axes() {
let sel = pleme_program_in_aplicacao_selector("cart", "checkout");
assert_eq!(sel.len(), 2);
assert_eq!(sel.get(LABEL_PROGRAM).map(String::as_str), Some("cart"));
assert_eq!(
sel.get(LABEL_APLICACAO).map(String::as_str),
Some("checkout")
);
}
#[test]
fn pleme_program_in_aplicacao_selector_iterates_alphabetically() {
// BTreeMap iteration is sorted by key — pin that the renderer
// (which translates the selector into a serde_yaml::Mapping
// by iteration) gets a deterministic key order. `aplicacao`
// sorts before `program`, so the rendered YAML's
// `matchLabels:` block appears in that order regardless of
// call-site arg order. Mirrors the M2 overlay helper's
// alphabetical-iteration determinism property
// (THEORY.md §V.2.7 render determinism).
let sel = pleme_program_in_aplicacao_selector("cart", "checkout");
let keys: Vec<_> = sel.keys().copied().collect();
assert_eq!(keys, vec![LABEL_APLICACAO, LABEL_PROGRAM]);
}
#[test]
fn pleme_program_in_aplicacao_selector_arg_order_independent() {
// Renaming the program vs. the aplicacao must each only affect
// its own axis — pin that the helper doesn't transpose its
// args silently (a footgun the prior inline-string approach
// had: `program: <de>` and `aplicacao: <name>` were two
// adjacent insert() calls with structurally identical arms,
// trivially swappable in a refactor).
let sel = pleme_program_in_aplicacao_selector("cart", "checkout");
assert_eq!(sel.get(LABEL_PROGRAM).map(String::as_str), Some("cart"));
assert_eq!(
sel.get(LABEL_APLICACAO).map(String::as_str),
Some("checkout")
);
let swapped = pleme_program_in_aplicacao_selector("checkout", "cart");
assert_eq!(
swapped.get(LABEL_PROGRAM).map(String::as_str),
Some("checkout")
);
assert_eq!(
swapped.get(LABEL_APLICACAO).map(String::as_str),
Some("cart")
);
}
#[test]
fn yaml_string_mapping_empty_input_returns_empty_mapping() {
// Empty input → empty Mapping. Pinned because the caller's
// emptiness contract (e.g. caixa-mesh's CNP labels block: the
// policy's metadata.labels exists iff there are pleme-prefixed
// labels to carry) depends on this being faithful.
let v: serde_yaml::Value = yaml_string_mapping(BTreeMap::<&'static str, String>::new());
let m = v.as_mapping().expect("mapping shape");
assert!(m.is_empty());
}
#[test]
fn yaml_string_mapping_round_trips_string_values() {
let mut input = BTreeMap::new();
input.insert("foo", "1".to_string());
input.insert("bar", "2".to_string());
let v = yaml_string_mapping(input);
let m = v.as_mapping().expect("mapping shape");
assert_eq!(m.len(), 2);
assert_eq!(m.get("foo").and_then(|x| x.as_str()), Some("1"));
assert_eq!(m.get("bar").and_then(|x| x.as_str()), Some("2"));
}
#[test]
fn yaml_string_mapping_iterates_alphabetically_on_btreemap() {
// Pin that BTreeMap input → alphabetical iteration → alphabetical
// YAML key order. THEORY.md §V.2.7 render determinism.
let mut input = BTreeMap::new();
input.insert("zebra", "z".to_string());
input.insert("apple", "a".to_string());
input.insert("mango", "m".to_string());
let v = yaml_string_mapping(input);
let m = v.as_mapping().expect("mapping shape");
let keys: Vec<&str> = m.iter().filter_map(|(k, _)| k.as_str()).collect();
assert_eq!(keys, vec!["apple", "mango", "zebra"]);
}
#[test]
fn yaml_string_mapping_accepts_pleme_selector_helpers() {
// The lift's load-bearing use case: passing the typed pleme-io
// selectors directly into yaml_string_mapping yields the K8s
// matchLabels surface every Cilium / Gateway selector field
// expects, with the alphabetical key order the pleme helpers'
// own determinism contract guarantees. Pinning end-to-end
// composition so a future refactor of either helper can't
// silently break the integration.
let v = yaml_string_mapping(pleme_program_in_aplicacao_selector("cart", "checkout"));
let m = v.as_mapping().expect("mapping shape");
assert_eq!(m.len(), 2);
assert_eq!(m.get(LABEL_PROGRAM).and_then(|x| x.as_str()), Some("cart"));
assert_eq!(
m.get(LABEL_APLICACAO).and_then(|x| x.as_str()),
Some("checkout")
);
}
#[test]
fn kube_key_consts_have_expected_values() {
// Pin the actual string values — these are part of the K8s API
// surface that every emitted artifact's apiserver-side parser
// (Cilium, Gateway API, wasm-operator) depends on. Changing any
// of them is a coordinated multi-renderer migration, not an
// incidental edit.
assert_eq!(KUBE_KEY_API_VERSION, "apiVersion");
assert_eq!(KUBE_KEY_KIND, "kind");
assert_eq!(KUBE_KEY_METADATA, "metadata");
assert_eq!(KUBE_KEY_NAME, "name");
assert_eq!(KUBE_KEY_NAMESPACE, "namespace");
assert_eq!(KUBE_KEY_LABELS, "labels");
assert_eq!(KUBE_KEY_MATCH_LABELS, "matchLabels");
assert_eq!(KUBE_KEY_PORT, "port");
assert_eq!(KUBE_KEY_PROTOCOL, "protocol");
assert_eq!(KUBE_KEY_RULES, "rules");
assert_eq!(KUBE_KEY_SPEC, "spec");
}
#[test]
fn fleet_programs_key_programs_pins_canonical_value() {
// Bridge-arm pin: [`FLEET_PROGRAMS_KEY_PROGRAMS`] resolves to
// the canonical `"programs"` byte today — the exact YAML key
// the `lareira-fleet-programs` library chart's `values.yaml`
// reads under `.Values.programs[]` to iterate one `ComputeUnit`
// CR per entry, and the exact key both writer-side upsert paths
// in [`caixa_flux`] (`upsert_into_helmrelease_programs` on the
// aggregator-HelmRelease shape, `upsert_into_programs_yaml` on
// the bare-values.yaml shape) navigate to walk the entry
// sequence. Pin the literal here (peer with the
// [`M3_KEY_PLACEMENT`] / [`M2_KEY_LIMITS`] /
// [`M2_KEY_BEHAVIOR`] / [`M2_KEY_UPGRADE_FROM`] canonical-
// literal pins on the sibling fleet-programs / M2 overlay
// schema-key surfaces) so a future fleet-programs schema-key
// rebrand surfaces here as a coordinated edit-point: the
// sibling caixa-flux `fleet_programs_key_programs_re_export_
// points_at_caixa_core_canonical` pinning test already pins
// the equality at the re-export axis; this pin closes the
// second coordinate of the triangle by anchoring the lifted
// constant's current byte to the canonical fleet-programs
// library chart's documented shape.
assert_eq!(FLEET_PROGRAMS_KEY_PROGRAMS, "programs");
}
#[test]
fn fleet_programs_key_name_pins_canonical_value() {
// Bridge-arm pin: [`FLEET_PROGRAMS_KEY_NAME`] resolves to the
// canonical `"name"` byte today — the exact YAML key the
// `lareira-fleet-programs` library chart's `range .Values.programs`
// step reads per-entry to key each rendered `ComputeUnit` CR's
// `metadata.name` off, and the exact key both writer-side upsert
// paths in [`caixa_flux`] (`upsert_into_helmrelease_programs` on
// the aggregator-HelmRelease shape, `upsert_into_programs_yaml`
// on the bare-values.yaml shape) navigate to
// match-by-name-and-replace-or-append, and the exact key both
// emit-side entry builders ([`caixa_flux::programs_yaml_entry`]
// per-Servico, [`caixa_mesh::programs_for_aplicacao`] per-
// `:membros`) write the per-entry name-axis at. Pin the literal
// here (peer with the [`fleet_programs_key_programs_pins_canonical_value`]
// top-level array-key canonical-literal pin on the sibling
// fleet-programs schema surface, and with the
// [`M3_KEY_PLACEMENT`] / [`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`]
// / [`M2_KEY_UPGRADE_FROM`] canonical-literal pins on the peer
// per-entry overlay-key surfaces) so a future fleet-programs
// schema-key rebrand on the per-entry name-discriminator axis
// surfaces here as a coordinated edit-point at the definition
// site rather than a silent apply-time split between the two
// emitters and the two upsert readers.
assert_eq!(FLEET_PROGRAMS_KEY_NAME, "name");
}
#[test]
fn fleet_programs_key_aplicacao_pins_canonical_value() {
// Bridge-arm pin: [`FLEET_PROGRAMS_KEY_APLICACAO`] resolves
// to the canonical `"aplicacao"` byte today — the exact YAML
// key the substrate operator's fleet-aggregator reads to
// group each rendered `programs[]` entry back onto its parent
// Aplicacao graph, and the exact key the
// [`caixa_mesh::programs_for_aplicacao`] per-`:membros`
// entry-builder writes the parent-Aplicacao-nome annotation
// at. Pin the literal here (peer with the sibling
// [`fleet_programs_key_name_pins_canonical_value`] and
// [`fleet_programs_key_programs_pins_canonical_value`]
// canonical-literal pins on the peer fleet-programs schema
// key surfaces, and with the [`M3_KEY_PLACEMENT`] /
// [`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] /
// [`M2_KEY_UPGRADE_FROM`] pins on the per-entry overlay-key
// surfaces) so a future fleet-programs schema-key rebrand
// on the per-entry parent-graph-annotation axis surfaces
// here as a coordinated edit-point at the definition site
// rather than a silent apply-time split between the
// caixa-mesh Aplicacao-side emitter and the substrate
// operator's per-graph aggregator reduce step.
assert_eq!(FLEET_PROGRAMS_KEY_APLICACAO, "aplicacao");
}
#[test]
fn fleet_programs_key_versao_pins_canonical_value() {
// Bridge-arm pin: [`FLEET_PROGRAMS_KEY_VERSAO`] resolves to
// the canonical `"versao"` byte today — the exact YAML key
// the substrate operator's per-`:membros` resolver reads to
// fetch each `programs[]` entry's caixa.lisp release against
// the M3 Aplicacao's declared per-member semver / range
// constraint, and the exact key the
// [`caixa_mesh::programs_for_aplicacao`] per-`:membros`
// entry-builder writes the version-constraint at. Pin the
// literal here (peer with the sibling
// [`fleet_programs_key_name_pins_canonical_value`],
// [`fleet_programs_key_aplicacao_pins_canonical_value`], and
// [`fleet_programs_key_programs_pins_canonical_value`]
// canonical-literal pins on the peer fleet-programs schema
// key surfaces, and with the [`M3_KEY_PLACEMENT`] /
// [`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] /
// [`M2_KEY_UPGRADE_FROM`] pins on the per-entry overlay-key
// surfaces) so a future fleet-programs schema-key rebrand
// on the per-entry version-constraint axis surfaces here as
// a coordinated edit-point at the definition site rather
// than a silent apply-time split between the caixa-mesh
// Aplicacao-side emitter and the substrate operator's
// per-`:membros` resolver step.
assert_eq!(FLEET_PROGRAMS_KEY_VERSAO, "versao");
}
// ── label_selector — typed K8s LabelSelector wrapper ─────────────────
#[test]
fn label_selector_wraps_in_match_labels_envelope() {
// The lift's contract: input labels appear under the canonical
// `matchLabels` key, and the outer Value is a Mapping with
// exactly that one key. Pinning the shape so a future
// refactor can't silently drop the wrapper (which would emit
// bare `aplicacao: …, program: …` directly under the K8s
// selector field — a structurally invalid LabelSelector that
// some apiserver-side parsers tolerate by matching the empty
// set, a sharp footgun).
let mut labels = BTreeMap::new();
labels.insert(LABEL_APLICACAO, "checkout".to_string());
labels.insert(LABEL_PROGRAM, "cart".to_string());
let sel = label_selector(labels);
let m = sel.as_mapping().expect("mapping shape");
assert_eq!(m.len(), 1);
let inner = m
.get(KUBE_KEY_MATCH_LABELS)
.and_then(|v| v.as_mapping())
.expect("matchLabels inner mapping");
assert_eq!(inner.len(), 2);
assert_eq!(
inner.get(LABEL_APLICACAO).and_then(|x| x.as_str()),
Some("checkout")
);
assert_eq!(
inner.get(LABEL_PROGRAM).and_then(|x| x.as_str()),
Some("cart")
);
}
#[test]
fn label_selector_empty_input_yields_empty_match_labels() {
// Empty input → `{matchLabels: {}}`. The outer wrapper is
// present (the K8s LabelSelector schema requires it as a
// structural anchor, and apiserver-side parsers that see a
// bare `{}` selector match-everything; pinning the wrapper
// means an empty pleme-io selector at the call site renders
// as the canonical "no labels declared, match nothing
// specific" shape rather than an outright missing key).
let v: serde_yaml::Value = label_selector(BTreeMap::<&'static str, String>::new());
let m = v.as_mapping().expect("mapping shape");
assert_eq!(m.len(), 1);
let inner = m
.get(KUBE_KEY_MATCH_LABELS)
.and_then(|v| v.as_mapping())
.expect("matchLabels inner mapping");
assert!(inner.is_empty());
}
#[test]
fn label_selector_accepts_pleme_selector_helpers() {
// The lift's load-bearing use case: passing the typed pleme-io
// selectors directly into `label_selector` yields the K8s
// LabelSelector shape every Cilium / Gateway / future
// app-operator selector field expects. Pinning end-to-end
// composition so a future refactor of either helper can't
// silently break the integration.
let v = label_selector(pleme_program_in_aplicacao_selector("cart", "checkout"));
let inner = v
.as_mapping()
.and_then(|m| m.get(KUBE_KEY_MATCH_LABELS))
.and_then(|v| v.as_mapping())
.expect("matchLabels inner mapping");
assert_eq!(inner.len(), 2);
assert_eq!(
inner.get(LABEL_PROGRAM).and_then(|x| x.as_str()),
Some("cart")
);
assert_eq!(
inner.get(LABEL_APLICACAO).and_then(|x| x.as_str()),
Some("checkout")
);
// Single-axis variant — only LABEL_PROGRAM under matchLabels.
let v = label_selector(pleme_program_selector("cart"));
let inner = v
.as_mapping()
.and_then(|m| m.get(KUBE_KEY_MATCH_LABELS))
.and_then(|v| v.as_mapping())
.unwrap();
assert_eq!(inner.len(), 1);
assert_eq!(
inner.get(LABEL_PROGRAM).and_then(|x| x.as_str()),
Some("cart")
);
}
#[test]
fn label_selector_inner_iterates_alphabetically_on_btreemap() {
// BTreeMap input → alphabetical iteration → alphabetical YAML
// key order under `matchLabels`. THEORY.md §V.2.7 render
// determinism: the rendered YAML's matchLabels: block appears
// in a deterministic order independent of source-code
// declaration order.
let mut input = BTreeMap::new();
input.insert("zebra", "z".to_string());
input.insert("apple", "a".to_string());
input.insert("mango", "m".to_string());
let v = label_selector(input);
let inner = v
.as_mapping()
.and_then(|m| m.get(KUBE_KEY_MATCH_LABELS))
.and_then(|v| v.as_mapping())
.unwrap();
let keys: Vec<&str> = inner.iter().filter_map(|(k, _)| k.as_str()).collect();
assert_eq!(keys, vec!["apple", "mango", "zebra"]);
}
#[test]
fn label_selector_does_not_introduce_match_expressions_axis() {
// V0 emits matchLabels only — pinning that the helper doesn't
// pre-insert an empty `matchExpressions: []` block (which some
// apiserver-side parsers tolerate but renders noisily and
// shifts the per-rule diff). A future set-based selector
// extension is a deliberate API change to this helper, not an
// incidental shape leak.
let v = label_selector(pleme_program_selector("cart"));
let m = v.as_mapping().unwrap();
assert!(
m.get("matchExpressions").is_none(),
"label_selector must not pre-insert a matchExpressions key (V0 is matchLabels-only)"
);
}
#[test]
fn kube_resource_skeleton_carries_three_top_level_keys_no_spec() {
// The skeleton emits exactly apiVersion + kind + metadata; the
// caller adds spec (and any other top-level keys) themselves.
// Pin that contract so a future caller doesn't accidentally
// double-insert apiVersion / kind / metadata after the
// skeleton call. Namespace fixture arg reads through the
// canonical `DEFAULT_NAMESPACE` const so a future rebrand of
// the substrate's default namespace reaches every fixture by
// construction rather than through a per-fixture stray
// "tatara-system" byte-sequence.
let skel = kube_resource_skeleton(
"cilium.io/v2",
"CiliumNetworkPolicy",
"p-1",
DEFAULT_NAMESPACE,
BTreeMap::new(),
);
assert_eq!(skel.len(), 3);
assert_eq!(
skel.get(KUBE_KEY_API_VERSION).and_then(|v| v.as_str()),
Some("cilium.io/v2")
);
assert_eq!(
skel.get(KUBE_KEY_KIND).and_then(|v| v.as_str()),
Some("CiliumNetworkPolicy")
);
assert!(skel.get(KUBE_KEY_METADATA).is_some());
}
#[test]
fn kube_resource_skeleton_metadata_carries_name_and_namespace() {
let skel = kube_resource_skeleton(
"gateway.networking.k8s.io/v1",
"Gateway",
"checkout",
DEFAULT_NAMESPACE,
BTreeMap::new(),
);
let metadata = skel
.get(KUBE_KEY_METADATA)
.and_then(|v| v.as_mapping())
.expect("metadata mapping");
assert_eq!(
metadata.get(KUBE_KEY_NAME).and_then(|v| v.as_str()),
Some("checkout")
);
// Read-back probe reads through `DEFAULT_NAMESPACE` so a
// future substrate-namespace rebrand routes through the
// canonical const on both the emit-side fixture arg and the
// probe-side readback in one edit — a drift on either side
// would otherwise silently mask the round-trip pin.
assert_eq!(
metadata.get(KUBE_KEY_NAMESPACE).and_then(|v| v.as_str()),
Some(DEFAULT_NAMESPACE)
);
}
#[test]
fn kube_resource_skeleton_omits_labels_when_empty() {
// Empty labels → metadata.labels key absent (NOT present-as-empty).
// K8s API server treats a missing labels key as "no labels
// declared"; an empty-mapping `labels: {}` serializes
// differently in some YAML libraries and is a sharp tool for
// label-based selectors that match the empty set silently.
let skel = kube_resource_skeleton(
"gateway.networking.k8s.io/v1",
"HTTPRoute",
"r-1",
DEFAULT_NAMESPACE,
BTreeMap::new(),
);
let metadata = skel
.get(KUBE_KEY_METADATA)
.and_then(|v| v.as_mapping())
.unwrap();
assert!(
metadata.get(KUBE_KEY_LABELS).is_none(),
"metadata.labels must be absent when no labels passed"
);
// metadata then has exactly 2 keys: name, namespace.
assert_eq!(metadata.len(), 2);
}
#[test]
fn kube_resource_skeleton_includes_labels_when_present() {
let mut labels = BTreeMap::new();
labels.insert(LABEL_APLICACAO, "checkout".to_string());
labels.insert(LABEL_CONTRATO, "cart-to-catalog".to_string());
let skel = kube_resource_skeleton(
"cilium.io/v2",
"CiliumNetworkPolicy",
"p-1",
DEFAULT_NAMESPACE,
labels,
);
let metadata = skel
.get(KUBE_KEY_METADATA)
.and_then(|v| v.as_mapping())
.unwrap();
let labels_block = metadata
.get(KUBE_KEY_LABELS)
.and_then(|v| v.as_mapping())
.expect("metadata.labels mapping present");
assert_eq!(
labels_block.get(LABEL_APLICACAO).and_then(|v| v.as_str()),
Some("checkout")
);
assert_eq!(
labels_block.get(LABEL_CONTRATO).and_then(|v| v.as_str()),
Some("cart-to-catalog")
);
}
#[test]
fn kube_resource_skeleton_metadata_iterates_alphabetically() {
// Pin that the inner BTreeMap projection makes the rendered
// YAML's metadata: block alphabetical (labels, name, namespace),
// regardless of insert order. THEORY.md §V.2.7 render determinism.
let mut labels = BTreeMap::new();
labels.insert(LABEL_APLICACAO, "checkout".to_string());
let skel = kube_resource_skeleton(
"cilium.io/v2",
"CiliumNetworkPolicy",
"p-1",
DEFAULT_NAMESPACE,
labels,
);
let metadata = skel
.get(KUBE_KEY_METADATA)
.and_then(|v| v.as_mapping())
.unwrap();
let keys: Vec<&str> = metadata.iter().filter_map(|(k, _)| k.as_str()).collect();
assert_eq!(
keys,
vec![KUBE_KEY_LABELS, KUBE_KEY_NAME, KUBE_KEY_NAMESPACE]
);
}
#[test]
fn kube_resource_skeleton_top_level_iterates_in_insert_order() {
// The top-level Mapping is a plain serde_yaml::Mapping (insert-
// ordered), and the skeleton inserts apiVersion → kind →
// metadata in that order. Pin so a future refactor doesn't
// silently shift the rendered YAML's top-level key order
// (which K8s tooling tolerates but humans + diff readability
// care about — apiVersion-first is the K8s convention).
let skel = kube_resource_skeleton(
"cilium.io/v2",
"CiliumNetworkPolicy",
"p-1",
DEFAULT_NAMESPACE,
BTreeMap::new(),
);
let keys: Vec<&str> = skel.iter().filter_map(|(k, _)| k.as_str()).collect();
assert_eq!(
keys,
vec![KUBE_KEY_API_VERSION, KUBE_KEY_KIND, KUBE_KEY_METADATA]
);
}
#[test]
fn kube_resource_skeleton_does_not_introduce_spec_key() {
// Sanity: the skeleton is metadata-only — `spec` is the caller's
// responsibility. Pinning so a future "be helpful" refactor
// doesn't auto-insert an empty `spec: {}` (which would silently
// shadow caller-side spec construction).
let skel = kube_resource_skeleton(
"cilium.io/v2",
"CiliumNetworkPolicy",
"p-1",
DEFAULT_NAMESPACE,
BTreeMap::new(),
);
assert!(
skel.get("spec").is_none(),
"skeleton must not pre-insert a spec key"
);
}
// ── require_kind / KindMismatch — typed kind-check predicate ─────
#[test]
fn require_kind_accepts_matching_kind() {
// A Servico-kind caixa passes a `require_kind(_, Servico)`
// check — the happy path every renderer sees on a correctly-
// authored caixa.lisp, surfaced as `Ok(())` so the renderer's
// call site reads as a one-liner gate rather than a typed
// pattern match.
let c = bare_servico();
require_kind(&c, CaixaKind::Servico).unwrap();
}
#[test]
fn require_kind_rejects_with_typed_mismatch() {
// A Biblioteca-kind caixa fails a `require_kind(_, Servico)`
// check with a typed [`KindMismatch`] view that names the
// offending caixa's `:nome` plus both the expected and actual
// kinds. Pinning the typed shape so a future Display-format
// tweak can't silently drop any of the three load-bearing
// fields (which would regress the "feira verb whose error
// path doesn't name the offending caixa" punch-list item the
// protocol calls out).
let mut c = bare_servico();
c.kind = CaixaKind::Biblioteca;
c.servicos = vec![];
let err = require_kind(&c, CaixaKind::Servico).unwrap_err();
assert_eq!(err.nome, "hello-rio");
assert_eq!(err.expected, CaixaKind::Servico);
assert_eq!(err.actual, CaixaKind::Biblioteca);
}
#[test]
fn require_kind_routes_offending_nome_via_caixa_nome_accessor() {
// Pin: the [`KindMismatch::nome`] `String` the constructor
// writes must be a byte-identical copy of what the lifted
// [`crate::Caixa::nome`] accessor returns for the same
// [`Caixa`] input — the same discipline the sibling
// [`crate::LayoutInvariants::verify`] wrap-envelope emitters
// pin at 9842a4b's `expected_nome_via_accessor` line (the
// routing pin the 31-site converge introduced on the substrate's
// own layout-invariant verifier's per-axis diagnostic emitters).
//
// Guardrails a future regression that re-inlines the raw
// `caixa.nome.clone()` `String::clone()` of the underlying
// field at the constructor site — the accessor's borrow
// return + typed `.to_string()` `String` promotion is the
// one canonical shape the substrate's own [`KindMismatch`]
// typed-view constructor carries onto every downstream
// renderer's `Error::From<KindMismatch>` `#[from]` arm, so
// any drift (a byte-non-identical shape, e.g. a future
// `CaixaNome` newtype the [`crate::Caixa::nome`] accessor
// upgrades to project the display byte-string of, that
// `.nome.clone()` would silently ignore) surfaces here
// before the drift lands on a per-renderer `#[from]` arm.
let mut c = bare_servico();
c.kind = CaixaKind::Biblioteca;
c.servicos = vec![];
c.nome = "kind-mismatch-pin".into();
let expected_nome_via_accessor = c.nome().to_string();
assert_eq!(
expected_nome_via_accessor, "kind-mismatch-pin",
"the mutated fixture's `:nome` must be observable through \
the accessor before the kind-mismatch gate fires",
);
let err = require_kind(&c, CaixaKind::Servico).unwrap_err();
assert_eq!(
err.nome, expected_nome_via_accessor,
"the KindMismatch's `nome` field must equal \
`caixa.nome().to_string()` — the typed-view constructor \
must route through the lifted [`Caixa::nome`] accessor's \
`.to_string()` extension, not the raw `caixa.nome.clone()` \
`String::clone()` of the underlying field",
);
}
#[test]
fn kind_mismatch_display_names_offending_caixa_nome() {
// The Display impl is the load-bearing surface every renderer's
// `#[error("{0}")] NotAXKind(#[from] KindMismatch)` arm prints
// through. Pinning the exact rendered form so a future format
// change is a one-line edit + a one-line test update, not a
// silent regression of the diagnostic clarity.
let err = KindMismatch {
nome: "checkout".into(),
expected: CaixaKind::Aplicacao,
actual: CaixaKind::Servico,
};
let msg = format!("{err}");
assert!(
msg.contains("checkout"),
"Display must name the offending caixa nome (got: {msg:?})"
);
assert!(
msg.contains("Aplicacao"),
"Display must name the expected kind (got: {msg:?})"
);
assert!(
msg.contains("Servico"),
"Display must name the actual kind (got: {msg:?})"
);
}
#[test]
fn require_kind_distinguishes_every_pair_of_kinds() {
// Sanity: the predicate is kind-axis-agnostic — it works for
// every kind / expected pair, not just Servico/Biblioteca.
// Pinning that the caller can use `require_kind` for any of
// the five typed kinds (Biblioteca, Binario, Servico,
// Supervisor, Aplicacao) without a special-cased helper per
// kind. Same idiom every per-target renderer key off.
let mut c = bare_servico();
c.kind = CaixaKind::Aplicacao;
c.servicos = vec![];
let err = require_kind(&c, CaixaKind::Supervisor).unwrap_err();
assert_eq!(err.expected, CaixaKind::Supervisor);
assert_eq!(err.actual, CaixaKind::Aplicacao);
require_kind(&c, CaixaKind::Aplicacao).unwrap();
}
// ── require_ci / MissingCiSlot — Acao `:ci`-slot-presence gate ────
fn bare_acao_without_ci() -> Caixa {
let mut c = bare_servico();
c.kind = CaixaKind::Acao;
c.servicos = vec![];
c.ci = None;
c
}
fn sample_ci_run() -> canteiro_types::CiRun {
canteiro_types::CiRun {
workspace: "pleme-io".into(),
repo: "caixa".into(),
nodes: vec![],
}
}
#[test]
fn require_ci_accepts_present_slot_and_returns_borrowed_ci_run() {
// The happy path: an Acao-kind caixa that declares its `:ci`
// slot passes `require_ci`, and the borrowed
// [`canteiro_types::CiRun`] projected through the successful
// return is the same author-declared value the caller was about
// to bind — folding the check and the bind onto one call site,
// matching how every present + roadmapped per-`Acao` consumer
// uses the slot.
let mut c = bare_acao_without_ci();
c.ci = Some(sample_ci_run());
let ci = require_ci(&c).expect("Acao with declared :ci passes");
assert_eq!(ci.workspace, "pleme-io");
assert_eq!(ci.repo, "caixa");
}
#[test]
fn require_ci_rejects_absent_slot_with_typed_view() {
// The fail-before-pass-after pin: pre-lift `caixa-actions`'
// inline `.ok_or_else(|| Error::MissingCi { nome:
// caixa.nome().to_string() })` gate constructed an
// `Error::MissingCi { nome: String }` at exactly one crate's
// call site with no compile-time link to any typed named-caixa
// view the sibling per-renderer entry-gate axes carry. A future
// per-`Acao` consumer (the deferred `sui-supercacheci::canteiro
// ::emit_gha` workflow renderer named in the `caixa-actions`
// crate docs, the future per-`Acao` CR materializer) would
// re-inline the same `.ok_or_else(...)` construction on its own
// call site and open a second untracked `nome: String`-carry
// path — exactly the "feira verb whose error path doesn't name
// the offending caixa" punch-list item the compounding-mandate
// protocol calls out. Lifting the gate onto the typed
// [`MissingCiSlot`] view + [`require_ci`] predicate closes the
// drift potential structurally: every future per-`Acao`
// consumer reaches for the same one-liner + `#[from]` and gets
// the diagnostic-naming-the-offending-caixa contract for free.
let c = bare_acao_without_ci();
let err = require_ci(&c).unwrap_err();
assert_eq!(err.nome, "hello-rio");
}
#[test]
fn require_ci_routes_offending_nome_via_caixa_nome_accessor() {
// Pin: the [`MissingCiSlot::nome`] `String` the constructor
// writes must be a byte-identical copy of what the lifted
// [`crate::Caixa::nome`] accessor returns for the same
// [`Caixa`] input — the same routing pin discipline the peer
// [`require_kind`] / [`require_single_servico`] typed views
// already carry, so a future regression that re-inlines a raw
// `caixa.nome.clone()` `String::clone()` of the underlying
// field at the constructor site (which would silently ignore
// any future `CaixaNome` newtype the [`crate::Caixa::nome`]
// accessor upgrades to project the display byte-string of)
// trips here before the drift lands on a per-consumer `#[from]`
// arm.
let mut c = bare_acao_without_ci();
c.nome = "missing-ci-pin".into();
let expected_nome_via_accessor = c.nome().to_string();
assert_eq!(
expected_nome_via_accessor, "missing-ci-pin",
"the mutated fixture's `:nome` must be observable through \
the accessor before the `:ci` gate fires",
);
let err = require_ci(&c).unwrap_err();
assert_eq!(
err.nome, expected_nome_via_accessor,
"the MissingCiSlot's `nome` field must equal \
`caixa.nome().to_string()` — the typed-view constructor \
must route through the lifted [`Caixa::nome`] accessor's \
`.to_string()` extension, not the raw `caixa.nome.clone()` \
`String::clone()` of the underlying field",
);
}
#[test]
fn missing_ci_slot_display_names_offending_caixa_nome() {
// The Display impl is the load-bearing surface every per-
// `Acao` consumer's `#[error("{0}")] MissingCi(#[from]
// MissingCiSlot)` arm prints through. Pinning the exact rendered
// form so a future format change is a one-line edit + a one-line
// test update, not a silent regression of the diagnostic
// clarity. Same shape every peer per-axis lift carries.
let err = MissingCiSlot {
nome: "hello-acao".into(),
};
let msg = format!("{err}");
assert!(
msg.contains("hello-acao"),
"Display must name the offending caixa nome (got: {msg:?})"
);
assert!(
msg.contains(":ci"),
"Display must name the missing `:ci` slot (got: {msg:?})"
);
}
// ── CiDecomposeFailure — per-`Acao` decompose-failure diagnostic axis ─
#[test]
fn ci_decompose_failure_carries_offending_nome_and_source_verbatim() {
// Fail-before-pass-after pin on the [`CiDecomposeFailure`] typed
// view: the constructor writes the offending caixa's `:nome`
// (routed through the lifted [`crate::Caixa::nome`] accessor's
// `.to_string()` extension by every consumer) alongside the
// borrowed [`canteiro_types::DecomposeError`] source verbatim,
// so a per-`Acao` consumer that fans on the specific
// decompose-failure arm reaches for `err.source` directly
// rather than re-parsing the Display bytes. Peer of the sibling
// [`MissingCiSlot`] typed view's `nome`-carrying pin — extends
// the same "one typed view per axis, carrying the offending
// caixa's `:nome` + axis-specific detail" discipline onto the
// second per-`Acao` diagnostic axis after the presence-gate
// axis.
let err = CiDecomposeFailure {
nome: "hello-acao".into(),
source: canteiro_types::DecomposeError::Cycle,
};
assert_eq!(err.nome, "hello-acao");
assert_eq!(err.source, canteiro_types::DecomposeError::Cycle);
}
#[test]
fn ci_decompose_failure_display_names_offending_caixa_nome_and_source() {
// The Display impl is the load-bearing surface every per-`Acao`
// consumer's `#[error("{0}")] Decompose(#[from]
// CiDecomposeFailure)` arm prints through. Pinning the exact
// rendered form so a future format change is a one-line edit +
// a one-line test update, not a silent regression of the
// diagnostic clarity — same shape every peer per-axis lift
// carries.
let err = CiDecomposeFailure {
nome: "hello-acao".into(),
source: canteiro_types::DecomposeError::Cycle,
};
let msg = format!("{err}");
assert!(
msg.contains("hello-acao"),
"Display must name the offending caixa nome (got: {msg:?})"
);
assert!(
msg.contains(":ci"),
"Display must name the `:ci` slot the decompose failed on \
(got: {msg:?})"
);
assert!(
msg.contains("decompose"),
"Display must name the decompose axis (got: {msg:?})"
);
}
#[test]
fn ci_decompose_failure_exposes_source_via_error_trait() {
// Pin: the [`CiDecomposeFailure`] type routes its
// [`canteiro_types::DecomposeError`] carrier through the
// `#[source]` [`thiserror::Error`] derive so downstream
// `std::error::Error::source()`-consuming diagnostic frameworks
// (`anyhow`'s chain formatter, `tracing`'s `error!` event
// capture, the future `feira lint` sub-diagnostic emitter) see
// the underlying `DecomposeError` arm through the standard
// trait rather than only through the flattened Display bytes.
// Peer of the sibling per-slot `#[source]` wiring the caixa-*
// renderers already carry on their own typed-view error
// wrappers.
let err = CiDecomposeFailure {
nome: "hello-acao".into(),
source: canteiro_types::DecomposeError::Cycle,
};
let src = std::error::Error::source(&err)
.expect("CiDecomposeFailure must expose its DecomposeError via Error::source()");
// The `Error::source()` trait method returns a `&dyn Error`
// borrow of the underlying `DecomposeError`, so its Display
// bytes must equal the source arm's own Display bytes — a
// future accidental collapse of the `#[source]` wiring (which
// would erase the source chain and force downstream
// `anyhow::Chain` consumers back onto Display re-parsing) trips
// here at caixa-core build time.
let src_msg = format!("{src}");
let expected_msg = format!("{}", canteiro_types::DecomposeError::Cycle);
assert_eq!(src_msg, expected_msg);
}
// ── decompose_ci — per-`Acao` decompose-axis predicate ────────────
fn cyclic_ci_run() -> canteiro_types::CiRun {
// A minimal two-node cycle: `a` depends on `b`, `b` depends on
// `a`. Every failure mode `canteiro_types::decompose` refuses
// (duplicate node name, missing dependency, cycle) would work as
// a fixture; the cycle arm is the same one the `caixa-actions`
// per-`Acao` renderer's own `validate_rejects_a_cyclic_ci_run`
// test already reads for, so both the substrate primitive's own
// pin and the consumer's byte-parity pin share one canonical
// fixture shape.
canteiro_types::CiRun {
workspace: "pleme-io".into(),
repo: "caixa".into(),
nodes: vec![
canteiro_types::CiNode::new(
"a",
canteiro_types::EnvClass::None,
canteiro_types::ActionRef {
name: "a".into(),
command: "true".into(),
args: vec![],
},
vec!["b".into()],
),
canteiro_types::CiNode::new(
"b",
canteiro_types::EnvClass::None,
canteiro_types::ActionRef {
name: "b".into(),
command: "true".into(),
args: vec![],
},
vec!["a".into()],
),
],
}
}
fn linear_ci_run() -> canteiro_types::CiRun {
// A minimal two-node acyclic run: `test` depends on `build`.
// Same shape as the `caixa-actions` `validate_decomposes_a_two_
// node_build_then_test_run` happy-path test — one shared
// canonical fixture for every downstream substrate consumer.
canteiro_types::CiRun {
workspace: "pleme-io".into(),
repo: "caixa".into(),
nodes: vec![
canteiro_types::CiNode::new(
"build",
canteiro_types::EnvClass::None,
canteiro_types::ActionRef {
name: "build".into(),
command: "true".into(),
args: vec![],
},
vec![],
),
canteiro_types::CiNode::new(
"test",
canteiro_types::EnvClass::None,
canteiro_types::ActionRef {
name: "test".into(),
command: "true".into(),
args: vec![],
},
vec!["build".into()],
),
],
}
}
#[test]
fn decompose_ci_accepts_valid_ci_run_and_returns_canteiro_dag() {
// The happy path: a valid two-node acyclic run decomposes
// cleanly through `decompose_ci`, returning the owned
// `canteiro_types::CanteiroDag` the sibling `canteiro_types::
// decompose` returns — the substrate primitive is a
// pass-through on success, only wrapping the error arm in a
// typed named-caixa view. Matches the peer `require_ci`
// presence-axis happy path (accept-with-borrowed-CiRun) —
// extends the "one primitive per axis, pass-through on success"
// discipline onto the decompose axis.
let c = bare_acao_without_ci();
let ci = linear_ci_run();
let cd = decompose_ci(&c, &ci).expect("valid acyclic CiRun decomposes cleanly");
// The topo_order() call on a successful decompose is infallible
// by construction (no cycles present), so a downstream consumer
// reaches for the DAG's own algebra directly rather than a
// second gate. Iterating the returned order (rather than
// asserting on a concrete container shape) keeps the pin
// agnostic to whether topo_order returns Vec<NodeId>,
// SmallVec<NodeId>, or any future returned collection.
let topo = cd
.topo_order()
.expect("acyclic CanteiroDag returns a valid topo_order");
assert_eq!(
topo.iter().count(),
2,
"topo_order on a two-node acyclic run must yield two node ids"
);
}
#[test]
fn decompose_ci_rejects_cyclic_ci_run_with_typed_view() {
// The fail-before-pass-after pin: pre-lift `caixa-actions`'
// inline `.map_err(|source| CiDecomposeFailure { nome: nome
// .clone(), source })` gate constructed a `CiDecomposeFailure`
// at exactly one crate's call site with no compile-time link to
// any typed named-caixa predicate the sibling per-`Acao` /
// per-renderer entry-gate axes carry. A future per-`Acao`
// consumer (the deferred `sui-supercacheci::canteiro::emit_gha`
// workflow renderer named in the `caixa-actions` crate docs, a
// future per-`Acao` CR materializer's admission webhook) would
// re-inline the same `.map_err(...)` construction on its own
// call site and open a second untracked
// `caixa.nome().to_string()` re-projection path — exactly the
// "feira verb whose error path doesn't name the offending
// caixa" punch-list item the compounding-mandate protocol calls
// out. Lifting the gate onto the typed `decompose_ci` predicate
// closes the drift potential structurally: every future
// per-`Acao` consumer reaches for the same one-liner + `#[from]`
// and gets the diagnostic-naming-the-offending-caixa contract
// for free.
let c = bare_acao_without_ci();
let ci = cyclic_ci_run();
// `unwrap_err()` needs `T: Debug`, and the Ok half here carries
// `canteiro_types::CanteiroDag`, which does not derive it at the
// pinned sui rev — so the whole caixa-core test target failed to
// COMPILE. A let-else says the same thing without borrowing a
// bound from a foreign type we do not own.
let Err(err) = decompose_ci(&c, &ci) else {
panic!("a cyclic CiRun must fail decompose_ci");
};
assert_eq!(err.nome, "hello-rio");
assert_eq!(err.source, canteiro_types::DecomposeError::Cycle);
}
#[test]
fn decompose_ci_routes_offending_nome_via_caixa_nome_accessor() {
// Pin: the `CiDecomposeFailure::nome` `String` the constructor
// writes must be a byte-identical copy of what the lifted
// `crate::Caixa::nome` accessor returns for the same `Caixa`
// input — the same routing pin discipline the peer
// `require_kind` / `require_single_servico` / `require_ci`
// typed views already carry, so a future regression that
// re-inlines a raw `caixa.nome.clone()` `String::clone()` of
// the underlying field at the constructor site (which would
// silently ignore any future `CaixaNome` newtype the
// `crate::Caixa::nome` accessor upgrades to project the display
// byte-string of) trips here before the drift lands on a
// per-consumer `#[from]` arm.
let mut c = bare_acao_without_ci();
c.nome = "decompose-ci-pin".into();
let expected_nome_via_accessor = c.nome().to_string();
assert_eq!(
expected_nome_via_accessor, "decompose-ci-pin",
"the mutated fixture's `:nome` must be observable through \
the accessor before the decompose gate fires",
);
let ci = cyclic_ci_run();
// `unwrap_err()` needs `T: Debug`, and the Ok half here carries
// `canteiro_types::CanteiroDag`, which does not derive it at the
// pinned sui rev — so the whole caixa-core test target failed to
// COMPILE. A let-else says the same thing without borrowing a
// bound from a foreign type we do not own.
let Err(err) = decompose_ci(&c, &ci) else {
panic!("a cyclic CiRun must fail decompose_ci");
};
assert_eq!(
err.nome, expected_nome_via_accessor,
"the CiDecomposeFailure's `nome` field must equal \
`caixa.nome().to_string()` — the `decompose_ci` predicate \
must route through the lifted `Caixa::nome` accessor's \
`.to_string()` extension, not a raw `caixa.nome.clone()` \
`String::clone()` of the underlying field",
);
}
// ── ci_declared_edge_count — per-`Acao` declared-edge-count axis ─
#[test]
fn ci_declared_edge_count_returns_zero_for_leaf_only_run() {
// The empty-edges arm: a `CiRun` whose every node carries an
// empty `deps` list has zero declared edges. Pins the
// `usize::sum()` accumulator's starting value on the
// no-fan-out shape a `caixa-init`-scaffolded `:kind Acao` a
// caixa's stub `:ci` slot lands as before the author wires
// any `deps`. Fail-before-pass-after guard: pre-lift there was
// no substrate primitive, so an author-scaffolded no-deps run
// would have had its `edge_count = 0` re-derived at every
// consumer site through the same open-coded arithmetic. This
// test now anchors the projection to `ci_declared_edge_count`.
let ci = canteiro_types::CiRun {
workspace: "pleme-io".into(),
repo: "caixa".into(),
nodes: vec![
canteiro_types::CiNode::new(
"build",
canteiro_types::EnvClass::None,
canteiro_types::ActionRef {
name: "build".into(),
command: "true".into(),
args: vec![],
},
vec![],
),
canteiro_types::CiNode::new(
"lint",
canteiro_types::EnvClass::None,
canteiro_types::ActionRef {
name: "lint".into(),
command: "true".into(),
args: vec![],
},
vec![],
),
],
};
assert_eq!(
ci_declared_edge_count(&ci),
0,
"a two-leaf-node `:ci` run with empty `deps` lists carries \
zero declared edges — the substrate primitive's `usize` \
accumulator must start at zero and pass through untouched",
);
}
#[test]
fn ci_declared_edge_count_returns_deps_sum_across_nodes() {
// The multi-arity arm: a `CiRun` whose nodes carry `deps`
// lists of arities 0/1/2 has declared-edge-count 3 (0+1+2).
// Pins that the substrate primitive routes the sum through
// *every* node's `deps.len()` rather than only the first
// node's (a future regression that collapsed the `map(...)`
// + `sum()` fold onto a `first()` / `next()` shape would
// silently under-count the declared edges — the arity-3
// fixture surfaces it here before the drift lands on the
// `caixa-actions::validate` production `edge_count` artifact).
let ci = canteiro_types::CiRun {
workspace: "pleme-io".into(),
repo: "caixa".into(),
nodes: vec![
canteiro_types::CiNode::new(
"build",
canteiro_types::EnvClass::None,
canteiro_types::ActionRef {
name: "build".into(),
command: "true".into(),
args: vec![],
},
vec![],
),
canteiro_types::CiNode::new(
"test",
canteiro_types::EnvClass::None,
canteiro_types::ActionRef {
name: "test".into(),
command: "true".into(),
args: vec![],
},
vec!["build".into()],
),
canteiro_types::CiNode::new(
"publish",
canteiro_types::EnvClass::None,
canteiro_types::ActionRef {
name: "publish".into(),
command: "true".into(),
args: vec![],
},
vec!["build".into(), "test".into()],
),
],
};
assert_eq!(
ci_declared_edge_count(&ci),
3,
"declared-edge-count on a 0/1/2-arity node list is the sum \
(0 + 1 + 2 = 3) — the primitive must fold over every node, \
not just the first / last / any-single-index shape",
);
}
#[test]
fn ci_declared_edge_count_counts_edges_before_decompose_gate() {
// The count-is-shape-only arm: an author-declared *cyclic*
// `:ci` run — the exact fixture `decompose_ci` refuses at the
// sibling axis — still carries its declared edge count as a
// property of the *borrowed run's shape*, not of the owned
// `CanteiroDag` `decompose_ci` (would have) returned. Pins
// that a future consumer that wants the declared-edge summary
// *before* running `decompose_ci` (a `feira lint --acao`
// per-caixa pre-flight report that names the declared edge
// count on both accept + reject arms of the sibling
// `decompose_ci` gate) reads a stable count on both arms.
// The two-node cycle `a → b → a` from `cyclic_ci_run()`
// carries exactly 2 declared edges (one per node's singleton
// `deps`), so the primitive returns 2 without ever routing
// through `canteiro_types::decompose`.
let ci = cyclic_ci_run();
assert_eq!(
ci_declared_edge_count(&ci),
2,
"the two-node cycle carries 2 declared `deps` edges (one \
per node's singleton `deps`) — the primitive must read the \
count off the borrowed run's node-list shape, not off the \
`decompose_ci`-produced `CanteiroDag`'s edge algebra",
);
}
#[test]
fn ci_declared_edge_count_matches_open_coded_sum_across_shapes() {
// Byte-parity pin — the three-path convergence discipline
// every peer per-`Acao` substrate primitive carries: the
// primitive's return must equal the open-coded
// `ci.nodes.iter().map(|n| n.deps.len()).sum::<usize>()`
// expression at each of the three canonical `:ci` run shapes
// this test module already carries (`linear_ci_run` — the
// canonical happy-path with one edge, `cyclic_ci_run` — the
// canonical rejected-by-`decompose_ci` shape with two edges,
// and the empty-edges no-fan-out shape the peer
// `ci_declared_edge_count_returns_zero_for_leaf_only_run`
// fixture reads). Any future refactor of the primitive's fold
// shape trips here before landing on the consumer's
// `RenderedAcao::edge_count` artifact.
for (label, ci) in [
("linear-two-node", linear_ci_run()),
("cyclic-two-node", cyclic_ci_run()),
] {
let via_primitive = ci_declared_edge_count(&ci);
let via_open_coded: usize = ci.nodes.iter().map(|n| n.deps.len()).sum();
assert_eq!(
via_primitive, via_open_coded,
"{label}: `ci_declared_edge_count` must equal the \
open-coded `.nodes.iter().map(|n| n.deps.len()).sum()` \
the two prior `caixa-actions` open-coded sites carried \
— pre-lift regression check",
);
}
}
// ── require_single_servico / ServicoCountMismatch — V0 Servico-shape ─
#[test]
fn require_single_servico_accepts_singleton_list() {
// The happy path: the canonical V0 Servico carries exactly one
// `:servicos` entry (the ComputeUnit YAML pointer), the same
// shape every in-tree fixture + canonical example uses. Surfaced
// as `Ok(())` so the renderer's call site reads as a one-liner
// gate beside the peer [`require_kind`] check rather than a
// typed pattern match.
let c = bare_servico();
assert_eq!(
c.servicos.len(),
1,
"fixture pin: bare_servico() is singleton"
);
require_single_servico(&c).unwrap();
}
#[test]
fn require_single_servico_rejects_empty_list_with_typed_mismatch() {
// A Servico-kind caixa with zero `:servicos` entries fails
// `require_single_servico` with a typed [`ServicoCountMismatch`]
// view that names the offending caixa's `:nome` + the actual
// count (0). Pinning the typed shape so a future Display-format
// tweak can't silently drop either of the two load-bearing
// fields (which would regress the "feira verb whose error path
// doesn't name the offending caixa" punch-list item the protocol
// calls out — same shape every peer per-axis lift carries).
let mut c = bare_servico();
c.servicos = vec![];
let err = require_single_servico(&c).unwrap_err();
assert_eq!(err.nome, "hello-rio");
assert_eq!(err.count, 0);
}
#[test]
fn require_single_servico_rejects_multi_entry_list_with_typed_mismatch() {
// The peer arm on the upper-bound axis: a Servico-kind caixa
// with ≥ 2 `:servicos` entries fails the same gate, with the
// typed view carrying the actual count (2). Both empty and
// multi-entry lists land on the same [`ServicoCountMismatch`]
// arm — the V0 contract requires *exactly* one entry, not
// *at-least* one — so the single helper closes both directions
// of the V0 invariant in one call site.
let mut c = bare_servico();
c.servicos = vec![
"servicos/hello-rio.computeunit.yaml".into(),
"servicos/extra.computeunit.yaml".into(),
];
let err = require_single_servico(&c).unwrap_err();
assert_eq!(err.nome, "hello-rio");
assert_eq!(err.count, 2);
}
#[test]
fn require_single_servico_routes_offending_nome_via_caixa_nome_accessor() {
// Peer to the sibling
// [`require_kind_routes_offending_nome_via_caixa_nome_accessor`]
// pin on the V0 Servico-shape gate's `:nome`-carry axis:
// the [`ServicoCountMismatch::nome`] `String` the constructor
// writes must be a byte-identical copy of what the lifted
// [`crate::Caixa::nome`] accessor returns. Same 9842a4b-shaped
// routing pin the substrate's own [`crate::LayoutInvariants::verify`]
// wrap-envelope emitters carry, extended here to the second of
// the two [`crate::render`]-module typed-view constructor sites
// that carried a raw `caixa.nome.clone()` `String::clone()`
// field access at the pre-converge state.
let mut c = bare_servico();
c.servicos = vec![];
c.nome = "servico-count-pin".into();
let expected_nome_via_accessor = c.nome().to_string();
assert_eq!(
expected_nome_via_accessor, "servico-count-pin",
"the mutated fixture's `:nome` must be observable through \
the accessor before the servico-count gate fires",
);
let err = require_single_servico(&c).unwrap_err();
assert_eq!(
err.nome, expected_nome_via_accessor,
"the ServicoCountMismatch's `nome` field must equal \
`caixa.nome().to_string()` — the typed-view constructor \
must route through the lifted [`Caixa::nome`] accessor's \
`.to_string()` extension, not the raw `caixa.nome.clone()` \
`String::clone()` of the underlying field",
);
}
#[test]
fn servico_count_mismatch_display_names_offending_caixa_nome() {
// The Display impl is the load-bearing surface every renderer's
// `#[error("{0}")] UnsupportedServicoCount(#[from]
// ServicoCountMismatch)` arm prints through. Pinning the exact
// rendered form so a future format change is a one-line edit +
// a one-line test update, not a silent regression of the
// diagnostic clarity that motivated the lift (the prior
// per-renderer `UnsupportedServicoCount(usize)` arm named only
// the count). Same shape every peer [`KindMismatch`] / typed-
// view Display tests pin.
let err = ServicoCountMismatch {
nome: "checkout".into(),
count: 3,
};
let msg = format!("{err}");
assert!(
msg.contains("checkout"),
"Display must name the offending caixa nome (got: {msg:?})"
);
assert!(
msg.contains('3'),
"Display must name the actual count (got: {msg:?})"
);
assert!(
msg.contains(":servicos"),
"Display must name the offending field axis (got: {msg:?})"
);
assert!(
msg.contains("exactly one"),
"Display must name the V0 invariant (got: {msg:?})"
);
}
#[test]
fn overlay_kind_agnostic_for_field_projection() {
// The helper projects fields, not kind — every Caixa carries
// the M2 slot fields by construction. Renderer-level kind
// gates (NotAServico in caixa-helm / caixa-flux) are the
// shape filter; this helper is the field projector. Keeping
// them separate means the same overlay can apply to any
// future per-kind renderer (e.g. when M2.4 supervisor
// rendering acquires its own M2-shaped overlay path).
let mut c = bare_servico();
c.kind = CaixaKind::Biblioteca;
c.servicos = vec![];
c.limits = Some(LimitsSpec {
memory: Some(crate::LIMITS_MEMORY_WASM32_PAGE_BYTES),
..Default::default()
});
let overlay = servico_m2_overlay(&c).unwrap();
assert!(overlay.contains_key(M2_KEY_LIMITS));
}
// ── require_v0_servico_shape — compound V0-shape entry gate ──────
/// Local `thiserror`-shaped renderer-error stand-in that mirrors the
/// three production callers' shape (`caixa-flux::Error`,
/// `caixa-helm::Error`) at the two `#[from]` variants the compound
/// helper's `E: From<KindMismatch> + From<ServicoCountMismatch>`
/// bound targets. Pinning the shape here so the compound helper's
/// type-inference contract is unit-testable inside caixa-core
/// without a workspace-crate dependency (which would bloat the
/// build graph).
#[derive(Debug, thiserror::Error)]
enum RendererStandIn {
#[error("{0}")]
NotAServico(#[from] KindMismatch),
#[error("{0}")]
UnsupportedServicoCount(#[from] ServicoCountMismatch),
}
#[test]
fn require_v0_servico_shape_accepts_v0_servico() {
// Happy path: a `:kind Servico` caixa with exactly one
// `:servicos` entry — the canonical V0 shape every per-Servico
// renderer's entry-point sees — passes the compound gate. Same
// outcome as the two-line pair the compound helper replaces:
// both predicates surface `Ok(())`, and the compound helper's
// return type carries the caller's `E` inferred from the `?`
// context (unit test uses [`RendererStandIn`] as the stand-in
// for `caixa-flux::Error` / `caixa-helm::Error`).
let c = bare_servico();
let r: Result<(), RendererStandIn> = require_v0_servico_shape(&c);
r.expect("v0 servico shape accepted");
}
#[test]
fn require_v0_servico_shape_forwards_kind_mismatch_first() {
// Order pin: the kind gate fires before the count gate, so a
// `:kind Biblioteca` caixa with zero `:servicos` entries
// surfaces the [`KindMismatch`] arm (the more actionable
// diagnostic — the author has the wrong `:kind`), not the
// [`ServicoCountMismatch`] arm (a downstream consequence of
// the mis-kinded input). Both invariants are violated on this
// input, so the ordering matters — reversing it would flip
// every current caller's diagnostic on a mis-kinded input.
let mut c = bare_servico();
c.kind = CaixaKind::Biblioteca;
c.servicos = vec![];
let err: RendererStandIn = require_v0_servico_shape(&c).unwrap_err();
match err {
RendererStandIn::NotAServico(k) => {
assert_eq!(k.nome, "hello-rio");
assert_eq!(k.expected, CaixaKind::Servico);
assert_eq!(k.actual, CaixaKind::Biblioteca);
}
RendererStandIn::UnsupportedServicoCount(_) => {
panic!("kind gate must fire before count gate on mis-kinded input")
}
}
}
#[test]
fn require_v0_servico_shape_forwards_count_mismatch_on_kind_match() {
// A `:kind Servico` caixa with the wrong `:servicos` count
// (empty or multi-entry) passes the kind gate and lands on the
// [`ServicoCountMismatch`] arm — the same typed view every
// per-renderer `#[from] ServicoCountMismatch` arm already
// surfaces at the two-line pair this helper replaces. Both
// directions of the V0 count invariant (empty AND ≥ 2) land on
// the same arm — pinning the multi-entry direction here; the
// empty direction is covered by the peer
// `require_single_servico_rejects_empty_list_with_typed_mismatch`
// test on the single-axis primitive.
let mut c = bare_servico();
c.servicos = vec![
"servicos/hello-rio.computeunit.yaml".into(),
"servicos/extra.computeunit.yaml".into(),
];
let err: RendererStandIn = require_v0_servico_shape(&c).unwrap_err();
match err {
RendererStandIn::UnsupportedServicoCount(c) => {
assert_eq!(c.nome, "hello-rio");
assert_eq!(c.count, 2);
}
RendererStandIn::NotAServico(_) => {
panic!("count gate must fire when kind gate passes")
}
}
}
#[test]
fn require_v0_servico_shape_matches_two_line_pair_semantic() {
// Equivalence pin: on every input, the compound helper's
// Ok/Err discrimination matches the two-line pair verbatim —
// the lift is a behavioral no-op at the caller boundary. Peer
// to the sibling `entry_or_default_<variant>` equivalence
// tests that pin the lifted primitive against the inline
// block it replaces.
//
// Three axes covered: V0 shape (Ok/Ok), kind gate fires
// (Err/Ok on the two-line pair — pair short-circuits at the
// kind gate), count gate fires (Ok/Err on the two-line pair —
// pair reaches the count gate).
let cases: Vec<(CaixaKind, Vec<String>)> = vec![
(CaixaKind::Servico, vec!["servicos/x.yaml".into()]),
(CaixaKind::Biblioteca, vec![]),
(CaixaKind::Servico, vec![]),
(CaixaKind::Aplicacao, vec!["servicos/x.yaml".into()]),
(
CaixaKind::Servico,
vec!["servicos/a.yaml".into(), "servicos/b.yaml".into()],
),
];
for (kind, servicos) in cases {
let mut c = bare_servico();
c.kind = kind;
c.servicos = servicos;
let pair: Result<(), RendererStandIn> = (|| {
require_kind(&c, CaixaKind::Servico)?;
require_single_servico(&c)?;
Ok(())
})();
let compound: Result<(), RendererStandIn> = require_v0_servico_shape(&c);
assert_eq!(
pair.is_ok(),
compound.is_ok(),
"compound helper must match two-line pair on kind={kind:?} servicos.len()={}",
c.servicos.len(),
);
}
}
// ── require_aplicacao_view — compound per-Aplicacao entry gate ───
/// Local `thiserror`-shaped renderer-error stand-in that mirrors
/// `caixa-mesh::Error`'s two `#[from]` arms at the compound
/// helper's `E: From<KindMismatch> + From<AplicacaoError>` bound.
/// Same discipline as the sibling [`RendererStandIn`] stand-in on
/// the peer per-Servico [`require_v0_servico_shape`] gate: pins
/// the compound helper's type-inference contract inside caixa-core
/// without a workspace-crate dependency (which would bloat the
/// build graph).
#[derive(Debug, thiserror::Error)]
enum AplicacaoRendererStandIn {
#[error("{0}")]
NotAnAplicacao(#[from] KindMismatch),
#[error("{0}")]
InvalidAplicacao(#[from] crate::aplicacao::AplicacaoError),
}
fn bare_aplicacao() -> Caixa {
let mut c = bare_servico();
c.nome = "checkout".into();
c.kind = CaixaKind::Aplicacao;
c.servicos = vec![];
c.membros = vec![
crate::aplicacao::Membro {
caixa: "cart".into(),
versao: "^0.1".into(),
},
crate::aplicacao::Membro {
caixa: "catalog".into(),
versao: "^0.1".into(),
},
];
// `:placement` needs at least one named cluster (every strategy
// uses the list as a hosting/takeover/shard pool per
// MESH-COMPOSITION §II.1/§II.4); the fold-through
// [`Caixa::aplicacao_view`] uses `Placement::default()` which
// carries an empty `:clusters` and would trip
// `AplicacaoError::PlacementWithoutClusters` at
// `AplicacaoSpec::validate` — the peer per-Aplicacao
// renderer fixtures (`caixa-mesh::aplicacao_caixa`) pin the
// same non-empty `:clusters` shape.
c.placement = Some(crate::aplicacao::Placement {
estrategia: crate::aplicacao::PlacementStrategy::SingleNode,
clusters: vec!["default".into()],
affinity: None,
shard_key: None,
});
c
}
#[test]
fn require_aplicacao_view_accepts_valid_aplicacao() {
// Happy path: a `:kind Aplicacao` caixa with a well-formed
// `:membros` stanza — the canonical V0 shape every
// per-Aplicacao renderer's entry-point sees — passes the
// compound three-arm gate and returns a validated
// [`AplicacaoSpec`]. Same outcome as the three-line cascade
// the compound helper replaces: [`require_kind`] passes,
// [`Caixa::aplicacao_view`] returns `Some(spec)`, and
// [`AplicacaoSpec::validate`] passes. Peer to
// `require_v0_servico_shape_accepts_v0_servico` on the
// sibling per-Servico compound gate.
let c = bare_aplicacao();
let spec: crate::aplicacao::AplicacaoSpec =
require_aplicacao_view::<AplicacaoRendererStandIn>(&c)
.expect("valid aplicacao shape accepted");
// Route the per-Aplicacao `:membros` slice-projection through
// the substrate-canonical [`AplicacaoSpec::membros`] `&[Membro]`-
// return accessor rather than the raw `spec.membros` `Vec<Membro>`
// field access, and the per-member `:caixa` scalar-projection
// through the sibling [`crate::aplicacao::Membro::nome`] `&str`-
// return accessor rather than the raw `.caixa` `String`-field
// borrow, so a future rebrand of either storage (a per-cluster
// `:membros`-overlay the caixa-operator reconciles ahead of
// dispatch, an M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
// materializer's per-member alias table, a promotion of the
// per-`Membro` `caixa: String` slot to a typed `ServicoName`
// newtype the accessor materializes behind the same `&str`
// return contract) reaches this per-fixture happy-path
// acceptance-shape probe through the one accessor edit at the
// canonical caixa-core declaration rather than a coordinated
// rewrite that would include this render-side test-fixture
// navigation too. Peer to the sibling caixa-flux
// [`sample_caixa_nome_accessor_byte_equals_raw_field`] (2ffdb44)
// / caixa-crd `round_trip_preserves_core_fields` (1a160cd) /
// caixa-feira load.rs (e853d45) test-side accessor
// convergences on the peer per-`Caixa` scalar-axis field —
// extended here onto the render-side per-`AplicacaoSpec`
// `:membros` slice + per-`Membro` `:caixa` scalar axes.
let membros = spec.membros();
assert_eq!(membros.len(), 2);
assert_eq!(membros[0].nome(), "cart");
assert_eq!(membros[1].nome(), "catalog");
}
#[test]
fn require_aplicacao_view_accepts_valid_aplicacao_membros_accessor_byte_equals_raw_field() {
// Byte-parity pin: [`AplicacaoSpec::membros`]'s `&[Membro]`-
// return accessor must project the same slice-length and
// per-entry `:caixa` bytes as the raw `spec.membros`
// `Vec<Membro>` + per-`Membro` `caixa: String` field access
// on the shared per-test [`bare_aplicacao`] fixture the sibling
// [`require_aplicacao_view_accepts_valid_aplicacao`] happy-
// path acceptance pin navigates through. Guards the paired
// per-fixture convergence that just routed the three raw
// `spec.membros.len()` / `spec.membros[0].caixa` /
// `spec.membros[1].caixa` sites through the accessor pair: a
// future implementation of [`AplicacaoSpec::membros`] that
// returned a differently-shaped view (a filter over
// storage-dropping optional members, a cached
// `Cow<[Membro]>` materialization, an operator-side per-CR
// alias-rewritten membership overlay), or a future
// [`crate::aplicacao::Membro::nome`] projection that read a
// canonicalized rewrite (a per-tenant namespace prefix, an
// ASCII-lowered normalization) rather than the raw storage-
// side `.caixa` bytes, would silently split every render-
// side test-fixture navigation that routes through the
// accessors from the storage-side field the peer
// [`AplicacaoSpec::validate`] production membership-lookup
// path still reads through the same accessor pair — this
// pin surfaces the drift at caixa-core build time rather
// than at a downstream per-Aplicacao renderer's
// membership-lookup diagnostic on the fleet.
//
// Same byte-parity-pin discipline the sibling caixa-flux
// `sample_caixa_nome_accessor_byte_equals_raw_field` (2ffdb44)
// + caixa-crd `round_trip_preserves_core_fields` accessor
// convergence (1a160cd) + caixa-feira load.rs (e853d45)
// per-`Caixa` scalar-axis byte-parity pins added to lock the
// peer per-`Caixa` scalar-accessor family against the raw
// field-access at each crate's fixture — extended here onto
// the render-side per-`AplicacaoSpec` `:membros` slice + per-
// `Membro` `:caixa` scalar axes' shared test fixture.
let c = bare_aplicacao();
let spec: crate::aplicacao::AplicacaoSpec =
require_aplicacao_view::<AplicacaoRendererStandIn>(&c)
.expect("valid aplicacao shape accepted");
assert_eq!(
spec.membros().len(),
spec.membros.len(),
"AplicacaoSpec::membros() slice-length must byte-equal \
the raw `membros: Vec<Membro>` field storage's `.len()`; \
any implementation drift here silently splits every \
render-side test-fixture navigation that routes through \
the accessor from the storage-side field the peer \
AplicacaoSpec::validate production membership-lookup \
path still reads through the same accessor"
);
for (i, m) in spec.membros().iter().enumerate() {
assert_eq!(
m.nome(),
spec.membros[i].caixa.as_str(),
"Membro::nome() must borrow the same bytes as the raw \
`caixa: String` field storage at member index {i}; \
any implementation drift here silently splits every \
render-side test-fixture navigation that routes \
through the accessor from the storage-side field the \
peer AplicacaoSpec::validate production membership-\
lookup path still reads through the same accessor"
);
}
}
#[test]
fn require_aplicacao_view_forwards_kind_mismatch_first() {
// Order pin: the kind gate fires before the aplicacao_view
// fold-in + [`AplicacaoSpec::validate`], so a `:kind Servico`
// caixa carrying a well-formed `:membros` stanza (the manifest
// field's documented "silently ignored" case on a non-Aplicacao
// kind) surfaces the [`KindMismatch`] arm — the more actionable
// diagnostic — rather than any spec-side arm the manifest
// author never intended to hit. Reversing the order would flip
// every current caller's diagnostic on a mis-kinded input.
// Peer to `require_v0_servico_shape_forwards_kind_mismatch_first`
// on the sibling per-Servico compound gate.
let mut c = bare_aplicacao();
c.kind = CaixaKind::Servico;
c.servicos = vec!["servicos/hello.computeunit.yaml".into()];
let err: AplicacaoRendererStandIn = require_aplicacao_view(&c).unwrap_err();
match err {
AplicacaoRendererStandIn::NotAnAplicacao(k) => {
assert_eq!(k.nome, "checkout");
assert_eq!(k.expected, CaixaKind::Aplicacao);
assert_eq!(k.actual, CaixaKind::Servico);
}
AplicacaoRendererStandIn::InvalidAplicacao(_) => {
panic!("kind gate must fire before aplicacao-view fold-in on mis-kinded input")
}
}
}
#[test]
fn require_aplicacao_view_forwards_aplicacao_error_on_kind_match() {
// A `:kind Aplicacao` caixa that passes the kind gate but
// fails [`AplicacaoSpec::validate`] (empty `:membros` here —
// the [`AplicacaoError::NoMembros`] arm every Aplicacao must
// satisfy per MESH-COMPOSITION §III.1) lands on the
// [`AplicacaoError`] arm through the compound helper's
// `E: From<AplicacaoError>` bound. Same diagnostic the
// three-line cascade the compound helper replaces surfaces at
// `spec.validate()?`. Peer to
// `require_v0_servico_shape_forwards_count_mismatch_on_kind_match`
// on the sibling per-Servico compound gate.
let mut c = bare_aplicacao();
c.membros = vec![]; // trips AplicacaoError::NoMembros
let err: AplicacaoRendererStandIn = require_aplicacao_view(&c).unwrap_err();
match err {
AplicacaoRendererStandIn::InvalidAplicacao(
crate::aplicacao::AplicacaoError::NoMembros,
) => {}
AplicacaoRendererStandIn::InvalidAplicacao(other) => {
panic!("expected NoMembros arm, got {other:?}")
}
AplicacaoRendererStandIn::NotAnAplicacao(_) => {
panic!("spec-validate arm must fire when kind gate passes")
}
}
}
#[test]
fn require_aplicacao_view_matches_three_line_cascade_semantic() {
// Equivalence pin: on every input, the compound helper's
// Ok/Err discrimination matches the three-line cascade
// verbatim — the lift is a behavioral no-op at the caller
// boundary. Peer to the sibling
// `require_v0_servico_shape_matches_two_line_pair_semantic`
// equivalence pin on the per-Servico compound gate.
//
// Four axes covered: Aplicacao shape (Ok/Ok), kind gate fires
// (Err/Ok on the cascade — cascade short-circuits at the kind
// gate), spec-validate arm fires (Ok/Err on the cascade —
// cascade reaches [`AplicacaoSpec::validate`]), and a
// mis-kinded caixa with a spec-invalid `:membros` stanza (both
// invariants violated — the kind gate must still fire first).
let cases: Vec<(CaixaKind, Vec<crate::aplicacao::Membro>)> = vec![
(
CaixaKind::Aplicacao,
vec![
crate::aplicacao::Membro {
caixa: "cart".into(),
versao: "^0.1".into(),
},
crate::aplicacao::Membro {
caixa: "catalog".into(),
versao: "^0.1".into(),
},
],
),
(CaixaKind::Servico, vec![]),
(CaixaKind::Aplicacao, vec![]),
(
CaixaKind::Biblioteca,
vec![crate::aplicacao::Membro {
caixa: "cart".into(),
versao: "^0.1".into(),
}],
),
];
for (kind, membros) in cases {
let mut c = bare_aplicacao();
c.kind = kind;
c.membros = membros.clone();
if kind == CaixaKind::Servico {
c.servicos = vec!["servicos/hello.computeunit.yaml".into()];
} else {
c.servicos = vec![];
}
let cascade: Result<crate::aplicacao::AplicacaoSpec, AplicacaoRendererStandIn> =
(|| {
require_kind(&c, CaixaKind::Aplicacao)?;
let spec = c.aplicacao_view().expect(
"require_kind(Aplicacao) guarantees Caixa::aplicacao_view returns Some",
);
spec.validate()?;
Ok(spec)
})();
let compound: Result<crate::aplicacao::AplicacaoSpec, AplicacaoRendererStandIn> =
require_aplicacao_view(&c);
assert_eq!(
cascade.is_ok(),
compound.is_ok(),
"compound helper must match three-line cascade on kind={kind:?} membros.len()={}",
membros.len(),
);
// Compound helper's Ok-arm return matches cascade's
// Ok-arm return byte-for-byte (via serde YAML round-trip
// — the `AplicacaoSpec` derives `Serialize`, so equal-
// rendering values are the substrate-canonical equality
// signal the peer downstream renderers key off).
if let (Ok(cascade_spec), Ok(compound_spec)) = (cascade, compound) {
assert_eq!(
serde_yaml::to_string(&cascade_spec).expect("cascade AplicacaoSpec serializes"),
serde_yaml::to_string(&compound_spec)
.expect("compound AplicacaoSpec serializes"),
"compound helper's Ok arm must return byte-equal AplicacaoSpec to cascade"
);
}
}
}
// ── require_acao_view — compound per-`Acao` entry gate ───────────
/// Local `thiserror`-shaped renderer-error stand-in that mirrors
/// `caixa-actions::Error`'s three `#[from]` arms at the compound
/// helper's `E: From<KindMismatch> + From<MissingCiSlot> +
/// From<CiDecomposeFailure>` bound. Same discipline as the sibling
/// [`RendererStandIn`] / [`AplicacaoRendererStandIn`] stand-ins on
/// the peer per-Servico [`require_v0_servico_shape`] and
/// per-Aplicacao [`require_aplicacao_view`] compound gates: pins
/// the compound helper's type-inference contract inside caixa-core
/// without a workspace-crate dependency (which would bloat the
/// build graph).
#[derive(Debug, thiserror::Error)]
enum AcaoRendererStandIn {
#[error("{0}")]
NotAnAcao(#[from] KindMismatch),
#[error("{0}")]
MissingCi(#[from] MissingCiSlot),
#[error("{0}")]
Decompose(#[from] CiDecomposeFailure),
}
#[test]
fn require_acao_view_accepts_valid_acao() {
// Happy path: a `:kind Acao` caixa with a well-formed `:ci`
// stanza — the canonical V0 shape every per-`Acao` consumer's
// entry-point sees — passes the compound three-arm gate and
// returns the borrowed [`canteiro_types::CiRun`] paired with
// the owned [`canteiro_types::CanteiroDag`] the substrate
// primitive produced. Same outcome as the three-line prelude
// the compound helper replaces: [`require_kind`] passes,
// [`require_ci`] returns the borrowed slot, [`decompose_ci`]
// accepts the run. Peer to
// `require_aplicacao_view_accepts_valid_aplicacao` and
// `require_v0_servico_shape_accepts_v0_servico` on the sibling
// per-Aplicacao / per-Servico compound gates.
let mut c = bare_acao_without_ci();
c.ci = Some(linear_ci_run());
let (ci, cd) = require_acao_view::<AcaoRendererStandIn>(&c)
.expect("valid Acao shape accepted by compound helper");
assert_eq!(ci.workspace, "pleme-io");
assert_eq!(ci.nodes.len(), 2);
// `topo_order()` is infallible on the DAG the compound helper
// returns, mirroring the substrate-side pass-through pin at
// [`decompose_ci_accepts_valid_ci_run_and_returns_canteiro_dag`].
let topo = cd
.topo_order()
.expect("acyclic CanteiroDag returns a valid topo_order");
assert_eq!(
topo.iter().count(),
2,
"topo_order on the compound helper's returned DAG must yield \
two node ids on a two-node acyclic run"
);
}
#[test]
fn require_acao_view_forwards_kind_mismatch_first() {
// Order pin: the kind gate fires before the presence gate + the
// decompose gate, so a `:kind Servico` caixa carrying a
// well-formed `:ci` stanza (the manifest field's documented
// "silently ignored" case on a non-`Acao` kind) surfaces the
// [`KindMismatch`] arm — the more actionable diagnostic —
// rather than either downstream arm the manifest author never
// intended to hit. Reversing the order would flip every
// current caller's diagnostic on a mis-kinded input. Peer to
// `require_aplicacao_view_forwards_kind_mismatch_first` and
// `require_v0_servico_shape_forwards_kind_mismatch_first` on
// the sibling per-Aplicacao / per-Servico compound gates.
let mut c = bare_acao_without_ci();
c.kind = CaixaKind::Servico;
c.servicos = vec!["servicos/hello.computeunit.yaml".into()];
c.ci = Some(linear_ci_run());
// `unwrap_err()` needs `T: Debug`, and the Ok half here carries
// `canteiro_types::CanteiroDag`, which does not derive it at the
// pinned sui rev — so the whole caixa-core test target failed to
// COMPILE. A let-else says the same thing without borrowing a
// bound from a foreign type we do not own.
let Err(err): Result<_, AcaoRendererStandIn> = require_acao_view(&c) else {
panic!("this fixture must not produce an Acao view");
};
match err {
AcaoRendererStandIn::NotAnAcao(k) => {
assert_eq!(k.nome, "hello-rio");
assert_eq!(k.expected, CaixaKind::Acao);
assert_eq!(k.actual, CaixaKind::Servico);
}
AcaoRendererStandIn::MissingCi(_) => {
panic!("kind gate must fire before presence gate on mis-kinded input")
}
AcaoRendererStandIn::Decompose(_) => {
panic!("kind gate must fire before decompose gate on mis-kinded input")
}
}
}
#[test]
fn require_acao_view_forwards_missing_ci_slot_on_kind_match() {
// A `:kind Acao` caixa that passes the kind gate but declares
// no `:ci` slot lands on the [`MissingCiSlot`] arm through the
// compound helper's `E: From<MissingCiSlot>` bound — the same
// typed view the peer [`require_ci`] presence gate produces at
// the single-axis primitive, propagated through the compound
// gate's second arm.
let c = bare_acao_without_ci();
// `unwrap_err()` needs `T: Debug`, and the Ok half here carries
// `canteiro_types::CanteiroDag`, which does not derive it at the
// pinned sui rev — so the whole caixa-core test target failed to
// COMPILE. A let-else says the same thing without borrowing a
// bound from a foreign type we do not own.
let Err(err): Result<_, AcaoRendererStandIn> = require_acao_view(&c) else {
panic!("this fixture must not produce an Acao view");
};
match err {
AcaoRendererStandIn::MissingCi(m) => {
assert_eq!(m.nome, "hello-rio");
}
AcaoRendererStandIn::NotAnAcao(_) => {
panic!("presence gate must fire when kind gate passes")
}
AcaoRendererStandIn::Decompose(_) => {
panic!("presence gate must fire before decompose gate on missing `:ci` input")
}
}
}
#[test]
fn require_acao_view_forwards_decompose_failure_on_ci_present() {
// A `:kind Acao` caixa that passes the kind + presence gates
// but carries a cyclic `:ci` run lands on the
// [`CiDecomposeFailure`] arm through the compound helper's
// `E: From<CiDecomposeFailure>` bound — the same typed view
// the peer [`decompose_ci`] gate produces at the single-axis
// primitive, propagated through the compound gate's third
// arm.
let mut c = bare_acao_without_ci();
c.ci = Some(cyclic_ci_run());
// `unwrap_err()` needs `T: Debug`, and the Ok half here carries
// `canteiro_types::CanteiroDag`, which does not derive it at the
// pinned sui rev — so the whole caixa-core test target failed to
// COMPILE. A let-else says the same thing without borrowing a
// bound from a foreign type we do not own.
let Err(err): Result<_, AcaoRendererStandIn> = require_acao_view(&c) else {
panic!("this fixture must not produce an Acao view");
};
match err {
AcaoRendererStandIn::Decompose(f) => {
assert_eq!(f.nome, "hello-rio");
assert_eq!(f.source, canteiro_types::DecomposeError::Cycle);
}
AcaoRendererStandIn::NotAnAcao(_) => {
panic!("decompose gate must fire when kind + presence gates pass")
}
AcaoRendererStandIn::MissingCi(_) => {
panic!("decompose gate must fire when presence gate passes")
}
}
}
#[test]
fn require_acao_view_matches_three_line_prelude_semantic() {
// Equivalence pin: on every input, the compound helper's
// Ok/Err discrimination matches the three-line prelude
// verbatim — the lift is a behavioral no-op at the caller
// boundary. Peer to the sibling
// `require_aplicacao_view_matches_three_line_cascade_semantic`
// and `require_v0_servico_shape_matches_two_line_pair_semantic`
// equivalence pins on the per-Aplicacao / per-Servico compound
// gates.
//
// Five axes covered: valid Acao (Ok/Ok), kind gate fires
// (Err/Err on the prelude — prelude short-circuits at the kind
// gate), presence gate fires (Ok/Err on the prelude — prelude
// reaches [`require_ci`]), decompose gate fires (Ok/Err on the
// prelude — prelude reaches [`decompose_ci`]), and a
// mis-kinded caixa with a well-formed `:ci` (both invariants
// relevant — the kind gate must still fire first).
let cases: Vec<(CaixaKind, Option<canteiro_types::CiRun>)> = vec![
(CaixaKind::Acao, Some(linear_ci_run())),
(CaixaKind::Servico, Some(linear_ci_run())),
(CaixaKind::Acao, None),
(CaixaKind::Acao, Some(cyclic_ci_run())),
(CaixaKind::Biblioteca, None),
];
for (kind, ci) in cases {
let mut c = bare_acao_without_ci();
c.kind = kind;
c.ci = ci.clone();
if kind == CaixaKind::Servico {
c.servicos = vec!["servicos/hello.computeunit.yaml".into()];
} else {
c.servicos = vec![];
}
let prelude: Result<
(&canteiro_types::CiRun, canteiro_types::CanteiroDag),
AcaoRendererStandIn,
> = (|| {
require_kind(&c, CaixaKind::Acao)?;
let ci_borrowed = require_ci(&c)?;
let cd = decompose_ci(&c, ci_borrowed)?;
Ok((ci_borrowed, cd))
})();
let compound: Result<
(&canteiro_types::CiRun, canteiro_types::CanteiroDag),
AcaoRendererStandIn,
> = require_acao_view(&c);
assert_eq!(
prelude.is_ok(),
compound.is_ok(),
"compound helper must match three-line prelude on kind={kind:?} ci.is_some()={}",
ci.is_some(),
);
// Compound helper's Ok-arm return matches prelude's
// Ok-arm return byte-for-byte on both projections: the
// borrowed `&CiRun`'s node count + workspace / repo
// identity, and the owned `CanteiroDag`'s
// topological-order node-name projection (the substrate-
// canonical equality signal every downstream per-`Acao`
// consumer keys off).
if let (Ok((prelude_ci, prelude_cd)), Ok((compound_ci, compound_cd))) =
(prelude, compound)
{
assert_eq!(
prelude_ci.workspace, compound_ci.workspace,
"compound helper's borrowed CiRun's workspace must \
equal prelude's byte-for-byte"
);
assert_eq!(
prelude_ci.repo, compound_ci.repo,
"compound helper's borrowed CiRun's repo must equal \
prelude's byte-for-byte"
);
assert_eq!(
prelude_ci.nodes.len(),
compound_ci.nodes.len(),
"compound helper's borrowed CiRun's node count must \
equal prelude's"
);
let prelude_topo = prelude_cd
.topo_order()
.expect("prelude's DAG produces a valid topo_order");
let compound_topo = compound_cd
.topo_order()
.expect("compound's DAG produces a valid topo_order");
let prelude_names: Vec<String> = prelude_topo
.iter()
.filter_map(|id| prelude_cd.nodes.get(id).map(|n| n.name.clone()))
.collect();
let compound_names: Vec<String> = compound_topo
.iter()
.filter_map(|id| compound_cd.nodes.get(id).map(|n| n.name.clone()))
.collect();
assert_eq!(
prelude_names, compound_names,
"compound helper's DAG must produce byte-equal \
topological-order node-name projection to prelude's"
);
}
}
}
// ── single_field_overlay — typed per-axis overlay primitive ──────────
#[test]
fn single_field_overlay_none_yields_none() {
// Empty-axis-skip semantic at the typed-primitive layer: a
// `None` slot returns `None`, not `Some(empty Mapping)`. The
// caller's `if let Some(overlay) = …` guard then becomes the
// single emission gate, and a malformed `outer: {}` (the
// empty-mapping form some K8s parsers reject) is structurally
// impossible by construction.
let v: Option<serde_yaml::Value> = single_field_overlay::<u32, _>(None, "attempts", |n| {
serde_yaml::Value::Number(n.into())
});
assert!(v.is_none());
}
#[test]
fn single_field_overlay_some_yields_single_field_mapping() {
// The Some arm builds exactly one inner key/value pair, no
// more, no less. Pinning the shape so a future refactor can't
// accidentally introduce a second field (which would render
// as a malformed `timeouts: { request: "30s", <leak>: ... }`
// overlay block).
let v = single_field_overlay(Some(30u32), "attempts", |n| {
serde_yaml::Value::Number(n.into())
})
.expect("Some arm yields Some(...)");
let m = v.as_mapping().expect("mapping shape");
assert_eq!(m.len(), 1);
assert_eq!(m.get("attempts").and_then(|x| x.as_u64()), Some(30));
}
#[test]
fn single_field_overlay_threads_typed_value_through_closure() {
// The closure receives the unwrapped typed `T` (not the
// wrapping `Option<T>`), so the per-overlay value-shaping
// logic stays at the call site. Three different Value shapes
// pin the closure's type-flow: a `String` (for canonical
// duration / enum scalars), a `Number` (for typed integer
// attempt counts), and a derived `Bool` (for tristate enums).
// Mirrors the three landed overlays' shapes letter-for-letter.
let dur = single_field_overlay(Some("30s".to_string()), "request", |s| {
serde_yaml::Value::String(s)
})
.unwrap();
assert_eq!(dur.get("request").and_then(|v| v.as_str()), Some("30s"));
let num = single_field_overlay(Some(3u32), "attempts", |n| {
serde_yaml::Value::Number(n.into())
})
.unwrap();
assert_eq!(num.get("attempts").and_then(|v| v.as_u64()), Some(3));
// The mtls tristate's two non-None arms map to enum strings,
// not raw bools (the Cilium CRD's `mode: required|disabled`
// shape — pinned end-to-end at every emit site by the
// `cnp_authentication_mode_serialized_as_yaml_string` test).
// Both scalar-values thread through the lifted canonical
// [`cilium_auth_mode`] bijection — the same `bool → &'static
// str` projection the production `cilium_network_policies`
// per-`(:de, :para)` overlay closure reaches for, so a future
// Cilium CNP `MutualAuthenticationMode` OpenAPI schema enum
// rebrand (either arm's scalar-value string, or the per-arm
// dispatch) lands at the two consts + one projection body
// rather than duplicated across the production emitter site
// and this generic-helper pin.
let mode = single_field_overlay(Some(true), CILIUM_KEY_MODE, |b| {
serde_yaml::Value::String(cilium_auth_mode(b).into())
})
.unwrap();
assert_eq!(
mode.get(CILIUM_KEY_MODE).and_then(|v| v.as_str()),
Some(CILIUM_AUTH_MODE_REQUIRED)
);
}
#[test]
fn single_field_overlay_outer_key_is_callers_concern() {
// The helper builds the *inner* (single-field) Mapping; the
// *outer* key (`timeouts` / `retry` / `authentication`) is
// the caller's `if let Some(overlay) = … { rule.insert(<outer>,
// overlay.clone()) }` insertion. Pinning that the helper's
// returned Value carries no outer-key wrapping — emitting the
// outer-key-wrapped form here would silently double-wrap
// every overlay (`timeouts: { timeouts: { request: "30s" } }`
// post-insertion).
let v = single_field_overlay(Some(30u32), "attempts", |n| {
serde_yaml::Value::Number(n.into())
})
.unwrap();
let m = v.as_mapping().unwrap();
// Only the inner key — no `timeouts:` / `retry:` /
// `authentication:` wrapper at this layer.
for k in ["timeouts", "retry", "authentication"] {
assert!(
m.get(k).is_none(),
"single_field_overlay must not pre-insert the outer key {k:?} \
(the caller's per-rule insert is the canonical insertion site)"
);
}
}
#[test]
fn single_field_overlay_value_is_clonable_for_per_rule_dispatch() {
// The build-once-clone-many idiom every emit-site uses: the
// overlay is computed once per renderer call (so the closure
// runs exactly once) and `.clone()`d into each rule of the
// emitted sequence. Pin that the returned Value is in fact
// cloneable (a `serde_yaml::Value` always is, but the test
// pins the contract end-to-end so a future refactor that
// returns a non-Cloneable wrapper surfaces here).
let v = single_field_overlay(Some(30u32), "attempts", |n| {
serde_yaml::Value::Number(n.into())
})
.unwrap();
let v_clone = v.clone();
assert_eq!(v, v_clone);
}
// ── upsert_named_entry — typed sequence-upsert primitive ─────────────
#[test]
fn upsert_named_entry_appends_when_empty() {
// Empty-sequence-first arm: an initially-empty aggregator
// programs.yaml carries no matching entry, so the upsert falls
// through to the append-new tail and returns
// `Ok(true)` (newly inserted). Pins the append-new contract
// both writer-side [`caixa_flux`] upsert paths lean on when
// the aggregator's `programs:` sequence is empty
// (`upsert_inserts_new_entry` at the values.yaml layer,
// `upsert_helmrelease_inserts_under_spec_values_programs` at
// the HelmRelease layer) — the same shape at the typed-
// primitive layer as the two production sites.
let mut arr: Vec<serde_yaml::Value> = Vec::new();
let entry: serde_yaml::Value =
serde_yaml::from_str("{ name: hello-rio, module: { source: oci://x } }").unwrap();
let inserted =
upsert_named_entry::<()>(&mut arr, entry, FLEET_PROGRAMS_KEY_NAME, || ()).unwrap();
assert!(inserted, "empty sequence + new entry must append");
assert_eq!(arr.len(), 1);
assert_eq!(
arr[0].get(FLEET_PROGRAMS_KEY_NAME).and_then(|n| n.as_str()),
Some("hello-rio")
);
}
#[test]
fn upsert_named_entry_appends_when_no_match() {
// Non-matching-name append arm: an aggregator sequence with a
// differently-named entry carries no matching name-key value,
// so the upsert falls through to the append-new tail (never
// replacing) and returns `Ok(true)`. Pins the append-only
// semantic that keeps every unrelated entry untouched.
let mut arr: Vec<serde_yaml::Value> = vec![
serde_yaml::from_str("{ name: other, module: { source: github:foo/bar } }").unwrap(),
];
let entry: serde_yaml::Value =
serde_yaml::from_str("{ name: hello-rio, module: { source: oci://x } }").unwrap();
let inserted =
upsert_named_entry::<()>(&mut arr, entry, FLEET_PROGRAMS_KEY_NAME, || ()).unwrap();
assert!(inserted);
assert_eq!(arr.len(), 2);
assert_eq!(
arr[0].get(FLEET_PROGRAMS_KEY_NAME).and_then(|n| n.as_str()),
Some("other")
);
assert_eq!(
arr[1].get(FLEET_PROGRAMS_KEY_NAME).and_then(|n| n.as_str()),
Some("hello-rio")
);
}
#[test]
fn upsert_named_entry_replaces_when_match() {
// Match-and-replace arm: an aggregator sequence carrying an
// entry whose `<name_key>` matches the new entry's name-scalar
// gets its slot rewritten in place and the helper returns
// `Ok(false)` (replaced-not-appended). Pins the idempotency
// contract every writer-side upsert path lands on — the same
// caixa.lisp deployed twice must upsert to the same
// aggregator entry, never grow a duplicated `programs[]`
// entry. Peer at the substrate layer with the two production
// `upsert_replaces_existing_entry` /
// `upsert_helmrelease_replaces_existing` tests
// ([`caixa_flux`]).
let mut arr: Vec<serde_yaml::Value> = vec![
serde_yaml::from_str("{ name: hello-rio, module: { source: oci://old } }").unwrap(),
];
let entry: serde_yaml::Value =
serde_yaml::from_str("{ name: hello-rio, module: { source: oci://new } }").unwrap();
let inserted =
upsert_named_entry::<()>(&mut arr, entry, FLEET_PROGRAMS_KEY_NAME, || ()).unwrap();
assert!(!inserted, "matching name must replace, not append");
assert_eq!(arr.len(), 1);
assert_eq!(
arr[0]
.get(COMPUTEUNIT_SPEC_KEY_MODULE)
.and_then(|m| m.get(COMPUTEUNIT_MODULE_KEY_SOURCE))
.and_then(|s| s.as_str()),
Some("oci://new")
);
}
#[test]
fn upsert_named_entry_preserves_position_on_replace() {
// Position-preserving-replace pin: when an interior entry
// matches, its slot is rewritten in place and the surrounding
// entries stay put (first / last / any middle position). The
// aggregator's fanout consumers filter `programs[]` in
// declaration order (the `lareira-fleet-programs` chart's
// `.Values.programs` iteration + the future
// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
// per-entry admission bind); a replace-then-move-to-tail shift
// (silently promoting the just-upserted entry to end-of-list)
// would silently reorder every downstream consumer's iteration
// window. Same declaration-order-preservation contract the
// aggregator side relies on.
let mut arr: Vec<serde_yaml::Value> = vec![
serde_yaml::from_str("{ name: alpha, module: { source: github:a/a } }").unwrap(),
serde_yaml::from_str("{ name: beta, module: { source: github:b/old } }").unwrap(),
serde_yaml::from_str("{ name: gamma, module: { source: github:g/g } }").unwrap(),
];
let entry: serde_yaml::Value =
serde_yaml::from_str("{ name: beta, module: { source: github:b/new } }").unwrap();
let inserted =
upsert_named_entry::<()>(&mut arr, entry, FLEET_PROGRAMS_KEY_NAME, || ()).unwrap();
assert!(!inserted);
assert_eq!(arr.len(), 3);
// Order pin: alpha stays at 0, beta stays at 1 (rewritten),
// gamma stays at 2 — replace must preserve position.
let names: Vec<&str> = arr
.iter()
.filter_map(|v| v.get(FLEET_PROGRAMS_KEY_NAME).and_then(|n| n.as_str()))
.collect();
assert_eq!(names, ["alpha", "beta", "gamma"]);
assert_eq!(
arr[1]
.get(COMPUTEUNIT_SPEC_KEY_MODULE)
.and_then(|m| m.get(COMPUTEUNIT_MODULE_KEY_SOURCE))
.and_then(|s| s.as_str()),
Some("github:b/new")
);
}
#[test]
fn upsert_named_entry_calls_error_closure_on_missing_name_key() {
// Missing-name-scalar arm: when the new entry doesn't carry
// `<name_key>` as a string scalar, the helper calls the
// caller's `on_missing_name` closure — the caller's own typed
// [`crate::RenderError`]-shaped error surface remains
// authoritative. Threaded through a closure so this crate
// stays agnostic to the caller's error enum shape (the two
// production sites in [`caixa_flux`] surface
// `Error::MissingField(FLEET_PROGRAMS_KEY_NAME)` verbatim,
// and any future upsert path — the M4
// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
// per-entry upsert, the `caixa-otel` per-scrape upsert —
// surfaces its own typed variant).
let mut arr: Vec<serde_yaml::Value> = Vec::new();
let entry: serde_yaml::Value =
serde_yaml::from_str("{ module: { source: oci://x } }").unwrap();
let err = upsert_named_entry(&mut arr, entry, FLEET_PROGRAMS_KEY_NAME, || {
"missing-name".to_string()
})
.unwrap_err();
assert_eq!(err, "missing-name");
assert!(arr.is_empty(), "missing-name entry must not land in arr");
}
#[test]
fn upsert_named_entry_calls_error_closure_on_non_string_name_scalar() {
// Non-string-name-scalar arm: when the new entry's
// `<name_key>` is present but not a string (a number, a
// mapping, a sequence — the paste-from-binary footgun where
// an author or a schema-migration script accidentally lands a
// JSON-Number in the name slot), the helper takes the same
// path as the missing-name arm and calls the caller's
// `on_missing_name` closure. Peer arm to the
// upsert_named_entry_calls_error_closure_on_missing_name_key
// pin — both non-string-scalar paths route through the same
// caller-owned diagnostic.
let mut arr: Vec<serde_yaml::Value> = Vec::new();
let entry: serde_yaml::Value =
serde_yaml::from_str("{ name: 42, module: { source: oci://x } }").unwrap();
let err =
upsert_named_entry(&mut arr, entry, FLEET_PROGRAMS_KEY_NAME, || 7u32).unwrap_err();
assert_eq!(err, 7u32);
assert!(arr.is_empty());
}
#[test]
fn upsert_named_entry_uses_parametric_name_key() {
// Name-key-axis-parametric pin: the helper matches on the
// `name_key` parameter, not the pinned
// [`FLEET_PROGRAMS_KEY_NAME`] const — a future writer-side
// upsert path keying on a different discriminator scalar
// (the M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
// per-entry `spec.selector` axis, an in-progress rebrand
// promoting `id:` alongside `name:`) reaches for the same
// helper with a different key rather than re-inlining the
// upsert loop.
let mut arr: Vec<serde_yaml::Value> =
vec![serde_yaml::from_str("{ id: alpha, payload: original }").unwrap()];
let entry: serde_yaml::Value =
serde_yaml::from_str("{ id: alpha, payload: replaced }").unwrap();
let inserted = upsert_named_entry::<()>(&mut arr, entry, "id", || ()).unwrap();
assert!(!inserted, "matching `id:` must replace, not append");
assert_eq!(arr.len(), 1);
assert_eq!(
arr[0].get("payload").and_then(|p| p.as_str()),
Some("replaced")
);
}
// ── is_dns_1123_label — shared DNS-1123 label predicate ──────────────
#[test]
fn dns_1123_label_accepts_canonical_forms() {
// Substrate-side pin: the predicate accepts the same canonical
// shapes its three caller axes (`:membros :caixa`,
// `:placement :clusters`, `:children :caixa`) accept at their own
// gates. Drift between this list and the per-axis positive-set
// sweeps surfaces here — one source of truth for the rule.
for s in [
"worker",
"a",
"0",
"cache-v2",
"payment-retry",
"2-pool",
"mar-east",
] {
is_dns_1123_label(s)
.unwrap_or_else(|e| panic!("canonical DNS-1123 label {s:?} must pass: {e:?}"));
}
}
#[test]
fn dns_1123_label_rejects_uppercase_with_lower_suggestion() {
// The diagnostic carries the lower-cased fix verbatim so every
// caller's per-axis `*Invalid { reason }` wrapping the predicate's
// output reads back as a one-edit-fix suggestion. Pinned at the
// substrate layer so the suggestion shape lives in one place.
let err = is_dns_1123_label("Rio").unwrap_err();
assert!(err.contains("uppercase"), "got: {err:?}");
assert!(err.contains("\"rio\""), "got: {err:?}");
}
#[test]
fn dns_1123_label_rejects_at_64_byte_boundary() {
// The 63-byte cap pin — both the boundary-exceeding case and
// the boundary-accepting case in one place, so a future cap
// shift surfaces both arms simultaneously.
let max_ok = "a".repeat(63);
is_dns_1123_label(&max_ok).unwrap();
let too_long = "a".repeat(64);
let err = is_dns_1123_label(&too_long).unwrap_err();
assert!(err.contains("63"), "got: {err:?}");
assert!(err.contains("64"), "got: {err:?}");
}
#[test]
fn dns_1123_label_rejects_empty_defensively() {
// Defensive re-check pin — every peer value-shape predicate in
// this module (`is_gateway_api_http_path`, `is_wit_world_ref`,
// `is_nats_subject`, `is_wasi_keyvalue_slot`, `is_git_ref_name`)
// carries the same empty-first arm, so `is_dns_1123_label("")`
// returns a clean parser-shaped `must not be empty` reason
// instead of panicking at the boundary arm's `bytes[0]` access
// (`bytes[0].is_ascii_alphanumeric()` on an empty slice would
// index out of bounds). The per-axis narrower `*Empty` variant
// (`MembroCaixaEmpty`, `PlacementClusterEmpty`, `EmptyChildName`,
// `ModuleEmpty`) still fires at every current call site — this
// arm exists so any future call site missing the pre-check gets
// a self-locating diagnostic rather than a `panic!` far from the
// source caixa.lisp, matching the "usable from any future call
// site without a shape-mismatch footgun" discipline every peer
// predicate's doc-comment already promises.
let err = is_dns_1123_label("").unwrap_err();
assert!(err.contains("empty"), "got: {err:?}");
assert_eq!(err, "must not be empty");
}
// ── is_gateway_api_http_path — shared HTTP-path predicate ────────────
#[test]
fn gateway_api_http_path_accepts_canonical_forms() {
// Substrate-side pin: the predicate accepts the same canonical
// shapes both caller axes (`:entrada :paths` and `:contratos
// :endpoint`) accept at their own gates. Drift between this
// list and the per-axis positive-set sweeps surfaces here —
// one source of truth for the rule. Includes the bare-root
// `/` (the catch-all both renderers fall back to), the
// `/foo..bar` interior-`..`-substring (not a `..` segment),
// the `/...` and `/foo.` `.`-bearing names (not `.` segments),
// and the percent-encoded form.
for p in [
"/",
"/api/cart",
"/healthz",
"/api/.config",
"/v1/products",
"/products/:id",
"/api/cart/",
"/api/caf%C3%A9",
"/foo..bar",
"/...",
"/charge",
] {
is_gateway_api_http_path(p)
.unwrap_or_else(|e| panic!("canonical HTTP path {p:?} must pass: {e:?}"));
}
}
#[test]
fn gateway_api_http_path_rejects_each_arm_with_substring_pinned_reason() {
// Substrate-side diagnostic-shape pin: each grammar arm
// surfaces its own distinct reason substring. Pinned here so
// a future reason-wording rephrase that drops any of these
// substrings surfaces at this one place, not piecemeal across
// every per-axis test sweep.
for (path, needle) in [
("/api?q=1", "must not contain `?`"),
("/api#frag", "must not contain `#`"),
("/api my", "whitespace"),
("/api\x01x", "control character"),
("/api/café", "non-ASCII"),
("/api//x", "consecutive `/`"),
("/api/./x", "`.` segment"),
("/api/../x", "`..` parent-segment"),
] {
let err = is_gateway_api_http_path(path)
.err()
.unwrap_or_else(|| panic!("path {path:?} must be rejected"));
assert!(
err.contains(needle),
"path {path:?} reason must contain {needle:?}; got {err:?}"
);
}
}
#[test]
fn gateway_api_http_path_rejects_at_1025_byte_boundary() {
// The 1024-byte cap pin — both the boundary-exceeding case and
// the boundary-accepting case in one place, so a future cap
// shift surfaces both arms simultaneously, mirroring
// `dns_1123_label_rejects_at_64_byte_boundary` on the peer
// predicate.
let max_ok = format!("/{}", "a".repeat(1023));
assert_eq!(max_ok.len(), 1024);
is_gateway_api_http_path(&max_ok).unwrap();
let too_long = format!("/{}", "a".repeat(1024));
assert_eq!(too_long.len(), 1025);
let err = is_gateway_api_http_path(&too_long).unwrap_err();
assert!(err.contains("1024"), "got: {err:?}");
assert!(err.contains("1025"), "got: {err:?}");
}
#[test]
fn gateway_api_http_path_rejects_empty_defensively() {
// The predicate is called only after each caller's narrower
// `*Empty` arm has fired; re-checking here keeps the predicate
// usable from any future call site without an empty-precondition
// footgun, and avoids a panic on `bytes[0]`-style indexing if
// a future arm is added. Same defensive empty-check
// `validate_entrada_path` carries at its call site (55410e4).
let err = is_gateway_api_http_path("").unwrap_err();
assert!(err.contains("empty"), "got: {err:?}");
}
#[test]
fn gateway_api_http_path_rejects_not_absolute_defensively() {
// Defensive re-check of the leading-`/` invariant the per-axis
// call site enforces with its own narrower `*NotAbsolute` arm;
// ensures the predicate is callable from any future call site
// without a shape-mismatch footgun.
let err = is_gateway_api_http_path("api/cart").unwrap_err();
assert!(err.contains('/'), "got: {err:?}");
}
#[test]
fn gateway_api_http_path_rejects_every_reserved_printable_ascii_byte() {
// Substrate-side sweep: every one of the eleven printable-ASCII
// bytes outside the K8s Gateway API HTTPPathMatch.value
// apiserver-side OpenAPI regex
// `^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$`
// accepted set surfaces a self-locating reason naming the
// offending byte verbatim plus the canonical `%XX` percent-
// encoding remediation. RFC 3986 §3.3's `pchar = unreserved /
// pct-encoded / sub-delims / ":" / "@"` grammar excludes these
// bytes from every path segment, so the apiserver rejects them
// at admission time on every
// `HTTPRoute.spec.rules[].matches[].path.value` landing site —
// peer with the `?` / `#` / whitespace / control / non-ASCII
// arms `gateway_api_http_path_rejects_each_arm_with_substring_
// pinned_reason` covers.
//
// Each char surfaces in a path-shape that pins the canonical
// authoring footgun the K8s apiserver would otherwise catch
// far from the caixa.lisp: `{id}` / `[0]` / `<placeholder>`
// template forms, the Windows path-separator typo, the
// shell-regex character footgun, the SQL-string-literal /
// YAML-flow-mapping accidents.
for (path, ch) in [
("/api/cart\"path", '"'),
("/api/cart<id>", '<'),
("/api/cart/<id>", '<'),
("/api/cart[0]", '['),
("/api/cart\\path", '\\'),
("/api/cart]", ']'),
("/api/cart/^foo", '^'),
("/api/cart/`foo", '`'),
("/api/cart/{id}", '{'),
("/api/cart|alt", '|'),
("/api/cart}", '}'),
] {
let err = is_gateway_api_http_path(path)
.err()
.unwrap_or_else(|| panic!("path {path:?} must be rejected"));
assert!(
err.contains("reserved character"),
"path {path:?} reason must name the reserved-character axis; got {err:?}"
);
assert!(
err.contains(&format!("{ch:?}")),
"path {path:?} reason must name the offending byte {ch:?} verbatim; got {err:?}"
);
let hex = format!("%{:02X}", ch as u8);
assert!(
err.contains(&hex),
"path {path:?} reason must surface the canonical {hex:?} percent-encoding \
remediation; got {err:?}"
);
}
}
#[test]
fn gateway_api_http_path_reserved_char_arm_fires_before_consecutive_slash() {
// Precedence pin: the per-byte loop runs before the post-loop
// structural arms (`//`, `/./`, `/../`), so a path that is
// *both* reserved-char-bearing and consecutive-`/`-bearing
// surfaces the more self-locating reserved-character diagnostic
// first, naming the offending byte verbatim. Mirrors the
// existing `?` / `#` / whitespace / control / non-ASCII arms'
// implicit precedence the
// `gateway_api_http_path_rejects_each_arm_with_substring_
// pinned_reason` pin already establishes for the peer per-byte
// shapes.
let err = is_gateway_api_http_path("/api/{id}//x").unwrap_err();
assert!(
err.contains("reserved character") && err.contains("'{'"),
"got: {err:?}"
);
assert!(
!err.contains("consecutive"),
"the reserved-char arm must fire before the consecutive-`/` arm; got: {err:?}"
);
}
#[test]
fn gateway_api_http_path_accepts_percent_encoded_reserved_chars() {
// Positive-control complement to the reserved-byte rejection
// sweep: every one of the eleven reserved printable-ASCII bytes
// is admissible *when* properly percent-encoded, matching the
// canonical Gateway API HTTPPathMatch.value apiserver-side
// OpenAPI regex's `[%][0-9a-fA-F]{2}` alternative. Pins the
// canonical remediation pathway the reserved-byte arm's reason
// wording names — author who carries a literal `{` percent-
// encodes as `%7B` and the typed slot accepts.
for path in [
"/api/cart%22path",
"/api/cart%3Cid%3E",
"/api/cart%5B0%5D",
"/api/cart%5Cpath",
"/api/cart/%5Efoo",
"/api/cart/%60foo",
"/api/cart/%7Bid%7D",
"/api/cart%7Calt",
] {
is_gateway_api_http_path(path)
.unwrap_or_else(|e| panic!("percent-encoded path {path:?} must pass: {e:?}"));
}
}
// ── is_wit_world_ref — shared WIT world-reference predicate ──────────
#[test]
fn wit_world_ref_accepts_canonical_forms() {
// Substrate-side pin: the predicate accepts every canonical
// WIT identifier the `:contratos :wit` axis already carries in
// the test fixtures + the example checkout-aplicacao (each
// hand-curated to match real WIT registry references). Drift
// between this list and the per-axis positive-set sweep
// surfaces here — one source of truth for the rule. Includes
// every shape variant: HTTP-prefixed (`wasi:http/proxy`),
// KV-prefixed (`wasi:keyvalue/store`), pubsub-prefixed
// (`nats:pub-sub`, `kafka:topic`), capability-only
// (`custom:exchange`, `pleme:cap/audit`), the optional
// `@<version>` suffix (`wasi:http/proxy@0.2.0`), and the
// multi-segment `/iface/iface` form the WIT IDL grammar allows.
for s in [
"wasi:http/proxy",
"wasi:keyvalue/store",
"nats:pub-sub",
"kafka:topic",
"custom:exchange",
"pleme:cap/audit",
"http:server",
"kv:store",
"wasi:http/proxy@0.2.0",
"wasi:keyvalue/store@0.2.0-rc.1",
"pleme:cap/audit/v2",
// Every legal shape SemVer 2.0.0 admits in the `@<version>`
// body — bare numeric core, pre-release suffix (single +
// dot-separated identifiers), build-metadata suffix (single
// + dot-separated identifiers), combined pre-release +
// build-metadata, and leading-zero-avoiding pre-release
// identifiers — pinned here so a future tightening of the
// per-byte accepted set that rejects a canonical semver
// shape surfaces here rather than at the M4 CR materializer's
// WIT-parse boundary.
"wasi:http/proxy@1.0.0",
"wasi:http/proxy@0.2.0-alpha",
"wasi:http/proxy@1.0.0-alpha.1",
"wasi:http/proxy@2.0.0+build.42",
"wasi:http/proxy@0.0.0-rc.1+abc.def",
] {
is_wit_world_ref(s)
.unwrap_or_else(|e| panic!("canonical WIT reference {s:?} must pass: {e:?}"));
}
}
#[test]
fn wit_world_ref_rejects_each_arm_with_substring_pinned_reason() {
// Substrate-side diagnostic-shape pin: each grammar arm
// surfaces its own distinct reason substring. Pinned here so a
// future reason-wording rephrase that drops any of these
// substrings surfaces at this one place, not piecemeal across
// every per-axis test sweep. Mirrors
// `gateway_api_http_path_rejects_each_arm_with_substring_pinned_reason`
// on the peer predicate.
for (s, needle) in [
// Missing `:` separator → silent capability demotion.
("wasi-http/proxy", "must contain a `:`"),
// Multiple `:` → can't split into ns + pkg.
("wasi:http:proxy", "exactly one `:`"),
// Uppercase → silently bypasses the lowercase dispatch.
("WASI:http/proxy", "lowercase"),
("wasi:HTTP/proxy", "lowercase"),
// Empty package half → can't resolve via WIT registry.
("wasi:", "must not be empty"),
// Empty namespace half.
(":http/proxy", "must not be empty"),
// Underscore → DNS-1123 / WIT kebab-case footgun.
("wasi:http_proxy", "_"),
// Leading digit → WIT identifiers begin with a letter.
("wasi:1http/proxy", "digit"),
// Consecutive hyphens → invalid kebab-case.
("wasi:pub--sub", "consecutive `-`"),
// Trailing hyphen → invalid kebab-case.
("wasi:proxy-", "must not end with `-`"),
// Whitespace inside the token.
("wasi:http proxy", "whitespace"),
// Control characters.
("wasi:http\x01proxy", "control character"),
// Non-ASCII byte (café-style un-percent-encoded literal).
("wasi:caf\u{e9}/proxy", "non-ASCII"),
// Trailing `@` with no version body.
("wasi:http/proxy@", "trailing `@`"),
// Version body carrying `:` or `/`.
("wasi:http/proxy@0.2:rc1", "must not contain `:` or `/`"),
// Doubled `@`.
("wasi:http/proxy@0.2@beta", "at most one `@`"),
// Version body carrying a byte outside the SemVer 2.0.0
// accepted set `[0-9A-Za-z.\-+]` — the canonical
// author-side paste footguns (`?` from URL-query-separator
// paste, `#` from URL-fragment paste, `!` from
// history-expansion, `(` from parenthetical doc annotation,
// `~` from tilde-range npm/Cargo semver-req paste that
// strayed into the version body itself). Each surfaces the
// `invalid character` reason substring so the diagnostic
// wording is pinned alongside every peer per-byte rejection.
("wasi:http/proxy@0.2.0?rc1", "invalid character"),
("wasi:http/proxy@0.2.0#build", "invalid character"),
("wasi:http/proxy@0.2.0!alpha", "invalid character"),
("wasi:http/proxy@0.2.0(rc1)", "invalid character"),
("wasi:http/proxy@~0.2.0", "invalid character"),
// Version body byte-set-valid but *structurally* invalid
// SemVer 2.0.0 — the canonical author-side paste footguns
// the byte-set gate above cannot catch. Every entry passes
// the accepted-set arm `[0-9A-Za-z.\-+]` verbatim and
// fails only at [`semver::Version::parse`]: two-part
// numeric core (`@1.0` — Node.js `"engines"` field paste),
// one-part numeric core (`@1` — Docker `:v1` tag paste),
// four-part numeric core (`@1.0.0.0` — Microsoft / Java
// build-number convention), `v`-prefixed version body
// (`@v0.2.0` — git-tag-shape paste), leading-zero major
// (`@01.0.0` — mistaken zero-padded date-based version),
// trailing hyphen with empty pre-release (`@1.0.0-` —
// half-typed pre-release), trailing plus with empty
// build-metadata (`@1.0.0+` — peer for build-metadata),
// empty pre-release identifier between dots
// (`@1.0.0-.rc1` — accidental leading `.`), empty build-
// metadata identifier between dots (`@1.0.0+.abc` — peer
// for build-metadata), numeric pre-release identifier
// with leading zero (`@1.0.0-01` — SemVer 2.0.0 rule 9),
// consecutive dots inside pre-release (`@1.0.0-alpha..beta`).
// Each surfaces the `structurally valid SemVer 2.0.0`
// reason substring so the diagnostic wording is pinned
// alongside every peer structural rejection.
("wasi:http/proxy@1.0", "structurally valid SemVer 2.0.0"),
("wasi:http/proxy@1", "structurally valid SemVer 2.0.0"),
("wasi:http/proxy@1.0.0.0", "structurally valid SemVer 2.0.0"),
("wasi:http/proxy@v0.2.0", "structurally valid SemVer 2.0.0"),
("wasi:http/proxy@01.0.0", "structurally valid SemVer 2.0.0"),
("wasi:http/proxy@1.0.0-", "structurally valid SemVer 2.0.0"),
("wasi:http/proxy@1.0.0+", "structurally valid SemVer 2.0.0"),
(
"wasi:http/proxy@1.0.0-.rc1",
"structurally valid SemVer 2.0.0",
),
(
"wasi:http/proxy@1.0.0+.abc",
"structurally valid SemVer 2.0.0",
),
(
"wasi:http/proxy@1.0.0-01",
"structurally valid SemVer 2.0.0",
),
(
"wasi:http/proxy@1.0.0-alpha..beta",
"structurally valid SemVer 2.0.0",
),
// Digit-immediately-after-`-` word-start rule — the WIT IDL
// `word ::= [a-z][a-z0-9]*` per-word first-byte gate the
// predicate's doc-comment already documented, closed at the
// implementation layer. Each identifier passes the outer
// `[a-z0-9-]` byte set, the leading-`-` rejection, the
// consecutive-`-` rejection, and the trailing-`-` rejection,
// and was silently accepted before the arm landed — surfaces
// the `word after `-`` reason substring so a future
// diagnostic-wording rephrase surfaces here alongside every
// peer per-arm substring pin. Canonical author-side
// footguns: `"pub-1sub"` (version-shape digit paste),
// `"proxy-2beta"` (v2 tag paste), `"cap-9"` (numeric
// suffix). Namespace-side and interface-side variants pin
// the arm fires uniformly on every WIT segment (`ns:pkg`,
// `ns:pkg/iface`, not just the first).
("wasi:pub-1sub", "word after `-`"),
("wasi:proxy-2beta", "word after `-`"),
("wasi:cap-9", "word after `-`"),
("pleme-1cap:audit", "word after `-`"),
("wasi:http/proxy-3rc", "word after `-`"),
] {
let err = is_wit_world_ref(s)
.err()
.unwrap_or_else(|| panic!("WIT reference {s:?} must be rejected"));
assert!(
err.contains(needle),
"WIT reference {s:?} reason must contain {needle:?}; got {err:?}"
);
}
}
#[test]
fn wit_world_ref_word_after_hyphen_digit_arm_names_offending_byte_and_word_rule() {
// Pin the per-word first-byte arm's diagnostic quality: the
// offending byte appears verbatim in the reason, the WIT
// grammar production is named (`[a-z][a-z0-9]*`), and the
// remediation suggests a lowercase-letter prefix on the
// offending word. Mirrors the `wit_world_ref_leading_digit`
// sibling pin on the *first-word* first-byte arm — the two
// arms enforce the same rule at complementary positions
// (whole-id first byte vs. per-hyphen-word first byte), so
// their diagnostic shapes stay peer.
let err = is_wit_world_ref("wasi:pub-1sub").unwrap_err();
assert!(err.contains("'1'"), "must name offending byte: {err:?}");
assert!(
err.contains("[a-z][a-z0-9]*"),
"must name WIT word grammar: {err:?}"
);
assert!(
err.contains("pub-v1sub"),
"must suggest the letter-prefix remediation: {err:?}"
);
}
#[test]
fn wit_world_ref_word_after_hyphen_lowercase_letter_still_accepted() {
// Complement-side pin: the per-word first-byte arm strictly
// targets *digits* after `-`; every canonical multi-word
// lowercase identifier (`pub-sub`, `pub-sub-async`,
// `wasi:http/incoming-handler`, `wasi:keyvalue/atomic-batch`)
// remains in the accepted set with no new false-positive.
// Pinned here so a future tightening that spills the digit-
// rejection arm onto the letter-after-hyphen class surfaces
// as a test failure at this positive-set pin, not at the M4
// CR materializer's WIT-parse boundary. Mirrors the
// `wit_world_ref_accepts_canonical_forms` positive-set
// sweep, extended here to the multi-word-lowercase axis.
for s in [
"nats:pub-sub",
"wasi:http/incoming-handler",
"wasi:keyvalue/atomic-batch",
"pleme:cap/audit-log",
"http:server-side",
] {
is_wit_world_ref(s).unwrap_or_else(|e| {
panic!("canonical multi-word WIT identifier {s:?} must pass: {e:?}")
});
}
}
#[test]
fn wit_world_ref_word_after_hyphen_digit_arm_fires_before_byte_set_arm() {
// Diagnostic-precedence pin: an identifier that is *both*
// digit-after-`-` and byte-set-invalid (`"pub-1$"`) surfaces
// the more self-locating word-start diagnostic, not the
// generic invalid-character diagnostic. The arm order in the
// loop is deliberate — the per-word first-byte gate fires on
// the first offending byte (position 4 = the `1`) before the
// byte-set gate can reach the `$` at position 5. Pinned here
// so a future arm-reordering that moves the byte-set gate
// earlier surfaces the drift at this test rather than
// silently value-laundering the diagnostic.
let err = is_wit_world_ref("wasi:pub-1$").unwrap_err();
assert!(
err.contains("word after `-`"),
"must surface the per-word first-byte diagnostic, not the invalid-character one: {err:?}"
);
// And the `$` case *without* the digit-after-`-` still lands
// on the invalid-character arm — the two diagnostics don't
// collide when only one applies.
let err = is_wit_world_ref("wasi:pub-x$").unwrap_err();
assert!(
err.contains("invalid character"),
"byte-set-only rejection must still name invalid character: {err:?}"
);
}
#[test]
fn wit_world_ref_rejects_empty_defensively() {
// The predicate is called from `WitContract::target()` only
// after the per-axis `EmptyWit` arm has fired at validate
// time; re-checking here keeps the predicate usable from any
// future call site without an empty-precondition footgun.
// Same defensive empty-check `is_dns_1123_label` /
// `is_gateway_api_http_path` carry at their call sites.
let err = is_wit_world_ref("").unwrap_err();
assert!(err.contains("empty"), "got: {err:?}");
}
#[test]
fn wit_world_ref_rejects_at_129_byte_boundary() {
// The 128-byte cap pin — both the boundary-exceeding case and
// the boundary-accepting case in one place, so a future cap
// shift surfaces both arms simultaneously, mirroring
// `dns_1123_label_rejects_at_64_byte_boundary` and
// `gateway_api_http_path_rejects_at_1025_byte_boundary` on the
// peer predicates. Constructed as `wasi:<long-pkg>` so the
// kebab-shape arms don't fire first and obscure the cap arm.
let pad = "a".repeat(123); // 5 + 123 = 128 (`wasi:` + pad)
let max_ok = format!("wasi:{pad}");
assert_eq!(max_ok.len(), 128);
is_wit_world_ref(&max_ok).unwrap();
let pad_over = "a".repeat(124);
let too_long = format!("wasi:{pad_over}");
assert_eq!(too_long.len(), 129);
let err = is_wit_world_ref(&too_long).unwrap_err();
assert!(err.contains("128"), "got: {err:?}");
assert!(err.contains("129"), "got: {err:?}");
}
// ── is_nats_subject — shared NATS subject predicate ──────────────────
#[test]
fn nats_subject_accepts_canonical_forms() {
// Substrate-side pin: the predicate accepts every canonical
// NATS subject the `:contratos :subject` axis carries in the
// caixa-mesh test fixtures + the example checkout-aplicacao
// (each hand-curated to match real NATS server-side admission
// shapes). Drift between this list and the per-axis positive-
// set sweep surfaces here — one source of truth for the rule.
// Includes single-token subjects, multi-dot subjects, snake-
// case + kebab-case tokens (NATS accepts both), digit-bearing
// tokens, the `*` single-token wildcard at every segment
// position, and the `>` multi-token wildcard at the final
// position (the two NATS subscription patterns the protocol
// defines). Mirrors the canonical-forms sweeps on the peer
// value-shape predicates (`gateway_api_http_path_accepts_…`,
// `wit_world_ref_accepts_…`).
for s in [
"checkout.events.charge.failed",
"rio.events.order.charged",
"orders",
"orders.123",
"snake_case.token",
"kebab-case.token",
"MixedCase.Token",
"alpha.beta.gamma.delta.epsilon",
"orders.*.charged",
"*.events.*",
"orders.>",
"*",
">",
] {
is_nats_subject(s)
.unwrap_or_else(|e| panic!("canonical NATS subject {s:?} must pass: {e:?}"));
}
}
#[test]
fn nats_subject_rejects_each_arm_with_substring_pinned_reason() {
// Substrate-side diagnostic-shape pin: each grammar arm
// surfaces its own distinct reason substring. Pinned here so
// a future reason-wording rephrase that drops any of these
// substrings surfaces at this one place, not piecemeal across
// every per-axis test sweep. Mirrors
// `gateway_api_http_path_rejects_each_arm_with_substring_pinned_reason`
// and `wit_world_ref_rejects_each_arm_with_substring_pinned_reason`
// on the peer predicates.
for (s, needle) in [
// Whitespace inside the token.
("foo bar", "whitespace"),
("foo\tbar", "whitespace"),
// Control characters.
("foo\x01bar", "control character"),
// Non-ASCII byte (un-percent-encoded café-style literal).
("foo.caf\u{e9}", "non-ASCII"),
// Leading `.` — empty leading token.
(".foo", "must not start with `.`"),
// Trailing `.` — empty trailing token.
("foo.", "must not end with `.`"),
// Consecutive `.` — empty token between separators.
("foo..bar", "consecutive `.`"),
// Non-trailing `>` multi-token wildcard.
("foo.>.bar", "only allowed as the final segment"),
// Mid-segment `*` (not a standalone wildcard token).
("foo*.bar", "`*` mid-segment"),
// Mid-segment `>` (not a standalone wildcard token).
("foo>", "`>` mid-segment"),
// `.` is the separator, so `,` (or any other punctuation)
// surfaces as an invalid-character arm.
("foo,bar", "invalid character"),
// `:` reserved-looking — distinct invalid-character arm
// (pinned separately so a future relaxation that accepts
// `:` mid-segment surfaces here, not in some downstream
// renderer's "this passed validate but the NATS server
// rejected at publish" footgun).
("foo:bar", "invalid character"),
] {
let err = is_nats_subject(s)
.err()
.unwrap_or_else(|| panic!("NATS subject {s:?} must be rejected"));
assert!(
err.contains(needle),
"NATS subject {s:?} reason must contain {needle:?}; got {err:?}"
);
}
}
#[test]
fn nats_subject_rejects_empty_defensively() {
// The predicate is called from `WitContract::target()` only
// after the per-axis `ContratoSubjectEmpty` arm has fired at
// validate time; re-checking here keeps the predicate usable
// from any future call site without an empty-precondition
// footgun. Same defensive empty-check `is_dns_1123_label`,
// `is_gateway_api_http_path`, and `is_wit_world_ref` carry at
// their call sites.
let err = is_nats_subject("").unwrap_err();
assert!(err.contains("empty"), "got: {err:?}");
}
#[test]
fn nats_subject_rejects_at_257_byte_boundary() {
// The 256-byte cap pin — both the boundary-exceeding case and
// the boundary-accepting case in one place, so a future cap
// shift surfaces both arms simultaneously, mirroring
// `dns_1123_label_rejects_at_64_byte_boundary`,
// `gateway_api_http_path_rejects_at_1025_byte_boundary`, and
// `wit_world_ref_rejects_at_129_byte_boundary` on the peer
// predicates. Constructed as a single all-`a` token (no `.`)
// so the segment / wildcard arms don't fire first and obscure
// the cap arm.
let max_ok = "a".repeat(256);
assert_eq!(max_ok.len(), 256);
is_nats_subject(&max_ok).unwrap();
let too_long = "a".repeat(257);
assert_eq!(too_long.len(), 257);
let err = is_nats_subject(&too_long).unwrap_err();
assert!(err.contains("256"), "got: {err:?}");
assert!(err.contains("257"), "got: {err:?}");
}
#[test]
fn nats_subject_lone_wildcard_tokens_validate() {
// The two NATS wildcards stand alone as the entire subject —
// a `subscribe("*")` matches any single-token publish, a
// `subscribe(">")` matches every NATS message on the connection.
// Both are protocol-legal; the typed substrate accepts them
// structurally and leaves the "should the typed `:contratos`
// edge subscribe to literally everything?" question to a
// future semantic-level gate. Pinned alongside the canonical-
// forms sweep so a future tighten that disallows lone wildcards
// surfaces both arms simultaneously.
is_nats_subject("*").unwrap();
is_nats_subject(">").unwrap();
}
#[test]
fn nats_subject_trailing_multi_wildcard_validates() {
// `>` at the final segment is the canonical "match all trailing
// tokens" subscription pattern. Pinned alongside the non-
// trailing-`>` rejection arm so the boundary between the two
// is in one place — a future relaxation that allows `>` at
// non-trailing positions or a tighten that disallows trailing
// `>` surfaces both arms simultaneously.
is_nats_subject("orders.>").unwrap();
is_nats_subject("orders.events.>").unwrap();
// And the `*` single-token wildcard combines freely with the
// trailing `>` — the canonical "match one middle token, then
// anything trailing" subscription pattern.
is_nats_subject("orders.*.>").unwrap();
}
// ── is_wasi_keyvalue_slot — shared kv slot-template predicate ────────
#[test]
fn wasi_kv_slot_accepts_canonical_forms() {
// Substrate-side pin: the predicate accepts every canonical kv
// slot template the `:contratos :slot` axis carries in the
// caixa-mesh test fixtures + plausible authoring patterns
// (each maps to a realistic wasi:keyvalue/store key the runtime
// resolves on dispatch). Drift between this list and the
// per-axis positive-set sweep surfaces here — one source of
// truth for the rule. Includes:
// - single-token identifiers (`"checkout"`, `"events"`);
// - dot-namespaced templates (`"session.tokens.<sid>"`);
// - path-namespaced templates with `$`-prefixed variables
// (`"checkout/$orderId"`, the canonical Akka-cluster-
// sharding-style template);
// - colon-namespaced templates with brace placeholders
// (`"users:{tenant}/{id}"`, the canonical multi-tenant
// Redis-key shape);
// - angle-bracket placeholders (`"session.<sid>"`);
// - underscore identifiers (`"snake_case_key"`);
// - kebab identifiers (`"kebab-case-key"`);
// - mixed-case (`"MixedCase"` — kv slot templates are case-
// sensitive; the predicate doesn't lowercase-fold);
// - digit-bearing tokens (`"shard0"`, `"v2/key"`);
// - percent-encoded fragments (`"users/caf%C3%A9"`); the
// encoded form is the *valid* shape, the raw `café` is
// rejected on the non-ASCII arm.
// Mirrors the canonical-forms sweeps on the peer value-shape
// predicates (`gateway_api_http_path_accepts_…`,
// `nats_subject_accepts_canonical_forms`).
for s in [
"checkout",
"events",
"checkout/$orderId",
"users:{tenant}/{id}",
"session.<sid>",
"session.tokens.<sid>",
"snake_case_key",
"kebab-case-key",
"MixedCase",
"shard0",
"v2/key",
"users/caf%C3%A9",
] {
is_wasi_keyvalue_slot(s)
.unwrap_or_else(|e| panic!("canonical kv slot {s:?} must pass: {e:?}"));
}
}
#[test]
fn wasi_kv_slot_rejects_each_arm_with_substring_pinned_reason() {
// Substrate-side diagnostic-shape pin: each grammar arm
// surfaces its own distinct reason substring. Pinned here so
// a future reason-wording rephrase that drops any of these
// substrings surfaces at this one place, not piecemeal across
// every per-axis test sweep. Mirrors
// `nats_subject_rejects_each_arm_with_substring_pinned_reason`
// and `gateway_api_http_path_rejects_each_arm_with_substring_pinned_reason`
// on the peer predicates.
for (s, needle) in [
// Raw space inside the template — the canonical paste-from-
// doc footgun.
("check out/$order", "whitespace"),
// Tab byte — distinct arm-pinned reason from the space arm.
("check\tout", "whitespace"),
// Control character (SOH = 0x01) — pinned separately from
// the whitespace arm so a future relaxation that admits
// raw whitespace but still rejects controls surfaces here.
("checkout/\x01order", "control character"),
// Newline — the canonical "the paste-from-binary slug
// spans multiple lines" footgun. Distinct from the
// whitespace arm because `\n` is a control character.
("checkout\norder", "control character"),
// DEL byte (0x7F) — the upper boundary of the control-
// character range, pinned so a future relaxation that
// only checks `< 0x20` surfaces here.
("checkout\x7forder", "control character"),
// Un-percent-encoded non-ASCII byte — the canonical
// "I copied the key from a doc with smart quotes /
// accented characters" footgun. Author must percent-
// encode (the canonical-forms sweep covers
// `"users/caf%C3%A9"`).
("ch\u{e9}ckout/$order", "non-ASCII"),
] {
let err = is_wasi_keyvalue_slot(s)
.err()
.unwrap_or_else(|| panic!("kv slot {s:?} must be rejected"));
assert!(
err.contains(needle),
"kv slot {s:?} reason must contain {needle:?}; got {err:?}"
);
}
}
#[test]
fn wasi_kv_slot_rejects_empty_defensively() {
// The predicate is called from `WitContract::target()` only
// after the per-axis `ContratoSlotEmpty` arm has fired at
// validate time; re-checking here keeps the predicate usable
// from any future call site without an empty-precondition
// footgun. Same defensive empty-check `is_dns_1123_label`,
// `is_gateway_api_http_path`, `is_wit_world_ref`, and
// `is_nats_subject` carry at their call sites.
let err = is_wasi_keyvalue_slot("").unwrap_err();
assert!(err.contains("empty"), "got: {err:?}");
}
#[test]
fn wasi_kv_slot_rejects_at_513_byte_boundary() {
// The 512-byte cap pin — both the boundary-exceeding case and
// the boundary-accepting case in one place, so a future cap
// shift surfaces both arms simultaneously, mirroring
// `dns_1123_label_rejects_at_64_byte_boundary`,
// `gateway_api_http_path_rejects_at_1025_byte_boundary`,
// `wit_world_ref_rejects_at_129_byte_boundary`, and
// `nats_subject_rejects_at_257_byte_boundary` on the peer
// predicates. Constructed as a single all-`a` token (no
// separator / template syntax) so only the cap arm fires.
let max_ok = "a".repeat(512);
assert_eq!(max_ok.len(), 512);
is_wasi_keyvalue_slot(&max_ok).unwrap();
let too_long = "a".repeat(513);
assert_eq!(too_long.len(), 513);
let err = is_wasi_keyvalue_slot(&too_long).unwrap_err();
assert!(err.contains("512"), "got: {err:?}");
assert!(err.contains("513"), "got: {err:?}");
}
#[test]
fn wasi_kv_slot_admits_full_printable_ascii_range() {
// Structural pin: the predicate admits every printable ASCII
// byte from `0x21` (`!`) to `0x7E` (`~`) inclusive, including
// every template-variable bracket the documented authoring
// patterns use (`$`, `{`, `}`, `<`, `>`) and every namespace
// separator (`/`, `:`, `.`, `-`, `_`). Drift here = a future
// tighten that removes any byte from the admitted set surfaces
// a name-the-byte test failure, not piecemeal across per-axis
// sweeps. Constructed as a single all-bytes template (`b!`,
// `b"`, …, `b~`) — the predicate doesn't impose structure,
// only character-class.
for b in 0x21u8..=0x7E {
let s = std::str::from_utf8(&[b]).unwrap().to_string();
is_wasi_keyvalue_slot(&s)
.unwrap_or_else(|e| panic!("printable ASCII byte 0x{b:02x} must pass: {e:?}"));
}
}
#[test]
fn git_ref_name_accepts_canonical_forms() {
// Substrate-side pin: the predicate accepts every canonical
// refname the `:fonte :tag` / `:fonte :branch` axes carry in
// realistic authoring patterns (each maps to a refname `git
// fetch <remote> tag '<value>'` and `git checkout '<value>'`
// resolve cleanly at clone time). Drift between this list and
// any per-axis positive-set sweep surfaces here — one source
// of truth for the rule. Includes:
// - semver tag with `v` prefix (`"v0.1.0"`, the canonical
// pleme-io release shape);
// - bare semver tag (`"0.1.0"`, the npm / Cargo idiom);
// - pre-release tag (`"v0.1.0-alpha.1"`);
// - release-line tag with hyphens (`"release-1.0"`);
// - leaf branch (`"main"` / `"master"`);
// - hierarchical feature branch (`"feature/checkout"`);
// - multi-component branch with hyphens and digits
// (`"user-1/feat-x-v2"`);
// - dot-bearing tag (`"v0.1.0.rc1"`, mid-component dot
// allowed — only consecutive `..` and trailing `.` are
// rejected).
// Mirrors the canonical-forms sweeps on the peer value-shape
// predicates (`wasi_kv_slot_accepts_canonical_forms`,
// `nats_subject_accepts_canonical_forms`).
for s in [
"v0.1.0",
"0.1.0",
"v0.1.0-alpha.1",
"release-1.0",
"main",
"master",
"feature/checkout",
"user-1/feat-x-v2",
"v0.1.0.rc1",
"stable",
] {
is_git_ref_name(s)
.unwrap_or_else(|e| panic!("canonical git ref {s:?} must pass: {e:?}"));
}
}
#[test]
fn git_ref_name_rejects_each_arm_with_substring_pinned_reason() {
// Substrate-side diagnostic-shape pin: each grammar arm
// surfaces its own distinct reason substring. Pinned here so
// a future reason-wording rephrase that drops any of these
// substrings surfaces at this one place, not piecemeal across
// every per-axis test sweep. Mirrors
// `wasi_kv_slot_rejects_each_arm_with_substring_pinned_reason`
// and `nats_subject_rejects_each_arm_with_substring_pinned_reason`
// on the peer predicates.
for (s, needle) in [
// Trailing space — the canonical paste-from-doc footgun.
("v0.1.0 ", "whitespace"),
// Embedded space (branch with spaces).
("feature/foo bar", "whitespace"),
// Tab byte.
("v0.1.0\t", "whitespace"),
// Newline — the canonical "paste-from-multiline-doc"
// footgun. Distinct from the whitespace arm because `\n`
// is a control character.
("v0.1.0\n", "control character"),
// DEL byte (0x7F) — upper boundary of the control range.
("v0.1.0\x7f", "control character"),
// Non-ASCII byte (the canonical "I copied the tag from a
// doc with smart quotes" footgun).
("v0.1.0\u{e9}", "non-ASCII"),
// Tilde — git's revision grammar (`HEAD~3`).
("v0.1.0~1", "`~`"),
// Caret — git's revision grammar (`HEAD^`).
("v0.1.0^", "`^`"),
// Colon — git's refspec separator.
("v0.1.0:rebase", "`:`"),
// Question mark — git's refspec glob.
("v0.1.0?", "`?`"),
// Asterisk — git's refspec glob.
("v0.1.*", "`*`"),
// Open bracket — git's refspec glob.
("v0.1.0[1]", "`[`"),
// Backslash — the canonical Windows-path-leak footgun.
("feature\\foo", "`\\`"),
// Consecutive dots — git's `<rev1>..<rev2>` range grammar.
("v0.1..0", "`..`"),
// Reflog grammar.
("main@{upstream}", "`@{`"),
// The bare `@` — git aliases to `HEAD`.
("@", "bare `@`"),
// Leading slash.
("/main", "begin with `/`"),
// Trailing slash.
("feature/", "end with `/`"),
// Consecutive slashes.
("feature//foo", "consecutive `/`"),
// Trailing dot.
("v0.1.0.", "end with `.`"),
// Fully-qualified branch ref — the canonical
// `git show-ref`-output-leak footgun.
("refs/heads/main", "fully-qualified"),
// Fully-qualified tag ref.
("refs/tags/v0.1.0", "fully-qualified"),
// Component beginning with `.` (per-component rule).
("feature/.hidden", "begin with `.`"),
// Component ending with `.lock` (per-component rule).
("feature/main.lock", "`.lock`"),
// Leaf ref named `<x>.lock` — same per-component rule on
// the single-component refname.
("main.lock", "`.lock`"),
// Case-insensitive `.LOCK` — APFS / NTFS / HFS+ admit
// both spellings as the same on-disk file, so a
// `:tag "v1.LOCK"` collides with git's atomic-rename
// guard on case-insensitive filesystems. Pinned
// separately from the canonical lowercase arm so a
// future relaxation that only catches lowercase
// surfaces here.
("v1.LOCK", "`.lock`"),
("feature/Main.Lock", "`.lock`"),
] {
let err = is_git_ref_name(s)
.err()
.unwrap_or_else(|| panic!("git ref {s:?} must be rejected"));
assert!(
err.contains(needle),
"git ref {s:?} reason must contain {needle:?}; got {err:?}"
);
}
}
#[test]
fn git_ref_name_rejects_empty_defensively() {
// The predicate is called from `DepSource::validate` only
// after the per-axis `FontePinEmpty` arm has fired at
// validate time; re-checking here keeps the predicate usable
// from any future call site without an empty-precondition
// footgun. Same defensive empty-check `is_dns_1123_label`,
// `is_gateway_api_http_path`, `is_wit_world_ref`,
// `is_nats_subject`, and `is_wasi_keyvalue_slot` carry at
// their call sites.
let err = is_git_ref_name("").unwrap_err();
assert!(err.contains("empty"), "got: {err:?}");
}
#[test]
fn git_ref_name_rejects_at_256_byte_boundary() {
// The 255-byte cap pin — both the boundary-exceeding case and
// the boundary-accepting case in one place, so a future cap
// shift surfaces both arms simultaneously, mirroring
// `dns_1123_label_rejects_at_64_byte_boundary`,
// `gateway_api_http_path_rejects_at_1025_byte_boundary`,
// `wit_world_ref_rejects_at_129_byte_boundary`,
// `nats_subject_rejects_at_257_byte_boundary`, and
// `wasi_kv_slot_rejects_at_513_byte_boundary` on the peer
// predicates. Constructed as a single all-`a` leaf so only
// the cap arm fires.
let max_ok = "a".repeat(255);
assert_eq!(max_ok.len(), 255);
is_git_ref_name(&max_ok).unwrap();
let too_long = "a".repeat(256);
assert_eq!(too_long.len(), 256);
let err = is_git_ref_name(&too_long).unwrap_err();
assert!(err.contains("255"), "got: {err:?}");
assert!(err.contains("256"), "got: {err:?}");
}
#[test]
fn git_ref_name_qualified_prefix_diagnostic_quotes_leaf() {
// Diagnostic-shape pin: the `refs/heads/` / `refs/tags/`
// rejection arm enumerates the leaf the author probably
// meant, so the author's grep target is the *intended*
// refname literal rather than the (rejected) qualified form.
// Pinned across both prefixes so a future relaxation that
// drops the leaf-suggestion surfaces here.
for (qualified, leaf) in [
("refs/heads/main", "main"),
("refs/tags/v0.1.0", "v0.1.0"),
("refs/heads/feature/checkout", "feature/checkout"),
] {
let err = is_git_ref_name(qualified).unwrap_err();
assert!(
err.contains(&format!("{leaf:?}")),
"qualified ref {qualified:?} diagnostic must quote the leaf \
{leaf:?}; got {err:?}"
);
}
}
// ── is_git_ref_name canonical-OID-shape partition arm ────────────────
#[test]
fn git_ref_name_rejects_canonical_sha1_oid() {
// The fail-before-pass-after pin on the canonical SHA-1 OID
// partition arm: a 40-char lowercase-hex string is the shape
// `is_git_oid` accepts, so `is_git_ref_name` must reject it.
// Until this arm landed `is_git_ref_name` accepted every
// 40-char lowercase-hex string (pure hex carries none of the
// forbidden refname characters, no `..`/`@{`/`/`-prefix/
// `/`-suffix/`.lock`-suffix/`refs/heads/`-prefix), silently
// breaking the cross-axis partition the
// [`DepSource::validate`] gate routes the `:fonte` axes
// through and admitting `:tag "deadbeef…"` /
// `:branch "deadbeef…"` as legitimate refnames — the
// canonical paste-from-`git show --format=%H` mis-slot
// footgun. The diagnostic names the `:rev` axis so the author
// grep-fixes in one edit.
for oid in [
"0123456789abcdef0123456789abcdef01234567",
"deadbeefcafebabe0123456789abcdef01234567",
"ffffffffffffffffffffffffffffffffffffffff",
"0000000000000000000000000000000000000000",
] {
assert_eq!(oid.len(), GIT_OID_SHA1_LEN);
let err = is_git_ref_name(oid).unwrap_err();
assert!(
err.contains("OID") && err.contains(":rev"),
"canonical SHA-1 OID {oid:?} must surface a diagnostic \
naming OID + `:rev`; got {err:?}"
);
assert!(
err.contains("SHA-1"),
"canonical SHA-1 OID {oid:?} diagnostic must name the \
hash algorithm; got {err:?}"
);
}
}
#[test]
fn git_ref_name_rejects_canonical_sha256_oid() {
// The fail-before-pass-after pin on the canonical SHA-256 OID
// partition arm — Git 2.42+ `extensions.objectFormat = sha256`
// mode. 64-char lowercase-hex strings are equally OID-shaped
// and must surface the same `:rev`-axis diagnostic. Pinned
// separately from SHA-1 so a future relaxation that only
// catches one width surfaces here.
let sha256_zeros = "0".repeat(GIT_OID_SHA256_LEN);
let sha256_ones = "f".repeat(GIT_OID_SHA256_LEN);
let sha256_mixed = format!("deadbeefcafebabe{}", "0123456789abcdef".repeat(3));
for oid in [&sha256_zeros, &sha256_ones, &sha256_mixed] {
assert_eq!(oid.len(), GIT_OID_SHA256_LEN);
let err = is_git_ref_name(oid).unwrap_err();
assert!(
err.contains("OID") && err.contains(":rev"),
"canonical SHA-256 OID {oid:?} must surface a \
diagnostic naming OID + `:rev`; got {err:?}"
);
assert!(
err.contains("SHA-256"),
"canonical SHA-256 OID {oid:?} diagnostic must name \
the hash algorithm; got {err:?}"
);
}
}
#[test]
fn git_ref_name_partition_excludes_off_by_one_lengths() {
// Boundary pin: lengths that *aren't* exactly 40 or 64 hex
// characters are NOT canonical OIDs, so the partition arm
// must not fire — they remain accepted as refnames (consistent
// with `is_git_oid` rejecting them on its exact-width check).
// Abbreviated OIDs (`"c0ffee0"`, 7-char prefix) are ambiguous
// across repository history and `is_git_oid` rejects them
// separately, but they're legitimate refname shapes per `git
// check-ref-format`, so `is_git_ref_name` accepts them here.
// Pinned across the 39/41/63/65-char and abbreviated arms so
// a future widening of the partition arm to "any hex-shaped
// value" surfaces here as a regression rather than silently
// rejecting valid refnames.
for accept in [
// 39 hex chars — one short of SHA-1 width.
"0123456789abcdef0123456789abcdef0123456",
// 41 hex chars — one over SHA-1 width.
"0123456789abcdef0123456789abcdef012345670",
// 63 hex chars — one short of SHA-256 width.
&"a".repeat(63),
// 65 hex chars — one over SHA-256 width.
&"a".repeat(65),
// Abbreviated 7-char SHA — the `git log --short` width.
"c0ffee0",
// Pure-numeric 8-char (looks vaguely SHA-shaped but
// isn't canonical-width).
"00000000",
] {
is_git_ref_name(accept).unwrap_or_else(|e| {
panic!(
"off-canonical-width hex-shaped value {accept:?} \
(len {len}) must still pass is_git_ref_name — \
the partition arm is exact-width 40/64, not a \
prefix or pattern: {e:?}",
len = accept.len()
)
});
}
}
#[test]
fn git_ref_name_partition_excludes_uppercase_canonical_widths() {
// Boundary pin: the partition arm targets the canonical
// *lowercase-hex* OID shape `git rev-parse HEAD` /
// `git show --format=%H` emit. Uppercase or mixed-case
// 40/64-char hex strings are legitimate refnames per
// `git check-ref-format` (uppercase letters are admitted in
// refnames), so `is_git_ref_name` accepts them here; the
// `:rev` axis separately rejects uppercase OIDs via
// [`is_git_oid`]'s lowercase-only contract — so neither
// axis silently admits an uppercase-hex value cross-slot.
// Pinned across both widths + both uppercase variants so a
// future relaxation of either predicate surfaces here.
for accept in [
// Uppercase 40-char hex — passes is_git_ref_name (valid
// refname), rejected by is_git_oid on lowercase contract.
"DEADBEEFCAFEBABE0123456789ABCDEF01234567",
// Mixed case 40-char hex.
"DeadBeefCafeBabe0123456789abcdef01234567",
// Uppercase 64-char hex.
&"A".repeat(64),
] {
is_git_ref_name(accept).unwrap_or_else(|e| {
panic!(
"uppercase canonical-width hex value {accept:?} \
must still pass is_git_ref_name — the partition \
arm targets lowercase-canonical only (uppercase \
is a legitimate refname character per \
git-check-ref-format); the `:rev` axis catches \
uppercase via is_git_oid's lowercase contract: \
{e:?}"
)
});
// And confirm is_git_oid rejects it on the lowercase arm
// (so neither axis silently admits the value).
let oid_err = is_git_oid(accept).unwrap_err();
assert!(
oid_err.contains("lowercase") || oid_err.contains("uppercase"),
"uppercase hex value {accept:?} must be rejected by \
is_git_oid on its lowercase contract; got {oid_err:?}"
);
}
}
#[test]
fn git_ref_name_partition_arm_fires_before_per_byte_scan() {
// Order pin: the partition arm runs after the length check
// but before the per-byte refname-character scan, so a
// canonical-OID-shaped value surfaces the `:rev`-axis
// diagnostic rather than (e.g.) falling through to a generic
// per-component arm. Pinned via a canonical OID — pure hex
// can't violate any of the per-byte / `..` / `@{` / `/` /
// `.lock` / `refs/heads/` arms (which is precisely why the
// partition arm is needed), so position-wise this pin
// forecloses a future refactor that splits the partition arm
// across the scan (where uppercase / mixed-case canonical-
// width values would silently route through one branch).
let oid = "0123456789abcdef0123456789abcdef01234567";
let err = is_git_ref_name(oid).unwrap_err();
// The diagnostic mentions OID + `:rev`; it does NOT contain
// any of the per-byte-arm needle substrings the
// `git_ref_name_rejects_each_arm_with_substring_pinned_reason`
// sweep pins, structurally — canonical OIDs can't violate
// those arms.
assert!(err.contains("OID"), "got: {err:?}");
assert!(err.contains(":rev"), "got: {err:?}");
}
#[test]
fn git_ref_name_rejects_leading_hyphen_cli_arg_injection() {
// The CLI-arg-injection arm pin on the `:tag` / `:branch` axis.
// Git's `check-ref-format` grammar admits a leading `-` (the
// byte is a legitimate kebab continuation), so every prior
// shape arm passes the value through; the diagnostic moves
// the gate to the subprocess-argument boundary the resolver
// consumes. Pinned across the canonical CLI-arg-injection
// shapes — short-flag-shaped `"-X"`, long-option-shaped
// `"-stable"`, git-config-injection-shaped
// `"-c=core.merge=ours"`, the canonical
// `"--upload-pack=…"` long-flag form, and the
// `"--config"`-shape repeat-arg form — every shape would
// silently escape `git checkout --quiet --detach <ref>` (the
// resolver's invocation in `caixa-resolver/src/git.rs:41`,
// no `--` argument-list terminator) and get reinterpreted by
// `git checkout`'s argument parser. Peer with the
// `is_git_repo_url` leading-`-` arm (same vector on the
// sibling `:repo` axis), `is_cargo_feature_name` leading-`-`
// arm, and `is_dns_1123_label` leading-`-` arm — the
// substrate-wide "no leading `-` anywhere in a typed
// single-token string slot routed through a subprocess
// argument" invariant is now structurally consistent across
// every value-shape-gated typed surface.
for s in [
"-X", // short-flag-shape
"-stable", // long-option-shape
"-c=core.merge=ours", // git-config-injection-shape
"--upload-pack=cat /etc", // long-flag with-value
"--config", // repeat-arg shape
"-", // degenerate single-byte
] {
let err = is_git_ref_name(s)
.err()
.unwrap_or_else(|| panic!("git ref {s:?} must be rejected"));
assert!(
err.contains("`-`"),
"git ref {s:?} reason must surface the leading-`-` arm: {err:?}"
);
assert!(
err.contains("CLI-argument-injection"),
"git ref {s:?} reason must name the CLI-argument-injection \
vector: {err:?}"
);
}
// Positive control: a mid-name `-` (the canonical kebab
// separator) passes — `"v0-1-0"`, `"feature-x"`, `"main-2"`
// — pinning that the arm only fires at the leading position,
// not anywhere else.
for s in ["v0-1-0", "feature-x", "main-2"] {
is_git_ref_name(s).unwrap_or_else(|e| {
panic!("mid-name `-` ref {s:?} must pass the leading-`-` arm: {e:?}")
});
}
}
#[test]
fn git_ref_name_leading_hyphen_fires_before_per_byte_scan() {
// Cascade-precedence pin: a `"-flag\n"` value carries both a
// leading `-` and an embedded `\n` control byte; the leading-`-`
// arm fires first (the byte sits at the leading position the
// arm probes, before the per-byte cascade loop's control-byte
// arm). Mirrors the order pin
// `git_ref_name_partition_arm_fires_before_per_byte_scan`
// establishes on the canonical-OID partition arm — both
// pre-loop arms structurally precede the per-byte scan.
let err = is_git_ref_name("-flag\n").unwrap_err();
assert!(err.contains("`-`"), "got: {err:?}");
assert!(
!err.contains("control character"),
"leading-`-` arm must fire before the control-byte per-byte arm: {err:?}"
);
}
#[test]
fn git_ref_name_leading_hyphen_fires_after_canonical_oid_partition() {
// Cascade-precedence pin: the partition arm structurally
// precedes the leading-`-` arm because a canonical OID shape
// (40 / 64 lowercase hex bytes) cannot start with `-` — the
// byte sets are disjoint, so the precedence pin is a no-op at
// value level. The pin matters only at the diagnostic-shape
// level — it ensures a future codec round-trip that
// synthesizes a probe-as-both value (impossible today;
// possible if the OID partition arm ever relaxes its byte
// set) surfaces the more self-locating `:rev`-mis-slot
// diagnostic rather than the broader CLI-arg-injection one.
let oid = "0123456789abcdef0123456789abcdef01234567";
let err = is_git_ref_name(oid).unwrap_err();
assert!(err.contains("OID"), "got: {err:?}");
assert!(
!err.contains("CLI-argument-injection"),
"OID partition arm must precede leading-`-` arm: {err:?}"
);
}
// ── is_git_oid — `:fonte :rev` value-shape predicate ────────────────
#[test]
fn git_oid_canonical_widths_match_sha1_and_sha256() {
// The single-source-of-truth pin on the two canonical widths.
// Drift between the predicate's accepted widths and the const
// values would surface here as a build error, not as a silent
// round-trip break at the renderer layer. Mirrors
// `wasm32_memory_cap_matches_parsed_4_gib` (9d49a3a) — the
// constant equality pin keeps the contract one place.
assert_eq!(GIT_OID_SHA1_LEN, 40);
assert_eq!(GIT_OID_SHA256_LEN, 64);
// Doubled width: SHA-256 is exactly twice SHA-1 in hex char
// count (256 / 4 = 64; 160 / 4 = 40). Pinned so a future
// hash-algorithm widening reads the relationship here.
assert_eq!(GIT_OID_SHA256_LEN, GIT_OID_SHA1_LEN * 2 - 16);
}
#[test]
fn git_oid_accepts_canonical_sha1() {
// Positive control on the SHA-1 OID width: 40 lowercase hex
// characters — the canonical `git rev-parse HEAD` emission
// shape every realistic pleme-io upstream uses today. The all-
// `f` boundary is the lexicographically-largest OID (a real
// commit's hash could land here, and the predicate accepts it
// because it's structurally a valid OID — the null-OID
// sentinel arm partitions the all-`0` boundary only, not the
// all-`f` one).
is_git_oid("0123456789abcdef0123456789abcdef01234567").unwrap();
is_git_oid("deadbeefcafebabe0123456789abcdef01234567").unwrap();
is_git_oid("ffffffffffffffffffffffffffffffffffffffff").unwrap();
}
#[test]
fn git_oid_accepts_canonical_sha256() {
// Positive control on the SHA-256 OID width: 64 lowercase hex
// characters — `git`'s `extensions.objectFormat = sha256`
// emission (GA since Git 2.42 / Oct 2023). Doubled SHA-1 width.
let sha256_one = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
assert_eq!(sha256_one.len(), 64);
is_git_oid(sha256_one).unwrap();
let sha256_fs = "f".repeat(64);
is_git_oid(&sha256_fs).unwrap();
}
#[test]
fn git_oid_rejects_null_oid_sentinel_sha1() {
// Canonical "I copy-pasted the no-such-commit sentinel out of
// `git update-ref --stdin` docs / pre-receive hook example"
// footgun on the SHA-1 width — the all-zero 40-char hex
// string is git's `null OID` sentinel (used to indicate ref
// create / delete in update-ref flows) and never names a real
// commit in any repo's object database. Until the null-OID
// arm landed it passed every other shape arm (canonical
// length, lowercase hex) and surfaced at `git fetch <remote>
// 0000…0000` time with a quoting-confused "couldn't find
// remote ref" error far from the source caixa.lisp, with the
// lacre's content-address locked to a `git:0000…0000` closure
// that never equals any upstream's actual `HEAD`. The
// diagnostic carries the `40` width verbatim so a future
// SHA-256 fixture surfaces the same arm at the doubled width
// boundary.
let null_sha1 = "0".repeat(40);
let err = is_git_oid(&null_sha1).unwrap_err();
assert!(
err.contains("null-OID sentinel"),
"reason must name the sentinel: {err}",
);
assert!(err.contains("40"), "reason must name the width: {err}",);
assert!(
err.contains("no-such-commit") || err.contains("update-ref"),
"reason must reference git's null-OID semantics: {err}",
);
}
#[test]
fn git_oid_rejects_null_oid_sentinel_sha256() {
// Same sentinel on the SHA-256 width — `git`'s
// `extensions.objectFormat = sha256` mode (GA Git 2.42 / Oct
// 2023) carries the same null-OID semantics on the doubled
// 64-char width. Pinned separately so a future relaxation that
// only catches the SHA-1 width surfaces here, peer with the
// SHA-1 / SHA-256 pair-pinning posture
// `git_oid_accepts_canonical_sha1` /
// `git_oid_accepts_canonical_sha256` already establishes for
// the positive controls.
let null_sha256 = "0".repeat(64);
let err = is_git_oid(&null_sha256).unwrap_err();
assert!(
err.contains("null-OID sentinel"),
"reason must name the sentinel: {err}",
);
assert!(err.contains("64"), "reason must name the width: {err}",);
}
#[test]
fn git_oid_null_oid_fires_after_length_and_hex_arms() {
// Cascade-precedence pin: the null-OID arm runs *after* the
// length + character-class arms, so an off-by-one-length all-
// zeros value surfaces the narrower `abbreviated` diagnostic
// (the length arm's own reason wording) before the structural
// null-OID diagnostic, and an uppercase all-zeros value (which
// can't actually exist — `0` has no case — but pinned via the
// mixed-case-but-non-null fixture) routes the same way. The
// null-OID arm is the *fourth* arm, structurally the
// lexicographic-content-arm after length and per-byte
// character-class.
let off_by_one_zeros = "0".repeat(41);
let err = is_git_oid(&off_by_one_zeros).unwrap_err();
assert!(
err.contains("abbreviated"),
"off-by-one-length all-zeros surfaces length arm first: {err}",
);
// The all-`f` 40-char value — same boundary class as null-OID
// but at the opposite hex extreme — passes the predicate,
// confirming the null-OID arm doesn't over-fire on lexicographic
// boundaries.
is_git_oid("ffffffffffffffffffffffffffffffffffffffff").unwrap();
}
#[test]
fn git_oid_rejects_empty_defensively() {
// The predicate is called from `crate::dep::DepSource::validate`
// only after the per-axis `FontePinEmpty` arm has fired at
// validate time; re-checking here keeps the predicate usable
// from any future call site without an empty-precondition
// footgun. Same defensive empty-check `is_dns_1123_label`,
// `is_gateway_api_http_path`, `is_wit_world_ref`,
// `is_nats_subject`, `is_wasi_keyvalue_slot`, and
// `is_git_ref_name` carry at their call sites.
let err = is_git_oid("").unwrap_err();
assert!(err.contains("empty"), "got: {err:?}");
}
#[test]
fn git_oid_rejects_each_arm_with_substring_pinned_reason() {
// Substrate-side diagnostic-shape pin: each grammar arm
// surfaces its own distinct reason substring. Pinned here so a
// future reason-wording rephrase that drops any of these
// substrings surfaces at this one place, not piecemeal across
// every per-axis test sweep. Mirrors
// `git_ref_name_rejects_each_arm_with_substring_pinned_reason`,
// `wasi_kv_slot_rejects_each_arm_with_substring_pinned_reason`,
// and `nats_subject_rejects_each_arm_with_substring_pinned_reason`
// on the peer predicates.
for (s, needle) in [
// Abbreviated 7-char prefix — the canonical `git log
// --short` paste-from-release-notes footgun.
("c0ffee0", "abbreviated"),
// Abbreviated 12-char prefix — `git log --short=12`.
("c0ffee001234", "abbreviated"),
// Off-by-one above SHA-1 width.
("0123456789abcdef0123456789abcdef012345670", "abbreviated"),
// Off-by-one below SHA-256 width.
(
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde",
"abbreviated",
),
// Off-by-one above SHA-256 width.
(
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0",
"abbreviated",
),
// Uppercase SHA-1 — `git porcelain` lowercases on output.
("DEADBEEFCAFEBABE0123456789ABCDEF01234567", "uppercase"),
// Mixed-case SHA-1 — same path as pure-uppercase; the first
// uppercase byte fires the arm.
("deadbeefCAFEbabe0123456789abcdef01234567", "uppercase"),
// Non-hex character at exact SHA-1 length — the cross-axis
// mis-slot footgun (a refname-style char landing in `:rev`).
// `g` is the first non-hex byte; the non-hex arm fires
// ahead of any other rule. The hyphen / colon / slash arms
// are the same path on the same predicate.
("g123456789abcdef0123456789abcdef01234567", "non-hex"),
("0123456789abcdef-123456789abcdef01234567", "non-hex"),
("0123456789abcdef/123456789abcdef01234567", "non-hex"),
("0123456789abcdef:123456789abcdef01234567", "non-hex"),
// Whitespace inside an otherwise-SHA-shaped value (length
// 41 — fails the length arm first; pinned to ensure the
// diagnostic surfaces *some* parser wording).
("0123456789abcdef0123456789abcdef01234567 ", "abbreviated"),
] {
let err = is_git_oid(s)
.err()
.unwrap_or_else(|| panic!("git OID {s:?} must be rejected"));
assert!(
err.contains(needle),
"git OID {s:?} reason must contain {needle:?}; got {err:?}"
);
}
}
#[test]
fn git_oid_rejects_at_canonical_width_boundaries() {
// Boundary pin on the two canonical widths simultaneously: 39
// (below SHA-1), 40 (SHA-1 exactly), 41 (just above), 63 (just
// below SHA-256), 64 (SHA-256 exactly), 65 (just above). Pinned
// so a future relaxation that admits "close enough" widths
// surfaces here. The failing-length fixtures use all-zero hex
// so only the length arm fires (the null-OID sentinel arm is
// structurally downstream of the length arm — a non-canonical
// length fires the abbreviated diagnostic before the null
// diagnostic). The passing-length fixtures use a non-null hex
// value so the null-OID arm doesn't fire (the all-zero
// canonical-width value is the sentinel and is rejected by its
// own arm, pinned in `git_oid_rejects_null_oid_sentinel_*`).
let nonzero_sha1 = "0123456789abcdef0123456789abcdef01234567";
assert_eq!(nonzero_sha1.len(), 40);
let nonzero_sha256 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
assert_eq!(nonzero_sha256.len(), 64);
for (len, ok) in [
(1usize, false),
(7, false),
(39, false),
(40, true),
(41, false),
(63, false),
(64, true),
(65, false),
(128, false),
] {
let s = if ok && len == 40 {
nonzero_sha1.to_string()
} else if ok && len == 64 {
nonzero_sha256.to_string()
} else {
"0".repeat(len)
};
let result = is_git_oid(&s);
if ok {
result.unwrap_or_else(|e| panic!("len {len} must pass: {e:?}"));
} else {
let err = result.expect_err(&format!("len {len} must fail"));
assert!(
err.contains("abbreviated") || err.contains(&len.to_string()),
"len {len} reason must name the offending length or surface \
the abbreviation arm, got {err:?}"
);
}
}
}
#[test]
fn git_oid_rejection_is_disjoint_from_ref_name_acceptance() {
// Structural pin: the two predicates partition the `:fonte`
// pin axes — every canonical refname is rejected by
// `is_git_oid`, and every canonical OID is rejected by
// `is_git_ref_name`. The intersection of the two valid sets
// is exactly the empty set. Drift here = a value that passes
// both predicates would land at *both* axes silently, defeating
// the structural "cross-axis mis-slot is a build error"
// contract. Pinned with a representative cross-set so a future
// predicate weakening surfaces here.
let canonical_refnames = [
"v0.1.0",
"main",
"feature/checkout",
"release-1.0",
"user-1/feat-x-v2",
];
for refname in canonical_refnames {
is_git_ref_name(refname).unwrap_or_else(|e| {
panic!("setup: canonical refname {refname:?} must pass is_git_ref_name: {e:?}")
});
assert!(
is_git_oid(refname).is_err(),
"canonical refname {refname:?} must NOT pass is_git_oid \
(predicate-partition pin)"
);
}
let canonical_oids = [
"0123456789abcdef0123456789abcdef01234567",
"deadbeefcafebabe0123456789abcdef01234567",
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
];
for oid in canonical_oids {
is_git_oid(oid).unwrap_or_else(|e| {
panic!("setup: canonical OID {oid:?} must pass is_git_oid: {e:?}")
});
assert!(
is_git_ref_name(oid).is_err(),
"canonical OID {oid:?} must NOT pass is_git_ref_name \
(predicate-partition pin)"
);
}
}
// ── is_sandboxed_relative_path — `:behavior :on-*` + `:upgrade-from ─
// ── :state-change :script` value-shape predicate ────────────────────
#[test]
fn sandboxed_relative_path_accepts_canonical_relative_paths() {
// Positive controls: every documented authoring shape across
// the two existing call sites (`:behavior :on-init` / `:on-call`
// / `:on-cast` / `:on-info` / `:on-state-change` / `:on-terminate`
// and `:upgrade-from :state-change :script`) — bare filename,
// standard `lib/` subdirectory, deeply-nested migrations
// subdirectory, sibling-folder-shaped path, and explicit
// current-dir-relative-prefixed path. Pin every leg so a
// future tightening that rejects any of these (e.g. demanding
// a `lib/` prefix specifically, or forbidding the explicit
// `./` segment) surfaces here as a test-failure at the predicate
// boundary, not piecemeal across per-axis call sites.
for relpath in [
"init.lisp",
"lib/init.lisp",
"lib/handlers.lisp",
"lib/migrations/v01-to-v02.lisp",
"callbacks/on_call.lisp",
"./lib/init.lisp",
"a",
] {
is_sandboxed_relative_path(Path::new(relpath)).unwrap_or_else(|v| {
panic!("canonical relative path {relpath:?} must pass, got {v:?}")
});
}
}
#[test]
fn sandboxed_relative_path_rejects_empty() {
// The fail-before-pass-after pin on the empty arm. Both
// `PathBuf::new()` (no bytes) and `PathBuf::from("")` (empty
// string) hit the `as_os_str().is_empty()` precondition; both
// resolve to `root` under `root.join(p)` and silently point the
// `LisleLoader` at the project directory rather than a file.
assert_eq!(
is_sandboxed_relative_path(Path::new("")),
Err(PathShapeViolation::Empty)
);
let blank = PathBuf::new();
assert_eq!(
is_sandboxed_relative_path(&blank),
Err(PathShapeViolation::Empty)
);
}
#[test]
fn sandboxed_relative_path_rejects_absolute() {
// The fail-before-pass-after pin on the absolute arm. Sweep
// the canonical sandbox-escape paste-from-shell-prompt
// footguns: an `/etc/...` Lunatic-style sandbox bypass, a
// user-home leak that the renderer's `root.join(p)` would
// silently replace, the project-relative-shaped `/lib/...`
// typo where the author meant `lib/...` without a leading
// slash, and the bare root `/`. `Path::join` replaces the
// base with an absolute right-hand side, so every one of
// these resolves verbatim to outside the caixa root regardless
// of where the layout checker rooted itself.
for abs in [
"/etc/passwd",
"/home/user/escape.lisp",
"/lib/init.lisp",
"/",
] {
assert_eq!(
is_sandboxed_relative_path(Path::new(abs)),
Err(PathShapeViolation::Absolute),
"absolute path {abs:?} must surface as PathShapeViolation::Absolute"
);
}
}
#[test]
fn sandboxed_relative_path_rejects_parent_escape_at_every_position() {
// The fail-before-pass-after pin on the parent-escape arm.
// Position sweep — `..` as a leading component (the canonical
// "I meant the sibling caixa" mis-author), as a mid-path
// component (the canonical "lib/../../escape" path-traversal
// that's structurally identical regardless of how many `..`
// segments stack), as a trailing component (lib/.., resolving
// to the project root via a delayed escape), and the bare `..`
// (project parent directory). Each must surface as
// `PathShapeViolation::ParentEscape` regardless of position —
// pinned per-position so a future relaxation that only
// checks one position surfaces at this one place, not
// piecemeal across per-axis call sites.
for escape in [
"../sibling/init.lisp",
"lib/../../escaped.lisp",
"lib/..",
"..",
"lib/handlers/../../escape.lisp",
] {
assert_eq!(
is_sandboxed_relative_path(Path::new(escape)),
Err(PathShapeViolation::ParentEscape),
"parent-escape path {escape:?} must surface as \
PathShapeViolation::ParentEscape"
);
}
}
#[test]
fn sandboxed_relative_path_arm_ordering_is_empty_absolute_parent_escape() {
// Order pin: the predicate evaluates Empty → Absolute →
// ParentEscape — the same arm-ordering both inlined call sites
// followed verbatim (b0c8389 `BehaviorSpec::validate`'s
// `validate_callback_path`, 26da2c7
// `UpgradeInstruction::StateChange::validate`). A future
// reordering would silently flip which diagnostic the per-axis
// wrapper surfaces (e.g. an absolute-and-empty hybrid value
// would suddenly raise `Absolute` instead of `Empty`). Pinned
// here so a future reorder surfaces at the predicate boundary.
//
// The empty case can't *also* be absolute (empty paths are
// relative-by-construction) or parent-escaping, so the
// empty-first ordering only matters relative to the OS-string
// emptiness check vs. the absolute-prefix check. Pin the two
// legs that *can* compose: an absolute path with `..` segments
// must raise `Absolute` (not `ParentEscape`); an absolute-but-
// not-parent-escaping path must also raise `Absolute`. The
// arm-ordering pin is structural — every parent-escape case
// tested above is relative, so the ParentEscape arm is reached
// only when both Empty and Absolute arms have been cleared.
assert_eq!(
is_sandboxed_relative_path(Path::new("/etc/../passwd")),
Err(PathShapeViolation::Absolute),
"absolute path with `..` segments must surface as Absolute (not \
ParentEscape) — Empty → Absolute → ParentEscape arm-ordering pin"
);
}
#[test]
fn sandboxed_relative_path_distinguishes_curdir_from_parent_escape() {
// Boundary pin: `Component::CurDir` (`.`) is NOT a sandbox
// escape — `root.join("./lib/x.lisp")` resolves to
// `root/lib/x.lisp`, identical to `root.join("lib/x.lisp")`,
// so `./` segments must pass the predicate. The arm-ordering
// check above pins that `Component::ParentDir` is the only
// escape vector caught here. Pinned separately so a future
// tightening that *does* reject `.` segments (e.g. requiring
// canonical normalized form) lands at this one predicate.
is_sandboxed_relative_path(Path::new("./lib/init.lisp")).unwrap();
is_sandboxed_relative_path(Path::new("lib/./handlers.lisp")).unwrap();
}
#[test]
fn sandboxed_relative_path_violations_are_distinct_variants() {
// Diagnostic-shape pin: the three `PathShapeViolation` variants
// are distinct enum tags so each per-axis caller can match-and-
// wrap into its own typed `*Path` / `*Script` variant without
// a string-parse step (the trap [`is_dns_1123_label`] etc.
// avoid by returning `Result<(), String>` — but the path-shape
// callers were already split three ways across `BehaviorError`
// / `UpgradeError`, so a `String` return would *regress* the
// diagnostic shape rather than preserve it). The PartialEq /
// Copy / Hash derives on `PathShapeViolation` are pinned here
// so a future API rework reads the requirement off this test.
let v1 = PathShapeViolation::Empty;
let v2 = PathShapeViolation::Absolute;
let v3 = PathShapeViolation::ParentEscape;
assert_ne!(v1, v2);
assert_ne!(v2, v3);
assert_ne!(v1, v3);
// Copy + Eq round-trip: predicate consumers like
// `BehaviorSpec::validate` and `UpgradeInstruction::validate`
// pattern-match on the variant without consuming it.
let v_copy = v1;
assert_eq!(v1, v_copy);
}
#[test]
fn sandboxed_relative_path_matches_inlined_call_site_semantics() {
// End-to-end pin: every value the two pre-lift inline gates
// (`BehaviorSpec::validate_callback_path` and
// `UpgradeInstruction::StateChange::validate`'s inline arms)
// accepted-or-rejected must surface from the lifted predicate
// with identically-classified violation tags. Drift here would
// mean a previously-accepted authoring shape would suddenly
// fail (or vice versa) silently across the lift commit. Pinned
// by sweeping the canonical authoring shapes both pre-lift call
// sites' tests cover.
// Pre-lift accepts (must still pass):
for accept in [
"lib/init.lisp",
"lib/handlers.lisp",
"lib/migrations.lisp",
"lib/cleanup.lisp",
"lib/migrations/v01-to-v02.lisp",
"callbacks/handle_call.lisp",
] {
is_sandboxed_relative_path(Path::new(accept))
.unwrap_or_else(|v| panic!("pre-lift accept {accept:?} regressed, got {v:?}"));
}
// Pre-lift rejects (must still reject, with the same tag):
let cases: &[(&str, PathShapeViolation)] = &[
("", PathShapeViolation::Empty),
("/etc/passwd", PathShapeViolation::Absolute),
("/etc/migrations.lisp", PathShapeViolation::Absolute),
(
"../sibling/migrations.lisp",
PathShapeViolation::ParentEscape,
),
("lib/../../escaped.lisp", PathShapeViolation::ParentEscape),
];
for (reject, expected) in cases {
assert_eq!(
is_sandboxed_relative_path(Path::new(reject)).unwrap_err(),
*expected,
"pre-lift reject {reject:?} must classify as {expected:?}"
);
}
}
#[test]
fn path_shape_violation_all_lists_every_variant_in_declaration_order() {
// Fail-before-pass-after pin on the paired
// [`PathShapeViolation::ALL`] exhaustive-iteration surface.
// Two axes in one assertion, both must hold:
//
// (1) The slice enumerates every arm in the closed
// three-arm discriminator set exactly once, in
// declaration order (`Empty` → `Absolute` →
// `ParentEscape`) — the arm-ordering the
// [`is_sandboxed_relative_path`] gate + every per-axis
// caller in [`crate::manifest::ManifestError`] preserve
// for diagnostic-precedence continuity. A future variant
// addition (a `Symlink` arm the future symlink-escape
// gate would raise, a `TrailingSpace` arm a future
// whitespace-hygiene gate would surface) that lands on
// the enum without extending `ALL` trips this test at
// build time rather than surfacing as a silent
// under-coverage across every downstream sweep.
//
// (2) For every arm in the slice, exactly one of the
// [`gen_platform::IsVariant`]-derive-generated `is_*`
// predicates returns `true` and the other two return
// `false` — the partition property every peer closed-set
// enum's `IsVariant` derive carries
// ([`crate::CaixaKind`] at kind.rs,
// [`crate::supervisor::RestartStrategy`] +
// [`crate::supervisor::RestartPolicy`] at supervisor.rs,
// [`crate::upgrade::UpgradeInstruction`] at upgrade.rs,
// [`crate::aplicacao::PlacementStrategy`] +
// [`crate::aplicacao::RateLimitUnit`] at aplicacao.rs,
// [`crate::dep::DepList`] at dep.rs). A future variant
// addition that lands on the enum without threading a
// new column into the per-arm-partition assertion table
// trips here at build time.
assert_eq!(
PathShapeViolation::ALL,
&[
PathShapeViolation::Empty,
PathShapeViolation::Absolute,
PathShapeViolation::ParentEscape,
],
"PathShapeViolation::ALL must list every arm in \
declaration order (Empty → Absolute → ParentEscape) — \
the arm-ordering is_sandboxed_relative_path and every \
per-axis ManifestError caller preserve for \
diagnostic-precedence continuity"
);
let rows: [(PathShapeViolation, [bool; 3]); 3] = [
(PathShapeViolation::Empty, [true, false, false]),
(PathShapeViolation::Absolute, [false, true, false]),
(PathShapeViolation::ParentEscape, [false, false, true]),
];
for (variant, expected) in rows {
let observed = [
variant.is_empty(),
variant.is_absolute(),
variant.is_parent_escape(),
];
assert_eq!(
observed, expected,
"PathShapeViolation::{variant:?} is_* predicates must \
partition the arm set (empty, absolute, parent_escape); \
got {observed:?}"
);
}
}
#[test]
fn path_shape_violation_predicates_are_byte_equal_to_matches_family() {
// Byte-equal pin on the [`gen_platform::IsVariant`]-derive-
// generated per-arm predicate family. For every arm on the
// closed three-arm [`PathShapeViolation`] discriminator, each
// per-arm `is_*` predicate must agree byte-for-byte with the
// hand-rolled `matches!(_, PathShapeViolation::…)` shape a
// future consumer (a `feira lint --explain-path-shape=<axis>`
// per-arm listing, a future symlink-escape / whitespace-hygiene
// gate that keys off "is this a sandbox-escape arm" boolean, a
// future single-arm `matches!` in a downstream renderer that
// treats `Empty` distinctly from the other two) would
// otherwise open-code at each caller. A future rebrand (a
// `#[is_variant(name = "…")]` attribute drift on the derive,
// an accidental peer predicate that shadows the derive-generated
// one, a hand-rolled `impl PathShapeViolation` block that
// shadows one of the derive-generated methods) trips this test
// the moment the two paths' bytes diverge. Peer of the sibling
// `caixa_kind_is_variant_predicates_partition_the_arm_set`
// (kind.rs) and every peer closed-set-enum byte-equal pin.
for &variant in PathShapeViolation::ALL {
assert_eq!(
variant.is_empty(),
matches!(variant, PathShapeViolation::Empty),
"PathShapeViolation::{variant:?}.is_empty() must agree \
with matches!(_, PathShapeViolation::Empty)"
);
assert_eq!(
variant.is_absolute(),
matches!(variant, PathShapeViolation::Absolute),
"PathShapeViolation::{variant:?}.is_absolute() must agree \
with matches!(_, PathShapeViolation::Absolute)"
);
assert_eq!(
variant.is_parent_escape(),
matches!(variant, PathShapeViolation::ParentEscape),
"PathShapeViolation::{variant:?}.is_parent_escape() must agree \
with matches!(_, PathShapeViolation::ParentEscape)"
);
}
}
// ── is_lisp_extension — `:behavior :on-*` + `:upgrade-from ───────────
// ── :state-change :script` file-type predicate ───────────────────────
#[test]
fn lisp_extension_accepts_canonical_shapes() {
// Positive controls: every documented authoring shape across
// both existing call sites — bare filename, standard `lib/`
// subdirectory, deeply-nested migrations subdirectory,
// explicit current-dir-relative prefix, mid-path `./`
// segment, single-letter stem, and the multi-dot stem
// (`lib/migrations/v.0.1.lisp`) an author might use to
// encode the migration's `:from` version into the filename.
// The predicate only inspects the terminating extension —
// `Path::extension()` returns the substring after the final
// `.` — so the multi-dot stem is structurally accepted
// because the final extension is still `lisp`. Drift here =
// a future tightening that rejects any of these surfaces as
// a test-failure at the predicate boundary, not piecemeal
// across per-axis call sites (`BehaviorSpec::validate`,
// `UpgradeInstruction::StateChange::validate`).
for relpath in [
"init.lisp",
"lib/init.lisp",
"lib/handlers.lisp",
"lib/migrations.lisp",
"lib/migrations/v01-to-v02.lisp",
"./lib/init.lisp",
"lib/./handlers.lisp",
"lib/migrations/v.0.1.lisp",
"a.lisp",
] {
assert!(
is_lisp_extension(Path::new(relpath)),
"canonical `.lisp` shape {relpath:?} must pass is_lisp_extension"
);
}
}
#[test]
fn lisp_extension_rejects_no_extension() {
// The fail-before-pass-after pin on the no-extension shape.
// A path with no `.` component (`Path::extension()` returns
// `None`) is the canonical "I declared the slot but forgot
// the `.lisp` extension" authoring footgun. The wasm-engine's
// `tatara_lisp::read` consumer can't infer the file type from
// the path alone, so the gate refuses the value at validate
// time.
for relpath in [
"lib/init",
"init",
"lib/handlers",
"lib/migrations/v01-to-v02",
"a",
] {
assert!(
!is_lisp_extension(Path::new(relpath)),
"no-extension shape {relpath:?} must fail is_lisp_extension"
);
}
}
#[test]
fn lisp_extension_rejects_wrong_extension() {
// Wrong-extension sweep: the canonical authoring footguns
// an author might drag in from the workspace tree (`.txt`,
// `.md`, `.json`, `.yaml`, `.toml`), the `.rs` shape that
// an IDE auto-complete might propose, the `.lisp.bak` shape
// an editor might leave behind (the predicate only inspects
// the *terminating* extension — `Path::extension()` returns
// `bak` here, not `lisp.bak` — so the gate refuses it as a
// no-`.lisp` final extension), and the `.lispx` / `.lis`
// near-miss shapes that a typo would produce. Each must
// fail the predicate — the wasm-engine's `tatara_lisp::read`
// consumer rejects all of these at hot-upgrade migration /
// instance-start time.
for relpath in [
"lib/init.rs",
"lib/init.txt",
"lib/init.md",
"lib/init.json",
"lib/init.yaml",
"lib/init.toml",
"lib/init.lisp.bak",
"lib/init.lispx",
"lib/init.lis",
] {
assert!(
!is_lisp_extension(Path::new(relpath)),
"wrong-extension shape {relpath:?} must fail is_lisp_extension"
);
}
}
#[test]
fn lisp_extension_is_case_sensitive() {
// Strict lowercase pin: every case-folded shape a
// case-insensitive volume's existence check would match the
// on-disk file must still fail the predicate — the
// canonical-form codec emits lowercase `.lisp` verbatim, so
// a case-folded shape mismatches the round-trip-stable
// canonical form (THEORY.md §V.2.7 render-determinism).
// Same case-sensitive discipline the byte-size / duration
// codecs and every other shape-gate predicate in `render.rs`
// (label / scheme / unit boundaries) carry. Pinned at the
// predicate boundary so any future case-folding regression
// surfaces here rather than piecemeal across per-axis call
// sites.
for relpath in [
"lib/init.LISP",
"lib/init.Lisp",
"lib/init.LiSp",
"lib/init.lISP",
"lib/init.LISp",
] {
assert!(
!is_lisp_extension(Path::new(relpath)),
"case-folded `.lisp` shape {relpath:?} must fail is_lisp_extension \
(strict lowercase, render-determinism pin)"
);
}
}
#[test]
fn lisp_extension_constant_matches_predicate() {
// Cross-pin: the [`LISP_SOURCE_EXTENSION`] const and the
// predicate's accepted set are the same single source of
// truth. Drift would let a future renderer / per-axis
// wrapper emit `.<const>` while the predicate accepts only
// `.lisp` (or vice versa), silently breaking the
// round-trip-stable canonical form. Pinned by constructing
// a path from the const and round-tripping through the
// predicate.
assert_eq!(LISP_SOURCE_EXTENSION, "lisp");
let p = PathBuf::from(format!("lib/init.{LISP_SOURCE_EXTENSION}"));
assert!(
is_lisp_extension(&p),
"path constructed from LISP_SOURCE_EXTENSION must pass is_lisp_extension"
);
}
#[test]
fn lisp_extension_matches_inlined_call_site_semantics() {
// End-to-end pin: every value the pre-lift inline gate
// (`BehaviorSpec::validate_callback_path`, c97815a) accepted-
// or-rejected must surface from the lifted predicate
// identically. Drift here would mean a previously-accepted
// authoring shape would suddenly fail (or vice versa)
// silently across the lift commit. Sweeps the canonical
// authoring shapes the pre-lift call site's tests covered
// verbatim.
// Pre-lift accepts (must still pass):
for accept in [
"lib/init.lisp",
"lib/handlers.lisp",
"lib/migrations/v01-to-v02.lisp",
"init.lisp",
"a.lisp",
"./lib/init.lisp",
"lib/./handlers.lisp",
"lib/migrations/v.0.1.lisp",
] {
assert!(
is_lisp_extension(Path::new(accept)),
"pre-lift accept {accept:?} regressed"
);
}
// Pre-lift rejects (must still reject):
for reject in [
"lib/init",
"init",
"lib/init.rs",
"lib/init.txt",
"lib/init.lisp.bak",
"lib/init.lispx",
"lib/init.LISP",
"lib/init.Lisp",
] {
assert!(
!is_lisp_extension(Path::new(reject)),
"pre-lift reject {reject:?} regressed"
);
}
}
// ── is_computeunit_yaml_extension — `:servicos` compound-suffix predicate ───
#[test]
fn computeunit_yaml_extension_accepts_canonical_shapes() {
// Positive controls: every canonical authoring shape every
// in-tree fixture and the `Caixa::template` scaffold use. The
// predicate inspects the final file-name component and checks
// for the compound `.computeunit.yaml` suffix with at least
// one byte of stem preceding it.
for relpath in [
"servicos/demo.computeunit.yaml",
"servicos/hello-rio.computeunit.yaml",
"servicos/my-service.computeunit.yaml",
"servicos/a.computeunit.yaml",
"./servicos/demo.computeunit.yaml",
"servicos/./demo.computeunit.yaml",
"servicos/sub/nested.computeunit.yaml",
"servicos/v0.1.computeunit.yaml",
] {
assert!(
is_computeunit_yaml_extension(Path::new(relpath)),
"canonical `.computeunit.yaml` shape {relpath:?} must pass \
is_computeunit_yaml_extension"
);
}
}
#[test]
fn computeunit_yaml_extension_rejects_no_extension() {
// No-extension shape — the canonical "I declared the slot
// but forgot the `.computeunit.yaml` suffix" footgun. The
// peer caixa-helm / caixa-flux `serde_yaml::from_str`
// consumer can't infer the file type from the path alone, so
// the gate refuses the value at validate time.
for relpath in ["servicos/demo", "demo", "servicos/sub/nested"] {
assert!(
!is_computeunit_yaml_extension(Path::new(relpath)),
"no-extension shape {relpath:?} must fail \
is_computeunit_yaml_extension"
);
}
}
#[test]
fn computeunit_yaml_extension_rejects_wrong_extension() {
// Wrong-extension sweep across the canonical authoring footguns
// an author might drag in from the workspace tree — bare
// `.yaml` (the canonical "I forgot the `.computeunit` segment"
// typo), `.yml` (Helm-shorthand leak), `.json` (FluxCD
// bundle leak), `.toml` (Cargo workspace leak), `.txt`
// / `.md` (paste-from-doc footguns), `.yaml.bak` (editor
// backup), the near-miss `.computeunit.yam` / `.computeunit.yamls`
// typo, and the off-by-one-segment `computeunit-yaml`
// / `computeunit_yaml` shapes. Each must fail the predicate.
for relpath in [
"servicos/demo.yaml",
"servicos/demo.yml",
"servicos/demo.json",
"servicos/demo.toml",
"servicos/demo.txt",
"servicos/demo.md",
"servicos/demo.computeunit.yaml.bak",
"servicos/demo.computeunit.yam",
"servicos/demo.computeunit.yamls",
"servicos/demo.computeunit",
"servicos/demo-computeunit.yaml",
"servicos/demo_computeunit.yaml",
] {
assert!(
!is_computeunit_yaml_extension(Path::new(relpath)),
"wrong-extension shape {relpath:?} must fail \
is_computeunit_yaml_extension"
);
}
}
#[test]
fn computeunit_yaml_extension_is_case_sensitive() {
// Strict lowercase pin: every case-folded shape a
// case-insensitive volume's existence check would match the
// on-disk file must still fail the predicate — the canonical-
// form codec emits lowercase `.computeunit.yaml` verbatim, so
// a case-folded shape mismatches the round-trip-stable
// canonical form (THEORY.md §V.2.7 render-determinism). Same
// case-sensitive discipline the byte-size / duration codecs
// and the peer `is_lisp_extension` predicate carry.
for relpath in [
"servicos/demo.ComputeUnit.yaml",
"servicos/demo.COMPUTEUNIT.yaml",
"servicos/demo.computeunit.YAML",
"servicos/demo.computeunit.Yaml",
"servicos/demo.COMPUTEUNIT.YAML",
] {
assert!(
!is_computeunit_yaml_extension(Path::new(relpath)),
"case-folded `.computeunit.yaml` shape {relpath:?} must fail \
is_computeunit_yaml_extension (strict lowercase, \
render-determinism pin)"
);
}
}
#[test]
fn computeunit_yaml_extension_rejects_empty_stem() {
// Degenerate hidden-file shape: a file name exactly equal to
// the suffix (`.computeunit.yaml` — no stem preceding the
// suffix) is the structural "Servico declared with no
// identity" footgun. The substrate identifies each ComputeUnit
// by the file-stem segment that precedes `.computeunit.yaml`
// (the rendered `lareira-<stem>` Helm chart, the per-Servico
// `metadata.name`, the M3 `:contratos` membership lookup), so
// an empty stem leaves the Servico unidentifiable. Predicate
// pin: the `name.len() > SUFFIX.len()` bound rejects the
// hidden-file shape at the predicate boundary.
for relpath in [".computeunit.yaml", "servicos/.computeunit.yaml"] {
assert!(
!is_computeunit_yaml_extension(Path::new(relpath)),
"empty-stem shape {relpath:?} must fail \
is_computeunit_yaml_extension"
);
}
}
#[test]
fn computeunit_yaml_extension_constant_matches_predicate() {
// Cross-pin: the [`COMPUTEUNIT_YAML_SUFFIX`] const and the
// predicate's accepted set are the same single source of
// truth. Drift would let a future renderer / per-axis wrapper
// emit `<stem><const>` while the predicate accepts only
// `.computeunit.yaml` (or vice versa), silently breaking the
// round-trip-stable canonical form. Pinned by constructing a
// path from the const and round-tripping through the
// predicate. Mirrors the peer
// `lisp_extension_constant_matches_predicate` pin.
assert_eq!(COMPUTEUNIT_YAML_SUFFIX, ".computeunit.yaml");
let p = PathBuf::from(format!("servicos/demo{COMPUTEUNIT_YAML_SUFFIX}"));
assert!(
is_computeunit_yaml_extension(&p),
"path constructed from COMPUTEUNIT_YAML_SUFFIX must pass \
is_computeunit_yaml_extension"
);
}
// ── is_cargo_feature_name — shared `:caracteristicas` feature-name predicate ──
#[test]
fn cargo_feature_name_accepts_canonical_forms() {
// Substrate-side pin: the predicate accepts every canonical Cargo
// feature name shape `:caracteristicas` entries carry. Drift between
// this list and the per-axis `dep::tests::validate_accepts_canonical_caracteristicas`
// positive-set sweep surfaces here — one source of truth for the
// rule. Includes single-token (`http`), kebab-case (`runtime-tokio`),
// snake-case (`derive_macros`), namespaced-dot (`tokio.full`),
// version-suffix (`v0.1`), `+`-separated (`http+json`), leading
// underscore (`_internal`), doubled-underscore (`__private`),
// and digit-starting (`v0_1`) — the canonical authoring shapes
// every realistic Cargo feature in the pleme-io ecosystem uses.
for s in [
"http",
"json",
"derive",
"serde",
"serde_json",
"runtime-tokio",
"tokio.full",
"v0.1",
"v1",
"http+json",
"_internal",
"__private",
"default",
"rt-multi-thread",
"12factor",
"feat.v2",
"client+server",
] {
is_cargo_feature_name(s)
.unwrap_or_else(|e| panic!("canonical Cargo feature name {s:?} must pass: {e:?}"));
}
}
#[test]
fn cargo_feature_name_rejects_each_arm_with_substring_pinned_reason() {
// Substrate-side diagnostic-shape pin: each grammar arm
// surfaces its own distinct reason substring. Pinned here so a
// future reason-wording rephrase that drops any of these
// substrings surfaces at this one place, not piecemeal across
// every per-axis test sweep. Mirrors
// `git_repo_url`'s and `git_ref_name`'s arm-substring sweeps
// on the peer predicates.
for (s, needle) in [
// Leading `+` — the canonical paste-from-`+optional-feature`
// activation-form-in-feature-name-slot footgun.
("+http", "`+`"),
// Leading `-` — kebab-leak / CLI-arg-injection adjacent.
("-json", "`-`"),
// Leading `.` — dotted-version-suffix-as-feature-name typo.
(".feat", "`.`"),
// Whitespace inside — multi-token blob.
("http feature", "whitespace"),
// Tab inside.
("http\tjson", "whitespace"),
// Leading whitespace — paste-from-aligned-doc.
(" http", "whitespace"),
// Comma — list-separator-belongs-to-list-grammar.
("http,json", "`,`"),
// Forward slash — Cargo's `dep/feat` namespaced-dep syntax.
("http/json", "`/`"),
// Question mark — URL-reserved.
("http?", "`?`"),
// Hash — URL-reserved.
("http#frag", "`#`"),
// Embedded control character.
("http\x01json", "control character"),
// Newline — paste-from-multiline-doc.
("http\njson", "control character"),
// DEL byte (0x7F).
("http\x7fjson", "control character"),
// Non-ASCII byte — un-percent-encoded character.
("caf\u{e9}", "non-ASCII"),
// Non-ASCII at first byte.
("\u{e9}feat", "non-ASCII"),
// Forbidden punctuation in the continuation set.
("http@1", "invalid character"),
("http&json", "invalid character"),
("http=v1", "invalid character"),
] {
let err = is_cargo_feature_name(s)
.err()
.unwrap_or_else(|| panic!("Cargo feature name {s:?} must be rejected"));
assert!(
err.contains(needle),
"Cargo feature name {s:?} reason must contain {needle:?}; got {err:?}"
);
}
}
#[test]
fn cargo_feature_name_rejects_empty_defensively() {
// The predicate is called from `crate::dep::Dep::validate_caracteristicas`
// only after the per-axis `CaracteristicaEmpty` arm has fired
// at validate time; re-checking here keeps the predicate usable
// from any future call site without an empty-precondition
// footgun. Same defensive empty-check `is_dns_1123_label`,
// `is_gateway_api_http_path`, `is_wit_world_ref`,
// `is_nats_subject`, `is_wasi_keyvalue_slot`, `is_git_ref_name`,
// `is_git_oid`, and `is_git_repo_url` carry at their call sites.
let err = is_cargo_feature_name("").unwrap_err();
assert!(err.contains("empty"), "got: {err:?}");
}
#[test]
fn cargo_feature_name_rejects_at_65_byte_boundary() {
// The 64-byte cap pin — both the boundary-exceeding case and
// the boundary-accepting case in one place, so a future cap
// shift surfaces both arms simultaneously, mirroring
// `dns_1123_label_rejects_at_64_byte_boundary`,
// `gateway_api_http_path_rejects_at_1025_byte_boundary`,
// `wit_world_ref_rejects_at_129_byte_boundary`,
// `nats_subject_rejects_at_257_byte_boundary`,
// `wasi_kv_slot_rejects_at_513_byte_boundary`, and
// `git_ref_name_rejects_at_256_byte_boundary` on the peer
// predicates. Constructed as a single all-`a` token so only
// the cap arm fires.
let max_ok = "a".repeat(CARGO_FEATURE_NAME_MAX_LEN);
assert_eq!(max_ok.len(), 64);
is_cargo_feature_name(&max_ok).unwrap();
let too_long = "a".repeat(CARGO_FEATURE_NAME_MAX_LEN + 1);
assert_eq!(too_long.len(), 65);
let err = is_cargo_feature_name(&too_long).unwrap_err();
assert!(err.contains("64"), "got: {err:?}");
assert!(err.contains("65"), "got: {err:?}");
}
#[test]
fn cargo_feature_name_first_byte_diagnostics_name_the_leading_char() {
// Diagnostic-shape pin: the leading-character rejection arms
// name the specific punctuation (`+`, `-`, `.`) verbatim so the
// author's grep target is unambiguous. Pinned across the three
// canonical leading-char footguns so a future relaxation that
// drops any of the three surfaces here. The `+`-arm's wording
// additionally points the author at the canonical Cargo
// `+<feature>` activation-form-vs-feature-name discipline so
// the paste-from-doc footgun lands its remediation in the
// diagnostic itself.
let err_plus = is_cargo_feature_name("+http").unwrap_err();
assert!(err_plus.contains("`+`"), "got: {err_plus:?}");
assert!(
err_plus.contains("activation"),
"got: {err_plus:?} (must name the Cargo +<feature> activation-form)"
);
let err_hyphen = is_cargo_feature_name("-json").unwrap_err();
assert!(err_hyphen.contains("`-`"), "got: {err_hyphen:?}");
let err_dot = is_cargo_feature_name(".feat").unwrap_err();
assert!(err_dot.contains("`.`"), "got: {err_dot:?}");
}
// ── is_spdx_expression_shape — shared `:licenca` SPDX-expression predicate ──
#[test]
fn spdx_expression_shape_accepts_canonical_forms() {
// Substrate-side pin: the predicate accepts every canonical
// SPDX expression shape the `:licenca` axis carries. Drift
// between this list and the per-axis
// `manifest::tests::validate_licenca_accepts_canonical_expressions`
// positive-set sweep surfaces here — one source of truth for
// the rule. Covers single-license, `OR`/`AND`-compound,
// `WITH`-exception, parenthesis-grouped, `+`-suffix, and
// `LicenseRef-` / `DocumentRef-:LicenseRef-` shapes.
for s in [
"MIT",
"Apache-2.0",
"BSD-3-Clause",
"MPL-2.0",
"GPL-3.0-or-later",
"GPL-2.0+",
"Apache-2.0 OR MIT",
"Apache-2.0 AND MIT",
"Apache-2.0 WITH LLVM-exception",
"(MIT OR Apache-2.0) AND BSD-3-Clause",
"(MIT OR Apache-2.0) AND BSD-3-Clause AND ISC",
"LicenseRef-MyLicense",
"DocumentRef-spdx-tool:LicenseRef-MIT-Style",
"x",
] {
is_spdx_expression_shape(s)
.unwrap_or_else(|e| panic!("canonical SPDX expression {s:?} must pass: {e:?}"));
}
}
#[test]
fn spdx_expression_shape_rejects_each_arm_with_substring_pinned_reason() {
// Substrate-side diagnostic-shape pin: each alphabet arm
// surfaces its own distinct reason substring. Pinned here so a
// future reason-wording rephrase that drops any of these
// substrings surfaces at this one place, not piecemeal across
// every per-axis test sweep. Mirrors
// `cargo_feature_name_rejects_each_arm_with_substring_pinned_reason`
// on the peer predicate.
for (s, needle) in [
// Leading whitespace — paste-from-aligned-doc.
(" MIT", "whitespace"),
// Trailing whitespace — paste-from-doc.
("MIT ", "whitespace"),
// Tab inside — tab-from-aligned-doc.
("MIT\tOR Apache-2.0", "tab"),
// Embedded control character.
("MIT\x01OR Apache-2.0", "control character"),
// Newline — paste-from-multiline-doc.
("MIT\nOR Apache-2.0", "control character"),
// CRLF — paste-from-multiline-doc.
("MIT\rApache-2.0", "control character"),
// DEL byte (0x7F).
("MIT\x7fApache-2.0", "control character"),
// Non-ASCII byte — smart-quote paste.
("MIT\u{a0}OR Apache-2.0", "non-ASCII"),
// Non-ASCII at first byte — fullwidth letter.
("\u{ff2d}IT", "non-ASCII"),
// Underscore — snake-case-instead-of-kebab-case typo.
("Apache_2.0", "`_`"),
// Comma — list-separator-belongs-to-list-grammar.
("MIT, Apache-2.0", "`,`"),
// Forward slash — colloquial dual-license idiom.
("MIT/Apache-2.0", "`/`"),
// Semicolon — list-separator confusion.
("MIT; Apache-2.0", "`;`"),
// Forbidden punctuation in the alphabet.
("MIT@1.0", "invalid character"),
("MIT&Apache-2.0", "invalid character"),
("MIT=Apache-2.0", "invalid character"),
("MIT*1.0", "invalid character"),
] {
let err = is_spdx_expression_shape(s)
.err()
.unwrap_or_else(|| panic!("SPDX expression {s:?} must be rejected"));
assert!(
err.contains(needle),
"SPDX expression {s:?} reason must contain {needle:?}; got {err:?}"
);
}
}
#[test]
fn spdx_expression_shape_rejects_empty_defensively() {
// The predicate is called from `crate::Caixa::validate_licenca`
// only after the per-axis `LicencaEmpty` arm has fired at
// validate time; re-checking here keeps the predicate usable
// from any future call site without an empty-precondition
// footgun. Same defensive empty-check `is_dns_1123_label`,
// `is_gateway_api_http_path`, `is_wit_world_ref`,
// `is_nats_subject`, `is_wasi_keyvalue_slot`,
// `is_git_ref_name`, `is_git_oid`, `is_git_repo_url`, and
// `is_cargo_feature_name` carry at their call sites.
let err = is_spdx_expression_shape("").unwrap_err();
assert!(err.contains("empty"), "got: {err:?}");
}
#[test]
fn spdx_expression_shape_rejects_at_257_byte_boundary() {
// The 256-byte cap pin — both the boundary-exceeding case and
// the boundary-accepting case in one place, so a future cap
// shift surfaces both arms simultaneously, mirroring the peer
// cap-boundary pins. Constructed as a single all-`a` token so
// only the cap arm fires (256 `a` bytes is alphabet-valid).
let max_ok = "a".repeat(SPDX_EXPRESSION_MAX_LEN);
assert_eq!(max_ok.len(), 256);
is_spdx_expression_shape(&max_ok).unwrap();
let too_long = "a".repeat(SPDX_EXPRESSION_MAX_LEN + 1);
assert_eq!(too_long.len(), 257);
let err = is_spdx_expression_shape(&too_long).unwrap_err();
assert!(err.contains("256"), "got: {err:?}");
assert!(err.contains("257"), "got: {err:?}");
}
// ── is_chart_description_shape — shared `:descricao` chart-description predicate ──
#[test]
fn chart_description_shape_accepts_canonical_forms() {
// Substrate-side pin: the predicate accepts every canonical
// chart-description shape the `:descricao` axis carries.
// Drift between this list and the per-axis
// `manifest::tests::validate_descricao_accepts_canonical_summary`
// positive-set sweep surfaces here — one source of truth for
// the rule. Covers ASCII summaries, the Unicode `→` from the
// canonical Rust→wasm fixture, and the Unicode `—` em-dash
// from the `Caixa::template` scaffold every `feira init`
// emits.
for s in [
"Canonical Rust→wasm32-wasip2 caixa Servico.",
"Checkout flow.",
"AWS provider caixa for tatara-lisp",
"FIXME — describe this caixa",
"x",
] {
is_chart_description_shape(s)
.unwrap_or_else(|e| panic!("canonical chart description {s:?} must pass: {e:?}"));
}
}
#[test]
fn chart_description_shape_rejects_each_arm_with_substring_pinned_reason() {
// Substrate-side diagnostic-shape pin: each arm surfaces its
// own distinct reason substring. Pinned here so a future
// reason-wording rephrase that drops any of these substrings
// surfaces at this one place, not piecemeal across every
// per-axis test sweep. Mirrors
// `spdx_expression_shape_rejects_each_arm_with_substring_pinned_reason`
// on the peer predicate.
for (s, needle) in [
// Leading whitespace — paste-from-aligned-doc.
(" Checkout flow.", "whitespace"),
// Trailing whitespace — paste-from-doc.
("Checkout flow. ", "whitespace"),
// Tab inside — tab-from-aligned-doc.
("Checkout\tflow.", "tab"),
// Newline — paste-from-multiline-doc.
("Checkout\nflow.", "newline"),
// Carriage return — paste-from-Windows-CRLF-doc.
("Checkout\rflow.", "carriage return"),
// NUL byte — paste-from-binary-blob.
("Checkout\x00flow.", "control character"),
// BEL byte — paste-from-binary-blob.
("Checkout\x07flow.", "control character"),
// ESC byte — paste-from-binary-blob.
("Checkout\x1bflow.", "control character"),
// DEL byte (0x7F).
("Checkout\x7fflow.", "control character"),
] {
let err = is_chart_description_shape(s)
.err()
.unwrap_or_else(|| panic!("chart description {s:?} must be rejected"));
assert!(
err.contains(needle),
"chart description {s:?} reason must contain {needle:?}; got {err:?}"
);
}
}
#[test]
fn chart_description_shape_accepts_unicode() {
// Positive control on the non-ASCII arm: the predicate must
// accept Unicode beyond the ASCII alphabet — the canonical
// pleme-io descricao fixtures carry `→` (U+2192) and `—`
// (U+2014), and every downstream consumer (YAML 1.2, Helm v3,
// every chart-aware UI) round-trips Unicode losslessly.
// Mirrors the spdx-rejects-non-ASCII arm by inverting it — a
// future tightening that bans non-ASCII bytes would regress
// every canonical fixture and surface here as a regression.
for s in [
"Canonical Rust→wasm32-wasip2",
"FIXME — describe this caixa",
"Caixa pour le projet tâche",
"日本語の説明",
"naïve",
] {
is_chart_description_shape(s)
.unwrap_or_else(|e| panic!("Unicode chart description {s:?} must pass: {e:?}"));
}
}
#[test]
fn chart_description_shape_rejects_empty_defensively() {
// The predicate is called from `crate::Caixa::validate_descricao`
// only after the per-axis `DescricaoEmpty` arm has fired at
// validate time; re-checking here keeps the predicate usable
// from any future call site without an empty-precondition
// footgun. Same defensive empty-check `is_dns_1123_label`,
// `is_gateway_api_http_path`, `is_wit_world_ref`,
// `is_nats_subject`, `is_wasi_keyvalue_slot`,
// `is_git_ref_name`, `is_git_oid`, `is_git_repo_url`,
// `is_cargo_feature_name`, and `is_spdx_expression_shape`
// carry at their call sites.
let err = is_chart_description_shape("").unwrap_err();
assert!(err.contains("empty"), "got: {err:?}");
}
#[test]
fn chart_description_shape_rejects_at_513_byte_boundary() {
// The 512-byte cap pin — both the boundary-exceeding case and
// the boundary-accepting case in one place, so a future cap
// shift surfaces both arms simultaneously, mirroring the peer
// cap-boundary pins. Constructed as a single all-`a` token so
// only the cap arm fires (512 `a` bytes is alphabet-valid).
let max_ok = "a".repeat(CHART_DESCRIPTION_MAX_LEN);
assert_eq!(max_ok.len(), 512);
is_chart_description_shape(&max_ok).unwrap();
let too_long = "a".repeat(CHART_DESCRIPTION_MAX_LEN + 1);
assert_eq!(too_long.len(), 513);
let err = is_chart_description_shape(&too_long).unwrap_err();
assert!(err.contains("512"), "got: {err:?}");
assert!(err.contains("513"), "got: {err:?}");
}
#[test]
fn chart_description_shape_rejects_each_unicode_bidi_override_codepoint() {
// The Trojan Source (CVE-2021-42574) arm — pins every UAX #9
// bidirectional-override / isolate format codepoint as a
// structural rejection on the typed `:descricao` axis. The
// per-byte non-ASCII pass deliberately admits Unicode letters
// / em-dash / arrows because the canonical fixtures carry them
// (`Canonical Rust→wasm32-wasip2`, `FIXME — describe this
// caixa`); only the typed codepoint scan catches the nine
// bidi-override codepoints that flip the rendered visual order
// of every following character, so a future drop of any one
// arm here surfaces as a `must be rejected` panic at this one
// place rather than as a silent regression downstream. Each
// case carries an alphabet-valid prefix + suffix so only the
// bidi-override arm fires.
for (cp, name) in [
('\u{202A}', "U+202A"),
('\u{202B}', "U+202B"),
('\u{202C}', "U+202C"),
('\u{202D}', "U+202D"),
('\u{202E}', "U+202E"),
('\u{2066}', "U+2066"),
('\u{2067}', "U+2067"),
('\u{2068}', "U+2068"),
('\u{2069}', "U+2069"),
] {
let s = format!("alice{cp}bob");
let err = is_chart_description_shape(&s)
.err()
.unwrap_or_else(|| panic!("chart description with {name} must be rejected"));
assert!(
err.contains(name),
"chart description reason for {name} must name the codepoint verbatim; got {err:?}"
);
assert!(
err.contains("bidirectional-override")
|| err.contains("Unicode bidi")
|| err.contains("Trojan Source"),
"chart description reason for {name} must name the Trojan-Source banner; \
got {err:?}"
);
}
}
#[test]
fn chart_description_shape_accepts_pure_rtl_text_without_bidi_override() {
// Positive control on the bidi-override arm: pure visual
// right-to-left scripts (Hebrew, Arabic) decode to non-bidi-
// override codepoints and the predicate must accept them
// natively — banning all RTL would regress every Hebrew /
// Arabic-authored caixa, which the substrate explicitly
// supports via the non-ASCII byte arm. The structural axis the
// bidi-override arm closes is the explicit direction-mark
// codepoint, not the RTL script itself.
for s in [
// Hebrew word (RTL script, no bidi-override codepoint).
"שלום",
// Arabic word (RTL script, no bidi-override codepoint).
"مرحبا",
// Mixed LTR / RTL caixa — the canonical multilingual
// description shape every YAML 1.2 + Helm v3 + Artifact
// Hub consumer round-trips losslessly.
"Caixa para שלום",
] {
is_chart_description_shape(s).unwrap_or_else(|e| {
panic!("pure-RTL chart description {s:?} must pass without bidi override: {e:?}")
});
}
}
#[test]
fn chart_description_shape_rejects_each_unicode_line_break_codepoint() {
// The non-ASCII Unicode line-break arm — pins each of the three
// UAX #14 / YAML 1.1 §4.1 line-break codepoints outside the
// ASCII `\n` / `\r` bytes already caught at the per-byte pass.
// Each case carries an alphabet-valid prefix + suffix so only
// the line-break arm fires; the per-byte `\n` / `\r` arms
// would shadow the codepoint scan if the line-break helper
// accepted single-byte ASCII line terminators. A future drop
// of any one arm here surfaces as a `must be rejected` panic
// at this one place rather than as a silent regression
// through YAML 1.1-compat downstream consumers (go-yaml v2 /
// Helm v3 / kubectl). Mirrors the peer
// `chart_maintainer_name_shape_rejects_each_unicode_line_break_codepoint`
// on the sibling predicate — both predicates route through the
// same lifted `find_unicode_line_break` helper.
for (cp, name) in [
('\u{0085}', "U+0085"),
('\u{2028}', "U+2028"),
('\u{2029}', "U+2029"),
] {
let s = format!("first line{cp}second line");
let err = is_chart_description_shape(&s)
.err()
.unwrap_or_else(|| panic!("chart description with {name} must be rejected"));
assert!(
err.contains(name),
"chart description reason for {name} must name the codepoint verbatim; got {err:?}"
);
assert!(
err.contains("line-break") || err.contains("UAX #14") || err.contains("YAML 1.1"),
"chart description reason for {name} must name the Unicode-line-break banner; \
got {err:?}"
);
}
}
#[test]
fn chart_description_shape_accepts_non_line_break_unicode() {
// Positive control on the line-break arm: the predicate must
// accept every non-line-break Unicode shape the canonical
// fixtures carry. Pinned alongside the per-codepoint rejection
// sweep so a future helper widening that accidentally rejects
// a non-line-break codepoint (the structural-floor regression
// class) surfaces here as a single-source-of-truth pin. The
// canonical multilingual descriptions, RTL text, em-dash and
// arrows must all pass.
for s in [
"Canonical Rust→wasm32-wasip2 caixa Servico.",
"FIXME — describe this caixa",
"Caixa para שלום",
"日本語の説明テスト",
// U+00A0 NO-BREAK SPACE is NOT a line-break codepoint
// (UAX #14 class GL — Glue, non-breaking) — must pass.
"Caixa\u{00A0}for tests",
] {
is_chart_description_shape(s).unwrap_or_else(|e| {
panic!(
"non-line-break Unicode chart description {s:?} must pass without rejection: \
{e:?}"
)
});
}
}
#[test]
fn chart_description_shape_rejects_each_unicode_invisible_format_codepoint() {
// The Unicode invisible-format arm — pins each of the eight
// BMP Cf-category zero-width codepoints with no visible glyph
// in any conforming font. The per-byte non-ASCII pass
// deliberately admits multi-byte UTF-8 sequences (Unicode
// letters / arrows / em-dash are canonical fixtures); only the
// typed codepoint scan catches these eight. Each case carries
// an alphabet-valid prefix + suffix so only the invisible-
// format arm fires. A future drop of any one arm here surfaces
// as a `must be rejected` panic at this one place rather than
// as a silent regression through invisible-codepoint-homograph
// downstream consumers (Artifact Hub description-search
// misses, byte-level diff / grep / equality disagreement with
// the visible-glyph match). Peer of
// `chart_maintainer_name_shape_rejects_each_unicode_invisible_format_codepoint`
// on the sibling predicate — both predicates route through the
// same lifted `find_unicode_invisible_format` helper. Covers
// the four paste-from-Word / paste-from-BOM-editor / paste-
// from-typesetting shapes (U+00AD / U+200B / U+2060 / U+FEFF)
// and the four math-formula invisible operators (U+2061
// FUNCTION APPLICATION / U+2062 INVISIBLE TIMES / U+2063
// INVISIBLE SEPARATOR / U+2064 INVISIBLE PLUS — the canonical
// paste-from-MathJax / paste-from-LaTeX-rendered-formula
// footgun where the renderer emits an invisible operator
// between adjacent symbols for screen-reader operator
// semantics).
for (cp, name) in [
('\u{00AD}', "U+00AD"),
('\u{200B}', "U+200B"),
('\u{2060}', "U+2060"),
('\u{2061}', "U+2061"),
('\u{2062}', "U+2062"),
('\u{2063}', "U+2063"),
('\u{2064}', "U+2064"),
('\u{FEFF}', "U+FEFF"),
] {
let s = format!("Canonical{cp}Servico");
let err = is_chart_description_shape(&s)
.err()
.unwrap_or_else(|| panic!("chart description with {name} must be rejected"));
assert!(
err.contains(name),
"chart description reason for {name} must name the codepoint verbatim; got {err:?}"
);
assert!(
err.contains("invisible-format")
|| err.contains("Cf-category")
|| err.contains("zero-width"),
"chart description reason for {name} must name the invisible-format banner; \
got {err:?}"
);
}
}
#[test]
fn chart_description_shape_accepts_non_invisible_format_unicode() {
// Positive control on the invisible-format arm: the predicate
// must accept every non-invisible-format Unicode shape canonical
// fixtures carry — including U+200C ZWNJ / U+200D ZWJ
// (legitimate compositional load in Indic / Persian scripts and
// emoji ZWJ sequences) and U+200E LRM / U+200F RLM (legitimate
// single-character direction hints in mixed-script prose). A
// future helper widening that accidentally rejects any of these
// would regress legitimate fixture shapes and surfaces here as
// a single-source-of-truth pin. Mirrors
// `chart_maintainer_name_shape_accepts_non_invisible_format_unicode`
// on the sibling predicate.
for s in [
"Canonical Rust→wasm32-wasip2 caixa Servico.",
"FIXME — describe this caixa",
// Emoji ZWJ sequence (U+200D) — must NOT be rejected: the
// canonical multi-codepoint emoji authoring shape every
// chart-aware UI renders as a single glyph.
"Caixa for the 👨\u{200D}💻 family",
// ZWNJ (U+200C) — legitimate Persian / Indic script
// composition; the helper must NOT claim it.
"Caixa for می\u{200C}باشد",
// Bidi marks LRM (U+200E) and RLM (U+200F) — legitimate
// single-character direction hints, separate class from
// the bidi *overrides* the prior helper rejects.
"Caixa for ASCII\u{200E}embedded in RTL",
"Caixa for \u{200F}RTL hint",
] {
is_chart_description_shape(s).unwrap_or_else(|e| {
panic!(
"non-invisible-format Unicode chart description {s:?} must pass without \
rejection: {e:?}"
)
});
}
}
// ── is_chart_maintainer_name_shape — shared `:autores` chart-maintainer predicate ──
#[test]
fn chart_maintainer_name_shape_accepts_canonical_forms() {
// Substrate-side pin: the predicate accepts every canonical
// chart-maintainer-name shape the `:autores` axis carries.
// Drift between this list and the per-axis
// `manifest::tests::validate_autores_accepts_canonical_forms`
// positive-set sweep surfaces here — one source of truth for
// the rule. Covers the hello-rio / checkout-aplicacao
// `:autores ("pleme-io")` fixture, the multi-author
// `"Pleme Contributors"` shape, and the canonical Helm
// `"name <email>"` shape downstream packaging surfaces emit.
for s in [
"pleme-io",
"Pleme Contributors",
"alice <alice@example.com>",
"bob <bob@example.com>",
"Acme Corporation",
"x",
] {
is_chart_maintainer_name_shape(s).unwrap_or_else(|e| {
panic!("canonical chart maintainer name {s:?} must pass: {e:?}")
});
}
}
#[test]
fn chart_maintainer_name_shape_rejects_each_arm_with_substring_pinned_reason() {
// Substrate-side diagnostic-shape pin: each arm surfaces its
// own distinct reason substring. Pinned here so a future
// reason-wording rephrase that drops any of these substrings
// surfaces at this one place, not piecemeal across every
// per-axis test sweep. Mirrors
// `chart_description_shape_rejects_each_arm_with_substring_pinned_reason`
// on the peer predicate.
for (s, needle) in [
// Leading whitespace — paste-from-aligned-doc.
(" pleme-io", "whitespace"),
// Trailing whitespace — paste-from-doc.
("pleme-io ", "whitespace"),
// Tab inside — tab-from-aligned-doc.
("Pleme\tContributors", "tab"),
// Newline — paste-from-multiline-doc (author pasted
// multi-line author block into one entry).
("alice\nbob", "newline"),
// Carriage return — paste-from-Windows-CRLF-doc.
("alice\rbob", "carriage return"),
// NUL byte — paste-from-binary-blob.
("alice\x00bob", "control character"),
// BEL byte — paste-from-binary-blob.
("alice\x07bob", "control character"),
// ESC byte — paste-from-binary-blob.
("alice\x1bbob", "control character"),
// DEL byte (0x7F).
("alice\x7fbob", "control character"),
] {
let err = is_chart_maintainer_name_shape(s)
.err()
.unwrap_or_else(|| panic!("chart maintainer name {s:?} must be rejected"));
assert!(
err.contains(needle),
"chart maintainer name {s:?} reason must contain {needle:?}; got {err:?}"
);
}
}
#[test]
fn chart_maintainer_name_shape_accepts_unicode() {
// Positive control on the non-ASCII arm: the predicate must
// accept Unicode beyond the ASCII alphabet — realistic
// maintainer names carry Unicode (`François`, `日本語`,
// `naïve`), and every downstream consumer (YAML 1.2, Helm v3,
// every chart-aware UI) round-trips Unicode losslessly. A
// future tightening that bans non-ASCII bytes would regress
// every Unicode-named maintainer and surface here as a
// regression. Mirrors the peer
// `chart_description_shape_accepts_unicode`.
for s in [
"François Dupont",
"日本語の名前",
"naïve <naive@example.com>",
"André",
] {
is_chart_maintainer_name_shape(s)
.unwrap_or_else(|e| panic!("Unicode chart maintainer name {s:?} must pass: {e:?}"));
}
}
#[test]
fn chart_maintainer_name_shape_rejects_empty_defensively() {
// The predicate is called from `crate::Caixa::validate_autores`
// only after the per-axis `AutorEmpty` arm has fired at
// validate time; re-checking here keeps the predicate usable
// from any future call site without an empty-precondition
// footgun. Same defensive empty-check `is_dns_1123_label`,
// `is_gateway_api_http_path`, `is_wit_world_ref`,
// `is_nats_subject`, `is_wasi_keyvalue_slot`,
// `is_git_ref_name`, `is_git_oid`, `is_git_repo_url`,
// `is_cargo_feature_name`, `is_spdx_expression_shape`, and
// `is_chart_description_shape` carry at their call sites.
let err = is_chart_maintainer_name_shape("").unwrap_err();
assert!(err.contains("empty"), "got: {err:?}");
}
#[test]
fn chart_maintainer_name_shape_rejects_at_129_byte_boundary() {
// The 128-byte cap pin — both the boundary-exceeding case and
// the boundary-accepting case in one place, so a future cap
// shift surfaces both arms simultaneously, mirroring the peer
// cap-boundary pins (`chart_description_shape_rejects_at_513_byte_boundary`
// on the 512-byte sibling, `spdx_expression_shape_rejects_at_257_byte_boundary`
// on the 256-byte sibling). Constructed as a single all-`a`
// token so only the cap arm fires (128 `a` bytes is
// alphabet-valid).
let max_ok = "a".repeat(CHART_MAINTAINER_NAME_MAX_LEN);
assert_eq!(max_ok.len(), 128);
is_chart_maintainer_name_shape(&max_ok).unwrap();
let too_long = "a".repeat(CHART_MAINTAINER_NAME_MAX_LEN + 1);
assert_eq!(too_long.len(), 129);
let err = is_chart_maintainer_name_shape(&too_long).unwrap_err();
assert!(err.contains("128"), "got: {err:?}");
assert!(err.contains("129"), "got: {err:?}");
}
#[test]
fn chart_maintainer_name_shape_rejects_each_unicode_bidi_override_codepoint() {
// The Trojan Source (CVE-2021-42574) arm — pins every UAX #9
// bidirectional-override / isolate format codepoint as a
// structural rejection on the typed `:autores` axis. Mirrors
// `chart_description_shape_rejects_each_unicode_bidi_override_codepoint`
// on the peer predicate — both predicates route through the
// same lifted `find_unicode_bidi_override` helper, so dropping
// any one of the nine arms from the helper's match would
// regress both peer test sweeps simultaneously at this one
// structural floor rather than at piecemeal per-axis call
// sites. The canonical attacker shape: an `:autores
// "alice\u{202E}example.com<bob@"` entry renders in `helm
// list`'s maintainer column / Artifact Hub as the visually-
// reversed `alice<@bob>moc.elpmaxe` while riding verbatim
// into the Chart.yaml `maintainers:` array — exactly the
// class this arm closes.
for (cp, name) in [
('\u{202A}', "U+202A"),
('\u{202B}', "U+202B"),
('\u{202C}', "U+202C"),
('\u{202D}', "U+202D"),
('\u{202E}', "U+202E"),
('\u{2066}', "U+2066"),
('\u{2067}', "U+2067"),
('\u{2068}', "U+2068"),
('\u{2069}', "U+2069"),
] {
let s = format!("alice{cp}bob");
let err = is_chart_maintainer_name_shape(&s)
.err()
.unwrap_or_else(|| panic!("chart maintainer name with {name} must be rejected"));
assert!(
err.contains(name),
"chart maintainer name reason for {name} must name the codepoint verbatim; \
got {err:?}"
);
assert!(
err.contains("bidirectional-override")
|| err.contains("Unicode bidi")
|| err.contains("Trojan Source"),
"chart maintainer name reason for {name} must name the Trojan-Source banner; \
got {err:?}"
);
}
}
#[test]
fn chart_maintainer_name_shape_accepts_pure_rtl_text_without_bidi_override() {
// Positive control on the bidi-override arm: pure visual
// right-to-left scripts (Hebrew, Arabic) decode to non-bidi-
// override codepoints and the predicate must accept them
// natively — banning all RTL would regress every Hebrew /
// Arabic-authored maintainer-name entry, which the substrate
// supports via the non-ASCII byte arm. Peer of
// `chart_description_shape_accepts_pure_rtl_text_without_bidi_override`
// on the sibling YAML-plain-style-scalar surface.
for s in [
// Pure Hebrew maintainer name.
"שלום",
// Pure Arabic maintainer name.
"مرحبا",
// Mixed-script — canonical multilingual maintainer
// shape every YAML 1.2 + Helm v3 round-trips losslessly.
"Acme שלום",
] {
is_chart_maintainer_name_shape(s).unwrap_or_else(|e| {
panic!(
"pure-RTL chart maintainer name {s:?} must pass without bidi override: {e:?}"
)
});
}
}
#[test]
fn chart_maintainer_name_shape_rejects_each_unicode_line_break_codepoint() {
// The non-ASCII Unicode line-break arm — pins each of the three
// UAX #14 / YAML 1.1 §4.1 line-break codepoints outside the
// ASCII `\n` / `\r` bytes already caught at the per-byte pass.
// The canonical YAML-1.1-vs-YAML-1.2 paste-from-doc footgun: an
// `:autores "alice\u{2028}bob"` entry parses as one
// `maintainers:` array entry through a YAML 1.2-strict parser
// and as two entries through a YAML 1.1 parser (go-yaml v2 /
// Helm v3). Mirrors
// `chart_description_shape_rejects_each_unicode_line_break_codepoint`
// on the peer predicate — both predicates route through the
// same lifted `find_unicode_line_break` helper, so dropping
// any one of the three arms from the helper's match would
// regress both peer test sweeps simultaneously at this one
// structural floor.
for (cp, name) in [
('\u{0085}', "U+0085"),
('\u{2028}', "U+2028"),
('\u{2029}', "U+2029"),
] {
let s = format!("alice{cp}bob");
let err = is_chart_maintainer_name_shape(&s)
.err()
.unwrap_or_else(|| panic!("chart maintainer name with {name} must be rejected"));
assert!(
err.contains(name),
"chart maintainer name reason for {name} must name the codepoint verbatim; \
got {err:?}"
);
assert!(
err.contains("line-break") || err.contains("UAX #14") || err.contains("YAML 1.1"),
"chart maintainer name reason for {name} must name the Unicode-line-break banner; \
got {err:?}"
);
}
}
#[test]
fn chart_maintainer_name_shape_accepts_non_line_break_unicode() {
// Positive control on the line-break arm: the predicate must
// accept every non-line-break Unicode shape canonical
// maintainer names carry. Pinned alongside the per-codepoint
// rejection sweep so a future helper widening that
// accidentally rejects a non-line-break codepoint surfaces
// here as a single-source-of-truth pin. Peer of
// `chart_description_shape_accepts_non_line_break_unicode`
// on the sibling YAML-plain-style-scalar surface.
for s in [
"François Dupont",
"日本語の名前",
"naïve <naive@example.com>",
"André",
// U+00A0 NO-BREAK SPACE is NOT a line-break codepoint
// (UAX #14 class GL — Glue, non-breaking) and is the
// canonical authoring shape for unbreakable space inside
// a multi-token maintainer name — must pass.
"Acme\u{00A0}Corp",
] {
is_chart_maintainer_name_shape(s).unwrap_or_else(|e| {
panic!(
"non-line-break Unicode chart maintainer name {s:?} must pass without \
rejection: {e:?}"
)
});
}
}
#[test]
fn chart_maintainer_name_shape_rejects_each_unicode_invisible_format_codepoint() {
// The Unicode invisible-format arm — pins each of the eight
// BMP Cf-category zero-width codepoints with no visible glyph.
// The canonical maintainer-identity homograph footgun: an
// `:autores "alice\u{200B}"` entry renders identically to
// `:autores "alice"` in `helm list` / Artifact Hub's
// maintainer column, but the byte sequence is distinct — the
// Artifact Hub maintainer-index lookup misses the authored
// `"alice"` entry, a future CLA-signer lookup matches a
// visually-identical-but-byte-distinct identity. Mirrors
// `chart_description_shape_rejects_each_unicode_invisible_format_codepoint`
// on the peer predicate — both predicates route through the
// same lifted `find_unicode_invisible_format` helper, so
// dropping any one of the eight arms from the helper's match
// would regress both peer test sweeps simultaneously at this
// one structural floor. Covers the four paste-from-Word /
// paste-from-BOM-editor / paste-from-typesetting shapes
// (U+00AD / U+200B / U+2060 / U+FEFF) and the four math-
// formula invisible operators (U+2061 FUNCTION APPLICATION /
// U+2062 INVISIBLE TIMES / U+2063 INVISIBLE SEPARATOR /
// U+2064 INVISIBLE PLUS — paste-from-MathJax / paste-from-
// LaTeX-rendered-formula footgun).
for (cp, name) in [
('\u{00AD}', "U+00AD"),
('\u{200B}', "U+200B"),
('\u{2060}', "U+2060"),
('\u{2061}', "U+2061"),
('\u{2062}', "U+2062"),
('\u{2063}', "U+2063"),
('\u{2064}', "U+2064"),
('\u{FEFF}', "U+FEFF"),
] {
let s = format!("alice{cp}bob");
let err = is_chart_maintainer_name_shape(&s)
.err()
.unwrap_or_else(|| panic!("chart maintainer name with {name} must be rejected"));
assert!(
err.contains(name),
"chart maintainer name reason for {name} must name the codepoint verbatim; \
got {err:?}"
);
assert!(
err.contains("invisible-format")
|| err.contains("Cf-category")
|| err.contains("zero-width"),
"chart maintainer name reason for {name} must name the invisible-format banner; \
got {err:?}"
);
}
}
#[test]
fn chart_maintainer_name_shape_accepts_non_invisible_format_unicode() {
// Positive control on the invisible-format arm: the predicate
// must accept the legitimate-use codepoints the helper
// deliberately excludes — U+200C ZWNJ / U+200D ZWJ (emoji ZWJ
// sequences are canonical for modern maintainer-display names;
// Indic / Persian script composition relies on ZWNJ to break
// inappropriate ligatures) and U+200E LRM / U+200F RLM
// (mixed-script direction hints are canonical for "Arabic name
// with embedded ASCII email" shapes). Peer of
// `chart_description_shape_accepts_non_invisible_format_unicode`
// on the sibling YAML-plain-style-scalar surface.
for s in [
"François Dupont",
"naïve <naive@example.com>",
// Emoji ZWJ sequence (U+200D) — canonical multi-codepoint
// emoji authoring shape.
"Joe 👨\u{200D}💻 Developer",
// ZWNJ (U+200C) — legitimate Persian / Indic composition.
"Persian می\u{200C}باشد maintainer",
// Bidi marks LRM / RLM — legitimate direction hints in
// mixed-script maintainer names.
"Arabic\u{200F}name <maintainer@example.com>",
"ASCII\u{200E}embedded in RTL context",
] {
is_chart_maintainer_name_shape(s).unwrap_or_else(|e| {
panic!(
"non-invisible-format Unicode chart maintainer name {s:?} must pass without \
rejection: {e:?}"
)
});
}
}
#[test]
fn find_unicode_bidi_override_pins_the_nine_codepoint_accepted_set() {
// The shared helper's accepted set — pinned in one place so
// every per-predicate caller (`is_chart_description_shape`,
// `is_chart_maintainer_name_shape`, every future free-form-
// prose surface) reads from one canonical accepted set. The
// nine UAX #9 bidirectional-override / isolate format
// codepoints in document order, plus negative controls on
// bytes the helper must NOT reject (ASCII / non-bidi Unicode
// letters / arrows / em-dash / RTL letters). A future shift
// in the accepted set surfaces here as a single-source-of-
// truth edit at this one test rather than across every
// per-predicate per-arm sweep.
for cp in [
'\u{202A}', '\u{202B}', '\u{202C}', '\u{202D}', '\u{202E}', '\u{2066}', '\u{2067}',
'\u{2068}', '\u{2069}',
] {
let s = format!("a{cp}b");
assert_eq!(
find_unicode_bidi_override(&s),
Some(cp),
"helper must flag bidi override U+{:04X} on input {s:?}",
cp as u32
);
}
for s in [
"alice",
"Canonical Rust→wasm32-wasip2",
"FIXME — describe this caixa",
"François Dupont",
"日本語の説明",
"naïve",
"שלום",
"مرحبا",
] {
assert_eq!(
find_unicode_bidi_override(s),
None,
"helper must accept {s:?} (no bidi-override codepoint)"
);
}
// Empty input — defensive precondition for the helper's
// call-site contract on any future caller that doesn't gate
// emptiness ahead of the scan.
assert_eq!(find_unicode_bidi_override(""), None);
}
#[test]
fn find_unicode_line_break_pins_the_three_codepoint_accepted_set() {
// The shared helper's accepted set — pinned in one place so
// every per-predicate caller (`is_chart_description_shape`,
// `is_chart_maintainer_name_shape`, every future free-form-
// prose surface) reads from one canonical accepted set. The
// three UAX #14 / YAML 1.1 §4.1 non-ASCII line-break
// codepoints in document order, plus negative controls on
// bytes the helper must NOT reject (ASCII text, Unicode
// letters / arrows / em-dash / RTL letters, the canonical
// non-line-break U+00A0 NBSP shape downstream YAML 1.2 +
// Helm v3 + every chart-aware UI round-trip losslessly). A
// future shift in the accepted set surfaces here as a
// single-source-of-truth edit at this one test rather than
// across every per-predicate per-arm sweep. Peer of
// `find_unicode_bidi_override_pins_the_nine_codepoint_accepted_set`
// on the sibling lifted-helper one trajectory earlier.
for cp in ['\u{0085}', '\u{2028}', '\u{2029}'] {
let s = format!("a{cp}b");
assert_eq!(
find_unicode_line_break(&s),
Some(cp),
"helper must flag line-break codepoint U+{:04X} on input {s:?}",
cp as u32
);
}
for s in [
"alice",
"Canonical Rust→wasm32-wasip2",
"FIXME — describe this caixa",
"François Dupont",
"日本語の説明",
"naïve",
"שלום",
"مرحبا",
// U+00A0 NO-BREAK SPACE — UAX #14 class GL (Glue,
// non-breaking) — must NOT be rejected: the canonical
// unbreakable-space shape every typed maintainer-name
// axis admits.
"Acme\u{00A0}Corp",
// U+0009 TAB and U+000A LF and U+000D CR — ASCII
// line-break / whitespace bytes the per-byte arm on the
// calling predicate already closes; the helper must NOT
// claim them as its own (single-source-of-truth: ASCII
// arms live in the per-byte loop, the helper closes the
// non-ASCII codepoints).
"alice\tbob",
"alice\nbob",
"alice\rbob",
] {
assert_eq!(
find_unicode_line_break(s),
None,
"helper must accept {s:?} (no non-ASCII line-break codepoint)"
);
}
// Empty input — defensive precondition for the helper's
// call-site contract on any future caller that doesn't gate
// emptiness ahead of the scan.
assert_eq!(find_unicode_line_break(""), None);
}
#[test]
fn find_unicode_invisible_format_pins_the_eight_codepoint_accepted_set() {
// The shared helper's accepted set — pinned in one place so
// every per-predicate caller (`is_chart_description_shape`,
// `is_chart_maintainer_name_shape`, every future free-form-
// prose surface) reads from one canonical accepted set. The
// eight BMP Cf-category zero-width codepoints in document
// order — the four paste-from-Word / paste-from-BOM-editor /
// paste-from-typesetting-doc shapes (U+00AD SHY / U+200B ZWSP /
// U+2060 WJ / U+FEFF ZWNBSP-BOM) and the four math-formula
// invisible operators (U+2061 FUNCTION APPLICATION / U+2062
// INVISIBLE TIMES / U+2063 INVISIBLE SEPARATOR / U+2064
// INVISIBLE PLUS — paste-from-MathJax / paste-from-LaTeX-
// rendered-formula / paste-from-InDesign-math-equation
// shapes) — plus negative controls on codepoints the helper
// must NOT reject — the deliberate exclusions: U+200C ZWNJ /
// U+200D ZWJ (emoji ZWJ sequences + Indic / Persian script
// composition) and U+200E LRM / U+200F RLM (mixed-script
// direction hints). A future shift in the accepted set
// surfaces here as a single-source-of-truth edit at this one
// test rather than across every per-predicate per-arm sweep.
// Third pin in the UAX-driven render-determinism trio (peer of
// `find_unicode_bidi_override_pins_the_nine_codepoint_accepted_set`
// on the visual-order axis and
// `find_unicode_line_break_pins_the_three_codepoint_accepted_set`
// on the single-line/multi-line axis).
for cp in [
'\u{00AD}', '\u{200B}', '\u{2060}', '\u{2061}', '\u{2062}', '\u{2063}', '\u{2064}',
'\u{FEFF}',
] {
let s = format!("a{cp}b");
assert_eq!(
find_unicode_invisible_format(&s),
Some(cp),
"helper must flag invisible-format codepoint U+{:04X} on input {s:?}",
cp as u32
);
}
for s in [
"alice",
"Canonical Rust→wasm32-wasip2",
"FIXME — describe this caixa",
"François Dupont",
"日本語の説明",
"naïve",
"שלום",
"مرحبا",
// U+00A0 NO-BREAK SPACE — class GL (Glue), visible-width
// codepoint — must NOT be claimed by the invisible-format
// helper (the canonical unbreakable-space shape).
"Acme\u{00A0}Corp",
// U+200C ZWNJ — deliberately excluded (Indic / Persian
// composition + emoji ZWJ-adjacent context).
"می\u{200C}باشد",
// U+200D ZWJ — deliberately excluded (emoji ZWJ
// sequences are canonical: 👨💻 is MAN + ZWJ + LAPTOP).
"Joe 👨\u{200D}💻 Developer",
// U+200E LRM — deliberately excluded (direction-hint
// mark, not a direction-override; legitimate in
// mixed-script prose).
"ASCII\u{200E}embedded",
// U+200F RLM — deliberately excluded (mirror of LRM
// on the RTL axis).
"Arabic\u{200F}name",
// Bidi-override codepoints (U+202A..U+202E, U+2066..U+2069)
// — caught by the sibling `find_unicode_bidi_override`
// helper, not this one (single-source-of-truth: each
// helper closes exactly its class).
"alice\u{202E}bob",
// Line-break codepoints (U+0085, U+2028, U+2029) — caught
// by the sibling `find_unicode_line_break` helper.
"alice\u{2028}bob",
] {
assert_eq!(
find_unicode_invisible_format(s),
None,
"helper must accept {s:?} (no invisible-format codepoint in the four-codepoint set)"
);
}
// Empty input — defensive precondition for the helper's
// call-site contract on any future caller that doesn't gate
// emptiness ahead of the scan.
assert_eq!(find_unicode_invisible_format(""), None);
}
// ── is_chart_keyword_shape — shared `:etiquetas` chart-keyword predicate ──
#[test]
fn chart_keyword_shape_accepts_canonical_forms() {
// Substrate-side pin: the predicate accepts every canonical
// chart-keyword shape the `:etiquetas` axis carries. Drift
// between this list and the per-axis
// `manifest::tests::validate_etiquetas_accepts_canonical_shaped_forms`
// positive-set sweep surfaces here — one source of truth for
// the rule. Covers the example fixtures'
// `:etiquetas` lists (`"example"`, `"aplicacao"`, `"mesh"`,
// `"ecommerce"`, `"demo"`, `"infrastructure"`, `"aws"`,
// `"akeyless"`, `"pangea-native"`) and the substrate-fixed
// tags caixa-helm unions in at chart render (`"lareira"`,
// `"wasm"`, `"tatara-lisp"`, `"caixa-servico"`).
let example_fixture_tags = [
"example",
"aplicacao",
"mesh",
"ecommerce",
"demo",
"infrastructure",
"aws",
"akeyless",
"pangea-native",
"hello-world",
"rust",
"Foo",
"Bar123",
"x",
"snake_case_tag",
];
for s in example_fixture_tags
.iter()
.copied()
.chain(LAREIRA_CHART_KEYWORDS.iter().copied())
{
is_chart_keyword_shape(s)
.unwrap_or_else(|e| panic!("canonical chart keyword {s:?} must pass: {e:?}"));
}
}
#[test]
fn lareira_chart_keywords_pins_canonical_ordered_set() {
// Substrate-side canonical-set pin: byte-pins the
// substrate-fixed `Chart.yaml` `keywords:` union caixa-helm's
// `build_chart_yaml` folds into every rendered `lareira-<nome>`
// chart on top of the caixa author's own `:etiquetas`. The
// ordered array shape (`BTreeSet`-canonical ascii-alphabetical)
// pins the same order the emitted `Chart.yaml` `keywords:`
// sequence lists them after the intermediate
// `BTreeSet<String>` fold at the caixa-helm emit site. A drift
// between the canonical array and either the production emit
// at `caixa-helm::build_chart_yaml` (the sole consumer) or
// the peer positive-set sweep tests (this crate's
// `chart_keyword_shape_accepts_canonical_forms` and
// `manifest::tests::validate_etiquetas_accepts_canonical_shaped_forms`)
// surfaces at this one substrate-side pin.
assert_eq!(
LAREIRA_CHART_KEYWORDS,
&["caixa-servico", "lareira", "tatara-lisp", "wasm"],
);
}
#[test]
fn lareira_chart_keywords_stays_btreeset_canonical_ordered() {
// Substrate-side ordering pin: the array is
// `BTreeSet`-canonical ascii-alphabetical, so its declared
// order matches the shape the emitted `Chart.yaml`
// `keywords:` sequence carries after
// `caixa-helm::build_chart_yaml`'s intermediate
// `BTreeSet<String>` fold — a future substrate-fixed keyword
// addition that lands out-of-order (an `"opentelemetry"` entry
// dropped before `"tatara-lisp"`, an `"lunatic"` entry dropped
// after `"wasm"`) trips this pin at caixa-core build time
// rather than surfacing as a byte-shape drift between the
// array's declared order and the emitted `keywords:` sequence
// order at chart render time downstream.
let mut sorted: Vec<&str> = LAREIRA_CHART_KEYWORDS.to_vec();
sorted.sort_unstable();
assert_eq!(LAREIRA_CHART_KEYWORDS, sorted.as_slice());
}
#[test]
fn lareira_chart_keywords_each_entry_passes_is_chart_keyword_shape() {
// Substrate-side shape-invariant pin: every substrate-fixed
// chart-keyword entry must satisfy the per-`Chart.yaml`
// `keywords:` entry validation predicate the substrate
// enforces on the author-side `:etiquetas` axis — a future
// substrate-fixed keyword addition that happens to break the
// shape rule (a leading digit, an uppercase letter, a byte
// over the `CHART_KEYWORD_MAX_LEN` cap, an ASCII whitespace,
// a Unicode-invisible-format code point) trips this pin at
// caixa-core build time rather than surfacing at
// `helm lint` time on the rendered chart downstream.
for keyword in LAREIRA_CHART_KEYWORDS {
is_chart_keyword_shape(keyword).unwrap_or_else(|e| {
panic!(
"substrate-fixed chart keyword {keyword:?} must pass \
is_chart_keyword_shape: {e:?}"
)
});
}
}
#[test]
fn chart_keyword_shape_rejects_each_arm_with_substring_pinned_reason() {
// Substrate-side diagnostic-shape pin: each arm surfaces its
// own distinct reason substring. Pinned here so a future
// reason-wording rephrase that drops any of these substrings
// surfaces at this one place, not piecemeal across every
// per-axis test sweep. Mirrors
// `chart_maintainer_name_shape_rejects_each_arm_with_substring_pinned_reason`
// on the peer predicate.
for (s, needle) in [
// Leading whitespace — paste-from-aligned-doc.
(" mesh", "whitespace"),
// Leading hyphen — kebab-leak footgun.
("-foo", "`-`"),
// Leading underscore — snake-leak footgun.
("_foo", "`_`"),
// Leading digit — paste-from-numbered-list footgun.
("1foo", "digit"),
// Embedded whitespace — multi-tag-blob footgun.
("web service", "whitespace"),
// Tab inside — tab-from-aligned-doc.
("mesh\thttp", "whitespace"),
// Newline — paste-from-multiline-doc.
("mesh\nhttp", "newline"),
// Carriage return — paste-from-Windows-CRLF-doc.
("mesh\rhttp", "carriage return"),
// Comma — CSV-list-separator confusion.
("mesh,http", "`,`"),
// Slash — path-separator confusion.
("caixa/servico", "`/`"),
// Semicolon — alt-list-separator confusion.
("mesh;http", "`;`"),
// Period — namespace / version-suffix confusion.
("http.1", "`.`"),
// NUL byte — paste-from-binary-blob.
("mesh\x00http", "control character"),
// DEL byte (0x7F).
("mesh\x7fhttp", "control character"),
// Non-ASCII inside.
("café", "non-ASCII"),
// Non-ASCII leading.
("éclair", "non-ASCII"),
] {
let err = is_chart_keyword_shape(s)
.err()
.unwrap_or_else(|| panic!("chart keyword {s:?} must be rejected"));
assert!(
err.contains(needle),
"chart keyword {s:?} reason must contain {needle:?}; got {err:?}"
);
}
}
#[test]
fn chart_keyword_shape_rejects_empty_defensively() {
// The predicate is called from `crate::Caixa::validate_etiquetas`
// only after the per-axis `EtiquetaEmpty` arm has fired at
// validate time; re-checking here keeps the predicate usable
// from any future call site without an empty-precondition
// footgun. Same defensive empty-check `is_dns_1123_label`,
// `is_gateway_api_http_path`, `is_wit_world_ref`,
// `is_nats_subject`, `is_wasi_keyvalue_slot`,
// `is_git_ref_name`, `is_git_oid`, `is_git_repo_url`,
// `is_cargo_feature_name`, `is_spdx_expression_shape`,
// `is_chart_description_shape`, and
// `is_chart_maintainer_name_shape` carry at their call sites.
let err = is_chart_keyword_shape("").unwrap_err();
assert!(err.contains("empty"), "got: {err:?}");
}
#[test]
fn chart_keyword_shape_rejects_at_21_byte_boundary() {
// The 20-byte cap pin — both the boundary-exceeding case and
// the boundary-accepting case in one place, so a future cap
// shift surfaces both arms simultaneously, mirroring the peer
// cap-boundary pins
// (`chart_maintainer_name_shape_rejects_at_129_byte_boundary`
// on the 128-byte sibling,
// `chart_description_shape_rejects_at_513_byte_boundary` on
// the 512-byte sibling). Constructed as a single all-`a`
// token so only the cap arm fires (20 `a` bytes is alphabet-
// valid).
let max_ok = "a".repeat(CHART_KEYWORD_MAX_LEN);
assert_eq!(max_ok.len(), 20);
is_chart_keyword_shape(&max_ok).unwrap();
let too_long = "a".repeat(CHART_KEYWORD_MAX_LEN + 1);
assert_eq!(too_long.len(), 21);
let err = is_chart_keyword_shape(&too_long).unwrap_err();
assert!(err.contains("20"), "got: {err:?}");
assert!(err.contains("21"), "got: {err:?}");
}
// ── shared predicate: find_ascii_whitespace_byte ──────────────────
//
// Pins the accepted / rejected set of the lifted ASCII byte-scan
// every typed-magnitude codec in caixa-core calls (`parse_byte_size`
// / `parse_duration` / `parse_millicores` / shared
// `duration_codec` / `rate_limit_codec`). Peer of the non-ASCII
// `find_non_ascii_whitespace_char` predicate below — together they
// partition the full Unicode `White_Space` axis.
#[test]
fn find_ascii_whitespace_byte_accepts_whitespace_free_strings() {
// Complement-side pin: every whitespace-free canonical form
// the renderers emit returns `None`.
assert!(find_ascii_whitespace_byte("64MiB").is_none());
assert!(find_ascii_whitespace_byte("30s").is_none());
assert!(find_ascii_whitespace_byte("500m").is_none());
assert!(find_ascii_whitespace_byte("100/s").is_none());
assert!(find_ascii_whitespace_byte("").is_none());
assert!(find_ascii_whitespace_byte("abcdef0123-_").is_none());
// Non-whitespace ASCII bytes near the whitespace range stay
// accepted (the predicate must not over-fire on peer control
// bytes like VT `0x0B` which POSIX admits but WhatWG excludes).
assert!(find_ascii_whitespace_byte("\u{0B}64MiB").is_none());
}
#[test]
fn find_ascii_whitespace_byte_flags_space() {
// Space (`0x20`) — the canonical paste-from-shell-history /
// paste-from-aligned-doc drift class.
assert_eq!(find_ascii_whitespace_byte(" 64MiB"), Some(0x20));
assert_eq!(find_ascii_whitespace_byte("30s "), Some(0x20));
assert_eq!(find_ascii_whitespace_byte("100 /s"), Some(0x20));
}
#[test]
fn find_ascii_whitespace_byte_flags_tab_lf_ff_cr() {
// Tab (`0x09`), LF (`0x0A`), FF (`0x0C`), CR (`0x0D`) —
// the remaining four bytes in the WhatWG ASCII whitespace
// set the predicate covers, verbatim.
assert_eq!(find_ascii_whitespace_byte("\t500m"), Some(0x09));
assert_eq!(find_ascii_whitespace_byte("30s\n"), Some(0x0A));
assert_eq!(find_ascii_whitespace_byte("\x0c64MiB"), Some(0x0C));
assert_eq!(find_ascii_whitespace_byte("100/s\r"), Some(0x0D));
}
#[test]
fn find_ascii_whitespace_byte_returns_first_match_byte_order() {
// The predicate returns the *first* offending byte in scan
// order — pinning this so a self-locating codec diagnostic can
// report "position 0" / "position N" verbatim without the
// predicate ever reordering matches.
assert_eq!(find_ascii_whitespace_byte(" \t30s"), Some(0x20));
assert_eq!(find_ascii_whitespace_byte("\t 30s"), Some(0x09));
}
#[test]
fn find_ascii_whitespace_byte_does_not_flag_non_ascii_whitespace() {
// NBSP (`\u{00A0}`), LINE SEPARATOR (`\u{2028}`), IDEOGRAPHIC
// SPACE (`\u{3000}`) — none of their UTF-8 bytes match
// `u8::is_ascii_whitespace` (NBSP's `0xC2 0xA0`, LINE
// SEPARATOR's `0xE2 0x80 0xA8`, IDEOGRAPHIC SPACE's `0xE3
// 0x80 0x80` all sit above `0x7F` or well outside the
// {`0x09`, `0x0A`, `0x0C`, `0x0D`, `0x20`} set). Pinning this
// exclusion so the peer `find_non_ascii_whitespace_char`
// predicate remains strictly complementary — the two together
// partition the full Unicode `White_Space` axis with zero
// overlap.
assert!(find_ascii_whitespace_byte("\u{00A0}64MiB").is_none());
assert!(find_ascii_whitespace_byte("30s\u{2028}").is_none());
assert!(find_ascii_whitespace_byte("64MiB\u{3000}").is_none());
}
// ── shared predicate: find_non_ascii_whitespace_char ──────────────────
//
// Pins the accepted / rejected set of the lifted predicate every
// typed-magnitude codec in caixa-core calls (byte-size / duration /
// shared duration / rate-limit). The predicate's job is exclusively
// to name the strictly-complementary drift class the peer
// `u8::is_ascii_whitespace` byte-scan cannot see — the non-ASCII
// Unicode `White_Space` subset that `str::trim` silently swallows.
#[test]
fn find_non_ascii_whitespace_char_accepts_ascii_only_strings() {
// Complement-side pin: every ASCII-only string (canonical form
// and ASCII whitespace alike) returns `None`. The predicate is
// strictly complementary to the per-codec ASCII byte-scan; it
// must not shadow its coverage.
assert!(find_non_ascii_whitespace_char("64MiB").is_none());
assert!(find_non_ascii_whitespace_char("30s").is_none());
assert!(find_non_ascii_whitespace_char("100/s").is_none());
assert!(find_non_ascii_whitespace_char(" \t\n").is_none());
assert!(find_non_ascii_whitespace_char("").is_none());
// Non-whitespace ASCII byte peers stay accepted too.
assert!(find_non_ascii_whitespace_char("abcdef0123-_").is_none());
}
#[test]
fn find_non_ascii_whitespace_char_flags_nbsp() {
// `\u{00A0}` NBSP — the canonical paste-from-typography /
// paste-from-word-processor drift class.
assert_eq!(
find_non_ascii_whitespace_char("64\u{00A0}MiB"),
Some('\u{00A0}')
);
assert_eq!(find_non_ascii_whitespace_char("\u{00A0}"), Some('\u{00A0}'));
}
#[test]
fn find_non_ascii_whitespace_char_flags_line_and_paragraph_separators() {
// LINE SEPARATOR (`\u{2028}`) / PARAGRAPH SEPARATOR
// (`\u{2029}`) — the paste-from-web-doc drift class every
// RTF/HTML → plain-text conversion emits at soft-wrap
// boundaries.
assert_eq!(
find_non_ascii_whitespace_char("30s\u{2028}"),
Some('\u{2028}')
);
assert_eq!(
find_non_ascii_whitespace_char("30s\u{2029}"),
Some('\u{2029}')
);
}
#[test]
fn find_non_ascii_whitespace_char_flags_ideographic_space() {
// IDEOGRAPHIC SPACE (`\u{3000}`) — the CJK-typography drift
// class every full-width IME auto-widens ASCII space to on
// Japanese / Chinese input methods.
assert_eq!(
find_non_ascii_whitespace_char("64MiB\u{3000}"),
Some('\u{3000}')
);
}
#[test]
fn find_non_ascii_whitespace_char_does_not_flag_zwsp_or_bom() {
// BOM (`\u{FEFF}`, ZERO WIDTH NO-BREAK SPACE) and ZWSP
// (`\u{200B}`, ZERO WIDTH SPACE) — both have
// `char::is_whitespace() == false` per the Unicode
// `White_Space` property, so `str::trim` does *not* strip
// either. Both currently land on the downstream
// `BadByteMagnitude` / `BadDurationMagnitude` arm at parse time
// with the byte-shape diagnostic intact; the render-determinism
// contract is unbroken on those inputs today. This test pins
// the predicate's exclusion so a future widening that starts
// flagging BOM / ZWSP here surfaces as a test failure rather
// than a silent over-fire on a class the downstream arm
// already closes.
assert!(find_non_ascii_whitespace_char("\u{FEFF}64MiB").is_none());
assert!(find_non_ascii_whitespace_char("\u{200B}30s").is_none());
}
// ── shared predicate: is_leading_zero_padded_magnitude ──────────────
//
// Pins the accepted / rejected set of the lifted leading-zero
// predicate every typed-magnitude codec in caixa-core calls
// (`parse_byte_size` / `parse_duration` / `parse_millicores` /
// shared `duration_codec` / `rate_limit_codec`). Same lifted-
// source-of-truth discipline the peer whitespace predicates
// (`find_ascii_whitespace_byte` / `find_non_ascii_whitespace_char`)
// carry — drift between any two codec sites' rejection set becomes
// a single-edit fix at this predicate.
#[test]
fn is_leading_zero_padded_magnitude_accepts_canonical_forms() {
// Complement-side pin: every canonical form the typed-magnitude
// `render_*` canonicalizers emit — the single-byte `"0"` case
// and every non-leading-zero magnitude — returns `false`.
assert!(!is_leading_zero_padded_magnitude("0"));
assert!(!is_leading_zero_padded_magnitude("1"));
assert!(!is_leading_zero_padded_magnitude("64"));
assert!(!is_leading_zero_padded_magnitude("500"));
assert!(!is_leading_zero_padded_magnitude("1024"));
assert!(!is_leading_zero_padded_magnitude("999999"));
// Empty magnitude is not a leading-zero shape either — the
// upstream `digit_only` gate at each codec site refuses empty
// magnitudes on its own arm before this predicate is consulted.
assert!(!is_leading_zero_padded_magnitude(""));
// Non-digit-only bodies are outside the predicate's scope — the
// upstream `digit_only` gate refuses them with its own
// `NonInteger*` / `Bad*` diagnostic; this predicate is invoked
// only after that gate accepts.
assert!(!is_leading_zero_padded_magnitude("a"));
assert!(!is_leading_zero_padded_magnitude("1.5"));
}
#[test]
fn is_leading_zero_padded_magnitude_flags_two_byte_leading_zero() {
// The minimal leading-zero drift shape: two-byte magnitude
// starting with `'0'` — `"00"` / `"01"` / `"09"`. Every one
// round-trips through the peer codecs' `render_*` to the
// leading-zero-stripped form (`"0"` / `"1"` / `"9"`).
assert!(is_leading_zero_padded_magnitude("00"));
assert!(is_leading_zero_padded_magnitude("01"));
assert!(is_leading_zero_padded_magnitude("09"));
}
#[test]
fn is_leading_zero_padded_magnitude_flags_multi_byte_leading_zero() {
// The canonical paste-from-fixed-width-alignment /
// paste-from-columnar-report drift class each codec's
// `render_*` emits the stripped form for: `"0064"` (byte-size
// magnitude), `"030"` (duration magnitude), `"0500"`
// (millicores magnitude), `"0100"` (rate-limit magnitude),
// `"01024"` (multi-digit byte-size magnitude).
assert!(is_leading_zero_padded_magnitude("0064"));
assert!(is_leading_zero_padded_magnitude("030"));
assert!(is_leading_zero_padded_magnitude("0500"));
assert!(is_leading_zero_padded_magnitude("0100"));
assert!(is_leading_zero_padded_magnitude("01024"));
// All-zeros multi-byte magnitude — `"000"` / `"0000"` — every
// one round-trips to `"0"`. The single-byte `"0"` case is the
// canonical zero and stays accepted; the multi-byte all-zero
// shape is leading-zero drift.
assert!(is_leading_zero_padded_magnitude("000"));
assert!(is_leading_zero_padded_magnitude("0000"));
}
#[test]
fn is_leading_zero_padded_magnitude_pins_single_zero_boundary() {
// The single-byte magnitude `"0"` is the canonical zero the
// peer codecs' `render_*` canonicalizers emit for the zero
// value verbatim (`render_byte_size(0)` = `"0"`,
// `render_duration(Duration::ZERO)` = `"0s"` with `"0"` as
// the magnitude, `render_millicores(0)` = `"0m"` with `"0"`
// as the magnitude, `RateLimit::render` for rate=0 = `"0/s"`
// with `"0"` as the magnitude). Pinning this boundary so a
// future widening that starts flagging the single-byte `"0"`
// here surfaces as a test failure rather than a silent break
// of the codec-layer / typed-validate-layer partition — the
// semantic-zero gates at the typed-validate layer above
// (`LimitsError::MemoryZero`, `LimitsError::WallClockZero`,
// `LimitsError::CpuZero`, `SupervisorError::ZeroRestartWindow`,
// `AplicacaoError::PolicyTimeoutZero` /
// `PolicyCircuitBreakerWindowZero` / `PolicyRateLimitZero`)
// are what refuse zero-magnitude authoring, not this codec-
// layer predicate.
assert!(!is_leading_zero_padded_magnitude("0"));
}
// ── shared predicate: is_digit_only_magnitude ───────────────────────
//
// Pins the accepted / rejected set of the lifted digit-only
// predicate every typed-magnitude codec in caixa-core calls
// (`parse_byte_size` / `parse_duration` / `parse_millicores` /
// shared `duration_codec` / `rate_limit_codec`). Same lifted-
// source-of-truth discipline the peer canonical-form predicates
// (`find_ascii_whitespace_byte` / `find_non_ascii_whitespace_char`
// / `is_leading_zero_padded_magnitude`) carry — drift between any
// two codec sites' rejection set becomes a single-edit fix at
// this predicate.
#[test]
fn is_digit_only_magnitude_accepts_canonical_forms() {
// Complement-side pin: every canonical form the typed-magnitude
// `render_*` canonicalizers emit — the single-byte `"0"` case
// and every non-zero non-leading-zero magnitude — returns
// `true`.
assert!(is_digit_only_magnitude("0"));
assert!(is_digit_only_magnitude("1"));
assert!(is_digit_only_magnitude("64"));
assert!(is_digit_only_magnitude("500"));
assert!(is_digit_only_magnitude("1024"));
assert!(is_digit_only_magnitude("999999"));
}
#[test]
fn is_digit_only_magnitude_flags_empty_magnitude() {
// Defense-in-depth: the empty string is non-digit-only per the
// predicate's contract, so a future codec reaching for this
// predicate before landing its own upstream empty-magnitude
// arm still routes empty input to the non-canonical branch
// rather than silently accepting it via the vacuous
// `bytes().all(_)` truth on the empty byte-slice.
assert!(!is_digit_only_magnitude(""));
}
#[test]
fn is_digit_only_magnitude_flags_leading_sign() {
// The paste-from-signed-report drift class every codec's
// `render_*` emits the unsigned form for. On current Rust
// `u64::from_str` / `u32::from_str` permissively accept a
// leading `+` (`"+500"` → 500), so `"+30"`, `"+500"`, `"+100"`
// survive the parser and round-trip through `render_*` to the
// sign-stripped form (`"30"`, `"500"`, `"100"`) — a *different*
// canonical string on the next emit, breaking the THEORY.md
// Part V render-determinism contract. The digit-only gate is
// what closes the leading-sign class at each codec site.
assert!(!is_digit_only_magnitude("+30"));
assert!(!is_digit_only_magnitude("+500"));
assert!(!is_digit_only_magnitude("+100"));
assert!(!is_digit_only_magnitude("-30"));
assert!(!is_digit_only_magnitude("-1"));
}
#[test]
fn is_digit_only_magnitude_flags_fractional_and_decimal() {
// The paste-from-floating-point-source drift class every
// codec's `render_*` emits the integer form for. On the peer
// duration codec the parser accepts `f64`-shaped magnitudes
// (`"1.5s"` → 1500ms → `"1500ms"`, `"1.0s"` → 1s → `"1s"`,
// `"0.5m"` → 30s → `"30s"`) — a *different* canonical string
// on the next emit, breaking the THEORY.md Part V render-
// determinism contract. The digit-only gate closes the
// decimal-point / fractional / exponent class at each codec
// site.
assert!(!is_digit_only_magnitude("1.5"));
assert!(!is_digit_only_magnitude("1.0"));
assert!(!is_digit_only_magnitude("0.5"));
assert!(!is_digit_only_magnitude("1e3"));
assert!(!is_digit_only_magnitude(".5"));
assert!(!is_digit_only_magnitude("5."));
}
#[test]
fn is_digit_only_magnitude_flags_alphabetic_and_symbol_bytes() {
// Complement-side pin on the "garbage" branch: alphabetic
// bytes / symbol bytes / whitespace bytes each land on the
// non-digit-only side. At the codec site the downstream
// "non-canonical-but-numeric vs garbage" partition surfaces
// these with the narrower `Bad*` diagnostic; here the
// predicate simply reports `false`.
assert!(!is_digit_only_magnitude("a"));
assert!(!is_digit_only_magnitude("64a"));
assert!(!is_digit_only_magnitude("6_4"));
assert!(!is_digit_only_magnitude("64 "));
assert!(!is_digit_only_magnitude(" 64"));
}
#[test]
fn is_digit_only_magnitude_pins_leading_zero_boundary() {
// The leading-zero-padded magnitude shape stays inside the
// digit-only accepted set at this predicate — every byte is
// an ASCII digit. The peer
// [`is_leading_zero_padded_magnitude`] predicate closes the
// leading-zero drift class on a separate, strictly-later arm
// at each codec site. Pinning this partition so a future
// widening that collapses the two arms surfaces as a test
// failure rather than a silent break of the two-predicate
// codec-layer discipline.
assert!(is_digit_only_magnitude("00"));
assert!(is_digit_only_magnitude("0064"));
assert!(is_digit_only_magnitude("0500"));
}
// ── require_positive_bounded_{u32,u64} ──────────────────────────────
#[derive(Debug, PartialEq, Eq)]
enum TestErr {
Zero,
Cap(u64),
}
#[test]
fn require_positive_bounded_u32_accepts_in_range() {
assert_eq!(
require_positive_bounded_u32::<TestErr>(
1,
10,
|| TestErr::Zero,
|v| TestErr::Cap(u64::from(v))
),
Ok(())
);
assert_eq!(
require_positive_bounded_u32::<TestErr>(
10,
10,
|| TestErr::Zero,
|v| TestErr::Cap(u64::from(v))
),
Ok(())
);
assert_eq!(
require_positive_bounded_u32::<TestErr>(
5,
10,
|| TestErr::Zero,
|v| TestErr::Cap(u64::from(v))
),
Ok(())
);
}
#[test]
fn require_positive_bounded_u32_rejects_zero_with_self_locating_diagnostic() {
// The zero-floor arm strictly precedes the cap arm — a value of
// 0 surfaces the `on_zero` callback's discriminator (which every
// per-axis error variant documents an omit-axis remediation for),
// never the `on_cap_exceeded` callback (which would misframe
// "0 > cap == false" as an above-cap value).
assert_eq!(
require_positive_bounded_u32::<TestErr>(
0,
10,
|| TestErr::Zero,
|v| TestErr::Cap(u64::from(v))
),
Err(TestErr::Zero)
);
// Pin the ordering under the degenerate cap == 0 boundary: even
// when the cap itself is 0 (never valid for a positive-bounded
// axis in production, but pins the ordering contract), 0 routes
// through the zero arm — not the cap arm.
assert_eq!(
require_positive_bounded_u32::<TestErr>(
0,
0,
|| TestErr::Zero,
|v| TestErr::Cap(u64::from(v))
),
Err(TestErr::Zero)
);
}
#[test]
fn require_positive_bounded_u32_rejects_above_cap_with_value_threaded() {
assert_eq!(
require_positive_bounded_u32::<TestErr>(
11,
10,
|| TestErr::Zero,
|v| TestErr::Cap(u64::from(v))
),
Err(TestErr::Cap(11))
);
assert_eq!(
require_positive_bounded_u32::<TestErr>(
u32::MAX,
10,
|| TestErr::Zero,
|v| TestErr::Cap(u64::from(v))
),
Err(TestErr::Cap(u64::from(u32::MAX)))
);
}
#[test]
fn require_positive_bounded_u64_accepts_in_range() {
assert_eq!(
require_positive_bounded_u64::<TestErr>(1, 10, || TestErr::Zero, TestErr::Cap),
Ok(())
);
assert_eq!(
require_positive_bounded_u64::<TestErr>(10, 10, || TestErr::Zero, TestErr::Cap),
Ok(())
);
}
#[test]
fn require_positive_bounded_u64_rejects_zero_and_above_cap() {
assert_eq!(
require_positive_bounded_u64::<TestErr>(0, 10, || TestErr::Zero, TestErr::Cap),
Err(TestErr::Zero)
);
assert_eq!(
require_positive_bounded_u64::<TestErr>(11, 10, || TestErr::Zero, TestErr::Cap),
Err(TestErr::Cap(11))
);
assert_eq!(
require_positive_bounded_u64::<TestErr>(u64::MAX, 10, || TestErr::Zero, TestErr::Cap),
Err(TestErr::Cap(u64::MAX))
);
}
// ── require_positive_quantum_multiple_bounded_u64 ────────────────────
#[derive(Debug, PartialEq, Eq)]
enum QuantumTestErr {
Zero,
BelowQuantum(u64),
Cap(u64),
NotMultiple(u64),
}
fn q_gate(value: u64, quantum: u64, cap: u64) -> Result<(), QuantumTestErr> {
require_positive_quantum_multiple_bounded_u64(
value,
quantum,
cap,
|| QuantumTestErr::Zero,
QuantumTestErr::BelowQuantum,
QuantumTestErr::Cap,
QuantumTestErr::NotMultiple,
)
}
#[test]
fn require_positive_quantum_multiple_bounded_u64_accepts_in_range_multiples() {
// Every canonical quantum-multiple in `quantum..=cap` — the shared
// accepted set every quantized-byte-cap consumer inherits — must
// pass the gate. Pin the accepted set here so a future tightening
// surfaces as a test failure rather than a silent narrowing at
// the single consumer site (`:limits :memory`).
let quantum = 64 * 1024;
let cap = 4 * 1024 * 1024 * 1024;
for value in [quantum, quantum * 2, quantum * 100, quantum * 1000, cap] {
assert_eq!(
q_gate(value, quantum, cap),
Ok(()),
"quantum-multiple in-range value {value} must pass the gate",
);
}
}
#[test]
fn require_positive_quantum_multiple_bounded_u64_rejects_zero_before_other_arms() {
// The zero-floor arm strictly precedes the below-quantum, cap,
// and not-multiple arms — a value of 0 surfaces the
// caller's self-locating `on_zero` diagnostic (every per-axis
// error variant documents an "omit the axis to express no-bound"
// remediation for) rather than the misleading below-quantum arm
// (which would also fire because 0 < quantum) or the not-multiple
// arm (which the modulus check `0 % quantum == 0` would silently
// accept).
let quantum = 64 * 1024;
let cap = 4 * 1024 * 1024 * 1024;
assert_eq!(q_gate(0, quantum, cap), Err(QuantumTestErr::Zero));
// The degenerate `cap == 0` / `quantum == 1` boundaries: 0 still
// routes through the zero arm — the ordering contract holds even
// when the cap or quantum themselves take the degenerate shape
// (never valid production shapes for a positive-bounded quantized
// axis, but pin the arm ordering).
assert_eq!(q_gate(0, 1, 0), Err(QuantumTestErr::Zero));
assert_eq!(q_gate(0, quantum, 0), Err(QuantumTestErr::Zero));
}
#[test]
fn require_positive_quantum_multiple_bounded_u64_rejects_below_quantum_before_cap_and_multiple()
{
// The below-quantum arm strictly precedes the cap and
// not-multiple arms — a sub-quantum non-zero value (which is
// ALSO not a quantum-multiple by construction, since the
// smallest positive quantum-multiple *is* `quantum`) surfaces
// the more actionable "raise to at least one quantum" diagnostic
// rather than the not-multiple no-op. Pin the ordering across
// the value grid — every value in `1..quantum` must fire the
// below-quantum arm with the offending byte count threaded
// through the callback.
let quantum = 64 * 1024;
let cap = 4 * 1024 * 1024 * 1024;
for value in [1u64, 2, 32 * 1024, quantum - 1] {
assert_eq!(
q_gate(value, quantum, cap),
Err(QuantumTestErr::BelowQuantum(value)),
"sub-quantum {value} must surface BelowQuantum before Cap / NotMultiple",
);
}
}
#[test]
fn require_positive_quantum_multiple_bounded_u64_rejects_above_cap_before_not_multiple() {
// The cap arm strictly precedes the not-multiple arm — a value
// that is *both* above-cap and sub-quantum-residue must surface
// the more aggressive cap-shape diagnostic first (the
// not-multiple remediation would be misleading when the
// offending value exceeds the upper bracket anyway; the
// canonical fix collapses both into "pin a quantum-aligned
// value ≤ cap"). Pin the ordering across the value grid,
// including the boundary case `cap + 1`.
let quantum = 64 * 1024;
let cap = 4 * 1024 * 1024 * 1024;
for value in [
cap + 1, // above-cap AND sub-quantum-residue
cap + quantum, // above-cap and quantum-aligned
cap + quantum * 100, // well above-cap and quantum-aligned
u64::MAX, // maximally above-cap
] {
assert_eq!(
q_gate(value, quantum, cap),
Err(QuantumTestErr::Cap(value)),
"above-cap {value} must surface Cap before NotMultiple",
);
}
}
#[test]
fn require_positive_quantum_multiple_bounded_u64_rejects_not_multiple_with_value_threaded() {
// The not-multiple arm surfaces the offending value verbatim so
// the caller's `on_not_quantum_multiple` variant threads it into
// its discriminator field (`bytes:`). Pin the arm across the
// in-range-but-not-aligned value grid — every value in
// `quantum..=cap` carrying a sub-quantum residue must fire the
// not-multiple arm.
let quantum = 64 * 1024;
let cap = 4 * 1024 * 1024 * 1024;
for value in [
quantum + 1, // one page plus a 1-byte residue
quantum * 2 - 1, // two pages minus one byte
100_000, // ≈ 97.65 KiB — one page + 34_464-byte residue
quantum * 100 + 7, // 100 pages plus a 7-byte residue
] {
assert_eq!(
q_gate(value, quantum, cap),
Err(QuantumTestErr::NotMultiple(value)),
"sub-quantum-residue {value} must surface NotMultiple",
);
}
}
// ── require_positive_canonical_bounded_duration ─────────────────────
#[derive(Debug, PartialEq, Eq)]
enum DurationTestErr {
Zero,
NotCanonical(Duration),
Cap(Duration),
}
#[test]
fn require_positive_canonical_bounded_duration_accepts_in_range_canonical_values() {
// Every canonical integer-millisecond `Duration` in
// `1ms..=cap` — the shared accepted set every typed-`Duration`
// consumer inherits — must pass the gate. Pin the canonical
// set here so a future tightening surfaces as a test failure
// rather than a silent narrowing at one of the four consumer
// sites (`:politicas :timeout`, `:circuit-breaker :window`,
// `:limits :wall-clock`, `:supervisor :restart-window`).
let cap = Duration::from_secs(3600); // matches the 1h peer caps
for value in [
Duration::from_millis(1),
Duration::from_millis(500),
Duration::from_millis(1500),
Duration::from_secs(30),
Duration::from_secs(60),
cap,
] {
assert_eq!(
require_positive_canonical_bounded_duration::<DurationTestErr>(
value,
cap,
|| DurationTestErr::Zero,
DurationTestErr::NotCanonical,
DurationTestErr::Cap,
),
Ok(()),
"canonical in-range value {value:?} must pass the gate",
);
}
}
#[test]
fn require_positive_canonical_bounded_duration_rejects_zero_before_canonical_and_cap() {
// The zero-floor arm strictly precedes the canonical-form and
// cap arms — `Duration::ZERO` (which has `subsec_nanos() == 0`
// and would pass the canonical-form predicate; and would pass
// the cap arm since 0 ≤ cap) routes through the zero arm so
// the caller's self-locating `on_zero` diagnostic (every
// per-axis error variant documents an omit-axis remediation
// for) is surfaced, not the misleading no-op the two later
// arms would return.
let cap = Duration::from_secs(3600);
assert_eq!(
require_positive_canonical_bounded_duration::<DurationTestErr>(
Duration::ZERO,
cap,
|| DurationTestErr::Zero,
DurationTestErr::NotCanonical,
DurationTestErr::Cap,
),
Err(DurationTestErr::Zero),
);
// The degenerate `cap == Duration::ZERO` boundary: `Duration::ZERO`
// still routes through the zero arm — the ordering contract holds
// even when the cap itself is zero (never a valid production cap
// for a positive-bounded axis, but pins the arm ordering).
assert_eq!(
require_positive_canonical_bounded_duration::<DurationTestErr>(
Duration::ZERO,
Duration::ZERO,
|| DurationTestErr::Zero,
DurationTestErr::NotCanonical,
DurationTestErr::Cap,
),
Err(DurationTestErr::Zero),
);
}
#[test]
fn require_positive_canonical_bounded_duration_rejects_sub_millisecond_before_cap() {
// The canonical-form arm strictly precedes the cap arm — a
// `Duration` that is *both* sub-millisecond and above-cap must
// surface the more fundamental round-trip-shape diagnostic
// first (the cap arm's `1ms..=<cap>` remediation prose would
// be misleading when no integer-ms form of the offending
// value exists). Pin the ordering across the value grid.
let cap = Duration::from_secs(1);
for value in [
Duration::from_micros(1),
Duration::from_micros(500),
Duration::from_micros(1500),
Duration::from_nanos(1),
Duration::from_nanos(999_999),
Duration::from_nanos(1_000_001),
// Sub-millisecond *and* above-cap: canonical-form arm wins.
cap + Duration::from_nanos(1),
] {
let result = require_positive_canonical_bounded_duration::<DurationTestErr>(
value,
cap,
|| DurationTestErr::Zero,
DurationTestErr::NotCanonical,
DurationTestErr::Cap,
);
assert_eq!(
result,
Err(DurationTestErr::NotCanonical(value)),
"sub-millisecond {value:?} must surface NotCanonical before Cap",
);
}
}
#[test]
fn require_positive_canonical_bounded_duration_rejects_above_cap_with_value_threaded() {
// The cap arm surfaces the offending value verbatim so the
// caller's `on_cap_exceeded` variant threads it into its
// discriminator field (`timeout` / `window` / `wall_clock`).
// The value grid covers the canonical `<n>ms` / `<n>s`
// integer-millisecond shape past the 1h cap so the arm ordering
// (canonical-form first) doesn't intercept these values.
let cap = Duration::from_secs(3600);
for value in [
cap + Duration::from_millis(1),
cap + Duration::from_secs(1),
Duration::from_secs(24 * 3600), // 24h — canonical string
Duration::from_secs(7 * 24 * 3600), // 7d
] {
assert_eq!(
require_positive_canonical_bounded_duration::<DurationTestErr>(
value,
cap,
|| DurationTestErr::Zero,
DurationTestErr::NotCanonical,
DurationTestErr::Cap,
),
Err(DurationTestErr::Cap(value)),
"above-cap canonical value {value:?} must thread through the cap arm",
);
}
}
// ── require_valid_versao_requirement ────────────────────────────────
#[derive(Debug, PartialEq, Eq)]
enum VersaoTestErr {
Empty,
Invalid(String),
}
#[test]
fn require_valid_versao_requirement_accepts_canonical_forms() {
// Every Cargo-shaped requirement string the substrate accepts on
// any `:versao` axis (`:deps`, `:membros`, `:children`) must pass
// the shared gate — pin the canonical set here so a future
// tightening surfaces as a test failure rather than a silent
// narrowing at one of the three consumer sites. Same accepted set
// as `accepts_canonical_membro_versao_forms` /
// `accepts_canonical_dep_versao_forms` on the sibling per-axis
// pins.
for form in [
"^0.1", // caret — minor-range pin (the most common shape)
"~0.1.2", // tilde — patch-range pin
"0.1.0", // exact — single-version pin
"*", // wildcard — explicitly any-version (VersionReq::STAR)
">=0.1, <2", // multi-range — comma-separated comparators
] {
assert_eq!(
require_valid_versao_requirement::<VersaoTestErr>(
form,
|| VersaoTestErr::Empty,
VersaoTestErr::Invalid,
),
Ok(()),
"canonical form {form:?} must pass the gate",
);
}
}
#[test]
fn require_valid_versao_requirement_rejects_empty_before_parse() {
// The empty-first arm strictly precedes the parse arm. Without
// this arm the parser silently widens `""` to
// `VersionReq { comparators: [] }` (semantically `*`) — a
// "silent widening" footgun the three consumer sites each
// documented in their `MembroVersaoEmpty` / `EmptyChildVersion` /
// `VersaoEmpty` variants and now inherit by construction.
assert_eq!(
require_valid_versao_requirement::<VersaoTestErr>(
"",
|| VersaoTestErr::Empty,
VersaoTestErr::Invalid,
),
Err(VersaoTestErr::Empty),
);
}
#[test]
fn require_valid_versao_requirement_rejects_malformed_with_reason_threaded() {
// The canonical malformed-shape set the three consumer sites
// formerly each re-tested inline. The gate threads the
// parser's `to_string()` output through as the invalid arm's
// `reason:` verbatim — the field the three sibling error
// variants (`{Dep,Membro,Child}VersaoInvalid.reason`) each
// carry to the author's remediation prose.
for bad in [
"^^0.1", // doubled-caret typo
"v0.1", // git-tag-shape leaking into requirement slot
"abc", // gibberish
"~~", // stacked-operator gibberish
] {
let result = require_valid_versao_requirement::<VersaoTestErr>(
bad,
|| VersaoTestErr::Empty,
VersaoTestErr::Invalid,
);
match result {
Err(VersaoTestErr::Invalid(reason)) => {
assert!(
!reason.is_empty(),
"invalid arm must thread a non-empty reason for {bad:?}",
);
}
other => panic!("expected Invalid for {bad:?}, got {other:?}"),
}
}
}
// ── require_valid_dns_1123_label ────────────────────────────────────
#[derive(Debug, PartialEq, Eq)]
enum LabelTestErr {
Empty,
Invalid(String),
}
#[test]
fn require_valid_dns_1123_label_accepts_canonical_forms() {
// Every DNS-1123-label-shaped Servico-name reference the substrate
// accepts on any name axis (`:membros :caixa`, `:placement :clusters`,
// `:placement :affinity`, `:contratos :de`/`:para`, `:entrada :para`,
// `:children :caixa`, `:nome`, `:upgrade-from :module`) must pass
// the shared gate — pin the canonical set here so a future
// tightening surfaces as a test failure rather than a silent
// narrowing at one of the eight consumer sites. Same accepted set
// as the sibling per-axis DNS-1123-label pins already carry.
for form in [
"hello-rio", // canonical dashed
"cart", // single-token
"rio-1", // trailing digit
"1-rio", // leading digit
"a", // one byte
&"a".repeat(DNS_1123_LABEL_MAX_LEN), // max length exact
] {
assert_eq!(
require_valid_dns_1123_label::<LabelTestErr>(
form,
|| LabelTestErr::Empty,
LabelTestErr::Invalid,
),
Ok(()),
"canonical form {form:?} must pass the gate",
);
}
}
#[test]
fn require_valid_dns_1123_label_rejects_empty_before_shape() {
// The empty-first arm strictly precedes the shape arm so a
// literal `""` surfaces each per-axis error variant's narrower
// self-locating `_Empty` diagnostic rather than the shared
// predicate's generic "must not be empty" prose the shape arm
// would thread through — the same "misframed generic diagnostic"
// footgun the peer [`require_valid_versao_requirement`] closes
// on its empty arm. The eight consumer sites each documented
// this ordering in their `MembroCaixaEmpty` / `PlacementClusterEmpty`
// / `PlacementAffinityEmpty` / `ContratoCaixaEmpty` /
// `EntradaParaEmpty` / `NomeEmpty` / `EmptyChildName` /
// `ModuleEmpty` variants and now inherit it by construction.
assert_eq!(
require_valid_dns_1123_label::<LabelTestErr>(
"",
|| LabelTestErr::Empty,
LabelTestErr::Invalid,
),
Err(LabelTestErr::Empty),
);
}
#[test]
fn require_valid_dns_1123_label_rejects_malformed_with_reason_threaded() {
// The canonical malformed-shape set the eight consumer sites
// formerly each re-tested inline. The gate threads the
// predicate's shape-shaped reason through as the invalid arm's
// `reason:` verbatim — the field every sibling error variant
// (`{MembroCaixa,PlacementCluster,PlacementAffinity,ContratoCaixa,
// EntradaPara,Nome,ChildCaixa,Module}Invalid.reason`) each
// carry to the author's remediation prose.
for bad in [
"Rio", // uppercase — the canonical TitleCase-from-an-ADR typo
"my_cart", // underscore — the Python-module-name leak
"team.cart", // dot — the namespace-dot-on-a-label confusion
"-cart", // leading hyphen — boundary violation
"cart-", // trailing hyphen — boundary violation
] {
let result = require_valid_dns_1123_label::<LabelTestErr>(
bad,
|| LabelTestErr::Empty,
LabelTestErr::Invalid,
);
match result {
Err(LabelTestErr::Invalid(reason)) => {
assert!(
!reason.is_empty(),
"invalid arm must thread a non-empty reason for {bad:?}",
);
}
other => panic!("expected Invalid for {bad:?}, got {other:?}"),
}
}
}
// ── require_sandboxed_lisp_path ─────────────────────────────────────
#[derive(Debug, PartialEq, Eq)]
enum LispPathTestErr {
Empty,
Absolute,
ParentEscape,
NonLisp,
}
fn call_require_sandboxed_lisp_path(path: &Path) -> Result<(), LispPathTestErr> {
require_sandboxed_lisp_path(
path,
|| LispPathTestErr::Empty,
|| LispPathTestErr::Absolute,
|| LispPathTestErr::ParentEscape,
|| LispPathTestErr::NonLisp,
)
}
#[test]
fn require_sandboxed_lisp_path_accepts_canonical_forms() {
// Every sandboxed-relative `.lisp`-terminating path the substrate
// accepts on either M2 tatara-lisp source-path axis (`:behavior :on-*`
// callback paths, `:upgrade-from :state-change :script`) must pass
// the shared gate. Pin the canonical set here so a future tightening
// surfaces as a test failure rather than a silent narrowing at one
// of the two consumer sites.
for form in [
"lib/init.lisp", // canonical example
"lib/handlers.lisp", // multi-callback shape
"lib/migrations/v01-to-v02.lisp", // nested-directory shape
"a.lisp", // one-byte stem
"lib/deep/nested/path/to/file.lisp", // deeply nested
] {
assert_eq!(
call_require_sandboxed_lisp_path(Path::new(form)),
Ok(()),
"canonical sandboxed `.lisp` form {form:?} must pass the gate",
);
}
}
#[test]
fn require_sandboxed_lisp_path_rejects_empty_before_all_later_arms() {
// The empty-first arm strictly precedes every downstream arm — a
// literal `""` (which the is_absolute check would return false on,
// which carries no ParentDir component, and whose extension is
// absent) routes through the `on_empty` closure so the caller's
// narrower self-locating `_Empty` / `_EmptyScript` diagnostic fires,
// not a misleading `_Absolute` / `_ParentEscape` / `_NonLisp` miss
// downstream. Peer of every zero-first arm ordering the sibling
// require_positive_bounded_* helpers already carry.
assert_eq!(
call_require_sandboxed_lisp_path(Path::new("")),
Err(LispPathTestErr::Empty),
);
}
#[test]
fn require_sandboxed_lisp_path_rejects_absolute_before_parent_escape_and_non_lisp() {
// The absolute arm strictly precedes the parent-escape and
// non-`.lisp`-extension arms — an absolute path (regardless of
// whether it also carries `..` components or a non-`.lisp`
// extension) routes through the `on_absolute` closure so the
// caller's `_Absolute` / `_AbsoluteScript` diagnostic fires with
// its "must be relative to the caixa root" remediation, not the
// misleading later arms. Pin the ordering across the value grid
// covering "absolute + parent-escape" and "absolute + non-`.lisp`"
// compound-violation shapes so a future arm-reorder silently
// narrowing the accepted set would surface at build time.
for absolute in [
"/etc/passwd", // canonical absolute
"/lib/init.lisp", // absolute + `.lisp` (extension arm never reached)
"/lib/../init.lisp", // absolute + parent-escape (later arm never reached)
"/etc/init.txt", // absolute + non-`.lisp`
] {
assert_eq!(
call_require_sandboxed_lisp_path(Path::new(absolute)),
Err(LispPathTestErr::Absolute),
"absolute path {absolute:?} must route through Absolute arm",
);
}
}
#[test]
fn require_sandboxed_lisp_path_rejects_parent_escape_before_non_lisp() {
// The parent-escape arm strictly precedes the non-`.lisp`-extension
// arm — a relative path carrying any `..` component routes through
// the `on_parent_escape` closure so the caller's `_ParentEscape` /
// `_ParentEscapeScript` diagnostic fires with its "must not
// traverse above the caixa root" remediation, not the misleading
// extension-shape arm. Pin the ordering across leading / mid-path
// / trailing parent-escape positions plus the compound
// "parent-escape + non-`.lisp`" shape.
for escape in [
"../sibling/x.lisp", // leading `..`
"lib/../other.lisp", // mid-path `..`
"lib/handlers/../..", // trailing `..`
"../sibling/x.txt", // parent-escape + non-`.lisp`
] {
assert_eq!(
call_require_sandboxed_lisp_path(Path::new(escape)),
Err(LispPathTestErr::ParentEscape),
"parent-escaping path {escape:?} must route through ParentEscape arm",
);
}
}
#[test]
fn require_sandboxed_lisp_path_rejects_non_lisp_only_after_all_path_shape_arms_accept() {
// The non-`.lisp`-extension arm fires only when every prior arm
// (empty / absolute / parent-escape) accepts the path — a
// sandboxed relative path whose only violation is a non-`.lisp`
// terminating extension routes through the `on_non_lisp` closure
// so the caller's `_NonLispExtension` / `_NonLispExtensionScript`
// diagnostic fires with its `.lisp`-remediation prose. Pin the
// downstream-most-arm reachability across the canonical
// `.txt`/`.rs`/no-extension/double-extension-shadow shape set the
// two consumer sites' error variants each document.
for bad_ext in [
"lib/init.txt", // wrong extension
"lib/init.rs", // Rust source leaked into caixa
"lib/init.lisp.bak", // double-extension shadow
"lib/init", // no extension
"lib/migrations", // no extension, no dot
"lib/init.LISP", // uppercase — case-sensitive gate
] {
assert_eq!(
call_require_sandboxed_lisp_path(Path::new(bad_ext)),
Err(LispPathTestErr::NonLisp),
"non-`.lisp` path {bad_ext:?} must route through NonLisp arm",
);
}
}
#[test]
fn require_sandboxed_lisp_path_ordering_matches_inline_pre_lift_cascade() {
// Byte-for-byte the same `Empty → Absolute → ParentEscape → NonLisp`
// arm-ordering the two consumer sites (`validate_callback_path` in
// `caixa-core::behavior`, `UpgradeInstruction::validate`'s
// `StateChange` arm in `caixa-core::upgrade`) each formerly inlined
// verbatim. This pin catches any future reorder that would
// silently reshape the diagnostic dispatch at either site — the
// helper's ordering IS the two sites' ordering, not a re-derived
// convention. Pins the same
// smallest-scope-arm-fires-last three-path drift-detection
// posture the peer `require_positive_bounded_*` /
// `require_positive_canonical_bounded_duration` helpers already
// carry on their own arm sets.
assert_eq!(
call_require_sandboxed_lisp_path(Path::new("")),
Err(LispPathTestErr::Empty),
);
assert_eq!(
call_require_sandboxed_lisp_path(Path::new("/abs/x.lisp")),
Err(LispPathTestErr::Absolute),
);
assert_eq!(
call_require_sandboxed_lisp_path(Path::new("../x.lisp")),
Err(LispPathTestErr::ParentEscape),
);
assert_eq!(
call_require_sandboxed_lisp_path(Path::new("lib/x.txt")),
Err(LispPathTestErr::NonLisp),
);
assert_eq!(
call_require_sandboxed_lisp_path(Path::new("lib/x.lisp")),
Ok(()),
);
}
#[test]
fn gateway_api_hostname_max_len_pins_canonical_value() {
// Pin the actual byte count so a typo in this lift can't silently
// rebrand the K8s Gateway API v1 `Listener.hostname` /
// `HTTPRoute.spec.hostnames[]` admission-schema `maxLength:` cap
// the `AplicacaoSpec::validate` `:entrada :host` total-length arm
// reads. The value is part of the cluster-side contract with
// every Gateway API v1 CRD schema validator (apiserver-side +
// Cilium / Envoy Gateway / Istio / NGINX per-implementation
// webhooks) — the OpenAPI schema on the Hostname type binds
// `maxLength: 253` verbatim (RFC 1035 / RFC 1123 DNS name limit:
// 255 wire bytes minus the trailing-dot + one length prefix), so
// a drifted value at either the aplicacao-side validator or a
// downstream renderer's per-host validator silently emits a
// Gateway / HTTPRoute the apiserver rejects at admission time
// with an opaque `field is invalid` diagnostic far from the
// caixa.lisp source line. Changing this value is a coordinated
// Gateway API promotion alongside the upstream SIG-Network
// Hostname schema evolution, not an incidental edit. Peer to
// [`GATEWAY_API_HTTP_PATH_MAX_LEN`] (1024) on the sibling
// per-route path-value cap axis — both are apiserver-side
// `maxLength:` bounds on Gateway API v1 landing sites, both lift
// to `caixa-core::render` so the M4 CR materializer's per-axis
// validators (per-host, per-path) read from one place.
assert_eq!(GATEWAY_API_HOSTNAME_MAX_LEN, 253);
}
#[test]
fn gateway_api_hostname_max_len_exceeds_dns_1123_label_max_len() {
// Cross-axis structural invariant: every `.`-separated label in
// a Gateway API v1 Hostname is a DNS-1123 label, so the total
// Hostname cap must strictly exceed the per-label cap — otherwise
// even a single-label host `"foo"` couldn't reach the per-label
// ceiling before hitting the total-length ceiling, and the
// `AplicacaoSpec::validate` `:entrada :host` per-label arm at
// `validate_entrada_host` would be structurally unreachable via
// the total-length arm's own ordering. Pinning the ordering here
// means a future substrate-side tightening of either bound (a
// K8s SIG-Network Hostname promotion narrowing the total cap, a
// DNS-1123 label promotion widening the per-label cap) that
// inverted the two would fail this pin at build time rather than
// silently rendering the per-label arm unreachable.
assert!(
GATEWAY_API_HOSTNAME_MAX_LEN > DNS_1123_LABEL_MAX_LEN,
"GATEWAY_API_HOSTNAME_MAX_LEN ({GATEWAY_API_HOSTNAME_MAX_LEN}) must strictly \
exceed DNS_1123_LABEL_MAX_LEN ({DNS_1123_LABEL_MAX_LEN}) — every \
`.`-separated label in a Gateway API v1 Hostname is itself a DNS-1123 \
label under the apiserver's OpenAPI regex, so the total-length cap \
must be able to accommodate at least one per-label-max label",
);
}
#[test]
fn gateway_api_hostname_max_len_matches_rfc_1035_dns_name_limit() {
// Cross-axis structural invariant: the Gateway API v1 Hostname
// `maxLength: 253` cap is the RFC 1035 / RFC 1123 DNS name limit
// — 255 wire bytes minus one length prefix minus the implicit
// trailing dot — the same cap every DNS-compliant `HostName`
// primitive downstream substrate consumer (the future
// per-`Certificate` SAN emitter for cert-manager, the future
// multi-`:entrada` host-collision gate) will inherit by
// construction. Pinning the arithmetic here rather than the
// literal `253` makes the RFC derivation explicit at the const's
// test site so a future migration onto a different DNS-name
// ceiling (an eventual RFC-successor limit, a per-cluster
// override the operator pins) surfaces at this pin, not at every
// downstream renderer's admission-rejection loop.
assert_eq!(
GATEWAY_API_HOSTNAME_MAX_LEN,
255 - 1 - 1,
"GATEWAY_API_HOSTNAME_MAX_LEN must equal the RFC 1035 / RFC 1123 DNS \
name limit (255 wire bytes minus one length prefix minus the trailing \
dot)",
);
}
#[test]
fn gateway_api_default_http_listener_port_pins_canonical_80_literal() {
// The canonical-constant arm — pins
// [`GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT`] at the verbatim
// `80` literal the sole `caixa-mesh::gateway_routes` per-
// Aplicacao `Gateway` per-listener HTTP-listener-port axis
// reads from. Peer with the
// [`crate::DEFAULT_SERVICO_PORT`]-pins-`8080` discipline on the
// sibling per-renderer canonical-K8s-port-axis typed `u16`
// const: a future refactor that drifts the constant out from
// under either consumer surfaces here ahead of any per-renderer
// Gateway emission. The literal value is IANA's well-known
// `http` service port (RFC 9110 §4.2.2), so an
// `http://<entrada.host>/…` URL without a `:<port>` selector
// reaches the listener by construction.
assert_eq!(
GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT, 80,
"canonical Gateway API v1 HTTP listener port literal must remain \
`80` verbatim — this is the value the caixa-mesh Gateway emitter \
reads from and the IANA-registered well-known `http` service port"
);
}
#[test]
fn gateway_api_default_http_listener_port_distinct_from_default_servico_port() {
// Cross-axis structural invariant: the Gateway listener's
// external HTTP port ([`GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT`],
// 80) and the per-Servico in-cluster L4 port
// ([`DEFAULT_SERVICO_PORT`], 8080) are two distinct axes — the
// external-ingress port the K8s Gateway API controller opens on
// the cluster boundary, and the internal-Servico port the
// `pleme-computeunit` chart emits per Servico `Service`.
// Collapsing the two would silently emit a Gateway whose
// listener port matched the Servico's own port, so a stray
// Servico exposing its Service directly to a cluster-external
// LoadBalancer would shadow the Aplicacao's Gateway path — the
// typed two-axis distinction guards against a rebrand on either
// axis silently converging on the other's value. Peer with the
// [`GATEWAY_API_HOSTNAME_MAX_LEN`]-strictly-exceeds-[`DNS_1123_LABEL_MAX_LEN`]
// discipline on the sibling per-axis structural-ordering pin
// set — both are cross-axis invariants between two lifted
// constants that share a downstream renderer.
assert_ne!(
GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT,
crate::DEFAULT_SERVICO_PORT,
"GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT ({GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT}) \
must remain distinct from DEFAULT_SERVICO_PORT ({}) — the two axes name \
different scalars (external-Gateway listener port vs in-cluster Servico port), \
collapsing them silently shadows the Aplicacao's Gateway path",
crate::DEFAULT_SERVICO_PORT,
);
}
#[test]
fn gateway_api_default_http_listener_name_pins_canonical_http_literal() {
// The canonical-constant arm — pins
// [`GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME`] at the verbatim
// `"http"` literal the sole `caixa-mesh::gateway_routes` per-
// Aplicacao `Gateway` per-listener name-discriminator axis
// reads from. Peer with the
// [`GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT`]-pins-`80` discipline
// on the sibling per-listener HTTP-listener-port scalar-axis:
// both are the Aplicacao-side substrate-canonical scalar-value
// pins the sole per-Aplicacao `Gateway` emitter reaches for, so
// a future refactor that drifts either constant out from under
// the emitter surfaces here ahead of any per-renderer Gateway
// emission. The literal value is the substrate's V0 arbitrary-
// author-chosen short listener-name (K8s Gateway API v1's
// `SectionName`-typed field carries no CRD-schema-pinned value
// — the substrate picks `"http"` verbatim to match the
// listener's carried protocol shape at the reader's eye), so
// downstream `HTTPRoute` `sectionName` selectors bind to this
// exact byte-string by construction.
assert_eq!(
GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME, "http",
"canonical Gateway API v1 HTTP listener-name literal must remain \
`\"http\"` verbatim — this is the value the caixa-mesh Gateway \
emitter reads from and the substrate's V0 arbitrary-author-chosen \
short listener-name identifier every downstream `HTTPRoute` \
`parentRefs[].sectionName` selector binds to"
);
}
#[test]
fn gateway_api_default_http_listener_name_carries_dns_1123_label_shape() {
// Cross-axis invariant: K8s Gateway API v1 `Listener.name` is
// `SectionName`-typed — a required DNS-1123 label unique within
// the parent Gateway's listener list. Pinning the shape here
// means a future rebrand on the canonical lift can't silently
// land a malformed listener-name identifier (empty, uppercase,
// whitespace, `.` / `_` / non-alphanumeric characters, an
// overlong string past the DNS-1123 label ceiling) that the
// apiserver-side Gateway API CRD schema validator would reject
// far from the rebrand commit's source. The predicate the
// `caixa-mesh::gateway_routes` per-listener-name emitter never
// consults directly (the value is a const — no author input
// reaches this axis today) gets consulted here so any future
// rebrand routes through the same DNS-1123-label admission
// grammar every K8s CRD `name`-shaped axis carries. Peer to
// `default_gateway_class_name_is_a_valid_dns_1123_label` on
// the sibling per-Gateway `gatewayClassName` scalar-axis pin
// and `default_namespace_is_a_valid_dns_1123_label` on the
// canonical-K8s-namespace lifted scalar — every substrate-side
// K8s-CRD-name-shaped lift carries the same DNS-1123 label
// admission-grammar cross-axis invariant.
assert!(
is_dns_1123_label(GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME).is_ok(),
"GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME ({GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME:?}) \
must be a valid DNS-1123 label — K8s Gateway API v1 `Listener.name` is \
`SectionName`-typed and the apiserver-side CRD schema validator refuses \
any other shape"
);
}
#[test]
fn gateway_api_default_http_route_path_pins_canonical_root_literal() {
// The canonical-constant arm — pins
// [`GATEWAY_API_DEFAULT_HTTP_ROUTE_PATH`] at the verbatim `"/"`
// literal the sole `caixa-mesh::gateway_routes` per-Aplicacao
// `HTTPRoute` empty-`:entrada :paths` catch-all URL-path
// resolver reads from. Peer with the
// [`GATEWAY_API_DEFAULT_HTTP_LISTENER_NAME`]-pins-`"http"` and
// [`GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT`]-pins-`80`
// disciplines on the sibling per-listener substrate-canonical
// scalar-value axes: all three are the Aplicacao-side
// substrate-canonical scalar-value pins the sole per-Aplicacao
// Gateway API v1 CRD emitter reaches for, so a future refactor
// that drifts any one constant out from under the emitter
// surfaces here ahead of any per-renderer HTTPRoute emission.
// The literal value is the K8s Gateway API v1 canonical
// catch-all shape: `PathPrefix "/"` — the upstream docs at
// <https://gateway-api.sigs.k8s.io/api-types/httproute/#path-based-routing>
// pin the bare-root byte-string as the "match anything the
// listener admits" idiom every gateway-class controller treats
// as the equivalent of "no path predicate".
assert_eq!(
GATEWAY_API_DEFAULT_HTTP_ROUTE_PATH, "/",
"canonical Gateway API v1 HTTPRoute catch-all path literal must remain \
`\"/\"` verbatim — this is the value the caixa-mesh HTTPRoute emitter \
renders whenever the typed `:entrada :paths` list is empty and every \
gateway-class controller (Cilium's Envoy, Envoy Gateway, Istio Gateway) \
treats as the canonical `PathPrefix` catch-all"
);
}
#[test]
fn gateway_api_default_http_route_path_carries_valid_gateway_api_http_path_shape() {
// Cross-axis invariant: K8s Gateway API v1
// `HTTPPathMatch.value` is admitted by the apiserver-side CRD
// schema regex the substrate mirrors in the shared
// [`is_gateway_api_http_path`] predicate — the same admission
// grammar every author-supplied [`crate::aplicacao::Entrada`]
// `:paths` entry clears at typed-validate time. Pinning the
// shape here means a future rebrand on the canonical lift can't
// silently land a malformed catch-all URL-path scalar (empty,
// no leading `/`, overlong past the K8s Gateway API v1
// `HTTPPathMatch.value` ceiling, `..`-segment-bearing, ASCII-
// control-bearing, non-ASCII-bearing) that the apiserver-side
// Gateway API CRD schema validator would reject far from the
// rebrand commit's source. The paired
// [`caixa_mesh::gateway_routes`] emitter never consults the
// predicate directly (the catch-all value is a const — no
// author input reaches this axis today) so consulting it here
// means any future rebrand routes through the same
// admission-grammar the peer author-side
// `:entrada :paths` slot's `AplicacaoSpec::validate` gate
// carries. Peer to
// `gateway_api_default_http_listener_name_carries_dns_1123_label_shape`
// on the sibling per-listener name-scalar cross-axis invariant
// — every substrate-side Gateway-API-scalar lift carries the
// matching per-axis admission-grammar cross-axis pin.
assert!(
is_gateway_api_http_path(GATEWAY_API_DEFAULT_HTTP_ROUTE_PATH).is_ok(),
"GATEWAY_API_DEFAULT_HTTP_ROUTE_PATH ({GATEWAY_API_DEFAULT_HTTP_ROUTE_PATH:?}) \
must clear the shared HTTP-path admission grammar — K8s Gateway API v1 \
`HTTPPathMatch.value` is CRD-schema-regex-validated and the apiserver-side \
schema validator refuses any other shape at apply time"
);
}
// ── insert_first_seen ───────────────────────────────────────────────
#[derive(Debug, PartialEq, Eq)]
enum DupTestErr {
Dup(&'static str),
}
#[test]
fn insert_first_seen_accepts_distinct_keys_without_firing_closure() {
// The happy path — every distinct key returns `Ok(())` and the
// caller's `on_duplicate` closure is never invoked. Pins the
// `HashSet::insert`-returning-`true`-on-first-insertion contract
// the ten consumer sites (`:membros`, `:placement :clusters`,
// `:entrada :paths`, `:contratos`, `:children`, `:deps`,
// `:deps-dev`, `:etiquetas`, `:autores`, `:caracteristicas`,
// code-paths) each rely on — a future refactor that flips the
// sense of the delegated `insert` return would surface here
// ahead of every per-consumer duplicate arm silently mis-firing
// on distinct keys.
let mut seen: std::collections::HashSet<&str> = std::collections::HashSet::new();
for key in ["cart", "catalog", "payment"] {
assert_eq!(
insert_first_seen::<&str, DupTestErr, _>(&mut seen, key, || DupTestErr::Dup(
"must not fire"
)),
Ok(()),
"first insertion of {key:?} must return Ok(())",
);
}
assert_eq!(seen.len(), 3, "every distinct key must land in the set");
}
#[test]
fn insert_first_seen_surfaces_caller_shaped_error_on_second_insertion() {
// The duplicate arm — the second occurrence of any key surfaces
// the caller's `on_duplicate` return verbatim. Pins the
// "declaration-order-preserving first-collision" discipline every
// peer `Duplicate*` variant documents: the first colliding entry
// reports, not the last. Same shape the ten consumer sites'
// `*_duplicate_diagnostic_names_second_collision` posture tests
// pin at the caller layer; this lift makes the sequencing a
// property of the helper, not a per-call-site convention.
let mut seen: std::collections::HashSet<&str> = std::collections::HashSet::new();
assert_eq!(
insert_first_seen::<&str, DupTestErr, _>(&mut seen, "cart", || DupTestErr::Dup(
"first"
)),
Ok(()),
"first insertion must Ok",
);
assert_eq!(
insert_first_seen::<&str, DupTestErr, _>(&mut seen, "cart", || DupTestErr::Dup(
"second"
)),
Err(DupTestErr::Dup("second")),
"second insertion must fire the caller's closure with its own tag",
);
}
#[test]
fn insert_first_seen_generic_over_tuple_key_used_by_contratos_gate() {
// The [`crate::AplicacaoSpec::validate`] `:contratos` gate carries
// a six-tuple typed-edge identity key
// (`(de, para, wit, endpoint, subject, slot)`) — the only non-
// `&str` key shape in the crate's per-list uniqueness set. Pin
// the generic-over-`K` contract here so a future refactor that
// narrows the helper to `&str`-only keys (a hypothetical
// `HashSet<&str>`-specialized rewrite) surfaces at this pin
// rather than as a compile error at the sole tuple-carrying
// consumer. The tuple set here mirrors the shape
// `ContratoIdentity` carries.
let mut seen: std::collections::HashSet<(&str, &str, &str, Option<&str>)> =
std::collections::HashSet::new();
let key = ("cart", "catalog", "wasi:http/proxy", Some("/products"));
assert_eq!(
insert_first_seen::<_, DupTestErr, _>(&mut seen, key, || DupTestErr::Dup(
"must not fire"
)),
Ok(()),
);
assert_eq!(
insert_first_seen::<_, DupTestErr, _>(&mut seen, key, || DupTestErr::Dup("collision")),
Err(DupTestErr::Dup("collision")),
"identical tuple key on second insertion must fire the duplicate arm",
);
}
// ── assert_str_reexport_identity ──────────────────────────────────
#[test]
fn assert_str_reexport_identity_accepts_same_static_allocation() {
// Positive path — passing the same `&'static str` twice (the
// shape a `pub use caixa_core::X;` re-export produces at every
// consumer site) must not panic. This is the ~75-caller-site
// happy path that the lifted test-side pin gate collapses onto.
// The compiler-interned literal `"KUBE_KEY_SPEC"` reaches this
// helper twice through the same `&'static` allocation, so
// `std::ptr::eq(a.as_ptr(), b.as_ptr())` returns true and the
// second `assert!` arm passes without firing.
const CANONICAL: &str = "canonical-value";
assert_str_reexport_identity("CANONICAL_UNDER_TEST", CANONICAL, CANONICAL);
}
#[test]
#[should_panic(
expected = "SIBLING_UNDER_TEST must be a re-export of caixa_core::SIBLING_UNDER_TEST"
)]
fn assert_str_reexport_identity_rejects_sibling_allocation_with_same_bytes() {
// Negative path — passing two byte-equal `&'static str`s whose
// underlying allocations differ (the shape a sibling `pub const
// X: &str = "…"` at a renderer crate produces, silently carrying
// the same bytes but its own `&'static` allocation) must panic
// on the [`std::ptr::eq`] arm, naming the offending re-export.
// Reproduces the canonical drift footgun the lift closes: byte-
// equality via [`assert_eq!`] alone silently admits the drift
// — the two strings are equal — but the allocation-identity
// arm catches it structurally. Uses [`String::leak`] to
// materialize a fresh `&'static str` allocation carrying the
// same bytes as the compiler-interned canonical literal, so
// the two share bytes but differ in allocation.
const CANONICAL: &str = "canonical-value";
let sibling: &'static str = String::from("canonical-value").leak();
// Sanity — the sibling and canonical share bytes …
assert_eq!(sibling, CANONICAL);
// … but must live at distinct `&'static` allocations for this
// negative path to fire on the identity arm rather than
// silently pass on the equality arm.
assert!(!std::ptr::eq(sibling.as_ptr(), CANONICAL.as_ptr()));
assert_str_reexport_identity("SIBLING_UNDER_TEST", sibling, CANONICAL);
}
#[test]
#[should_panic(expected = "DRIFTED_UNDER_TEST must byte-equal caixa_core::DRIFTED_UNDER_TEST")]
fn assert_str_reexport_identity_rejects_bytes_drift_before_identity_arm() {
// Ordering pin — when the two byte-strings differ, the
// [`assert_eq!`] arm must fire *before* the [`std::ptr::eq`]
// identity arm reaches for `.as_ptr()`. Pins the arm sequencing
// so a future refactor that flipped the two arms (identity
// first, byte-equality second) would surface here rather than
// report the wrong diagnostic against a drifted canonical
// (the byte-equality diagnostic self-locates the value drift;
// the identity diagnostic self-locates the allocation drift —
// reporting the identity arm on a value-drifted pair points
// the reader at the wrong failure class). Same discipline as
// the peer `require_positive_canonical_bounded_duration`
// three-arm-ordering pin above.
const CANONICAL: &str = "canonical-value";
const DRIFTED: &str = "drifted-value";
assert_str_reexport_identity("DRIFTED_UNDER_TEST", DRIFTED, CANONICAL);
}
#[test]
fn computeunit_spec_key_module_pins_canonical_value() {
// Pin the actual byte-string so a typo in this lift can't silently
// rebrand the `wasm.pleme.io/v1alpha1/ComputeUnit` CRD per-CR
// `spec.module` sub-block key both caixa-flux and caixa-helm
// navigate to reach the per-Servico wasm-component reference the
// M2.5 wasm-engine instantiator loads at Servico bring-up. The
// value is part of the cluster-side contract with the
// `pleme-computeunit` library chart's per-values module-source
// routing + the `caixa-operator` `ComputeUnit` CR admission
// webhook's per-CR module-reference resolver; changing it is a
// coordinated ComputeUnit-CRD schema migration alongside the
// upstream substrate release, not an incidental edit. Peer to
// `default_namespace_pins_canonical_value` /
// `helm_values_yaml_filename_pins_canonical_value` /
// `helm_chart_yaml_filename_pins_canonical_value` on the sibling
// canonical-substrate-schema-key axes.
assert_eq!(COMPUTEUNIT_SPEC_KEY_MODULE, "module");
}
#[test]
fn computeunit_spec_key_trigger_pins_canonical_value() {
// Peer to `computeunit_spec_key_module_pins_canonical_value` on
// the same ComputeUnit-CRD per-`spec.*` sub-block axis — pins
// the per-CR invocation-shape sub-block key every
// `pleme-computeunit`-library-chart-driven per-Servico
// `trigger.service.port` / `trigger.service.paths` /
// `trigger.service.breathability` values-block route reads back.
assert_eq!(COMPUTEUNIT_SPEC_KEY_TRIGGER, "trigger");
}
#[test]
fn computeunit_spec_key_capabilities_pins_canonical_value() {
// Peer to `computeunit_spec_key_module_pins_canonical_value` and
// `computeunit_spec_key_trigger_pins_canonical_value` on the same
// ComputeUnit-CRD per-`spec.*` sub-block axis — pins the per-CR
// WASI-capability-token-list sub-block key the M2.5 wasm-engine
// instantiator reads to bind the per-component capability set
// (WASI-preview-2 preview-interfaces per the WIT Component Model)
// at Servico bring-up.
assert_eq!(COMPUTEUNIT_SPEC_KEY_CAPABILITIES, "capabilities");
}
#[test]
fn computeunit_spec_keys_carry_lowercase_shape() {
// Cross-axis invariant: every `wasm.pleme.io/v1alpha1/ComputeUnit`
// CRD per-`spec.*` sub-block key is all-ASCII-lowercase
// throughout — the ComputeUnit CRD's schema convention on the
// per-`spec.*` sub-block axis. A drifted UpperCamelCase /
// hyphenated variant (`"Module"` / `"module-source"` /
// `"Trigger"` / `"Capabilities"` — the OpenAPI-CRD-schema
// canonical-form footgun the peer `KUBE_KEY_*` axes share) would
// land the emit-side key outside the CRD's admitted per-sub-
// block set and the `caixa-operator` admission webhook would
// silently drop the per-Servico wasm-runtime binding — the
// Servico pods would come up under the library-chart defaults
// (no module bound, no trigger bound, no capability set)
// instead of the caixa.lisp's declared per-`:servicos` axis.
// Same all-ASCII-lowercase shape gate as the peer M2 typed-slot
// camelCase-key axes ([`M2_KEY_LIMITS`] / [`M2_KEY_BEHAVIOR`] —
// the compound-word slot [`M2_KEY_UPGRADE_FROM`] adds a
// camelHump per its `#[serde(rename_all = "camelCase")]`-derived
// shape, but the leading-word gate is the same).
for k in [
COMPUTEUNIT_SPEC_KEY_MODULE,
COMPUTEUNIT_SPEC_KEY_TRIGGER,
COMPUTEUNIT_SPEC_KEY_CAPABILITIES,
] {
assert!(
k.bytes().all(|b| b.is_ascii_lowercase()),
"ComputeUnit CRD per-`spec.*` sub-block key {k:?} must be \
all-ASCII-lowercase per the CRD schema convention"
);
}
}
#[test]
fn computeunit_spec_keys_appear_verbatim_in_sample_computeunit_yaml() {
// Round-trip pin: the exact byte-strings the three lifted
// constants carry appear verbatim as the top-level `spec.*`
// sub-block keys of a canonical in-tree `ComputeUnit` YAML —
// the same shape [`caixa_flux::programs_yaml_entry`] and
// [`caixa_helm::build_values_yaml`] consume via
// `serde_yaml::from_str`. Pins the const-to-schema round-trip
// so a future ComputeUnit-CRD schema rebrand (a `binary:` /
// `component:` / `invoke:` / `caps:` / `spec.wasm.*` axis
// rename the ABSORPTION-ROADMAP.md M4-M5 trajectory names)
// surfaces here as a build error rather than as a silent
// per-Servico wasm-runtime-binding drop at cluster-apply time.
let cu: serde_yaml::Value = serde_yaml::from_str(
r#"
apiVersion: wasm.pleme.io/v1alpha1
kind: ComputeUnit
metadata:
name: hello-rio
spec:
module:
source: oci://ghcr.io/pleme-io/hello-rio:v0.1.0
trigger:
service:
port: 8080
paths: ["/"]
capabilities:
- env
"#,
)
.unwrap();
let spec = cu.get(KUBE_KEY_SPEC).expect("spec key present");
assert!(
spec.get(COMPUTEUNIT_SPEC_KEY_MODULE).is_some(),
"spec.{COMPUTEUNIT_SPEC_KEY_MODULE} sub-block must be present"
);
assert!(
spec.get(COMPUTEUNIT_SPEC_KEY_TRIGGER).is_some(),
"spec.{COMPUTEUNIT_SPEC_KEY_TRIGGER} sub-block must be present"
);
assert!(
spec.get(COMPUTEUNIT_SPEC_KEY_CAPABILITIES).is_some(),
"spec.{COMPUTEUNIT_SPEC_KEY_CAPABILITIES} sub-block must be present"
);
// Nested `spec.module.source` leaf-scalar sub-block: every
// rendered ComputeUnit YAML declares the wasm-component
// reference under this leaf, and every downstream
// `programs[].module.source` readback the
// [`caixa_flux::programs_yaml_entry`] round-trip pins reaches
// for the same `&'static str`. Peer to the top-level
// `spec.{module,trigger,capabilities}` presence assertions
// above — extends the round-trip pin one level deeper onto
// the module-block's leaf reference-value axis.
let module = spec
.get(COMPUTEUNIT_SPEC_KEY_MODULE)
.expect("spec.module block present");
assert!(
module.get(COMPUTEUNIT_MODULE_KEY_SOURCE).is_some(),
"spec.{COMPUTEUNIT_SPEC_KEY_MODULE}.{COMPUTEUNIT_MODULE_KEY_SOURCE} \
leaf-scalar sub-block must be present"
);
assert_eq!(
module
.get(COMPUTEUNIT_MODULE_KEY_SOURCE)
.and_then(|s| s.as_str()),
Some("oci://ghcr.io/pleme-io/hello-rio:v0.1.0"),
"the ComputeUnit CRD per-`module.source` axis carries the wasm-\
component OCI/git reference verbatim"
);
}
#[test]
fn computeunit_module_key_source_pins_canonical_value() {
// Peer to `computeunit_spec_key_module_pins_canonical_value` on
// the nested `spec.module.*` sub-block axis — pins the per-CR
// wasm-component-reference leaf-scalar key every
// [`caixa_flux::programs_yaml_entry`] round-trip navigator and
// every [`caixa_flux::upsert_into_programs_yaml`] /
// [`caixa_flux::upsert_into_helmrelease_programs`] cross-
// upsert readback resolves under the parent
// `COMPUTEUNIT_SPEC_KEY_MODULE`. Changing this value is a
// coordinated ComputeUnit-CRD schema migration alongside the
// `pleme-computeunit` library chart's per-values module-source
// routing + the `caixa-operator` `ComputeUnit` CR admission
// webhook's per-CR module-reference resolver, not an
// incidental edit.
assert_eq!(COMPUTEUNIT_MODULE_KEY_SOURCE, "source");
}
#[test]
fn computeunit_module_key_source_carries_lowercase_shape() {
// Cross-axis invariant: the nested `spec.module.*` leaf-scalar
// sub-block key is all-ASCII-lowercase throughout — the
// ComputeUnit CRD's schema convention on the per-`spec.module.*`
// leaf axis, same as the top-level per-`spec.*` sub-block
// axis the sibling `COMPUTEUNIT_SPEC_KEY_*` peers gate.
// A drifted UpperCamelCase / hyphenated variant (`"Source"` /
// `"module-source"` / `"src"` — the OpenAPI-CRD-schema
// canonical-form footgun the peer `KUBE_KEY_*` axes share)
// would land the emit-side key outside the CRD's admitted
// per-`module.*` set and the `caixa-operator` admission
// webhook would silently drop the per-Servico wasm-module
// reference — the Servico pods would come up under the
// library-chart defaults (no module bound) instead of the
// caixa.lisp's declared per-`:servicos` axis. Same all-ASCII-
// lowercase shape gate as the peer `COMPUTEUNIT_SPEC_KEY_*`
// top-level axes.
assert!(
COMPUTEUNIT_MODULE_KEY_SOURCE
.bytes()
.all(|b| b.is_ascii_lowercase()),
"ComputeUnit CRD per-`spec.module.*` leaf-scalar sub-block key \
{COMPUTEUNIT_MODULE_KEY_SOURCE:?} must be all-ASCII-lowercase \
per the CRD schema convention"
);
}
#[test]
fn mapping_ext_insert_str_key_promotes_key_to_yaml_string() {
// The trait method promotes an arbitrary `&str` key to
// `Value::String(key.to_string())` — pin the promotion so a
// future refactor that reaches for a different `Value` variant
// for the key (e.g. `Value::Tagged`) is a compile-visible break,
// not a silent per-consumer regression at the K8s-artifact-emit
// surface.
let mut m = serde_yaml::Mapping::new();
let prior = m.insert_str_key("spec", serde_yaml::Value::Bool(true));
assert!(
prior.is_none(),
"insert_str_key returns None on first insertion, mirroring \
serde_yaml::Mapping::insert"
);
// Key is exactly the `Value::String` promotion of the input.
let got = m
.get(serde_yaml::Value::String("spec".to_string()))
.expect("inserted key is present under Value::String promotion");
assert_eq!(
got,
&serde_yaml::Value::Bool(true),
"insert_str_key routes value verbatim to the underlying \
serde_yaml::Mapping::insert"
);
}
#[test]
fn mapping_ext_insert_str_key_returns_prior_value_on_replace() {
// The trait method mirrors [`serde_yaml::Mapping::insert`]'s
// return contract: the prior value at that key, or `None` if
// absent. Pin the replace-returns-prior semantic so a future
// refactor that swaps to a `HashMap::entry`-style flow doesn't
// silently drop the prior-value handoff downstream consumers may
// reach for (the M4 per-`:politicas` overlay merger, the future
// `feira app deploy` idempotent-write dry-run comparator).
let mut m = serde_yaml::Mapping::new();
m.insert_str_key("kind", serde_yaml::Value::String("Gateway".into()));
let prior = m.insert_str_key("kind", serde_yaml::Value::String("HTTPRoute".into()));
assert_eq!(
prior,
Some(serde_yaml::Value::String("Gateway".into())),
"insert_str_key returns the prior value when replacing an existing key"
);
let got = m
.get(serde_yaml::Value::String("kind".to_string()))
.expect("key is still present after replace");
assert_eq!(
got,
&serde_yaml::Value::String("HTTPRoute".into()),
"replaced value is now the most-recently-inserted one"
);
}
#[test]
fn mapping_ext_insert_str_key_matches_hand_written_promotion() {
// Cross-check the trait method against the hand-written
// `mapping.insert(Value::String(key.into()), value)` shape the
// ~48 lifted call sites previously carried. A drift between the
// trait method's promotion and the inline promotion the prior
// call sites used would silently emit a different YAML mapping
// (a differently-quoted key, a different `Value` variant) at
// every routed consumer — pin the equivalence so the trait
// remains a drop-in replacement.
let mut via_trait = serde_yaml::Mapping::new();
via_trait.insert_str_key(KUBE_KEY_KIND, serde_yaml::Value::String("Gateway".into()));
let mut via_inline = serde_yaml::Mapping::new();
via_inline.insert(
serde_yaml::Value::String(KUBE_KEY_KIND.into()),
serde_yaml::Value::String("Gateway".into()),
);
assert_eq!(
via_trait, via_inline,
"insert_str_key(KEY, V) must byte-equal \
insert(Value::String(KEY.into()), V) — otherwise the \
~48 routed consumer sites drift silently at emit time"
);
}
#[test]
fn mapping_get_bare_str_key_byte_equals_value_string_wrapped_form() {
// The read-side twin of the `insert_str_key`-vs-hand-written pin.
// `serde_yaml::Mapping::get<I: Index>` accepts any `I: Index`;
// the crate ships `impl Index for str` (routing through a
// no-allocation `HashLikeValue(&str)` bucket lookup) and
// `impl Index for Value` (matching the `Value::String(_)`
// key verbatim). The ~78 test-side probes across `caixa-mesh`,
// `caixa-flux`, and `caixa-core::render` that previously spelled
// out `.get(serde_yaml::Value::String(<KEY>.into()))` were
// swept onto the shorter `.get(<KEY>)` form because the two
// must resolve to the same bucket for the sweep to be a
// drop-in. Pin the equivalence — the `HashLikeValue(&str)`
// hash must byte-equal the `Value::String(String)` hash so
// the two paths agree on `get`, `contains_key`, and the
// absence path (`None` when the key is missing) — otherwise
// a future `serde_yaml` upgrade could silently divert every
// swept probe past the value the emitter inserted.
let mut m = serde_yaml::Mapping::new();
m.insert_str_key(KUBE_KEY_KIND, serde_yaml::Value::String("Gateway".into()));
// Present-key path: both forms find the same value.
assert_eq!(
m.get(KUBE_KEY_KIND),
m.get(serde_yaml::Value::String(KUBE_KEY_KIND.into())),
"mapping.get(<KEY>) must byte-equal \
mapping.get(Value::String(<KEY>.into())) — otherwise the \
~78 swept test-side probes drift silently past the value \
the emitter inserted under the promoted Value::String key"
);
// Absent-key path: both forms return None.
assert_eq!(
m.get(KUBE_KEY_SPEC),
m.get(serde_yaml::Value::String(KUBE_KEY_SPEC.into())),
"absent-key lookup via bare-&str must byte-equal absent-key \
lookup via Value::String — both must return None so the \
swept `assert!(_.get(K).is_none())` shape stays load-bearing"
);
// contains_key parity: both forms agree on present + absent.
assert_eq!(
m.contains_key(KUBE_KEY_KIND),
m.contains_key(serde_yaml::Value::String(KUBE_KEY_KIND.into())),
"mapping.contains_key(<KEY>) must byte-equal \
mapping.contains_key(Value::String(<KEY>.into())) — \
otherwise the swept `assert!(_.contains_key(K))` shape \
disagrees with the emitter's `insert_str_key` promotion"
);
assert_eq!(
m.contains_key(KUBE_KEY_SPEC),
m.contains_key(serde_yaml::Value::String(KUBE_KEY_SPEC.into())),
"absent-key contains_key via bare-&str must byte-equal \
absent-key contains_key via Value::String"
);
}
#[test]
fn mapping_get_mut_bare_str_key_byte_equals_value_string_wrapped_form() {
// The mutation-path twin of the read-side pin above.
// `serde_yaml::Mapping::get_mut<I: Index>` accepts any
// `I: Index` — the crate ships `impl Index for str` (routing
// through the same no-allocation `HashLikeValue(&str)` bucket
// lookup the read-side `get` / `contains_key` sweep landed on
// in 0e84fb9) and `impl Index for Value` (matching the
// `Value::String(_)` key verbatim). Until this pin landed the
// sole production `.get_mut(serde_yaml::Value::String(<KEY>.into()))`
// probe — [`caixa_flux::upsert_into_helmrelease_programs`]'s
// `root.get_mut(…)` HelmRelease-side spec-mutate at
// `caixa-flux/src/lib.rs:845` (which the sibling
// `kube_key_spec_re_export_points_at_caixa_core_canonical`
// pinning test's docstring already described in the shorter
// `root.get_mut("spec")` form the 0e84fb9 read-side sweep
// landed elsewhere on) — carried the verbose `Value::String`-
// wrapped shape as the last stray hold-out on the `get_mut`
// axis. The sweep swaps it onto the bare-`&str` form, matching
// the ~78 read-side probes 0e84fb9 already swept and the
// in-file `kube_key_spec_re_export_points_at_caixa_core_canonical`
// docstring's canonical description. Pin the equivalence — the
// `HashLikeValue(&str)` hash must byte-equal the
// `Value::String(String)` hash so the two paths agree on both
// the present-key path (returns `Some(&mut _)` at the same
// slot) and the absent-key path (returns `None` when the key
// is missing) — otherwise a future `serde_yaml` upgrade could
// silently divert the writer-side upsert past the value the
// emitter previously mutated. Peer to the read-side
// [`mapping_get_bare_str_key_byte_equals_value_string_wrapped_form`]
// pin on the sibling `get` / `contains_key` axes; together the
// two pins pin every `Index`-polymorphic probe axis the
// caixa-flux upsert path walks.
let mut m = serde_yaml::Mapping::new();
m.insert_str_key(KUBE_KEY_KIND, serde_yaml::Value::String("Gateway".into()));
// Present-key path: both forms find the same slot.
// Cross-check by mutating through the bare-&str path and
// observing the mutation via the Value::String path (and vice
// versa) — anything short of exact bucket-equality would
// silently split the two probes onto different slots.
{
let via_bare = m
.get_mut(KUBE_KEY_KIND)
.expect("present key must resolve via bare-&str");
*via_bare = serde_yaml::Value::String("HTTPRoute".into());
}
assert_eq!(
m.get(serde_yaml::Value::String(KUBE_KEY_KIND.into())),
Some(&serde_yaml::Value::String("HTTPRoute".into())),
"mutation via mapping.get_mut(<KEY>) must be visible via \
mapping.get(Value::String(<KEY>.into())) — otherwise the \
swept `get_mut` writer-side probe drifts past the value \
the emitter reads through the promoted Value::String key"
);
{
let via_wrapped = m
.get_mut(serde_yaml::Value::String(KUBE_KEY_KIND.into()))
.expect("present key must also resolve via Value::String");
*via_wrapped = serde_yaml::Value::String("Gateway".into());
}
assert_eq!(
m.get(KUBE_KEY_KIND),
Some(&serde_yaml::Value::String("Gateway".into())),
"mutation via mapping.get_mut(Value::String(<KEY>.into())) \
must be visible via mapping.get(<KEY>) — the two paths \
address the same bucket in both directions"
);
// Absent-key path: both forms return None so the sole swept
// `.get_mut(<KEY>).ok_or(Error::MissingField(<KEY>))` shape
// stays load-bearing.
assert!(
m.get_mut(KUBE_KEY_SPEC).is_none(),
"absent-key mapping.get_mut(<KEY>) must return None"
);
assert!(
m.get_mut(serde_yaml::Value::String(KUBE_KEY_SPEC.into()))
.is_none(),
"absent-key mapping.get_mut(Value::String(<KEY>.into())) \
must also return None — the two forms must agree on \
absence so the swept `.ok_or(Error::MissingField(<KEY>))` \
diagnostic still fires on a missing spec block"
);
}
#[test]
fn mapping_ext_insert_string_promotes_value_to_yaml_string() {
// The trait method promotes an arbitrary `Into<String>` value
// to `Value::String(value.into())` — pin the promotion so a
// future refactor that reaches for a different `Value` variant
// for the string-scalar payload (e.g. `Value::Tagged` under a
// K8s Server-Side-Apply typed-field-ownership axis rebrand) is
// a compile-visible break, not a silent per-consumer regression
// at the K8s-artifact-emit surface. Peer with
// [`mapping_ext_insert_str_key_promotes_key_to_yaml_string`] on
// the sibling `insert_str_key` primitive's key-promotion pin.
let mut m = serde_yaml::Mapping::new();
let prior = m.insert_string("kind", "Gateway");
assert!(
prior.is_none(),
"insert_string returns None on first insertion, mirroring \
serde_yaml::Mapping::insert"
);
let got = m
.get("kind")
.expect("inserted key is present under Value::String promotion");
assert_eq!(
got,
&serde_yaml::Value::String("Gateway".into()),
"insert_string routes value verbatim through Value::String \
promotion"
);
}
#[test]
fn mapping_ext_insert_string_returns_prior_value_on_replace() {
// The trait method mirrors [`serde_yaml::Mapping::insert`]'s
// return contract: the prior value at that key, or `None` if
// absent. Pin the replace-returns-prior semantic so a future
// refactor that swaps to a `HashMap::entry`-style flow doesn't
// silently drop the prior-value handoff downstream consumers
// may reach for. Peer with
// [`mapping_ext_insert_str_key_returns_prior_value_on_replace`]
// on the sibling `insert_str_key` primitive's replace-semantics
// pin.
let mut m = serde_yaml::Mapping::new();
m.insert_string(KUBE_KEY_KIND, "Gateway");
let prior = m.insert_string(KUBE_KEY_KIND, "HTTPRoute");
assert_eq!(
prior,
Some(serde_yaml::Value::String("Gateway".into())),
"insert_string returns the prior value when replacing an \
existing key"
);
let got = m
.get(KUBE_KEY_KIND)
.expect("key is still present after replace");
assert_eq!(
got,
&serde_yaml::Value::String("HTTPRoute".into()),
"replaced value is now the most-recently-inserted one"
);
}
#[test]
fn mapping_ext_insert_string_matches_hand_written_promotion() {
// Cross-check the trait method against the hand-written
// `mapping.insert_str_key(KEY, Value::String(V.into()))` shape
// the ~17 lifted call sites previously carried. A drift between
// the trait method's promotion and the inline promotion would
// silently emit a different YAML mapping (a differently-quoted
// scalar, a different `Value` variant) at every routed
// consumer — pin the equivalence so the trait remains a drop-in
// replacement. Also cross-checks that all three input shapes
// (`&'static str` → `.into()`, `String` → `.clone()` /
// `.to_string()`, integer → `.to_string()`) converge on the same
// `Value::String` promotion, since the ~17 call sites cover all
// three input flavors.
let mut via_trait = serde_yaml::Mapping::new();
via_trait.insert_string(KUBE_KEY_KIND, "Gateway");
via_trait.insert_string(KUBE_KEY_NAME, String::from("hello"));
via_trait.insert_string(KUBE_KEY_PORT, 8080u16.to_string());
let mut via_inline = serde_yaml::Mapping::new();
via_inline.insert_str_key(KUBE_KEY_KIND, serde_yaml::Value::String("Gateway".into()));
via_inline.insert_str_key(
KUBE_KEY_NAME,
serde_yaml::Value::String(String::from("hello")),
);
via_inline.insert_str_key(
KUBE_KEY_PORT,
serde_yaml::Value::String(8080u16.to_string()),
);
assert_eq!(
via_trait, via_inline,
"insert_string(KEY, V) must byte-equal \
insert_str_key(KEY, Value::String(V.into())) — otherwise \
the ~17 routed consumer sites drift silently at emit time"
);
}
#[test]
fn mapping_ext_insert_number_promotes_value_to_yaml_number() {
// The trait method promotes an arbitrary `Into<serde_yaml::Number>`
// value to `Value::Number(value.into())` — pin the promotion so a
// future refactor that reaches for a different `Value` variant
// for the integer-scalar payload (e.g. `Value::Tagged` under a
// K8s Server-Side-Apply typed-field-ownership axis rebrand, or
// the deprecated `Value::String(n.to_string())` "stringy port"
// rendering some pre-Gateway-API-v1 CRDs still shipped with) is
// a compile-visible break, not a silent per-consumer regression
// at the K8s-artifact-emit surface. Peer with
// [`mapping_ext_insert_string_promotes_value_to_yaml_string`] on
// the sibling `insert_string` primitive's string-scalar
// promotion pin.
let mut m = serde_yaml::Mapping::new();
let prior = m.insert_number(KUBE_KEY_PORT, GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT);
assert!(
prior.is_none(),
"insert_number returns None on first insertion, mirroring \
serde_yaml::Mapping::insert"
);
let got = m
.get(KUBE_KEY_PORT)
.expect("inserted key is present under Value::Number promotion");
assert_eq!(
got.as_u64(),
Some(u64::from(GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT)),
"insert_number routes value verbatim through Value::Number \
promotion — the u16 payload survives round-trip as a Number \
the as_u64 accessor decodes verbatim"
);
assert!(
matches!(got, serde_yaml::Value::Number(_)),
"the promoted value is Value::Number, not Value::String — a \
stringy-port drift would emit `port: \"80\"` (rejected by \
Gateway API v1 apiserver as a type mismatch)"
);
}
#[test]
fn mapping_ext_insert_number_returns_prior_value_on_replace() {
// The trait method mirrors [`serde_yaml::Mapping::insert`]'s
// return contract: the prior value at that key, or `None` if
// absent. Pin the replace-returns-prior semantic so a future
// refactor that swaps to a `HashMap::entry`-style flow doesn't
// silently drop the prior-value handoff downstream consumers
// may reach for. Peer with
// [`mapping_ext_insert_string_returns_prior_value_on_replace`] on
// the sibling `insert_string` primitive's replace-semantics pin.
let mut m = serde_yaml::Mapping::new();
m.insert_number(KUBE_KEY_PORT, 80u16);
let prior = m.insert_number(KUBE_KEY_PORT, 443u16);
assert_eq!(
prior.as_ref().and_then(serde_yaml::Value::as_u64),
Some(80),
"insert_number returns the prior value when replacing an \
existing key — the u16 payload round-trips verbatim through \
the returned Value::Number handoff"
);
let got = m
.get(KUBE_KEY_PORT)
.expect("key is still present after replace");
assert_eq!(
got.as_u64(),
Some(443),
"replaced value is now the most-recently-inserted one"
);
}
#[test]
fn mapping_ext_insert_number_matches_hand_written_promotion() {
// Cross-check the trait method against the hand-written
// `mapping.insert_str_key(KEY, Value::Number(N.into()))` shape
// the two lifted caixa-mesh call sites previously carried. A
// drift between the trait method's promotion and the inline
// promotion would silently emit a different YAML mapping (a
// differently-typed scalar, a different `Value` variant) at
// every routed consumer — pin the equivalence so the trait
// remains a drop-in replacement. Two arms pin the axis end-to-
// end: a `u16` typed-const arm (the lifted
// `GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT` external HTTP
// listener-port, cd60fde) and a `u16` typed-field arm (the
// per-`entrada.port` backend-target Servico port routed through
// the `AplicacaoSpec` `:entrada :port` slot).
let mut via_trait = serde_yaml::Mapping::new();
via_trait.insert_number(KUBE_KEY_PORT, GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT);
via_trait.insert_number(GATEWAY_API_KEY_VALUE, 8443u16);
let mut via_inline = serde_yaml::Mapping::new();
via_inline.insert_str_key(
KUBE_KEY_PORT,
serde_yaml::Value::Number(GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT.into()),
);
via_inline.insert_str_key(
GATEWAY_API_KEY_VALUE,
serde_yaml::Value::Number(8443u16.into()),
);
assert_eq!(
via_trait, via_inline,
"insert_number(KEY, N) must byte-equal \
insert_str_key(KEY, Value::Number(N.into())) — otherwise \
the two routed caixa-mesh consumer sites drift silently at \
emit time"
);
}
#[test]
fn mapping_ext_insert_mapping_promotes_value_to_yaml_mapping() {
// The trait method promotes an arbitrary `serde_yaml::Mapping`
// value to `Value::Mapping(value)` — pin the promotion so a
// future refactor that reaches for a different `Value` variant
// for the nested-Mapping payload (e.g. `Value::Tagged` under a
// K8s Server-Side-Apply typed-field-ownership axis rebrand) is
// a compile-visible break, not a silent per-consumer regression
// at the K8s-artifact-emit surface. Peer with
// [`mapping_ext_insert_string_promotes_value_to_yaml_string`] on
// the sibling `insert_string` primitive's scalar-promotion pin
// and with
// [`mapping_ext_insert_str_key_promotes_key_to_yaml_string`] on
// the base `insert_str_key` primitive's key-promotion pin.
let mut inner = serde_yaml::Mapping::new();
inner.insert_string(KUBE_KEY_NAME, "hello-rio");
let mut m = serde_yaml::Mapping::new();
let prior = m.insert_mapping(KUBE_KEY_METADATA, inner.clone());
assert!(
prior.is_none(),
"insert_mapping returns None on first insertion, mirroring \
serde_yaml::Mapping::insert"
);
let got = m
.get(KUBE_KEY_METADATA)
.expect("inserted key is present under Value::Mapping promotion");
assert_eq!(
got,
&serde_yaml::Value::Mapping(inner),
"insert_mapping routes value verbatim through Value::Mapping \
promotion"
);
}
#[test]
fn mapping_ext_insert_mapping_returns_prior_value_on_replace() {
// The trait method mirrors [`serde_yaml::Mapping::insert`]'s
// return contract: the prior value at that key, or `None` if
// absent. Pin the replace-returns-prior semantic so a future
// refactor that swaps to a `HashMap::entry`-style flow doesn't
// silently drop the prior-value handoff downstream consumers
// may reach for. Peer with
// [`mapping_ext_insert_string_returns_prior_value_on_replace`]
// and
// [`mapping_ext_insert_str_key_returns_prior_value_on_replace`]
// on the sibling primitive-pair members' replace-semantics
// pins.
let mut first_inner = serde_yaml::Mapping::new();
first_inner.insert_string(KUBE_KEY_NAME, "first");
let mut second_inner = serde_yaml::Mapping::new();
second_inner.insert_string(KUBE_KEY_NAME, "second");
let mut m = serde_yaml::Mapping::new();
m.insert_mapping(KUBE_KEY_METADATA, first_inner.clone());
let prior = m.insert_mapping(KUBE_KEY_METADATA, second_inner.clone());
assert_eq!(
prior,
Some(serde_yaml::Value::Mapping(first_inner)),
"insert_mapping returns the prior value when replacing an \
existing key"
);
let got = m
.get(KUBE_KEY_METADATA)
.expect("key is still present after replace");
assert_eq!(
got,
&serde_yaml::Value::Mapping(second_inner),
"replaced value is now the most-recently-inserted one"
);
}
#[test]
fn mapping_ext_insert_mapping_matches_hand_written_promotion() {
// Cross-check the trait method against the hand-written
// `mapping.insert_str_key(KEY, Value::Mapping(inner))` shape the
// 6 lifted call sites previously carried. A drift between the
// trait method's promotion and the inline promotion would
// silently emit a different YAML mapping (a differently-wrapped
// outer variant, a differently-shaped inner Mapping) at every
// routed consumer — pin the equivalence so the trait remains a
// drop-in replacement. Two cases pin the shape end-to-end:
// an empty inner Mapping (no silent is_empty short-circuit) and
// a populated inner Mapping (the `metadata` / `spec` /
// `spec.rules[].path` sub-block shape).
let mut inner_empty = serde_yaml::Mapping::new();
let _ = &mut inner_empty; // keep as mut for parity with populated arm below
let mut inner_populated = serde_yaml::Mapping::new();
inner_populated.insert_string(KUBE_KEY_NAME, "hello-rio");
inner_populated.insert_string(KUBE_KEY_NAMESPACE, DEFAULT_NAMESPACE);
let mut via_trait = serde_yaml::Mapping::new();
via_trait.insert_mapping(KUBE_KEY_SPEC, inner_empty.clone());
via_trait.insert_mapping(KUBE_KEY_METADATA, inner_populated.clone());
let mut via_inline = serde_yaml::Mapping::new();
via_inline.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(inner_empty));
via_inline.insert_str_key(
KUBE_KEY_METADATA,
serde_yaml::Value::Mapping(inner_populated),
);
assert_eq!(
via_trait, via_inline,
"insert_mapping(KEY, inner) must byte-equal \
insert_str_key(KEY, Value::Mapping(inner)) — otherwise the \
six routed consumer sites drift silently at emit time"
);
}
#[test]
fn mapping_ext_insert_sequence_promotes_value_to_yaml_sequence() {
// The trait method promotes an arbitrary `Vec<Value>` value to
// `Value::Sequence(value)` — pin the promotion so a future
// refactor that reaches for a different `Value` variant for the
// list-shape payload (e.g. `Value::Tagged` under a K8s Server-
// Side-Apply typed-field-ownership axis rebrand, a serde_yaml
// successor's `Value::Array` / `Value::List` variant rename) is
// a compile-visible break, not a silent per-consumer regression
// at the K8s-artifact-emit surface. Peer with
// [`mapping_ext_insert_mapping_promotes_value_to_yaml_mapping`]
// on the sibling `insert_mapping` primitive's nested-Mapping-
// promotion pin, and with
// [`mapping_ext_insert_string_promotes_value_to_yaml_string`]
// on the sibling `insert_string` primitive's scalar-promotion
// pin.
let inner = vec![
serde_yaml::Value::String("hello".into()),
serde_yaml::Value::String("world".into()),
];
let mut m = serde_yaml::Mapping::new();
let prior = m.insert_sequence(GATEWAY_API_KEY_HOSTNAMES, inner.clone());
assert!(
prior.is_none(),
"insert_sequence returns None on first insertion, mirroring \
serde_yaml::Mapping::insert"
);
let got = m
.get(GATEWAY_API_KEY_HOSTNAMES)
.expect("inserted key is present under Value::Sequence promotion");
assert_eq!(
got,
&serde_yaml::Value::Sequence(inner),
"insert_sequence routes value verbatim through Value::Sequence \
promotion"
);
}
#[test]
fn mapping_ext_insert_sequence_returns_prior_value_on_replace() {
// The trait method mirrors [`serde_yaml::Mapping::insert`]'s
// return contract: the prior value at that key, or `None` if
// absent. Pin the replace-returns-prior semantic so a future
// refactor that swaps to a `HashMap::entry`-style flow doesn't
// silently drop the prior-value handoff downstream consumers
// may reach for. Peer with
// [`mapping_ext_insert_mapping_returns_prior_value_on_replace`],
// [`mapping_ext_insert_string_returns_prior_value_on_replace`],
// and
// [`mapping_ext_insert_str_key_returns_prior_value_on_replace`]
// on the sibling primitive-quadruple members' replace-semantics
// pins.
let first: Vec<serde_yaml::Value> = vec![serde_yaml::Value::String("a".into())];
let second: Vec<serde_yaml::Value> = vec![
serde_yaml::Value::String("b".into()),
serde_yaml::Value::String("c".into()),
];
let mut m = serde_yaml::Mapping::new();
m.insert_sequence(KUBE_KEY_RULES, first.clone());
let prior = m.insert_sequence(KUBE_KEY_RULES, second.clone());
assert_eq!(
prior,
Some(serde_yaml::Value::Sequence(first)),
"insert_sequence returns the prior value when replacing an \
existing key"
);
let got = m
.get(KUBE_KEY_RULES)
.expect("key is still present after replace");
assert_eq!(
got,
&serde_yaml::Value::Sequence(second),
"replaced value is now the most-recently-inserted one"
);
}
#[test]
fn mapping_ext_insert_sequence_matches_hand_written_promotion() {
// Cross-check the trait method against the hand-written
// `mapping.insert_str_key(KEY, Value::Sequence(v))` shape the 4
// lifted call sites previously carried. A drift between the
// trait method's promotion and the inline promotion would
// silently emit a different YAML mapping (a differently-wrapped
// outer variant, a differently-shaped inner sequence) at every
// routed consumer — pin the equivalence so the trait remains a
// drop-in replacement. Three cases pin the shape end-to-end:
// an empty inner Vec (no silent is_empty short-circuit), a
// singleton-Value inner Vec (the `fromEndpoints[<selector>]` /
// `hostnames[<host>]` singleton shape), and a multi-Value inner
// Vec (the `toPorts[…]` / `rules[…]` multi-entry shape).
let inner_empty: Vec<serde_yaml::Value> = Vec::new();
let inner_singleton: Vec<serde_yaml::Value> =
vec![serde_yaml::Value::String("example.com".into())];
let mut host_entry = serde_yaml::Mapping::new();
host_entry.insert_string(KUBE_KEY_NAME, "svc-a");
let mut port_entry = serde_yaml::Mapping::new();
port_entry.insert_string(KUBE_KEY_NAME, "svc-b");
let inner_multi: Vec<serde_yaml::Value> = vec![
serde_yaml::Value::Mapping(host_entry.clone()),
serde_yaml::Value::Mapping(port_entry.clone()),
];
let mut via_trait = serde_yaml::Mapping::new();
via_trait.insert_sequence(CILIUM_KEY_TO_PORTS, inner_empty.clone());
via_trait.insert_sequence(GATEWAY_API_KEY_HOSTNAMES, inner_singleton.clone());
via_trait.insert_sequence(KUBE_KEY_RULES, inner_multi.clone());
let mut via_inline = serde_yaml::Mapping::new();
via_inline.insert_str_key(
CILIUM_KEY_TO_PORTS,
serde_yaml::Value::Sequence(inner_empty),
);
via_inline.insert_str_key(
GATEWAY_API_KEY_HOSTNAMES,
serde_yaml::Value::Sequence(inner_singleton),
);
via_inline.insert_str_key(KUBE_KEY_RULES, serde_yaml::Value::Sequence(inner_multi));
assert_eq!(
via_trait, via_inline,
"insert_sequence(KEY, v) must byte-equal \
insert_str_key(KEY, Value::Sequence(v)) — otherwise the \
four routed consumer sites drift silently at emit time"
);
}
// ── insert_singleton_mapping_sequence — composed primitive ───────────
//
// The trait method composes [`Self::insert_str_key`] with
// [`singleton_mapping_sequence`]: every hand-inline
// `mapping.insert_str_key(K, singleton_mapping_sequence(m))` two-symbol
// composition previously carried at 7 sites across caixa-mesh
// collapses onto one method call. Three peer pins pin the trait
// method's shape end-to-end.
#[test]
fn mapping_ext_insert_singleton_mapping_sequence_promotes_value_to_singleton_mapping_seq() {
// First-insertion returns None (mirroring [`Mapping::insert`])
// and the inserted value is a `Value::Sequence` of exactly one
// element, wrapping the caller's Mapping as `Value::Mapping`.
// Peer with the sibling
// `mapping_ext_insert_sequence_promotes_value_to_yaml_sequence`
// / `mapping_ext_insert_mapping_promotes_value_to_yaml_mapping`
// / `mapping_ext_insert_string_promotes_value_to_yaml_string`
// first-insert pins on the sibling MappingExt primitive
// members.
let mut inner = serde_yaml::Mapping::new();
inner.insert_str_key(
GATEWAY_API_KEY_NAME,
serde_yaml::Value::String("gw-listener".into()),
);
let mut m = serde_yaml::Mapping::new();
let prior = m.insert_singleton_mapping_sequence(GATEWAY_API_KEY_LISTENERS, inner.clone());
assert_eq!(
prior, None,
"insert_singleton_mapping_sequence returns None on first insertion, \
mirroring serde_yaml::Mapping::insert"
);
let got = m
.get(GATEWAY_API_KEY_LISTENERS)
.expect("inserted key is present under Value::Sequence promotion");
assert_eq!(
got,
&serde_yaml::Value::Sequence(vec![serde_yaml::Value::Mapping(inner)]),
"insert_singleton_mapping_sequence routes value verbatim through \
the singleton_mapping_sequence(_) helper wrap"
);
}
#[test]
fn mapping_ext_insert_singleton_mapping_sequence_returns_prior_value_on_replace() {
// The trait method mirrors [`serde_yaml::Mapping::insert`]'s
// return contract: the prior value at that key, or `None` if
// absent. Pin the replace-returns-prior semantic so a future
// refactor that swaps to a `HashMap::entry`-style flow doesn't
// silently drop the prior-value handoff downstream consumers
// may reach for. Peer with the sibling
// `mapping_ext_insert_sequence_returns_prior_value_on_replace`
// and its siblings on the primitive-quintuple axis.
let mut first = serde_yaml::Mapping::new();
first.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("a".into()));
let mut second = serde_yaml::Mapping::new();
second.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("b".into()));
let mut m = serde_yaml::Mapping::new();
m.insert_singleton_mapping_sequence(GATEWAY_API_KEY_PARENT_REFS, first.clone());
let prior =
m.insert_singleton_mapping_sequence(GATEWAY_API_KEY_PARENT_REFS, second.clone());
assert_eq!(
prior,
Some(serde_yaml::Value::Sequence(vec![
serde_yaml::Value::Mapping(first)
])),
"insert_singleton_mapping_sequence returns the prior value \
when replacing an existing key"
);
let got = m
.get(GATEWAY_API_KEY_PARENT_REFS)
.expect("key is still present after replace");
assert_eq!(
got,
&serde_yaml::Value::Sequence(vec![serde_yaml::Value::Mapping(second)]),
"replaced value is now the most-recently-inserted singleton \
mapping sequence"
);
}
#[test]
fn mapping_ext_insert_singleton_mapping_sequence_matches_hand_written_composition() {
// Cross-check the trait method against the hand-written
// `mapping.insert_str_key(KEY, singleton_mapping_sequence(m))`
// two-symbol composition the 7 lifted call sites previously
// carried. A drift between the trait method's routing and the
// inline composition would silently emit a different YAML
// mapping (a differently-wrapped outer variant, a
// differently-shaped inner singleton-Mapping list) at every
// routed consumer — pin the equivalence so the trait remains a
// drop-in replacement. Three cases pin the shape end-to-end:
// an empty inner Mapping (no silent is_empty short-circuit,
// matches the sibling `singleton_mapping_sequence_preserves_empty_inner_mapping`
// pin), a single-key inner Mapping (the
// `CILIUM_KEY_HTTP` / `CILIUM_KEY_INGRESS` singleton-rule
// shape), and a multi-key inner Mapping (the
// `GATEWAY_API_KEY_LISTENERS` per-listener shape).
let inner_empty = serde_yaml::Mapping::new();
let mut inner_single_key = serde_yaml::Mapping::new();
inner_single_key
.insert_str_key(CILIUM_KEY_PATH, serde_yaml::Value::String("/health".into()));
let mut inner_multi_key = serde_yaml::Mapping::new();
inner_multi_key.insert_str_key(
GATEWAY_API_KEY_NAME,
serde_yaml::Value::String("http".into()),
);
inner_multi_key.insert_str_key(
KUBE_KEY_PORT,
serde_yaml::Value::Number(GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT.into()),
);
let mut via_trait = serde_yaml::Mapping::new();
via_trait.insert_singleton_mapping_sequence(CILIUM_KEY_HTTP, inner_empty.clone());
via_trait.insert_singleton_mapping_sequence(CILIUM_KEY_INGRESS, inner_single_key.clone());
via_trait
.insert_singleton_mapping_sequence(GATEWAY_API_KEY_LISTENERS, inner_multi_key.clone());
let mut via_inline = serde_yaml::Mapping::new();
via_inline.insert_str_key(CILIUM_KEY_HTTP, singleton_mapping_sequence(inner_empty));
via_inline.insert_str_key(
CILIUM_KEY_INGRESS,
singleton_mapping_sequence(inner_single_key),
);
via_inline.insert_str_key(
GATEWAY_API_KEY_LISTENERS,
singleton_mapping_sequence(inner_multi_key),
);
assert_eq!(
via_trait, via_inline,
"insert_singleton_mapping_sequence(KEY, m) must byte-equal \
insert_str_key(KEY, singleton_mapping_sequence(m)) — otherwise \
the seven routed caixa-mesh consumer sites drift silently at \
emit time"
);
}
// ── entry_str_key — entry-API twin of insert_str_key ─────────────────
#[test]
fn mapping_ext_entry_str_key_or_inserts_default_under_yaml_string_promoted_key_when_absent() {
// The trait method promotes an arbitrary `&str` key to
// `Value::String(key.to_string())` on the entry-API axis — pin
// the promotion + the entry-API contract so a future refactor
// that reaches for a different `Value` variant for the entry
// key (e.g. `Value::Tagged`) or breaks the entry-API
// `.or_insert(...)` composition is a compile-visible break,
// not a silent per-consumer regression at the 4 lifted
// `caixa-flux` idempotent-upsert sites. Peer with the sibling
// [`mapping_ext_insert_str_key_promotes_key_to_yaml_string`] on
// the fresh-emit axis of the same key promotion.
let mut m = serde_yaml::Mapping::new();
let default_val = serde_yaml::Value::Sequence(Vec::new());
let inserted = m.entry_str_key("programs").or_insert(default_val.clone());
assert_eq!(
inserted, &default_val,
"entry_str_key(K).or_insert(D) returns &mut D on the absent-key \
path, mirroring serde_yaml::mapping::Entry::or_insert"
);
// Key is exactly the `Value::String` promotion of the input.
let got = m
.get("programs")
.expect("or_insert-defaulted key is present under Value::String promotion");
assert_eq!(
got, &default_val,
"entry_str_key routes the default verbatim to the underlying \
serde_yaml::Mapping::entry(...).or_insert(...) path"
);
}
#[test]
fn mapping_ext_entry_str_key_leaves_prior_value_untouched_on_or_insert_when_present() {
// The trait method mirrors [`serde_yaml::mapping::Entry::or_insert`]'s
// present-key contract: the prior value is preserved, and the
// returned `&mut Value` points at that prior value (NOT the
// discarded default). Pin the leave-prior-untouched semantic so a
// future refactor that swaps to an `.insert`-style overwrite
// flow doesn't silently clobber every idempotent-upsert consumer
// (the M4 per-`:politicas` overlay merger, the `feira app
// deploy` idempotent-write dry-run comparator). Peer with the
// sibling [`mapping_ext_insert_str_key_returns_prior_value_on_replace`]
// pin on the fresh-emit axis (which mirrors the `insert`
// replace-and-return-prior semantic, not the `entry.or_insert`
// preserve-prior semantic — the two APIs partition the
// `Mapping`-write surface exactly on this axis).
let mut m = serde_yaml::Mapping::new();
m.insert_str_key(
FLEET_PROGRAMS_KEY_PROGRAMS,
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("existing".into())]),
);
let discarded_default = serde_yaml::Value::Sequence(Vec::new());
let returned = m
.entry_str_key(FLEET_PROGRAMS_KEY_PROGRAMS)
.or_insert(discarded_default);
assert_eq!(
returned,
&serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("existing".into())]),
"entry_str_key(K).or_insert(D) returns &mut prior on the \
present-key path — the discarded default must not overwrite \
the emitter's prior write"
);
// Value at the key is still the pre-existing one, verbatim.
let got = m
.get(FLEET_PROGRAMS_KEY_PROGRAMS)
.expect("key is still present after or_insert on the present-key path");
assert_eq!(
got,
&serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("existing".into())]),
"or_insert on the present-key path preserves the prior value \
verbatim — no clobber, no reshape"
);
}
#[test]
fn mapping_ext_entry_str_key_matches_hand_written_composition() {
// Cross-check the trait method against the hand-written
// `mapping.entry(Value::String(KEY.into()))` three-token
// composition the 4 lifted `caixa-flux` call sites previously
// carried. A drift between the trait method's promotion and the
// inline promotion the prior call sites used would silently
// route every idempotent-upsert consumer past a different bucket
// (a differently-promoted key on absent-key insert, a hash-key
// mismatch that always fires the `or_insert` default even when
// the emitter's `insert_str_key` already wrote a value under
// the same key). Two cases pin the shape end-to-end: an
// absent-key path (both routes take the vacant `or_insert`
// branch, both end up storing the same default under the
// promoted key) and a present-key path (both routes take the
// occupied `or_insert` branch, both leave the prior value
// untouched — the twin of the
// `mapping_ext_insert_str_key_matches_hand_written_promotion`
// pin on the fresh-emit axis).
//
// Absent-key path — the vacant `or_insert` branch.
let mut via_trait_absent = serde_yaml::Mapping::new();
via_trait_absent
.entry_str_key(FLEET_PROGRAMS_KEY_PROGRAMS)
.or_insert(serde_yaml::Value::Sequence(Vec::new()));
let mut via_inline_absent = serde_yaml::Mapping::new();
via_inline_absent
.entry(serde_yaml::Value::String(
FLEET_PROGRAMS_KEY_PROGRAMS.into(),
))
.or_insert(serde_yaml::Value::Sequence(Vec::new()));
assert_eq!(
via_trait_absent, via_inline_absent,
"entry_str_key(K).or_insert(D) must byte-equal \
entry(Value::String(K.into())).or_insert(D) on the absent-key \
path — otherwise the 4 routed caixa-flux consumer sites \
land the default under a different bucket than the emitter's \
`insert_str_key` write and the idempotent-upsert semantic \
silently doubles the entry on every call"
);
// Present-key path — the occupied `or_insert` branch. Seed both
// mappings via the fresh-emit `insert_str_key` peer (which the
// `matches_hand_written_promotion` pin already gates), so the
// present-key path here inherits the promotion-agreement guarantee
// from that peer and tests only the entry-API branch difference.
let seed = serde_yaml::Value::Mapping(serde_yaml::Mapping::new());
let mut via_trait_present = serde_yaml::Mapping::new();
via_trait_present.insert_str_key(FLUX_KEY_VALUES, seed.clone());
via_trait_present
.entry_str_key(FLUX_KEY_VALUES)
.or_insert(serde_yaml::Value::Sequence(Vec::new()));
let mut via_inline_present = serde_yaml::Mapping::new();
via_inline_present.insert_str_key(FLUX_KEY_VALUES, seed);
via_inline_present
.entry(serde_yaml::Value::String(FLUX_KEY_VALUES.into()))
.or_insert(serde_yaml::Value::Sequence(Vec::new()));
assert_eq!(
via_trait_present, via_inline_present,
"entry_str_key(K).or_insert(D) must byte-equal \
entry(Value::String(K.into())).or_insert(D) on the \
present-key path — otherwise a promoted-key mismatch would \
cause the trait routing to see the seed as absent and \
overwrite the emitter's prior write while the hand-written \
inline routing sees it as present and preserves it (or vice \
versa)"
);
}
// ── entry_or_default_{mapping,sequence} — entry-API-with-container-check ─
#[test]
fn mapping_ext_entry_or_default_mapping_seeds_empty_inner_when_absent() {
// Absent-key path — the helper mints an empty
// `Value::Mapping(Mapping::new())` under the promoted key and
// returns `Some(&mut inner)` pointing at the fresh empty inner.
// Pin the seed shape so a future refactor that reaches for a
// different empty-container variant (e.g. `Value::Null`, or a
// `Mapping::with_capacity(_)` non-empty pre-allocation) or
// breaks the `Option::Some` return contract is a compile-visible
// break, not a silent per-consumer regression at the caixa-flux
// `upsert_into_helmrelease_programs` `spec.values` container-
// upsert. Peer with the sibling
// [`mapping_ext_entry_or_default_sequence_seeds_empty_inner_when_absent`]
// on the sibling list-container axis.
let mut m = serde_yaml::Mapping::new();
{
let inner = m
.entry_or_default_mapping(FLUX_KEY_VALUES)
.expect("absent-key path seeds an empty Mapping and returns Some(&mut _)");
assert!(
inner.is_empty(),
"the seeded default must be an EMPTY Mapping — a \
non-empty pre-allocation would land a K8s CRD schema \
pre-populated block the emitter never authored"
);
}
// Key is exactly the `Value::String` promotion of the input,
// and the value is the empty-Mapping seed.
let got = m
.get(FLUX_KEY_VALUES)
.expect("or_default seeded the key under Value::String promotion");
assert_eq!(
got,
&serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
"entry_or_default_mapping seeds Value::Mapping(Mapping::new()) \
verbatim on the absent-key arm — no reshape, no wrap"
);
}
#[test]
fn mapping_ext_entry_or_default_mapping_preserves_prior_mapping_on_present_arm() {
// Present-key path with matching variant — the helper mirrors
// [`serde_yaml::mapping::Entry::or_insert_with`]'s occupied
// branch: the prior value is preserved, and the returned
// `&mut Mapping` points at that prior inner Mapping (NOT a
// fresh empty default). Pin the leave-prior-untouched semantic
// so a future refactor that reaches for an `.insert`-style
// overwrite flow doesn't silently clobber every idempotent-
// container-upsert consumer (the `feira app deploy` per-cluster
// write path, the M4 per-cluster HelmRelease overlay merger).
let mut m = serde_yaml::Mapping::new();
let mut prior_inner = serde_yaml::Mapping::new();
prior_inner.insert_str_key(HELM_VALUES_KEY_ENABLED, serde_yaml::Value::Bool(true));
m.insert_mapping(FLUX_KEY_VALUES, prior_inner.clone());
{
let inner = m
.entry_or_default_mapping(FLUX_KEY_VALUES)
.expect("present-Mapping-variant path returns Some(&mut prior)");
assert_eq!(
inner, &prior_inner,
"entry_or_default_mapping returns &mut prior on the \
present-key path — the default empty Mapping must not \
overwrite the emitter's prior write"
);
}
// Value at the key is still the pre-existing one, verbatim.
let got = m
.get(FLUX_KEY_VALUES)
.expect("key is still present after or_default on the present-key path");
assert_eq!(
got,
&serde_yaml::Value::Mapping(prior_inner),
"or_default on the present-key path preserves the prior \
value verbatim — no clobber, no reshape"
);
}
#[test]
fn mapping_ext_entry_or_default_mapping_returns_none_on_variant_mismatch() {
// Present-key path with mismatched variant — the helper returns
// `None`, letting the caller surface its domain-specific
// "expected Mapping at this schema key" diagnostic (rather than
// silently clobbering the mismatched prior value). Pin the
// structural-mismatch-is-None contract so a future refactor
// that reaches for a fallback-to-empty-default flow doesn't
// silently overwrite user-authored non-Mapping data at the
// canonical caixa-flux `Error::MissingField("spec.values must
// be a mapping")` site — the mismatched-variant arm is
// load-bearing for the domain-error diagnostic path, not just
// a corner case.
let mut m = serde_yaml::Mapping::new();
m.insert_string(FLUX_KEY_VALUES, "not-a-mapping");
let result = m.entry_or_default_mapping(FLUX_KEY_VALUES);
assert!(
result.is_none(),
"entry_or_default_mapping returns None on variant \
mismatch — the caller's `.ok_or(Error::MissingField(_))?` \
chain surfaces the structural type-mismatch diagnostic"
);
let got = m
.get(FLUX_KEY_VALUES)
.expect("mismatched-variant prior value stays present after variant-check");
assert_eq!(
got,
&serde_yaml::Value::String("not-a-mapping".into()),
"None arm on variant mismatch leaves the prior value \
untouched — the caller's domain-error path fires without \
clobbering the user-authored data"
);
}
#[test]
fn mapping_ext_entry_or_default_sequence_seeds_empty_inner_when_absent() {
// Absent-key path — the helper mints an empty
// `Value::Sequence(Vec::new())` under the promoted key and
// returns `Some(&mut inner)` pointing at the fresh empty
// `Vec<Value>`. Peer with
// [`mapping_ext_entry_or_default_mapping_seeds_empty_inner_when_absent`]
// on the nested-Mapping-container axis.
let mut m = serde_yaml::Mapping::new();
{
let inner = m
.entry_or_default_sequence(FLEET_PROGRAMS_KEY_PROGRAMS)
.expect("absent-key path seeds an empty Vec and returns Some(&mut _)");
assert!(
inner.is_empty(),
"the seeded default must be an EMPTY Vec — a non-empty \
pre-allocation would land a pre-populated fleet-programs \
list the emitter never authored"
);
}
let got = m
.get(FLEET_PROGRAMS_KEY_PROGRAMS)
.expect("or_default seeded the key under Value::String promotion");
assert_eq!(
got,
&serde_yaml::Value::Sequence(Vec::new()),
"entry_or_default_sequence seeds Value::Sequence(Vec::new()) \
verbatim on the absent-key arm — no reshape, no wrap"
);
}
#[test]
fn mapping_ext_entry_or_default_sequence_preserves_prior_sequence_on_present_arm() {
// Present-key path with matching variant — the helper mirrors
// [`serde_yaml::mapping::Entry::or_insert_with`]'s occupied
// branch: the prior `Vec` is preserved, and the returned
// `&mut Vec<Value>` points at that prior inner Vec (NOT a
// fresh empty default). The exact idempotent-upsert semantic
// caixa-flux's `upsert_into_programs_yaml` /
// `upsert_into_helmrelease_programs` depend on to preserve
// prior `programs[]` entries across per-Servico rewrites.
let mut m = serde_yaml::Mapping::new();
let prior_inner = vec![serde_yaml::Value::String("existing".into())];
m.insert_sequence(FLEET_PROGRAMS_KEY_PROGRAMS, prior_inner.clone());
{
let inner = m
.entry_or_default_sequence(FLEET_PROGRAMS_KEY_PROGRAMS)
.expect("present-Sequence-variant path returns Some(&mut prior)");
assert_eq!(
inner, &prior_inner,
"entry_or_default_sequence returns &mut prior on the \
present-key path — the default empty Vec must not \
overwrite the emitter's prior write"
);
}
let got = m
.get(FLEET_PROGRAMS_KEY_PROGRAMS)
.expect("key is still present after or_default on the present-key path");
assert_eq!(
got,
&serde_yaml::Value::Sequence(prior_inner),
"or_default on the present-key path preserves the prior \
value verbatim — no clobber, no reshape"
);
}
#[test]
fn mapping_ext_entry_or_default_sequence_returns_none_on_variant_mismatch() {
// Present-key path with mismatched variant — the helper returns
// `None`, letting the caller surface its domain-specific
// "programs must be a sequence" diagnostic (rather than
// silently clobbering the mismatched prior value). Pin the
// structural-mismatch-is-None contract so a future refactor
// that reaches for a fallback-to-empty-default flow doesn't
// silently overwrite user-authored non-Sequence data at the
// canonical caixa-flux `Error::MissingField("programs must be
// a sequence")` site.
let mut m = serde_yaml::Mapping::new();
m.insert_string(FLEET_PROGRAMS_KEY_PROGRAMS, "not-a-sequence");
let result = m.entry_or_default_sequence(FLEET_PROGRAMS_KEY_PROGRAMS);
assert!(
result.is_none(),
"entry_or_default_sequence returns None on variant \
mismatch — the caller's `.ok_or(Error::MissingField(_))?` \
chain surfaces the structural type-mismatch diagnostic"
);
let got = m
.get(FLEET_PROGRAMS_KEY_PROGRAMS)
.expect("mismatched-variant prior value stays present after variant-check");
assert_eq!(
got,
&serde_yaml::Value::String("not-a-sequence".into()),
"None arm on variant mismatch leaves the prior value \
untouched — the caller's domain-error path fires without \
clobbering the user-authored data"
);
}
// ── insert_str_key_if_some — arity-0-or-1 twin of insert_str_key ─────
#[test]
fn mapping_ext_insert_str_key_if_some_none_arm_leaves_mapping_untouched() {
// The None arm skips the insert entirely — no clone, no
// key-promotion, no bucket touch. Pin the no-op semantic so a
// future refactor that reaches for an `Option::unwrap_or_default`
// shape (which would emit `Value::Null` under the key on the
// None arm) or an `.into_iter().for_each` scaffold (which would
// still walk the bucket-lookup path) is a compile-visible break,
// not a silent per-consumer regression at the 3 lifted
// `caixa-mesh` overlay-insert sites (where the `None` arm is
// the author's default when no `:politicas` slot is set — a
// silent `Value::Null` emission would land a K8s CRD schema
// rejection at every unset-slot Aplicacao).
let mut m = serde_yaml::Mapping::new();
let prior = m.insert_str_key_if_some(CILIUM_KEY_AUTHENTICATION, None);
assert_eq!(
prior, None,
"insert_str_key_if_some(K, None) returns None — no insert \
fires, so no prior value can be surfaced"
);
assert!(
m.get(CILIUM_KEY_AUTHENTICATION).is_none(),
"None arm must leave the key absent — a silent `Value::Null` \
insertion would land a K8s CRD schema rejection at every \
`:politicas`-unset Aplicacao"
);
assert_eq!(
m.len(),
0,
"None arm must not touch any bucket — the Mapping stays \
empty verbatim"
);
}
#[test]
fn mapping_ext_insert_str_key_if_some_some_arm_promotes_key_to_yaml_string() {
// The Some arm clones the borrowed inner value and delegates to
// [`Self::insert_str_key`] — pin the promotion + the first-
// insert-returns-None contract so a future refactor that reaches
// for a different `Value` variant for the key (e.g.
// `Value::Tagged`) or breaks the underlying
// [`serde_yaml::Mapping::insert`] return contract is a compile-
// visible break, not a silent per-consumer regression at the 3
// lifted `caixa-mesh` overlay-insert sites. Peer with the sibling
// [`mapping_ext_insert_str_key_promotes_key_to_yaml_string`] on
// the always-1 arity axis of the same key promotion.
let mut m = serde_yaml::Mapping::new();
let overlay = serde_yaml::Value::Mapping({
let mut inner = serde_yaml::Mapping::new();
inner.insert_str_key(
CILIUM_KEY_MODE,
serde_yaml::Value::String("required".into()),
);
inner
});
let prior = m.insert_str_key_if_some(CILIUM_KEY_AUTHENTICATION, Some(&overlay));
assert_eq!(
prior, None,
"insert_str_key_if_some(K, Some(&V)) returns None on first \
insertion, mirroring serde_yaml::Mapping::insert"
);
// Key is exactly the `Value::String` promotion of the input.
let got = m
.get(CILIUM_KEY_AUTHENTICATION)
.expect("Some arm inserts under the Value::String-promoted key");
assert_eq!(
got, &overlay,
"insert_str_key_if_some routes the borrowed inner value \
through a `.clone()` verbatim to the underlying \
`insert_str_key` path — no reshape, no wrap, no unwrap"
);
// The borrowed input is untouched — the caller can reuse the
// outer overlay binding across the next iteration of a per-
// `(:de, :para)` loop (the exact reuse the three lifted
// caixa-mesh sites depend on).
assert!(
overlay.get(CILIUM_KEY_MODE).is_some(),
"insert_str_key_if_some must not move out of the borrowed \
overlay — the caller-side outer binding stays available \
for the next iteration of the enclosing per-`(:de, :para)` \
or per-rule loop"
);
}
#[test]
fn mapping_ext_insert_str_key_if_some_some_arm_returns_prior_value_on_replace() {
// The Some arm mirrors [`serde_yaml::Mapping::insert`]'s return
// contract on the replace-existing path: the prior value at that
// key, surfaced verbatim. Pin the replace-returns-prior semantic
// so a future refactor that reaches for an `entry.or_insert`-
// style preserve-prior flow doesn't silently swap the axis's
// semantic under the three routed caixa-mesh overlay sites (the
// `:politicas` overlay is meant to override an author-provided
// sub-block if one was present, not preserve it — the
// replace-and-return-prior semantic is load-bearing).
let mut m = serde_yaml::Mapping::new();
let existing = serde_yaml::Value::String("cluster-default".into());
let overlay = serde_yaml::Value::Mapping({
let mut inner = serde_yaml::Mapping::new();
inner.insert_str_key(
GATEWAY_API_KEY_REQUEST,
serde_yaml::Value::String("30s".into()),
);
inner
});
m.insert_str_key(GATEWAY_API_KEY_TIMEOUTS, existing.clone());
let prior = m.insert_str_key_if_some(GATEWAY_API_KEY_TIMEOUTS, Some(&overlay));
assert_eq!(
prior,
Some(existing),
"insert_str_key_if_some(K, Some(&V)) returns the prior value \
when replacing an existing key — the overlay overrides the \
author-provided sub-block; the prior value surfaces so the \
caller can log/compare/roll back if needed"
);
// Value at the key is now the overlay, verbatim.
let got = m
.get(GATEWAY_API_KEY_TIMEOUTS)
.expect("key is still present after replace");
assert_eq!(
got, &overlay,
"replaced value is now the most-recently-inserted overlay — \
the Some arm carries through to the underlying \
`insert_str_key` replace path"
);
}
#[test]
fn mapping_ext_insert_str_key_if_some_matches_hand_written_composition() {
// Cross-check the trait method against the hand-written
// `if let Some(x) = &overlay { m.insert_str_key(K, x.clone()); }`
// three-line block the 3 lifted `caixa-mesh` overlay call sites
// previously carried. A drift between the trait method's
// conditional-insert routing and the inline `if let Some`
// composition would silently emit a different Mapping (a
// present-key `Value::Null` on the None arm, a different clone-
// vs-move policy on the Some arm) at every routed consumer —
// pin the equivalence so the trait remains a drop-in replacement.
// Four cases pin the shape end-to-end: None arm (skip), Some
// arm on absent key (fresh insert), Some arm on present key
// (replace-and-return-prior), None arm on present key (no
// touch — the axis's load-bearing "author's value wins when
// overlay is unset" contract).
let overlay = serde_yaml::Value::Mapping({
let mut inner = serde_yaml::Mapping::new();
inner.insert_str_key(
CILIUM_KEY_MODE,
serde_yaml::Value::String("required".into()),
);
inner
});
// Case 1: None arm on empty mapping — both routes no-op.
let mut via_trait_none = serde_yaml::Mapping::new();
via_trait_none.insert_str_key_if_some(CILIUM_KEY_AUTHENTICATION, None);
let via_inline_none = serde_yaml::Mapping::new();
let overlay_slot_none: Option<serde_yaml::Value> = None;
let mut via_inline_none_mut = via_inline_none.clone();
if let Some(a) = &overlay_slot_none {
via_inline_none_mut.insert_str_key(CILIUM_KEY_AUTHENTICATION, a.clone());
}
assert_eq!(
via_trait_none, via_inline_none_mut,
"insert_str_key_if_some(K, None) must byte-equal \
`if let Some(_) = None {{ … }}` — the no-op arm must not \
emit a stray `Value::Null` under the key"
);
// Case 2: Some arm on empty mapping — both routes fresh-insert.
let mut via_trait_some = serde_yaml::Mapping::new();
via_trait_some.insert_str_key_if_some(CILIUM_KEY_AUTHENTICATION, Some(&overlay));
let mut via_inline_some = serde_yaml::Mapping::new();
let overlay_slot_some = Some(overlay.clone());
if let Some(a) = &overlay_slot_some {
via_inline_some.insert_str_key(CILIUM_KEY_AUTHENTICATION, a.clone());
}
assert_eq!(
via_trait_some, via_inline_some,
"insert_str_key_if_some(K, Some(&V)) must byte-equal \
`if let Some(x) = &Some(V.clone()) {{ m.insert_str_key(K, \
x.clone()); }}` on the fresh-insert path — same clone-and-\
insert semantics under the same Value::String-promoted \
bucket"
);
// Case 3: Some arm on present key — both routes replace-and-
// return-prior.
let existing = serde_yaml::Value::String("cluster-default".into());
let mut via_trait_replace = serde_yaml::Mapping::new();
via_trait_replace.insert_str_key(GATEWAY_API_KEY_TIMEOUTS, existing.clone());
let trait_prior =
via_trait_replace.insert_str_key_if_some(GATEWAY_API_KEY_TIMEOUTS, Some(&overlay));
let mut via_inline_replace = serde_yaml::Mapping::new();
via_inline_replace.insert_str_key(GATEWAY_API_KEY_TIMEOUTS, existing.clone());
let overlay_slot_replace = Some(overlay.clone());
let inline_prior = if let Some(a) = &overlay_slot_replace {
via_inline_replace.insert_str_key(GATEWAY_API_KEY_TIMEOUTS, a.clone())
} else {
None
};
assert_eq!(
trait_prior, inline_prior,
"insert_str_key_if_some replace-and-return-prior must byte-\
equal the hand-written `if let Some {{ insert_str_key }}` \
composition's return"
);
assert_eq!(
via_trait_replace, via_inline_replace,
"insert_str_key_if_some replace-post-state must byte-equal \
the hand-written composition's post-state — the overlay \
overrode the author's value in both routes"
);
// Case 4: None arm on present key — both routes preserve the
// author's value verbatim. The load-bearing "author's value
// wins when overlay is unset" contract the three lifted sites
// depend on.
let mut via_trait_preserve = serde_yaml::Mapping::new();
via_trait_preserve.insert_str_key(GATEWAY_API_KEY_TIMEOUTS, existing.clone());
via_trait_preserve.insert_str_key_if_some(GATEWAY_API_KEY_TIMEOUTS, None);
let mut via_inline_preserve = serde_yaml::Mapping::new();
via_inline_preserve.insert_str_key(GATEWAY_API_KEY_TIMEOUTS, existing.clone());
let overlay_slot_preserve: Option<serde_yaml::Value> = None;
if let Some(a) = &overlay_slot_preserve {
via_inline_preserve.insert_str_key(GATEWAY_API_KEY_TIMEOUTS, a.clone());
}
assert_eq!(
via_trait_preserve, via_inline_preserve,
"insert_str_key_if_some(K, None) on a present key must byte-\
equal the hand-written `if let Some(_) = None {{ … }}` — \
the None arm must preserve the author's value verbatim, \
not clobber it with `Value::Null` or drop the key"
);
assert_eq!(
via_trait_preserve
.get(GATEWAY_API_KEY_TIMEOUTS)
.expect("None arm preserves the pre-existing key"),
&existing,
"None arm on a present key surfaces the author's prior \
value verbatim — the load-bearing contract the three \
lifted `:politicas` overlay sites rest on"
);
}
// ── SequenceExt::push_mapping — Vec<Value>-side sibling ──────────────
#[test]
fn sequence_ext_push_mapping_appends_promoted_mapping_value() {
// The method appends the caller's `Mapping` as a fresh
// `Value::Mapping(_)` element on the tail of `self`. Pin the
// per-append routing (`.push(Value::Mapping(_))`) so a future
// refactor that reaches for a different outer variant (a
// Server-Side-Apply-typed `Value::Tagged`, a fresh singleton-list
// wrap via `singleton_mapping_sequence`) or a different
// Vec-mutation shape (e.g. `.insert(0, _)` shifting the axis
// from append to prepend) is a compile-visible break, not a
// silent per-consumer regression at the 4 lifted `caixa-mesh`
// append sites — where the emission order is load-bearing (the
// Cilium `spec.ingress[].toPorts[]` per-edge order, the
// Gateway API `spec.rules[]` per-path order, the top-level CNP
// and programs.yaml document order all depend on the append
// semantics).
let mut seq: Vec<serde_yaml::Value> = Vec::new();
let mut m = serde_yaml::Mapping::new();
m.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("first".into()));
seq.push_mapping(m.clone());
assert_eq!(
seq.len(),
1,
"push_mapping must append exactly one element — the axis's \
fresh-element semantic"
);
assert_eq!(
seq[0],
serde_yaml::Value::Mapping(m),
"the appended element must be the caller's Mapping wrapped \
verbatim as Value::Mapping — no reshape, no clone-and-drop"
);
}
#[test]
fn sequence_ext_push_mapping_preserves_prior_elements_in_insertion_order() {
// Successive push_mapping calls preserve the caller's per-
// iteration order — the Vec grows at the tail, prior elements
// stay at their prior indices. Pin the insertion-order semantic
// so a future refactor that reaches for a per-append sort /
// dedup / hoist-to-front reordering is a test-visible break,
// not a silent behavior shift at the 4 lifted `caixa-mesh`
// append sites (where THEORY.md §V.2.7 render determinism
// pins the per-iteration emission order to the source
// `:contratos` / `:paths` / `:membros` declaration order).
let mut seq: Vec<serde_yaml::Value> = Vec::new();
let mut first = serde_yaml::Mapping::new();
first.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("a".into()));
let mut second = serde_yaml::Mapping::new();
second.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("b".into()));
let mut third = serde_yaml::Mapping::new();
third.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("c".into()));
seq.push_mapping(first.clone());
seq.push_mapping(second.clone());
seq.push_mapping(third.clone());
assert_eq!(
seq.len(),
3,
"three push_mapping calls append three elements"
);
assert_eq!(
seq,
vec![
serde_yaml::Value::Mapping(first),
serde_yaml::Value::Mapping(second),
serde_yaml::Value::Mapping(third),
],
"push_mapping preserves per-iteration insertion order — the \
axis's render-determinism contract at the 4 lifted \
`caixa-mesh` append sites"
);
}
#[test]
fn sequence_ext_push_mapping_matches_hand_written_composition() {
// Cross-check the trait method against the hand-written
// `<vec>.push(serde_yaml::Value::Mapping(<M>))` three-token
// block the 4 lifted `caixa-mesh` append call sites previously
// carried. A drift between the trait method's routing and the
// inline `Value::Mapping(_)` promotion would silently emit a
// different `Vec<Value>` (a different outer variant on the
// appended element, a different length, a different order) at
// every routed consumer — pin the equivalence so the trait
// remains a drop-in replacement across the fresh-empty, prior-
// populated, and empty-payload cases.
// Case 1: fresh-empty Vec + non-empty Mapping payload.
let mut inner = serde_yaml::Mapping::new();
inner.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("policy-a".into()));
let mut via_trait: Vec<serde_yaml::Value> = Vec::new();
via_trait.push_mapping(inner.clone());
let mut via_inline: Vec<serde_yaml::Value> = Vec::new();
via_inline.push(serde_yaml::Value::Mapping(inner.clone()));
assert_eq!(
via_trait, via_inline,
"push_mapping(M) on empty Vec must byte-equal \
`.push(Value::Mapping(M))` — same variant-promotion, same \
append semantics"
);
// Case 2: prior-populated Vec + non-empty Mapping payload — pin
// that the append fires at the tail, not at the head or the
// middle.
let seed = serde_yaml::Value::String("seed".into());
let mut via_trait_populated: Vec<serde_yaml::Value> = vec![seed.clone()];
via_trait_populated.push_mapping(inner.clone());
let mut via_inline_populated: Vec<serde_yaml::Value> = vec![seed];
via_inline_populated.push(serde_yaml::Value::Mapping(inner.clone()));
assert_eq!(
via_trait_populated, via_inline_populated,
"push_mapping(M) on populated Vec must byte-equal \
`.push(Value::Mapping(M))` — the append fires at the tail, \
prior elements stay at their prior indices"
);
// Case 3: empty Mapping payload — the axis's "empty-vs-absent"
// distinction the 4 lifted sites rest on. An empty inner
// `Mapping` still round-trips as a `Value::Mapping(<empty>)`
// element, not as a skipped no-op, because some K8s CRD schemas
// (Cilium CNP `spec.ingress[].toPorts[].rules.http[]` with an
// empty match set) require an empty inner object to distinguish
// "explicitly-empty" from "absent".
let mut via_trait_empty: Vec<serde_yaml::Value> = Vec::new();
via_trait_empty.push_mapping(serde_yaml::Mapping::new());
let mut via_inline_empty: Vec<serde_yaml::Value> = Vec::new();
via_inline_empty.push(serde_yaml::Value::Mapping(serde_yaml::Mapping::new()));
assert_eq!(
via_trait_empty, via_inline_empty,
"push_mapping(empty Mapping) must byte-equal \
`.push(Value::Mapping(empty))` — no is_empty()-guarded \
short-circuit, no skip"
);
assert_eq!(
via_trait_empty.len(),
1,
"push_mapping on an empty Mapping still appends one element \
— the axis carries no is_empty() short-circuit"
);
}
#[test]
fn singleton_mapping_sequence_wraps_input_as_sole_element() {
// The helper wraps its input `Mapping` as the single element of
// a `Value::Sequence`. Pin the outer variant shape and the
// exactly-one-element length so a future refactor that reaches
// for a different container (e.g. `Value::Tagged`, a
// 0-or-1-element `Option`-shaped emission axis) is a
// compile-visible break, not a silent per-caller regression at
// every K8s-CRD-list-shape-required emit site.
let mut inner = serde_yaml::Mapping::new();
inner.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("hello".into()));
let out = singleton_mapping_sequence(inner.clone());
match out {
serde_yaml::Value::Sequence(seq) => {
assert_eq!(
seq.len(),
1,
"singleton_mapping_sequence emits exactly one element — \
the K8s-CRD-list-shape-required singleton axis"
);
assert_eq!(
seq[0],
serde_yaml::Value::Mapping(inner),
"the sole element must be the caller's Mapping wrapped \
verbatim as Value::Mapping — no reshape, no clone-and-drop"
);
}
other => panic!(
"singleton_mapping_sequence must return Value::Sequence, got {other:?} — \
an outer-variant drift breaks every K8s-CRD-list-shape consumer"
),
}
}
#[test]
fn singleton_mapping_sequence_preserves_empty_inner_mapping() {
// An empty inner `Mapping` still round-trips through the helper
// as a `Value::Sequence(vec![Value::Mapping(<empty>)])` — the
// helper carries no "skip-empty" short-circuit (empty-vs-absent
// is the caller's decision; some K8s CRD schemas require an
// empty inner object to distinguish "explicitly-empty" from
// "absent"). Pin the shape so a future refactor that reaches
// for an is_empty()-guarded short-circuit is a test-visible
// break, not a silent behavior shift.
let out = singleton_mapping_sequence(serde_yaml::Mapping::new());
let seq = match out {
serde_yaml::Value::Sequence(s) => s,
other => panic!("expected Value::Sequence, got {other:?}"),
};
assert_eq!(seq.len(), 1, "empty inner still wraps as a 1-element seq");
assert_eq!(
seq[0],
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
"the sole element is an empty Value::Mapping, verbatim"
);
}
#[test]
fn singleton_mapping_sequence_byte_equals_hand_written_inline_shape() {
// Cross-check the helper against the hand-written
// `Value::Sequence(vec![Value::Mapping(m)])` three-token shape
// the seven lifted call sites previously carried. A drift
// between the helper's wrapping and the inline shape would
// silently emit a different YAML sequence (a differently-shaped
// outer variant, a differently-wrapped inner Mapping) at every
// routed consumer — pin the byte-equivalence so the helper
// remains a drop-in replacement.
let mut inner = serde_yaml::Mapping::new();
inner.insert_str_key(
GATEWAY_API_KEY_NAME,
serde_yaml::Value::String("gw-listener".into()),
);
inner.insert_str_key(
KUBE_KEY_PORT,
serde_yaml::Value::Number(GATEWAY_API_DEFAULT_HTTP_LISTENER_PORT.into()),
);
let via_helper = singleton_mapping_sequence(inner.clone());
let via_inline = serde_yaml::Value::Sequence(vec![serde_yaml::Value::Mapping(inner)]);
assert_eq!(
via_helper, via_inline,
"singleton_mapping_sequence(m) must byte-equal \
Value::Sequence(vec![Value::Mapping(m)]) — otherwise the \
seven routed caixa-mesh call sites drift silently at emit time"
);
}
#[test]
fn string_keyed_entries_yields_each_string_key_and_value_ref() {
// The lift's load-bearing contract: given a Value::Mapping with
// string keys, yield each `(&str, &Value)` pair in insertion
// order. Both routed renderers (caixa-flux::programs_yaml_entry
// and caixa-helm::build_values_yaml) depend on the yielded pair
// shape to drive their per-destination insert — a drift in
// yielded item type is a compile-visible break, not a silent
// shape shift.
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(
COMPUTEUNIT_SPEC_KEY_MODULE,
serde_yaml::Value::String("oci://…".into()),
);
spec.insert_str_key(
COMPUTEUNIT_SPEC_KEY_TRIGGER,
serde_yaml::Value::String("http".into()),
);
spec.insert_str_key(
COMPUTEUNIT_SPEC_KEY_CAPABILITIES,
serde_yaml::Value::Sequence(vec![]),
);
let v = serde_yaml::Value::Mapping(spec);
let keys: Vec<&str> = string_keyed_entries(&v).map(|(k, _)| k).collect();
assert_eq!(
keys,
vec![
COMPUTEUNIT_SPEC_KEY_MODULE,
COMPUTEUNIT_SPEC_KEY_TRIGGER,
COMPUTEUNIT_SPEC_KEY_CAPABILITIES,
],
"string_keyed_entries must yield every string-keyed entry in \
the underlying Mapping's insertion order — both routed \
renderers depend on `spec.module` reaching the destination \
ahead of `spec.trigger` ahead of `spec.capabilities` so the \
emitted values.yaml / programs.yaml entry's key order tracks \
the upstream ComputeUnit YAML author's order"
);
// The paired &Value ref also reaches through — sanity-check on
// the second axis of the yielded tuple.
let module = string_keyed_entries(&v)
.find(|(k, _)| *k == COMPUTEUNIT_SPEC_KEY_MODULE)
.map(|(_, v)| v.clone())
.expect("module entry present");
assert_eq!(module, serde_yaml::Value::String("oci://…".into()));
}
#[test]
fn string_keyed_entries_short_circuits_on_non_mapping_shapes() {
// The prior inline `if let Value::Mapping(_) = spec { … }` arm
// silently no-oped on every non-Mapping shape (Null / String /
// Sequence / Number / Bool). The lift's iterator surface pins
// the same contract: a non-Mapping Value contributes zero
// yielded entries. Pinned because both routed renderers'
// "always splice `spec.*` if it's a Mapping, otherwise skip"
// contract is upstream-schema-validated at the ComputeUnit CRD
// parser but not at the renderer entry point — so a legally-
// authored `spec: null` short-circuits without raising.
for shape in [
serde_yaml::Value::Null,
serde_yaml::Value::String("scalar".into()),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::Number(0.into()),
serde_yaml::Value::Bool(false),
] {
let count = string_keyed_entries(&shape).count();
assert_eq!(
count, 0,
"string_keyed_entries({shape:?}) must yield zero entries — \
the prior `if let Value::Mapping(_)` arm silently \
short-circuited on this shape, so the lift must preserve \
that no-op contract or every routed renderer regresses on \
the legally-authored non-Mapping `spec:` axis"
);
}
}
#[test]
fn string_keyed_entries_drops_non_string_keys() {
// serde_yaml permits arbitrary `Value` keys — numeric, boolean,
// sub-mapping — that don't round-trip through the downstream
// K8s YAML-key surface (which requires string keys). Both
// routed renderers previously carried an inline `if let Some(s)
// = k.as_str()` filter to silently drop these; pin the lift's
// filter contract so a future refactor that reaches for
// `.as_str().unwrap()` (which would panic on a numeric key) is
// a test-visible break, not a runtime regression at the first
// ComputeUnit YAML that carries one.
let mut spec = serde_yaml::Mapping::new();
spec.insert(
serde_yaml::Value::String(COMPUTEUNIT_SPEC_KEY_MODULE.into()),
serde_yaml::Value::String("oci://…".into()),
);
spec.insert(
serde_yaml::Value::Number(42.into()),
serde_yaml::Value::String("dropped".into()),
);
spec.insert(
serde_yaml::Value::Bool(true),
serde_yaml::Value::String("also-dropped".into()),
);
spec.insert(
serde_yaml::Value::String(COMPUTEUNIT_SPEC_KEY_TRIGGER.into()),
serde_yaml::Value::String("http".into()),
);
let v = serde_yaml::Value::Mapping(spec);
let keys: Vec<&str> = string_keyed_entries(&v).map(|(k, _)| k).collect();
assert_eq!(
keys,
vec![COMPUTEUNIT_SPEC_KEY_MODULE, COMPUTEUNIT_SPEC_KEY_TRIGGER],
"string_keyed_entries must silently drop non-string-keyed \
entries (Value::Number, Value::Bool, Value::Mapping keys) \
— the K8s YAML-key surface downstream requires string keys, \
and every routed renderer's inline `k.as_str()` filter \
expected exactly this drop-not-panic contract"
);
}
#[test]
fn string_keyed_entries_matches_prior_inline_walk() {
// Cross-check the helper's yielded sequence against the prior
// inline `if let Value::Mapping(_) = spec { for (k, v) in _ {
// if let Some(s) = k.as_str() { <collect (s, v.clone())> } } }`
// walk both renderers previously carried. A drift between the
// helper's yielded sequence and the inline walk would silently
// emit a different destination map at every routed consumer —
// pin the byte-equivalence so the helper remains a drop-in
// replacement for both renderers' prior five-line block.
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(
COMPUTEUNIT_SPEC_KEY_MODULE,
serde_yaml::Value::String("oci://ghcr.io/pleme-io/hello-rio:0.1.0".into()),
);
spec.insert(
serde_yaml::Value::Number(1.into()),
serde_yaml::Value::String("silently-dropped".into()),
);
spec.insert_str_key(
COMPUTEUNIT_SPEC_KEY_TRIGGER,
serde_yaml::Value::String("http".into()),
);
let v = serde_yaml::Value::Mapping(spec);
let via_helper: Vec<(String, serde_yaml::Value)> = string_keyed_entries(&v)
.map(|(k, v)| (k.to_string(), v.clone()))
.collect();
let mut via_inline: Vec<(String, serde_yaml::Value)> = Vec::new();
if let serde_yaml::Value::Mapping(map) = &v {
for (k, v) in map {
if let Some(s) = k.as_str() {
via_inline.push((s.to_string(), v.clone()));
}
}
}
assert_eq!(
via_helper, via_inline,
"string_keyed_entries must yield the same (String, Value) \
sequence as the prior inline `if let Value::Mapping + for + \
if let Some(k.as_str())` walk — otherwise the two routed \
renderers drift silently at ComputeUnit-YAML-`spec.*`-splice \
time"
);
}
#[test]
fn kube_metadata_str_field_reads_metadata_name_and_namespace_string_scalars() {
// The lift's load-bearing contract: given a Value carrying a
// top-level `metadata: { name: <str>, namespace: <str> }` block
// (every K8s CR document the emit-side `kube_resource_skeleton`
// renders), the helper returns Some(<str>) borrowing into the
// input Value. Pinned because every routed test-side site (the
// six caixa-mesh CNP filters + the caixa-flux kustomization.yaml
// pin) reaches through this exact string-scalar readback, and a
// drift in the borrowed-string contract would silently regress
// every routed site's per-CR filter equality.
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(
KUBE_KEY_NAME,
serde_yaml::Value::String("checkout-cart-to-catalog".into()),
);
metadata.insert_str_key(
KUBE_KEY_NAMESPACE,
serde_yaml::Value::String(DEFAULT_NAMESPACE.into()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata_str_field(&value, KUBE_KEY_NAME),
Some("checkout-cart-to-catalog"),
"kube_metadata_str_field must read metadata.name as a string \
scalar — the six caixa-mesh CNP per-`(:de, :para)` filter \
sites reach through this axis for policy-identity equality"
);
assert_eq!(
kube_metadata_str_field(&value, KUBE_KEY_NAMESPACE),
Some(DEFAULT_NAMESPACE),
"kube_metadata_str_field must read metadata.namespace as a \
string scalar — the caixa-flux programs_yaml_entry \
production readback + the cluster_bundle kustomization.yaml \
test pin both reach through this axis"
);
}
#[test]
fn kube_metadata_str_field_returns_none_when_metadata_block_absent() {
// Every K8s CR document the emit-side `kube_resource_skeleton`
// renders carries a `metadata:` block, but the readback surface
// is called on arbitrary Value inputs (upstream ComputeUnit
// YAML documents, external YAML documents parsed by tests) that
// may legally omit the block. The prior inline three-hop chain
// silently short-circuits on the first `.get(KUBE_KEY_METADATA)`
// hop when the block is absent; pin the helper's None return so
// the prior no-panic contract holds. The two production-shape
// paths — caixa-flux's `programs_yaml_entry` production
// readback with `.unwrap_or(DEFAULT_NAMESPACE)` fallback, the
// caixa-mesh test-side `.unwrap()` after equality-filter —
// both depend on this None-arm for their fallback / test-harness
// semantics.
let value = serde_yaml::Value::Mapping(serde_yaml::Mapping::new());
assert_eq!(
kube_metadata_str_field(&value, KUBE_KEY_NAME),
None,
"kube_metadata_str_field must short-circuit to None when the \
top-level `metadata:` block is absent — the prior inline \
chain's `.get(KUBE_KEY_METADATA)` outer hop returned None \
here, and every routed caller (production fallback + test \
expect) depends on the None-arm reaching through"
);
// Also verify the shape on a non-Mapping outer Value — the K8s
// CR readback surface accepts arbitrary Value inputs, including
// the Value::Null / Value::Sequence / Value::String shapes an
// external YAML document may parse into.
for shape in [
serde_yaml::Value::Null,
serde_yaml::Value::String("scalar".into()),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::Number(0.into()),
serde_yaml::Value::Bool(false),
] {
assert_eq!(
kube_metadata_str_field(&shape, KUBE_KEY_NAME),
None,
"kube_metadata_str_field({shape:?}, KUBE_KEY_NAME) must \
return None on non-Mapping shapes — the prior inline \
`.get(KUBE_KEY_METADATA)` hop yields None on every \
non-Mapping Value, and the lift must preserve that \
contract"
);
}
}
#[test]
fn kube_metadata_str_field_returns_none_when_requested_field_absent() {
// A `metadata:` block present but missing the requested axis-key
// — a well-formed K8s CR that legally omits the requested field
// (a Cluster-scoped CR omits `metadata.namespace`, a
// Server-Side-Apply-authored CR omits `metadata.name` in favor
// of `metadata.generateName`). Every routed caller expects the
// three-hop chain to short-circuit through here to None; pin
// the middle-hop None-arm so a future refactor that reaches for
// `.get(field).unwrap()` (which would panic on a legally-omitted
// axis-key) is a test-visible break.
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(
KUBE_KEY_NAME,
serde_yaml::Value::String("cluster-scoped-cr".into()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata_str_field(&value, KUBE_KEY_NAMESPACE),
None,
"kube_metadata_str_field must return None when the requested \
`metadata.<field>` axis-key is absent — the prior inline \
chain's middle `.and_then(|m| m.get(<FIELD>))` hop short- \
circuited here, and the lift must preserve that None-arm \
for every legally-omitted axis-key"
);
}
#[test]
fn kube_metadata_str_field_returns_none_when_field_carries_non_string_type() {
// A `metadata.<field>` axis-key present but carrying a non-
// string YAML type — schema-invalid per the K8s apiserver's
// OpenAPI schema but tolerated here as None so the readback
// stays a total function. The prior inline chain's trailing
// `.and_then(|n| n.as_str())` shape gate silently short-
// circuits here; pin the helper's None-arm so a future refactor
// that reaches for `.as_str().unwrap()` (which would panic on
// a numeric axis-value) is a test-visible break, not a runtime
// regression at the first schema-invalid CR the reader sees.
for non_string in [
serde_yaml::Value::Null,
serde_yaml::Value::Number(42.into()),
serde_yaml::Value::Bool(true),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
] {
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_NAME, non_string.clone());
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata_str_field(&value, KUBE_KEY_NAME),
None,
"kube_metadata_str_field must return None when \
metadata.name carries a non-string YAML type ({non_string:?}) \
— the prior inline chain's `.and_then(|n| n.as_str())` \
shape gate short-circuited here, and every routed caller \
depends on that None-arm to keep the readback total"
);
}
}
#[test]
fn kube_metadata_str_field_matches_prior_inline_chain() {
// Cross-check the helper's output byte-for-byte against the
// prior inline three-hop chain both routed callers previously
// carried. A drift between the helper's return and the inline
// chain would silently regress every routed test-side filter's
// equality comparison + the caixa-flux production readback's
// fallback semantics — pin the byte-equivalence so the helper
// remains a drop-in replacement for every routed site's prior
// three-line block.
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(
KUBE_KEY_NAME,
serde_yaml::Value::String("checkout-payment-to-cart".into()),
);
metadata.insert_str_key(
KUBE_KEY_NAMESPACE,
serde_yaml::Value::String(DEFAULT_NAMESPACE.into()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
for field in [KUBE_KEY_NAME, KUBE_KEY_NAMESPACE] {
let via_helper = kube_metadata_str_field(&value, field);
let via_inline = value
.get(KUBE_KEY_METADATA)
.and_then(|m| m.get(field))
.and_then(|n| n.as_str());
assert_eq!(
via_helper, via_inline,
"kube_metadata_str_field(_, {field:?}) must yield the same \
Option<&str> as the prior inline three-hop chain — \
otherwise every routed caller's equality-filter / \
production-fallback drifts silently at readback time"
);
}
}
#[test]
fn kube_root_str_field_reads_api_version_and_kind_string_scalars() {
// The lift's load-bearing contract: given a Value carrying
// top-level `apiVersion:` + `kind:` string scalars (every K8s
// CR document the emit-side `kube_resource_skeleton` renders
// spells the pair by construction), the helper returns
// Some(<str>) borrowing into the input Value on both axes.
// Pinned because every routed test-side site — the
// caixa-flux `cluster_bundle_*_uses_lifted_flux_api_version`
// per-document apiVersion pins + the caixa-mesh
// `gateway_routes` per-`(Gateway, HTTPRoute)` kind-filter
// + the sibling caixa-mesh
// `cilium_authentication_mode_serialized_as_yaml_string`
// CNP-kind filter — reaches through this exact top-level
// string-scalar readback, and a drift in the borrowed-string
// contract would silently regress every routed site's
// per-CR filter / discriminator-pin equality.
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(
KUBE_KEY_API_VERSION,
serde_yaml::Value::String(GATEWAY_API_API_VERSION.into()),
);
cr.insert_str_key(
KUBE_KEY_KIND,
serde_yaml::Value::String(GATEWAY_API_KIND_GATEWAY.into()),
);
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_root_str_field(&value, KUBE_KEY_API_VERSION),
Some(GATEWAY_API_API_VERSION),
"kube_root_str_field must read top-level apiVersion as a \
string scalar — the caixa-flux `cluster_bundle_*_uses_\
lifted_flux_api_version` pins + caixa-mesh per-CR \
apiVersion pins reach through this axis for discriminator \
equality"
);
assert_eq!(
kube_root_str_field(&value, KUBE_KEY_KIND),
Some(GATEWAY_API_KIND_GATEWAY),
"kube_root_str_field must read top-level kind as a string \
scalar — the 15 caixa-mesh `gateway_routes` per-CR find \
sites reach through this axis to filter the multi-doc \
emission sequence by kind discriminator"
);
}
#[test]
fn kube_root_str_field_returns_none_when_field_absent() {
// Every K8s CR document the emit-side `kube_resource_skeleton`
// renders carries `apiVersion:` + `kind:` scalars, but the
// readback surface is called on arbitrary Value inputs
// (multi-doc sequences under iteration, upstream ComputeUnit
// YAML documents) that may legally omit either axis-key. The
// prior inline two-hop chain silently short-circuits on the
// outer `.get(field)` hop when the axis is absent; pin the
// helper's None return so the prior no-panic contract holds.
// Also verify on non-Mapping outer Value shapes an external
// YAML document may parse into.
let value = serde_yaml::Value::Mapping(serde_yaml::Mapping::new());
assert_eq!(
kube_root_str_field(&value, KUBE_KEY_API_VERSION),
None,
"kube_root_str_field must short-circuit to None when the \
requested top-level axis-key is absent — the prior inline \
`.get(field)` outer hop returned None here, and every \
routed caller (test pin + filter predicate) depends on \
that None-arm reaching through"
);
assert_eq!(
kube_root_str_field(&value, KUBE_KEY_KIND),
None,
"kube_root_str_field must short-circuit to None on a \
missing top-level kind axis-key — every routed \
caixa-mesh find-predicate compares against Some(<KIND>) \
and must reject None-shaped entries silently"
);
for shape in [
serde_yaml::Value::Null,
serde_yaml::Value::String("scalar".into()),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::Number(0.into()),
serde_yaml::Value::Bool(false),
] {
assert_eq!(
kube_root_str_field(&shape, KUBE_KEY_KIND),
None,
"kube_root_str_field({shape:?}, KUBE_KEY_KIND) must \
return None on non-Mapping shapes — the prior inline \
`.get(field)` hop yields None on every non-Mapping \
Value, and the lift must preserve that contract"
);
}
}
#[test]
fn kube_root_str_field_returns_none_when_field_carries_non_string_type() {
// A top-level `<field>` axis-key present but carrying a non-
// string YAML type — schema-invalid per the K8s apiserver's
// OpenAPI schema but tolerated here as None so the readback
// stays a total function. The prior inline chain's trailing
// `.and_then(|n| n.as_str())` shape gate silently short-
// circuits here; pin the helper's None-arm so a future
// refactor that reaches for `.as_str().unwrap()` (which would
// panic on a numeric axis-value) is a test-visible break, not
// a runtime regression at the first schema-invalid CR the
// reader sees.
for non_string in [
serde_yaml::Value::Null,
serde_yaml::Value::Number(42.into()),
serde_yaml::Value::Bool(true),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
] {
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_KIND, non_string.clone());
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_root_str_field(&value, KUBE_KEY_KIND),
None,
"kube_root_str_field must return None when top-level \
kind carries a non-string YAML type ({non_string:?}) \
— the prior inline `.and_then(|n| n.as_str())` shape \
gate short-circuited here, and every routed caller \
depends on that None-arm to keep the readback total"
);
}
}
#[test]
fn kube_root_str_field_matches_prior_inline_chain() {
// Cross-check the helper's output byte-for-byte against the
// prior inline two-hop chain both routed renderers previously
// carried. A drift between the helper's return and the inline
// chain would silently regress every routed test-side filter's
// equality comparison + the caixa-flux production-shape
// per-document apiVersion / kind pin — pin the byte-
// equivalence so the helper remains a drop-in replacement for
// every routed site's prior two-line block.
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(
KUBE_KEY_API_VERSION,
serde_yaml::Value::String(FLUX_HELMRELEASE_API_VERSION.into()),
);
cr.insert_str_key(
KUBE_KEY_KIND,
serde_yaml::Value::String(FLUX_KIND_HELM_RELEASE.into()),
);
let value = serde_yaml::Value::Mapping(cr);
for field in [KUBE_KEY_API_VERSION, KUBE_KEY_KIND] {
let via_helper = kube_root_str_field(&value, field);
let via_inline = value.get(field).and_then(|n| n.as_str());
assert_eq!(
via_helper, via_inline,
"kube_root_str_field(_, {field:?}) must yield the same \
Option<&str> as the prior inline two-hop chain — \
otherwise every routed caller's equality-filter / \
discriminator-pin drifts silently at readback time"
);
}
}
#[test]
fn kube_root_str_field_and_kube_metadata_str_field_bracket_the_readback_surface() {
// Peer-pin: the two lifted K8s-CR readback primitives cover
// orthogonal axes on the same document. Given a full K8s CR
// (top-level `apiVersion:` + `kind:` discriminator pair,
// sub-`metadata.name:` + `metadata.namespace:` identity pair),
// each helper reaches through its own axis and the two
// together enumerate every documented top-level string
// scalar the substrate emits + reads back. Pin the pairing so
// a future refactor that collapses the two into a single
// navigation primitive (or splits one further) surfaces here
// as a test-visible break, not a silent regression at the
// first routed caller's per-CR readback drift.
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(
KUBE_KEY_NAME,
serde_yaml::Value::String("checkout-cart-to-catalog".into()),
);
metadata.insert_str_key(
KUBE_KEY_NAMESPACE,
serde_yaml::Value::String(DEFAULT_NAMESPACE.into()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(
KUBE_KEY_API_VERSION,
serde_yaml::Value::String(CILIUM_API_VERSION.into()),
);
cr.insert_str_key(
KUBE_KEY_KIND,
serde_yaml::Value::String(CILIUM_KIND_NETWORK_POLICY.into()),
);
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_root_str_field(&value, KUBE_KEY_API_VERSION),
Some(CILIUM_API_VERSION)
);
assert_eq!(
kube_root_str_field(&value, KUBE_KEY_KIND),
Some(CILIUM_KIND_NETWORK_POLICY)
);
assert_eq!(
kube_metadata_str_field(&value, KUBE_KEY_NAME),
Some("checkout-cart-to-catalog")
);
assert_eq!(
kube_metadata_str_field(&value, KUBE_KEY_NAMESPACE),
Some(DEFAULT_NAMESPACE)
);
}
#[test]
fn kube_kind_is_matches_lifted_kube_root_str_field_equality_shape() {
// Byte-equivalence pin: the lifted predicate reproduces the
// three-token composition (`kube_root_str_field(v,
// KUBE_KEY_KIND) == Some(<KIND>)`) the 15 caixa-mesh test-side
// `.find`/`.filter` sites previously carried inline. Closes the
// "did the lift accidentally rename the pinned scalar-key axis
// to KUBE_KEY_API_VERSION or drop the `Some(...)` wrap" drift
// class every future re-lift on the peer-axis surface (a
// hypothetical `kube_api_version_is` peer, `kube_group_is` on a
// multi-group router harness) would otherwise reopen.
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(
KUBE_KEY_KIND,
serde_yaml::Value::String(GATEWAY_API_KIND_GATEWAY.into()),
);
let value = serde_yaml::Value::Mapping(cr);
assert!(kube_kind_is(&value, GATEWAY_API_KIND_GATEWAY));
assert_eq!(
kube_kind_is(&value, GATEWAY_API_KIND_GATEWAY),
kube_root_str_field(&value, KUBE_KEY_KIND) == Some(GATEWAY_API_KIND_GATEWAY),
);
}
#[test]
fn kube_kind_is_false_on_mismatched_kind_and_missing_kind() {
// Complement-side pin: the predicate returns `false` when
// either the kind axis carries a different discriminator or the
// top-level `kind:` scalar is absent altogether (the same
// vacuous-`None` short-circuit the parent
// `kube_root_str_field` closes on the underlying two-hop
// navigation). Consumer sites (`docs.iter().find(|d|
// kube_kind_is(d, X))`) rely on the false-on-mismatch shape to
// skip the wrong CRs across the multi-doc mesh emission and
// land on the intended per-kind document.
let mut cr_wrong_kind = serde_yaml::Mapping::new();
cr_wrong_kind.insert_str_key(
KUBE_KEY_KIND,
serde_yaml::Value::String(GATEWAY_API_KIND_HTTP_ROUTE.into()),
);
assert!(!kube_kind_is(
&serde_yaml::Value::Mapping(cr_wrong_kind),
GATEWAY_API_KIND_GATEWAY,
));
let cr_no_kind = serde_yaml::Mapping::new();
assert!(!kube_kind_is(
&serde_yaml::Value::Mapping(cr_no_kind),
GATEWAY_API_KIND_GATEWAY,
));
}
#[test]
fn find_by_kind_matches_inline_iter_find_kube_kind_is_shape() {
// Byte-equivalence pin: the lifted navigator reproduces the
// three-token combinator chain (`docs.iter().find(|d|
// kube_kind_is(d, <KIND>))`) the 14 caixa-mesh test-side
// per-Gateway / per-HTTPRoute find-by-kind sites previously
// carried inline. Closes the "did the lift accidentally
// widen the receiver, drop the closure, or swap `find` for
// `filter`" drift class every future re-lift on the sibling
// multi-doc-navigator axis (a hypothetical
// `filter_by_kind` peer that carries the same underlying
// predicate but returns an iterator) would otherwise reopen.
let mut gateway = serde_yaml::Mapping::new();
gateway.insert_str_key(
KUBE_KEY_KIND,
serde_yaml::Value::String(GATEWAY_API_KIND_GATEWAY.into()),
);
let mut route = serde_yaml::Mapping::new();
route.insert_str_key(
KUBE_KEY_KIND,
serde_yaml::Value::String(GATEWAY_API_KIND_HTTP_ROUTE.into()),
);
let docs = vec![
serde_yaml::Value::Mapping(gateway),
serde_yaml::Value::Mapping(route),
];
// Lifted navigator agrees with the inline combinator chain
// on every existing member of the multi-doc slice.
assert_eq!(
find_by_kind(&docs, GATEWAY_API_KIND_GATEWAY),
docs.iter()
.find(|d| kube_kind_is(d, GATEWAY_API_KIND_GATEWAY)),
);
assert_eq!(
find_by_kind(&docs, GATEWAY_API_KIND_HTTP_ROUTE),
docs.iter()
.find(|d| kube_kind_is(d, GATEWAY_API_KIND_HTTP_ROUTE)),
);
// And on the miss path: absent kind → None, matching the
// inline `.find` short-circuit that consumer sites rely on
// to distinguish "no such CR in this emission" from "wrong
// shape" in their `.unwrap()` / `.expect(...)` follow-ups.
assert_eq!(find_by_kind(&docs, CILIUM_KIND_NETWORK_POLICY), None);
let empty: Vec<serde_yaml::Value> = Vec::new();
assert_eq!(find_by_kind(&empty, GATEWAY_API_KIND_GATEWAY), None);
}
#[test]
fn find_by_kind_returns_first_match_on_duplicate_kind() {
// Order-preservation pin: the lifted navigator returns the
// first document of the matching kind (the same short-
// circuit `Iterator::find` exposes). Multi-doc mesh
// emissions never carry two documents of the same kind at
// V0 (`gateway_routes` emits exactly one `Gateway` + one
// `HTTPRoute` per Aplicacao), but the M4 cross-cluster
// fan-out will (one `HelmRelease` per cluster). Pinning the
// first-match contract keeps the M4 caller-side "the first
// hit is the primary" convention aligned with the helper's
// combinator half.
let mut gateway_a = serde_yaml::Mapping::new();
gateway_a.insert_str_key(
KUBE_KEY_KIND,
serde_yaml::Value::String(GATEWAY_API_KIND_GATEWAY.into()),
);
let mut meta_a = serde_yaml::Mapping::new();
meta_a.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("primary".into()));
gateway_a.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(meta_a));
let mut gateway_b = serde_yaml::Mapping::new();
gateway_b.insert_str_key(
KUBE_KEY_KIND,
serde_yaml::Value::String(GATEWAY_API_KIND_GATEWAY.into()),
);
let mut meta_b = serde_yaml::Mapping::new();
meta_b.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("secondary".into()));
gateway_b.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(meta_b));
let docs = vec![
serde_yaml::Value::Mapping(gateway_a),
serde_yaml::Value::Mapping(gateway_b),
];
let first = find_by_kind(&docs, GATEWAY_API_KIND_GATEWAY).unwrap();
assert_eq!(
kube_metadata_str_field(first, KUBE_KEY_NAME),
Some("primary"),
);
}
#[test]
fn kube_kind_matches_lifted_kube_root_str_field_readback_shape() {
// Byte-equivalence pin: the lifted accessor reproduces the
// two-token composition (`kube_root_str_field(v,
// KUBE_KEY_KIND)`) the 7 caixa-flux (3) + caixa-mesh (4)
// test-side per-CR discriminator readback sites previously
// carried inline around the readback intent "what kind did the
// emitter write into this CR?". Closes the "did the lift
// accidentally rename the pinned scalar-key axis to
// KUBE_KEY_API_VERSION (silently pulling the peer discriminator
// coordinate instead of the primary), drop the axis-key
// argument, or widen the return type" drift class every future
// re-lift on the peer top-level axis surface (a hypothetical
// `kube_api_version` accessor on a multi-version migration
// harness, a `kube_group` accessor for CRD-group filtering)
// would otherwise reopen. Peer of the sibling
// `kube_name_matches_lifted_kube_metadata_str_field_readback_shape`
// + `kube_namespace_matches_lifted_kube_metadata_str_field_readback_shape`
// pins on the sub-`metadata:` axis half of the same
// three-accessor closure — this pin brackets the top-level
// `kind:` discriminator axis, the two sibling pins bracket the
// sub-`metadata.{name, namespace}` coordinate pair, together
// closing the accessor-arity witness on every canonical per-CR
// axis the substrate emits + reads back.
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(
KUBE_KEY_KIND,
serde_yaml::Value::String(CILIUM_KIND_NETWORK_POLICY.into()),
);
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(kube_kind(&value), Some(CILIUM_KIND_NETWORK_POLICY));
assert_eq!(
kube_kind(&value),
kube_root_str_field(&value, KUBE_KEY_KIND),
"kube_kind must byte-agree with the parametric \
`kube_root_str_field(v, KUBE_KEY_KIND)` composition it \
replaces at every consumer site — drift on either half \
silently opens a per-CR discriminator readback that no \
longer routes through the pinned KUBE_KEY_KIND axis-key",
);
}
#[test]
fn kube_kind_none_when_kind_absent_or_non_string() {
// Complement-side pin: the accessor returns `None` when either
// the top-level `kind:` scalar is absent (a partially-authored
// CR the K8s API-server would reject at admission but that this
// readback tolerates as `None` so the accessor stays a total
// function) or the `kind:` scalar is present but carries a
// non-string YAML type (a numeric, boolean, or nested mapping —
// invalid CR shape per the K8s API-machinery OpenAPI schema).
// Peer of the sibling
// `kube_root_str_field_returns_none_when_field_absent` +
// `kube_root_str_field_returns_none_when_field_carries_non_string_type`
// pins on the parametric readback surface — this pin verifies
// the pinned-axis variant preserves the same total-function
// contract every consumer site's `.unwrap_or(...)` /
// `Some(...) ==` follow-up depends on.
let empty = serde_yaml::Value::Mapping(serde_yaml::Mapping::new());
assert_eq!(
kube_kind(&empty),
None,
"kube_kind must return None when the top-level kind: scalar \
is absent",
);
for non_string in [
serde_yaml::Value::Null,
serde_yaml::Value::Number(42.into()),
serde_yaml::Value::Bool(true),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
] {
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_KIND, non_string.clone());
assert_eq!(
kube_kind(&serde_yaml::Value::Mapping(cr)),
None,
"kube_kind must return None when top-level kind: carries \
a non-string YAML type ({non_string:?})",
);
}
}
#[test]
fn kube_api_version_matches_lifted_kube_root_str_field_readback_shape() {
// Byte-equivalence pin: the lifted accessor reproduces the
// two-token composition (`kube_root_str_field(v,
// KUBE_KEY_API_VERSION)`) the 10 caixa-flux (4) + caixa-mesh (6)
// test-side per-CR CRD-group/version readback sites previously
// carried inline around the readback intent "what apiVersion did
// the emitter write into this CR?". Closes the "did the lift
// accidentally rename the pinned scalar-key axis to
// KUBE_KEY_KIND (silently pulling the peer discriminator
// coordinate instead of the primary), drop the axis-key
// argument, or widen the return type" drift class every future
// re-lift on the peer top-level axis surface (a hypothetical
// `kube_group` accessor for CRD-group filtering on the pre-`/`-
// slash prefix of the same `apiVersion:` scalar, a
// `kube_version` accessor for the post-`/`-slash version
// suffix on a multi-version migration harness) would otherwise
// reopen. Peer of the sibling
// `kube_kind_matches_lifted_kube_root_str_field_readback_shape`
// pin on the sibling top-level `kind:` half of the same
// canonical `(apiVersion, kind)` discriminator-pair closure —
// together the two pins bracket the top-level per-CR
// CRD-registration coordinate pair, matching the sibling
// sub-`metadata.{name, namespace}` accessor-arity closure the
// peer `kube_name` / `kube_namespace` byte-equivalence pins
// carry on the sub-`metadata:` axis half of the same accessor-
// arity peer-set.
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(
KUBE_KEY_API_VERSION,
serde_yaml::Value::String(CILIUM_API_VERSION.into()),
);
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(kube_api_version(&value), Some(CILIUM_API_VERSION));
assert_eq!(
kube_api_version(&value),
kube_root_str_field(&value, KUBE_KEY_API_VERSION),
"kube_api_version must byte-agree with the parametric \
`kube_root_str_field(v, KUBE_KEY_API_VERSION)` composition \
it replaces at every consumer site — drift on either half \
silently opens a per-CR CRD-group/version readback that \
no longer routes through the pinned KUBE_KEY_API_VERSION \
axis-key",
);
}
#[test]
fn kube_api_version_none_when_api_version_absent_or_non_string() {
// Complement-side pin: the accessor returns `None` when either
// the top-level `apiVersion:` scalar is absent (a partially-
// authored CR the K8s API-server would reject at admission but
// that this readback tolerates as `None` so the accessor stays
// a total function) or the `apiVersion:` scalar is present but
// carries a non-string YAML type (a numeric, boolean, sequence,
// or nested mapping — invalid CR shape per the K8s
// API-machinery OpenAPI schema which pins `apiVersion` as a
// required string scalar). Peer of the sibling
// `kube_kind_none_when_kind_absent_or_non_string` +
// `kube_root_str_field_returns_none_when_field_absent` +
// `kube_root_str_field_returns_none_when_field_carries_non_string_type`
// pins on the sibling `kind:` half of the same canonical
// `(apiVersion, kind)` discriminator-pair + the parametric
// readback surface — this pin verifies the pinned-axis variant
// preserves the same total-function contract every consumer
// site's `.unwrap_or(...)` / `Some(...) ==` follow-up depends
// on.
let empty = serde_yaml::Value::Mapping(serde_yaml::Mapping::new());
assert_eq!(
kube_api_version(&empty),
None,
"kube_api_version must return None when the top-level \
apiVersion: scalar is absent",
);
for non_string in [
serde_yaml::Value::Null,
serde_yaml::Value::Number(42.into()),
serde_yaml::Value::Bool(true),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
] {
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_API_VERSION, non_string.clone());
assert_eq!(
kube_api_version(&serde_yaml::Value::Mapping(cr)),
None,
"kube_api_version must return None when top-level \
apiVersion: carries a non-string YAML type \
({non_string:?})",
);
}
}
#[test]
fn kube_api_version_is_matches_lifted_kube_api_version_equality_shape() {
// Byte-equivalence pin: the lifted predicate reproduces the
// three-token composition (`kube_api_version(v) ==
// Some(<AXIS>)`) the 10 caixa-flux (4) + caixa-mesh (6) test-
// side per-CR CRD-group/version equality-wrap sites previously
// carried inline around the readback intent "does this
// rendered CR declare CRD-group/version X?". Closes the "did
// the lift accidentally rename the pinned scalar-key axis to
// KUBE_KEY_KIND (silently pulling the peer discriminator
// coordinate instead of the primary), drop the `Some(...)`
// wrap, or widen the return type" drift class every future
// re-lift on the peer top-level axis surface (a hypothetical
// `kube_group_is` on the pre-`/`-slash CRD-group prefix, a
// `kube_version_is` on the post-`/`-slash version suffix on a
// multi-version migration harness) would otherwise reopen.
// Peer of the sibling
// `kube_kind_is_matches_lifted_kube_root_str_field_equality_shape`
// pin on the sibling top-level `kind:` half of the same
// canonical `(apiVersion, kind)` discriminator-pair closure —
// together the two pins bracket the top-level per-CR
// CRD-registration coordinate pair at predicate arity,
// matching the sibling sub-`metadata.{name, namespace}`
// predicate-arity closure the peer `kube_name_is` /
// `kube_namespace_is` byte-equivalence pins carry on the
// sub-`metadata:` axis half of the same predicate-arity
// peer-set.
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(
KUBE_KEY_API_VERSION,
serde_yaml::Value::String(CILIUM_API_VERSION.into()),
);
let value = serde_yaml::Value::Mapping(cr);
assert!(kube_api_version_is(&value, CILIUM_API_VERSION));
assert_eq!(
kube_api_version_is(&value, CILIUM_API_VERSION),
kube_api_version(&value) == Some(CILIUM_API_VERSION),
"kube_api_version_is must byte-agree with the composition \
`kube_api_version(v) == Some(api_version)` it replaces at \
every consumer site — drift on either half silently opens \
a per-CR CRD-group/version equality-wrap that no longer \
routes through the pinned KUBE_KEY_API_VERSION axis-key",
);
}
#[test]
fn kube_api_version_is_false_on_mismatched_api_version_and_missing_api_version() {
// Complement-side pin: the predicate returns `false` when
// either the top-level `apiVersion:` scalar declares a
// different CRD-group/version coordinate or the top-level
// `apiVersion:` scalar is absent altogether (a partially-
// authored CR the K8s API-server would reject at admission but
// that this predicate tolerates as `false` so the predicate
// stays a total function). Consumer sites
// (`assert!(kube_api_version_is(p, <AXIS>))` +
// `docs.iter().find(|d| kube_api_version_is(d, <AXIS>))`)
// rely on the false-on-mismatch shape to skip the wrong
// CRD-group/version-registration CRs across the multi-doc
// mesh emission and land on the intended CR. Peer of the
// sibling `kube_kind_is_false_on_mismatched_kind_and_missing_kind`
// pin on the sibling top-level `kind:` half of the same
// canonical `(apiVersion, kind)` discriminator-pair closure.
let mut cr_wrong_api_version = serde_yaml::Mapping::new();
cr_wrong_api_version.insert_str_key(
KUBE_KEY_API_VERSION,
serde_yaml::Value::String(FLUX_HELMRELEASE_API_VERSION.into()),
);
assert!(!kube_api_version_is(
&serde_yaml::Value::Mapping(cr_wrong_api_version),
CILIUM_API_VERSION,
));
let cr_no_api_version = serde_yaml::Mapping::new();
assert!(!kube_api_version_is(
&serde_yaml::Value::Mapping(cr_no_api_version),
CILIUM_API_VERSION,
));
}
#[test]
fn kube_api_version_is_composes_on_lifted_kube_api_version_accessor() {
// Composition pin: the predicate `kube_api_version_is`
// delegates through the lifted [`kube_api_version`] accessor
// rather than the parametric [`kube_root_str_field`] two-token
// navigation. Lock the delegation shape (`kube_api_version_is(v,
// g) == (kube_api_version(v) == Some(g))`) so a future refactor
// that reintroduces the direct `kube_root_str_field(v,
// KUBE_KEY_API_VERSION) == Some(g)` composition surfaces here
// as a test-visible break — the composition-symmetry with the
// sibling [`kube_kind_is`] / [`kube_name_is`] /
// [`kube_namespace_is`] predicates (each of which delegates
// through their respective sibling accessors) stays enforced.
// Peer of the sibling
// `kube_kind_is_composes_on_lifted_kube_kind_accessor` +
// `kube_name_is_composes_on_lifted_kube_name_accessor` +
// `kube_namespace_is_composes_on_lifted_kube_namespace_accessor`
// pins on the three sibling accessor-composition axes.
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(
KUBE_KEY_API_VERSION,
serde_yaml::Value::String(CILIUM_API_VERSION.into()),
);
let value = serde_yaml::Value::Mapping(cr);
for candidate in [
CILIUM_API_VERSION,
FLUX_HELMRELEASE_API_VERSION,
FLUX_GITREPOSITORY_API_VERSION,
FLUX_KUSTOMIZATION_API_VERSION,
] {
assert_eq!(
kube_api_version_is(&value, candidate),
kube_api_version(&value) == Some(candidate),
"kube_api_version_is(v, {candidate:?}) must reduce to \
`kube_api_version(v) == Some({candidate:?})` byte-for-byte — \
the composition-symmetry with the sibling kube_kind_is / \
kube_name_is / kube_namespace_is predicates is the load-\
bearing shape every future accessor-side refactor rides on",
);
}
}
#[test]
fn find_by_api_version_matches_inline_iter_find_kube_api_version_is_shape() {
// Byte-equivalence pin: the lifted navigator reproduces the
// three-token combinator chain (`docs.iter().find(|d|
// kube_api_version_is(d, <AXIS>))`) every future per-CRD-
// group/version multi-doc-navigator site (M4 cross-cluster
// Flux-triplet split, per-Aplicacao CR CRD-group/version join)
// would otherwise re-inline. Closes the "did the lift
// accidentally widen the receiver, drop the closure, or swap
// `find` for `filter`" drift class the sibling
// `find_by_kind_matches_inline_iter_find_kube_kind_is_shape` +
// `find_by_name_matches_inline_iter_find_kube_name_is_shape` +
// `find_by_namespace_matches_inline_iter_find_kube_namespace_is_shape`
// pins already close on the three sibling axes — this pin
// extends the same combinator-shape guarantee onto the top-
// level CRD-group/version half of the canonical
// `(apiVersion, kind)` coordinate pair.
let mut cilium = serde_yaml::Mapping::new();
cilium.insert_str_key(
KUBE_KEY_API_VERSION,
serde_yaml::Value::String(CILIUM_API_VERSION.into()),
);
let mut helm_release = serde_yaml::Mapping::new();
helm_release.insert_str_key(
KUBE_KEY_API_VERSION,
serde_yaml::Value::String(FLUX_HELMRELEASE_API_VERSION.into()),
);
let docs = vec![
serde_yaml::Value::Mapping(cilium),
serde_yaml::Value::Mapping(helm_release),
];
assert_eq!(
find_by_api_version(&docs, CILIUM_API_VERSION),
docs.iter()
.find(|d| kube_api_version_is(d, CILIUM_API_VERSION)),
);
assert_eq!(
find_by_api_version(&docs, FLUX_HELMRELEASE_API_VERSION),
docs.iter()
.find(|d| kube_api_version_is(d, FLUX_HELMRELEASE_API_VERSION)),
);
// Miss path: unknown CRD-group/version → None, matching the
// inline `.find` short-circuit consumer sites rely on to
// distinguish "no such CR in this emission" from "wrong shape"
// in their `.unwrap()` / `.expect(...)` follow-ups.
assert_eq!(
find_by_api_version(&docs, FLUX_GITREPOSITORY_API_VERSION),
None,
);
let empty: Vec<serde_yaml::Value> = Vec::new();
assert_eq!(find_by_api_version(&empty, CILIUM_API_VERSION), None);
}
#[test]
fn find_by_api_version_returns_first_match_on_duplicate_api_version() {
// Order-preservation pin: the lifted navigator returns the
// first document of the matching CRD-group/version (the same
// short-circuit `Iterator::find` exposes). The Flux v2
// controller-triplet emission shares the `.toolkit.fluxcd.io`
// root but distinct sub-groups today (`helm.` / `source.` /
// `kustomize.`), and the M4 cross-cluster fan-out will emit
// one `HelmRelease` per cluster all under the same
// `helm.toolkit.fluxcd.io/v2` CRD-group/version. Pinning the
// first-match contract keeps the M4 caller-side "the first hit
// is the primary" convention aligned with the helper's
// combinator half — peer of the sibling
// `find_by_kind_returns_first_match_on_duplicate_kind` +
// `find_by_name_returns_first_match_on_duplicate_name` +
// `find_by_namespace_returns_first_match_on_duplicate_namespace`
// pins on the three sibling navigator axes.
let mut primary = serde_yaml::Mapping::new();
primary.insert_str_key(
KUBE_KEY_API_VERSION,
serde_yaml::Value::String(FLUX_HELMRELEASE_API_VERSION.into()),
);
let mut meta_a = serde_yaml::Mapping::new();
meta_a.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("primary".into()));
primary.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(meta_a));
let mut secondary = serde_yaml::Mapping::new();
secondary.insert_str_key(
KUBE_KEY_API_VERSION,
serde_yaml::Value::String(FLUX_HELMRELEASE_API_VERSION.into()),
);
let mut meta_b = serde_yaml::Mapping::new();
meta_b.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("secondary".into()));
secondary.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(meta_b));
let docs = vec![
serde_yaml::Value::Mapping(primary),
serde_yaml::Value::Mapping(secondary),
];
let first = find_by_api_version(&docs, FLUX_HELMRELEASE_API_VERSION).unwrap();
assert_eq!(kube_name(first), Some("primary"));
}
#[test]
fn kube_kind_is_composes_on_lifted_kube_kind_accessor() {
// Composition pin: the predicate `kube_kind_is` now delegates
// through the lifted [`kube_kind`] accessor rather than the
// parametric [`kube_root_str_field`] two-hop navigation. Lock
// the delegation shape (`kube_kind_is(v, k) == (kube_kind(v)
// == Some(k))`) so a future refactor that reintroduces the
// direct `kube_root_str_field(v, KUBE_KEY_KIND) == Some(k)`
// composition surfaces here as a test-visible break — the
// composition-symmetry with the sibling
// [`kube_name_is`] / [`kube_namespace_is`] predicates (both of
// which delegate through their respective sibling accessors)
// stays enforced. Peer of the sibling
// `kube_name_is_composes_on_lifted_kube_name_accessor` +
// `kube_namespace_is_composes_on_lifted_kube_namespace_accessor`
// pins on the two `metadata.*` sub-axis predicates.
let mut cr_gw = serde_yaml::Mapping::new();
cr_gw.insert_str_key(
KUBE_KEY_KIND,
serde_yaml::Value::String(GATEWAY_API_KIND_GATEWAY.into()),
);
let value = serde_yaml::Value::Mapping(cr_gw);
for candidate in [
GATEWAY_API_KIND_GATEWAY,
GATEWAY_API_KIND_HTTP_ROUTE,
CILIUM_KIND_NETWORK_POLICY,
] {
assert_eq!(
kube_kind_is(&value, candidate),
kube_kind(&value) == Some(candidate),
"kube_kind_is(v, {candidate:?}) must reduce to \
`kube_kind(v) == Some({candidate:?})` byte-for-byte — \
the composition-symmetry with the sibling \
kube_name_is / kube_namespace_is predicates is the \
load-bearing shape every future accessor-side \
refactor rides on",
);
}
}
#[test]
fn kube_kind_closes_three_arity_closure_over_kube_kind_is_and_find_by_kind() {
// Closure-witness pin: the three-arity `(accessor / predicate /
// navigator)` closure on the top-level `kind:` discriminator
// axis is now closed by the same delegation chain the sibling
// `metadata.name` / `metadata.namespace` closures already carry
// — `kube_kind` reads (accessor), `kube_kind_is` composes on
// top of it as equality (predicate), `find_by_kind` composes
// on top of `kube_kind_is` as first-match (navigator). Assert
// the three arities reconcile on the same document: the
// accessor's readback drives the predicate's equality, and the
// predicate's equality drives the navigator's first-hit — a
// future refactor that decouples any of the three from the
// shared underlying [`KUBE_KEY_KIND`] axis-key surfaces here
// as a three-way disagreement, not a silent drift at the first
// routed caller. Peer of the sibling identity-axis + namespace-
// scoping-axis closure-witness pins on the two sub-`metadata:`
// coordinates — together the three witnesses bracket every
// canonical per-CR axis the substrate emits.
let mut cr_gw = serde_yaml::Mapping::new();
cr_gw.insert_str_key(
KUBE_KEY_KIND,
serde_yaml::Value::String(GATEWAY_API_KIND_GATEWAY.into()),
);
let mut cr_route = serde_yaml::Mapping::new();
cr_route.insert_str_key(
KUBE_KEY_KIND,
serde_yaml::Value::String(GATEWAY_API_KIND_HTTP_ROUTE.into()),
);
let docs = vec![
serde_yaml::Value::Mapping(cr_gw),
serde_yaml::Value::Mapping(cr_route),
];
for kind in [
GATEWAY_API_KIND_GATEWAY,
GATEWAY_API_KIND_HTTP_ROUTE,
CILIUM_KIND_NETWORK_POLICY,
] {
let via_navigator = find_by_kind(&docs, kind);
let via_predicate = docs.iter().find(|d| kube_kind_is(d, kind));
let via_accessor = docs.iter().find(|d| kube_kind(d) == Some(kind));
assert_eq!(
via_navigator, via_predicate,
"find_by_kind must reduce to `docs.iter().find(|d| \
kube_kind_is(d, {kind:?}))` — the navigator/predicate \
arity link on the kind axis must stay closed",
);
assert_eq!(
via_predicate, via_accessor,
"kube_kind_is must reduce to `kube_kind(d) == \
Some({kind:?})` — the predicate/accessor arity link \
on the kind axis must stay closed",
);
}
}
#[test]
fn kube_name_matches_lifted_kube_metadata_str_field_readback_shape() {
// Byte-equivalence pin: the lifted accessor reproduces the
// two-token composition (`kube_metadata_str_field(v,
// KUBE_KEY_NAME)`) the 12 caixa-mesh (9) + caixa-flux (3)
// test-side per-CR readback sites previously carried inline
// around the readback intent "what name did the emitter write
// into this CR?". Closes the "did the lift accidentally
// rename the pinned scalar-key axis to KUBE_KEY_NAMESPACE
// (silently pulling the peer identity coordinate instead of
// the primary), drop the axis-key argument, or widen the
// return type" drift class every future re-lift on the peer-
// axis surface (a hypothetical `kube_namespace` peer on the
// per-CR namespace-scoping coordinate, a `kube_uid` peer for
// ownerReference bookkeeping) would otherwise reopen. Peer of
// the sibling `kube_name_is_matches_lifted_kube_metadata_str_field_equality_shape`
// pin on the predicate-arity half of the same axis: the
// accessor pin asserts the readback intent, the predicate pin
// asserts the equality-wrap intent, together bracketing the
// two-arity closure the identity axis carries at V0.
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(
KUBE_KEY_NAME,
serde_yaml::Value::String("checkout-cart-to-catalog".into()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(kube_name(&value), Some("checkout-cart-to-catalog"));
assert_eq!(
kube_name(&value),
kube_metadata_str_field(&value, KUBE_KEY_NAME),
"kube_name must byte-agree with the parametric \
`kube_metadata_str_field(v, KUBE_KEY_NAME)` composition \
it replaces at every consumer site — drift on either \
half silently opens a per-CR identity readback that no \
longer routes through the pinned KUBE_KEY_NAME axis-key",
);
}
#[test]
fn kube_name_none_when_metadata_block_absent_or_name_absent() {
// Complement-side pin: the accessor returns `None` when
// either the enclosing `metadata:` block is absent (root-
// level CR with no metadata mapping at all — the vacuous
// shape the operator-side "not-yet-materialized" CR readback
// might momentarily observe under a partial apply) or the
// sub-`name:` scalar is absent inside a present `metadata:`
// block (a partially-authored CR the K8s API-server would
// reject at admission but that this readback tolerates as
// `None` so the accessor stays a total function). Consumer
// sites (`.expect(...)`, `.unwrap()`, `Some(...) == expected`
// equality wraps) rely on the None-on-absence short-circuit
// to distinguish "no such name on this doc" from "wrong
// shape" in the follow-up. Peer of the sibling
// `kube_name_is_false_on_mismatched_name_and_missing_name`
// pin on the predicate-arity half — the accessor short-
// circuits to `None`, the predicate short-circuits through it
// to `false` — same underlying vacuous-`None` gate.
let cr_no_metadata = serde_yaml::Mapping::new();
assert_eq!(
kube_name(&serde_yaml::Value::Mapping(cr_no_metadata)),
None,
"kube_name must return None when the enclosing metadata: \
block is absent",
);
let empty_meta = serde_yaml::Mapping::new();
let mut cr_no_name = serde_yaml::Mapping::new();
cr_no_name.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(empty_meta));
assert_eq!(
kube_name(&serde_yaml::Value::Mapping(cr_no_name)),
None,
"kube_name must return None when the sub-name: scalar is \
absent inside a present metadata: block",
);
}
#[test]
fn kube_name_none_when_metadata_name_carries_non_string_type() {
// Type-gate pin: the accessor returns `None` when the sub-
// `metadata.name:` scalar is present but carries a non-string
// YAML type (a numeric, boolean, or nested mapping — invalid
// K8s CR shape per the K8s API-machinery OpenAPI schema, but
// tolerated here as `None` so the readback stays a total
// function and defers the diagnostic to the caller's own
// `.expect(...)` / `.unwrap()` follow-up which names the
// caller's schema axis). Pins the type-gate half of the
// accessor's contract — the axis-key pin is asserted by the
// sibling byte-agreement test — so a hypothetical future
// widening (accepting numeric `metadata.name: 42` as the
// stringified `"42"`, an aliased YAML integer under a fresh
// `Value::from` conversion) is caught before it lands.
let mut metadata_int = serde_yaml::Mapping::new();
metadata_int.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::from(42u64));
let mut cr_int = serde_yaml::Mapping::new();
cr_int.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata_int));
assert_eq!(
kube_name(&serde_yaml::Value::Mapping(cr_int)),
None,
"kube_name must return None when metadata.name carries a \
non-string YAML type (numeric here)",
);
let mut inner = serde_yaml::Mapping::new();
inner.insert_str_key("nested", serde_yaml::Value::String("value".into()));
let mut metadata_map = serde_yaml::Mapping::new();
metadata_map.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::Mapping(inner));
let mut cr_map = serde_yaml::Mapping::new();
cr_map.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata_map));
assert_eq!(
kube_name(&serde_yaml::Value::Mapping(cr_map)),
None,
"kube_name must return None when metadata.name carries a \
nested mapping (invalid CR shape per K8s API-machinery)",
);
}
#[test]
fn kube_namespace_matches_lifted_kube_metadata_str_field_readback_shape() {
// Byte-equivalence pin: the lifted accessor reproduces the
// two-token composition (`kube_metadata_str_field(v,
// KUBE_KEY_NAMESPACE)`) the 4 caixa-flux (1 production + 1
// test) + caixa-mesh (2 test) per-CR namespace-scoping readback
// sites previously carried inline around the readback intent
// "what namespace did the emitter write into this CR?". Closes
// the "did the lift accidentally rename the pinned scalar-key
// axis to KUBE_KEY_NAME (silently pulling the peer identity
// coordinate instead of the namespace-scoping one), drop the
// axis-key argument, or widen the return type" drift class
// every future re-lift on the peer-axis surface (a hypothetical
// `kube_uid` peer for ownerReference bookkeeping, a
// `kube_resource_version` peer for optimistic-concurrency
// readback) would otherwise reopen. Peer of the sibling
// `kube_name_matches_lifted_kube_metadata_str_field_readback_shape`
// pin on the identity-axis half of the same
// `metadata.{name, namespace}` per-CR coordinate pair: the
// accessor pins the readback intent on both halves of the
// canonical K8s API-machinery per-CR disambiguation pair
// together, bracketing the two coordinates the emit-side
// `kube_resource_skeleton` writes into every rendered CR.
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(
KUBE_KEY_NAMESPACE,
serde_yaml::Value::String(DEFAULT_NAMESPACE.into()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(kube_namespace(&value), Some(DEFAULT_NAMESPACE));
assert_eq!(
kube_namespace(&value),
kube_metadata_str_field(&value, KUBE_KEY_NAMESPACE),
"kube_namespace must byte-agree with the parametric \
`kube_metadata_str_field(v, KUBE_KEY_NAMESPACE)` \
composition it replaces at every consumer site — drift on \
either half silently opens a per-CR namespace-scoping \
readback that no longer routes through the pinned \
KUBE_KEY_NAMESPACE axis-key",
);
}
#[test]
fn kube_namespace_none_when_metadata_block_absent_or_namespace_absent() {
// Complement-side pin: the accessor returns `None` when either
// the enclosing `metadata:` block is absent (root-level CR with
// no metadata mapping at all — the vacuous shape the operator-
// side "not-yet-materialized" CR readback might momentarily
// observe under a partial apply) or the sub-`namespace:` scalar
// is absent inside a present `metadata:` block (a
// cluster-scoped CR that legally omits the namespace-scoping
// coordinate, a partially-authored CR the K8s API-server would
// materialize with a `default` namespace at admission but that
// this readback tolerates as `None` so the accessor stays a
// total function). Consumer sites (`.expect(...)`,
// `.unwrap_or(DEFAULT_NAMESPACE)` fallback, `Some(...) ==
// expected` equality wraps) rely on the None-on-absence short-
// circuit — the caixa-flux `programs_yaml_entry` production
// fallback path in particular depends on the None-arm to
// substitute [`DEFAULT_NAMESPACE`] when the source
// ComputeUnit YAML omits `metadata.namespace`. Peer of the
// sibling `kube_name_none_when_metadata_block_absent_or_name_absent`
// pin on the identity-axis half.
let cr_no_metadata = serde_yaml::Mapping::new();
assert_eq!(
kube_namespace(&serde_yaml::Value::Mapping(cr_no_metadata)),
None,
"kube_namespace must return None when the enclosing \
metadata: block is absent — the caixa-flux \
programs_yaml_entry production fallback relies on this \
None-arm to substitute DEFAULT_NAMESPACE",
);
let empty_meta = serde_yaml::Mapping::new();
let mut cr_no_namespace = serde_yaml::Mapping::new();
cr_no_namespace.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(empty_meta));
assert_eq!(
kube_namespace(&serde_yaml::Value::Mapping(cr_no_namespace)),
None,
"kube_namespace must return None when the \
sub-namespace: scalar is absent inside a present \
metadata: block (the cluster-scoped-CR / \
partially-authored-CR arm)",
);
}
#[test]
fn kube_namespace_none_when_metadata_namespace_carries_non_string_type() {
// Type-gate pin: the accessor returns `None` when the sub-
// `metadata.namespace:` scalar is present but carries a non-
// string YAML type (a numeric, boolean, or nested mapping —
// invalid K8s CR shape per the K8s API-machinery OpenAPI
// schema, but tolerated here as `None` so the readback stays a
// total function and defers the diagnostic to the caller's own
// `.unwrap_or(...)` fallback / `.expect(...)` follow-up which
// names the caller's schema axis). Pins the type-gate half of
// the accessor's contract — the axis-key pin is asserted by
// the sibling byte-agreement test — so a hypothetical future
// widening (accepting numeric `metadata.namespace: 42` as the
// stringified `"42"`, an aliased YAML integer under a fresh
// `Value::from` conversion) is caught before it lands. Peer
// of the sibling
// `kube_name_none_when_metadata_name_carries_non_string_type`
// pin on the identity-axis half.
let mut metadata_int = serde_yaml::Mapping::new();
metadata_int.insert_str_key(KUBE_KEY_NAMESPACE, serde_yaml::Value::from(42u64));
let mut cr_int = serde_yaml::Mapping::new();
cr_int.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata_int));
assert_eq!(
kube_namespace(&serde_yaml::Value::Mapping(cr_int)),
None,
"kube_namespace must return None when metadata.namespace \
carries a non-string YAML type (numeric here)",
);
let mut inner = serde_yaml::Mapping::new();
inner.insert_str_key("nested", serde_yaml::Value::String("value".into()));
let mut metadata_map = serde_yaml::Mapping::new();
metadata_map.insert_str_key(KUBE_KEY_NAMESPACE, serde_yaml::Value::Mapping(inner));
let mut cr_map = serde_yaml::Mapping::new();
cr_map.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata_map));
assert_eq!(
kube_namespace(&serde_yaml::Value::Mapping(cr_map)),
None,
"kube_namespace must return None when metadata.namespace \
carries a nested mapping (invalid CR shape per K8s \
API-machinery)",
);
}
#[test]
fn kube_namespace_agrees_with_kube_metadata_str_field_across_permutations() {
// Load-bearing cross-check pin: the accessor and the parametric
// helper it delegates to must byte-agree on every closed
// permutation of the (metadata-present, sub-namespace-present,
// scalar-shape) product — the same cross-product the sibling
// parent `kube_metadata_str_field_matches_prior_inline_chain`
// pin bracket-tests on the parametric helper for both
// KUBE_KEY_NAME and KUBE_KEY_NAMESPACE arg permutations, here
// extended one layer up onto the pinned accessor's own axis-
// key pinning. Closes the "did the pinned accessor
// silently rewire itself off the parametric helper (open-coding
// a fresh two-hop walk instead of composing on the substrate
// primitive)" drift class every future accessor-family
// extension (a peer `kube_uid` on the ownerReference axis, a
// future `kube_labels` composite-return accessor) would
// otherwise reopen. Peer of the sibling
// `kube_name_is_composes_on_lifted_kube_name_accessor` pin on
// the predicate-arity's underlying accessor delegation.
let namespaces = [
"tatara-system",
DEFAULT_NAMESPACE,
"default",
"kube-system",
"flux-system",
];
for ns in namespaces {
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(
KUBE_KEY_NAMESPACE,
serde_yaml::Value::String(ns.to_string()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_namespace(&value),
kube_metadata_str_field(&value, KUBE_KEY_NAMESPACE),
"kube_namespace must byte-agree with \
kube_metadata_str_field(_, KUBE_KEY_NAMESPACE) across \
every canonical namespace-scoping value (ns={ns:?}) — \
drift here silently splits the two readback paths",
);
assert_eq!(
kube_namespace(&value),
Some(ns),
"kube_namespace must return the authored namespace-\
scoping value verbatim (ns={ns:?})",
);
}
}
#[test]
fn kube_namespace_borrows_from_input_value_storage() {
// Borrow-not-copy pin: the accessor returns a `&str` that
// borrows into the input `Value`'s own storage — pointer-equal
// to the underlying `String::as_str()` on the sub-
// `metadata.namespace:` scalar. Rules out a hypothetical
// future rewrite that returned a fresh `String` (via `.clone()`
// / `.to_string()`) or an owning `Cow` conversion, either of
// which would silently double-allocate at every per-CR readback
// consumer's fast path (the caixa-flux `programs_yaml_entry`
// production readback fans onto every `programs.yaml` entry
// emit at V0, so a per-entry allocation would compound across
// the whole fleet-programs render). Peer of the sibling
// per-storage-borrow pin discipline the sibling accessor family
// ([`Placement::shard_key`], [`Placement::affinity`],
// [`Membro::nome`], [`Entrada::destination`],
// [`Entrada::hostname`]) carries on their respective per-slot
// `&str`-return accessors.
let ns = "tatara-system".to_string();
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_NAMESPACE, serde_yaml::Value::String(ns.clone()));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
// Accessor must return the same byte-string as the underlying
// parametric helper's readback — the composition contract.
let via_accessor = kube_namespace(&value).expect("namespace present");
let via_helper = kube_metadata_str_field(&value, KUBE_KEY_NAMESPACE)
.expect("namespace present via helper");
assert_eq!(
via_accessor.as_ptr(),
via_helper.as_ptr(),
"kube_namespace must return the same borrowed slice as \
kube_metadata_str_field(_, KUBE_KEY_NAMESPACE) — a \
pointer-drift signals a hidden clone / owning conversion \
layer between the accessor and its delegate",
);
assert_eq!(via_accessor.len(), via_helper.len());
}
#[test]
fn kube_name_is_composes_on_lifted_kube_name_accessor() {
// Composition pin: after the accessor lift, the peer
// predicate `kube_name_is(v, n)` must resolve exactly as
// `kube_name(v) == Some(n)` — i.e. the predicate no longer
// carries an inline `kube_metadata_str_field(v,
// KUBE_KEY_NAME) == Some(n)` composition but composes on the
// sibling accessor. Pins the structural link between the
// three-arity closure (accessor / predicate / navigator) on
// the identity axis: a future re-implementation of `kube_name`
// (e.g. a caching short-circuit for repeated readback on the
// same document, a hypothetical alias-table dispatch on a
// `metadata.identity` sub-axis) reaches the predicate through
// one lift, not a second co-ordinated inline rewrite. Peer of
// the sibling `find_by_name_matches_inline_iter_find_kube_name_is_shape`
// pin on the navigator arity — the navigator composes on the
// predicate, the predicate composes on the accessor.
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(
KUBE_KEY_NAME,
serde_yaml::Value::String("checkout-cart-to-payment".into()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_name_is(&value, "checkout-cart-to-payment"),
kube_name(&value) == Some("checkout-cart-to-payment"),
"kube_name_is must byte-agree with the peer \
`kube_name(v) == Some(n)` composition it now delegates \
to — the predicate carries no more inline navigation, \
only the equality-wrap semantic distinct from the \
sibling accessor arity",
);
assert!(kube_name_is(&value, "checkout-cart-to-payment"));
assert!(!kube_name_is(&value, "checkout-cart-to-catalog"));
}
#[test]
fn kube_name_is_matches_lifted_kube_metadata_str_field_equality_shape() {
// Byte-equivalence pin: the lifted predicate reproduces the
// three-token composition (`kube_metadata_str_field(v,
// KUBE_KEY_NAME) == Some(<NAME>)`) the 6 caixa-mesh test-side
// `.find`/`.filter` sites previously carried inline. Closes the
// "did the lift accidentally rename the pinned scalar-key axis
// to KUBE_KEY_NAMESPACE or drop the `Some(...)` wrap" drift
// class every future re-lift on the peer-axis surface (a
// hypothetical `kube_namespace_is` peer on a per-namespace
// router harness, a `kube_uid_is` for ownerReference
// bookkeeping) would otherwise reopen. Peer of the sibling
// `kube_kind_is_matches_lifted_kube_root_str_field_equality_shape`
// pin on the `kind:` discriminator axis.
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(
KUBE_KEY_NAME,
serde_yaml::Value::String("checkout-cart-to-catalog".into()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert!(kube_name_is(&value, "checkout-cart-to-catalog"));
assert_eq!(
kube_name_is(&value, "checkout-cart-to-catalog"),
kube_metadata_str_field(&value, KUBE_KEY_NAME) == Some("checkout-cart-to-catalog"),
);
}
#[test]
fn kube_name_is_false_on_mismatched_name_and_missing_name() {
// Complement-side pin: the predicate returns `false` when
// either the name axis carries a different identity or the
// sub-`metadata.name:` scalar (or the enclosing `metadata:`
// block) is absent altogether (the same vacuous-`None`
// short-circuit the parent `kube_metadata_str_field` closes on
// the underlying two-hop navigation). Consumer sites
// (`docs.iter().find(|d| kube_name_is(d, X))`) rely on the
// false-on-mismatch shape to skip the wrong CRs across the
// multi-doc mesh emission and land on the intended per-name
// document. Peer of the sibling
// `kube_kind_is_false_on_mismatched_kind_and_missing_kind` pin
// on the `kind:` discriminator axis.
let mut wrong_meta = serde_yaml::Mapping::new();
wrong_meta.insert_str_key(
KUBE_KEY_NAME,
serde_yaml::Value::String("checkout-payment-to-cart".into()),
);
let mut cr_wrong_name = serde_yaml::Mapping::new();
cr_wrong_name.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(wrong_meta));
assert!(!kube_name_is(
&serde_yaml::Value::Mapping(cr_wrong_name),
"checkout-cart-to-catalog",
));
let cr_no_metadata = serde_yaml::Mapping::new();
assert!(!kube_name_is(
&serde_yaml::Value::Mapping(cr_no_metadata),
"checkout-cart-to-catalog",
));
let empty_meta = serde_yaml::Mapping::new();
let mut cr_no_name = serde_yaml::Mapping::new();
cr_no_name.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(empty_meta));
assert!(!kube_name_is(
&serde_yaml::Value::Mapping(cr_no_name),
"checkout-cart-to-catalog",
));
}
#[test]
fn find_by_name_matches_inline_iter_find_kube_name_is_shape() {
// Byte-equivalence pin: the lifted navigator reproduces the
// three-token combinator chain (`docs.iter().find(|d|
// kube_name_is(d, <NAME>))`) the 5 caixa-mesh test-side
// per-CNP-name find-by-name sites previously carried inline.
// Closes the "did the lift accidentally widen the receiver,
// drop the closure, or swap `find` for `filter`" drift class
// every future re-lift on the sibling multi-doc-navigator axis
// (a hypothetical `filter_by_name` peer that carries the same
// underlying predicate but returns an iterator) would otherwise
// reopen. Peer of the sibling
// `find_by_kind_matches_inline_iter_find_kube_kind_is_shape`
// pin on the `kind:` discriminator axis.
let mut meta_a = serde_yaml::Mapping::new();
meta_a.insert_str_key(
KUBE_KEY_NAME,
serde_yaml::Value::String("checkout-cart-to-catalog".into()),
);
let mut policy_a = serde_yaml::Mapping::new();
policy_a.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(meta_a));
let mut meta_b = serde_yaml::Mapping::new();
meta_b.insert_str_key(
KUBE_KEY_NAME,
serde_yaml::Value::String("checkout-payment-to-cart".into()),
);
let mut policy_b = serde_yaml::Mapping::new();
policy_b.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(meta_b));
let docs = vec![
serde_yaml::Value::Mapping(policy_a),
serde_yaml::Value::Mapping(policy_b),
];
assert_eq!(
find_by_name(&docs, "checkout-cart-to-catalog"),
docs.iter()
.find(|d| kube_name_is(d, "checkout-cart-to-catalog")),
);
assert_eq!(
find_by_name(&docs, "checkout-payment-to-cart"),
docs.iter()
.find(|d| kube_name_is(d, "checkout-payment-to-cart")),
);
// Miss path: absent name → None, matching the inline `.find`
// short-circuit that consumer sites rely on to distinguish
// "no such CR in this emission" from "wrong shape" in their
// `.unwrap()` / `.expect(...)` follow-ups.
assert_eq!(find_by_name(&docs, "checkout-cart-to-payment"), None);
let empty: Vec<serde_yaml::Value> = Vec::new();
assert_eq!(find_by_name(&empty, "checkout-cart-to-catalog"), None);
}
#[test]
fn find_by_name_returns_first_match_on_duplicate_name() {
// Order-preservation pin: the lifted navigator returns the
// first document of the matching name (the same short-circuit
// `Iterator::find` exposes). Multi-doc mesh emissions never
// carry two documents with identical `metadata.name` at V0
// (`cilium_network_policies` fans distinct `(:de, :para)`
// pairs into distinct CNP names — see the sibling
// `cilium_http_contracts_fan_multiple_edges_into_one_policy`
// fan-in pin), but the M4 cross-cluster fan-out will produce
// per-cluster CR duplicates on the identity axis (one
// `HelmRelease` per cluster carrying the same base name). Pin
// the first-match contract keeps the M4 caller-side "the
// first hit is the primary" convention aligned with the
// helper's combinator half. Peer of the sibling
// `find_by_kind_returns_first_match_on_duplicate_kind` pin on
// the `kind:` discriminator axis.
let mut meta_a = serde_yaml::Mapping::new();
meta_a.insert_str_key(
KUBE_KEY_NAME,
serde_yaml::Value::String("checkout-cart-to-catalog".into()),
);
meta_a.insert_str_key(
KUBE_KEY_NAMESPACE,
serde_yaml::Value::String("cluster-a".into()),
);
let mut policy_a = serde_yaml::Mapping::new();
policy_a.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(meta_a));
let mut meta_b = serde_yaml::Mapping::new();
meta_b.insert_str_key(
KUBE_KEY_NAME,
serde_yaml::Value::String("checkout-cart-to-catalog".into()),
);
meta_b.insert_str_key(
KUBE_KEY_NAMESPACE,
serde_yaml::Value::String("cluster-b".into()),
);
let mut policy_b = serde_yaml::Mapping::new();
policy_b.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(meta_b));
let docs = vec![
serde_yaml::Value::Mapping(policy_a),
serde_yaml::Value::Mapping(policy_b),
];
let first = find_by_name(&docs, "checkout-cart-to-catalog").unwrap();
assert_eq!(
kube_metadata_str_field(first, KUBE_KEY_NAMESPACE),
Some("cluster-a"),
);
}
#[test]
fn kube_namespace_is_composes_on_lifted_kube_namespace_accessor() {
// Composition pin: the peer predicate `kube_namespace_is(v, n)`
// must resolve exactly as `kube_namespace(v) == Some(n)` — no
// inline `kube_metadata_str_field(v, KUBE_KEY_NAMESPACE) ==
// Some(n)` composition, only the accessor + equality-wrap two-
// token shape. Pins the structural link between the three-arity
// closure (accessor / predicate / navigator) on the namespace-
// scoping axis: a future re-implementation of `kube_namespace`
// (a caching short-circuit for repeated readback on the same
// document, a hypothetical alias-table dispatch on a
// `metadata.tenant` sub-axis) reaches the predicate through one
// lift, not a second co-ordinated inline rewrite. Peer of the
// sibling `kube_name_is_composes_on_lifted_kube_name_accessor`
// pin on the identity axis.
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(
KUBE_KEY_NAMESPACE,
serde_yaml::Value::String("tatara-system".into()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_namespace_is(&value, "tatara-system"),
kube_namespace(&value) == Some("tatara-system"),
"kube_namespace_is must byte-agree with the peer \
`kube_namespace(v) == Some(n)` composition it delegates to \
— the predicate carries no more inline navigation, only \
the equality-wrap semantic distinct from the sibling \
accessor arity",
);
assert!(kube_namespace_is(&value, "tatara-system"));
assert!(!kube_namespace_is(&value, "flux-system"));
}
#[test]
fn kube_namespace_is_matches_lifted_kube_metadata_str_field_equality_shape() {
// Byte-equivalence pin: the lifted predicate reproduces the
// three-token composition (`kube_metadata_str_field(v,
// KUBE_KEY_NAMESPACE) == Some(<NS>)`) every future per-tenant
// `.find`/`.filter` site would otherwise carry inline. Closes
// the "did the lift accidentally rename the pinned scalar-key
// axis to KUBE_KEY_NAME (silently pulling the peer identity
// coordinate instead of the namespace-scoping one), drop the
// `Some(...)` wrap, or invert the comparator direction" drift
// class every future re-lift on the peer-axis surface (a
// hypothetical `kube_uid_is` for ownerReference bookkeeping,
// a `kube_resource_version_is` for optimistic-concurrency
// bookkeeping) would otherwise reopen. Peer of the sibling
// `kube_name_is_matches_lifted_kube_metadata_str_field_equality_shape`
// pin on the identity axis.
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(
KUBE_KEY_NAMESPACE,
serde_yaml::Value::String(DEFAULT_NAMESPACE.into()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert!(kube_namespace_is(&value, DEFAULT_NAMESPACE));
assert_eq!(
kube_namespace_is(&value, DEFAULT_NAMESPACE),
kube_metadata_str_field(&value, KUBE_KEY_NAMESPACE) == Some(DEFAULT_NAMESPACE),
"kube_namespace_is must byte-agree with the parametric \
`kube_metadata_str_field(v, KUBE_KEY_NAMESPACE) == \
Some(<NS>)` three-token composition — drift here silently \
splits the per-tenant router harness's namespace-scoping \
filter from the sibling accessor's readback",
);
}
#[test]
fn kube_namespace_is_false_on_mismatched_namespace_and_missing_namespace() {
// Complement-side pin: the predicate returns `false` when
// either the namespace-scoping axis carries a different
// coordinate or the sub-`metadata.namespace:` scalar (or the
// enclosing `metadata:` block) is absent altogether (the same
// vacuous-`None` short-circuit the parent
// `kube_metadata_str_field` closes on the underlying two-hop
// navigation). Consumer sites (`docs.iter().find(|d|
// kube_namespace_is(d, <NS>))`) rely on the false-on-mismatch
// shape to skip the wrong-namespace CRs across the multi-doc
// fleet emission and land on the intended per-tenant slice.
// Peer of the sibling
// `kube_name_is_false_on_mismatched_name_and_missing_name` pin
// on the identity axis.
let mut wrong_meta = serde_yaml::Mapping::new();
wrong_meta.insert_str_key(
KUBE_KEY_NAMESPACE,
serde_yaml::Value::String("flux-system".into()),
);
let mut cr_wrong_ns = serde_yaml::Mapping::new();
cr_wrong_ns.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(wrong_meta));
assert!(!kube_namespace_is(
&serde_yaml::Value::Mapping(cr_wrong_ns),
DEFAULT_NAMESPACE,
));
let cr_no_metadata = serde_yaml::Mapping::new();
assert!(!kube_namespace_is(
&serde_yaml::Value::Mapping(cr_no_metadata),
DEFAULT_NAMESPACE,
));
let empty_meta = serde_yaml::Mapping::new();
let mut cr_no_ns = serde_yaml::Mapping::new();
cr_no_ns.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(empty_meta));
assert!(!kube_namespace_is(
&serde_yaml::Value::Mapping(cr_no_ns),
DEFAULT_NAMESPACE,
));
}
#[test]
fn find_by_namespace_matches_inline_iter_find_kube_namespace_is_shape() {
// Byte-equivalence pin: the lifted navigator reproduces the
// three-token combinator chain (`docs.iter().find(|d|
// kube_namespace_is(d, <NS>))`) every future per-tenant fleet-
// slice site would otherwise carry inline. Closes the "did the
// lift accidentally widen the receiver, drop the closure, or
// swap `find` for `filter`" drift class every future re-lift on
// the sibling multi-doc-navigator axis (a hypothetical
// `filter_by_namespace` peer that returns an iterator across
// every matching per-tenant CR rather than the first hit) would
// otherwise reopen. Peer of the sibling
// `find_by_name_matches_inline_iter_find_kube_name_is_shape`
// pin on the identity axis.
let mut meta_a = serde_yaml::Mapping::new();
meta_a.insert_str_key(
KUBE_KEY_NAMESPACE,
serde_yaml::Value::String("tatara-system".into()),
);
let mut policy_a = serde_yaml::Mapping::new();
policy_a.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(meta_a));
let mut meta_b = serde_yaml::Mapping::new();
meta_b.insert_str_key(
KUBE_KEY_NAMESPACE,
serde_yaml::Value::String("flux-system".into()),
);
let mut policy_b = serde_yaml::Mapping::new();
policy_b.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(meta_b));
let docs = vec![
serde_yaml::Value::Mapping(policy_a),
serde_yaml::Value::Mapping(policy_b),
];
assert_eq!(
find_by_namespace(&docs, "tatara-system"),
docs.iter().find(|d| kube_namespace_is(d, "tatara-system")),
);
assert_eq!(
find_by_namespace(&docs, "flux-system"),
docs.iter().find(|d| kube_namespace_is(d, "flux-system")),
);
// Miss path: absent namespace-scoping coordinate → None,
// matching the inline `.find` short-circuit that consumer
// sites rely on to distinguish "no such per-tenant slice in
// this emission" from "wrong shape" in their `.unwrap()` /
// `.expect(...)` follow-ups. Picked a namespace-scoping value
// outside the two-fixture set so the miss-path answer is
// structurally None rather than coincidentally so — a fixture
// whose per-tenant coordinate happened to match one of the
// emitted CRs would silently short-circuit as `Some(...)` and
// never exercise the None-arm.
assert_eq!(find_by_namespace(&docs, "kube-system"), None);
let empty: Vec<serde_yaml::Value> = Vec::new();
assert_eq!(find_by_namespace(&empty, "tatara-system"), None);
}
#[test]
fn find_by_namespace_returns_first_match_on_duplicate_namespace() {
// Order-preservation pin: the lifted navigator returns the
// first document of the matching namespace-scoping coordinate
// (the same short-circuit `Iterator::find` exposes). Every
// per-tenant CR emission legally carries many CRs sharing a
// single `metadata.namespace` (a per-tenant namespace slices
// many `HelmRelease` + many `CiliumNetworkPolicy` +
// `Gateway` / `HTTPRoute` under one namespace-scoping
// coordinate), unlike the peer identity axis where each
// `metadata.name` is unique per namespace-scope. Pin the
// first-match contract keeps the M4 caller-side "the first hit
// is the primary per-tenant CR" convention aligned with the
// helper's combinator half. Peer of the sibling
// `find_by_name_returns_first_match_on_duplicate_name` pin on
// the identity axis.
let mut meta_a = serde_yaml::Mapping::new();
meta_a.insert_str_key(
KUBE_KEY_NAME,
serde_yaml::Value::String("checkout-cart-to-catalog".into()),
);
meta_a.insert_str_key(
KUBE_KEY_NAMESPACE,
serde_yaml::Value::String("tatara-system".into()),
);
let mut policy_a = serde_yaml::Mapping::new();
policy_a.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(meta_a));
let mut meta_b = serde_yaml::Mapping::new();
meta_b.insert_str_key(
KUBE_KEY_NAME,
serde_yaml::Value::String("checkout-payment-to-cart".into()),
);
meta_b.insert_str_key(
KUBE_KEY_NAMESPACE,
serde_yaml::Value::String("tatara-system".into()),
);
let mut policy_b = serde_yaml::Mapping::new();
policy_b.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(meta_b));
let docs = vec![
serde_yaml::Value::Mapping(policy_a),
serde_yaml::Value::Mapping(policy_b),
];
let first = find_by_namespace(&docs, "tatara-system").unwrap();
assert_eq!(
kube_name(first),
Some("checkout-cart-to-catalog"),
"find_by_namespace must return the first per-namespace \
CR in emission order — the M4 cross-cluster fan-out's \
per-tenant slicer treats the first-hit CR as the primary \
per-tenant coordinate, matching the peer navigator's \
first-match contract on the identity axis",
);
}
// ── kube_metadata_labels + kube_metadata_label lifts ────────────────
#[test]
fn kube_metadata_labels_reads_metadata_labels_sub_mapping() {
// The lift's load-bearing contract: given a Value carrying a
// top-level `metadata: { labels: { <label>: <str>, ... } }`
// block (every K8s CR the emit-side [`kube_resource_skeleton`]
// renders with a non-empty labels overlay), the helper returns
// Some(&Mapping) borrowing into the input Value. Pinned because
// the caixa-mesh per-CNP labels enumeration site
// (`cilium_policy_metadata_labels_carry_only_pleme_prefixed_
// canonical_label_set`) reaches through this exact sub-mapping
// readback, and a drift on the borrowed-mapping contract would
// silently regress the enumeration's `for (k, _) in labels` walk.
let mut labels = serde_yaml::Mapping::new();
labels.insert_str_key(
LABEL_APLICACAO,
serde_yaml::Value::String("checkout".into()),
);
labels.insert_str_key(
LABEL_CONTRATO,
serde_yaml::Value::String("cart-to-catalog".into()),
);
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_LABELS, serde_yaml::Value::Mapping(labels.clone()));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata_labels(&value),
Some(&labels),
"kube_metadata_labels must read the metadata.labels sub-\
mapping — the caixa-mesh per-CNP labels enumeration site \
reaches through this axis for per-key iteration"
);
}
#[test]
fn kube_metadata_labels_returns_none_when_metadata_block_absent() {
// The three-way vacuous-None short-circuit's first arm: an
// outer Value that legally omits the `metadata:` block short-
// circuits at the first hop through the underlying
// `.get(KUBE_KEY_METADATA)`. The K8s CR readback surface
// accepts arbitrary Value inputs, including external YAML
// documents that legally omit the `metadata:` block; pin the
// None-arm so a future refactor that reaches for
// `.get(...).unwrap()` (which would panic on the missing
// block) is a test-visible break. Peer of the sibling
// `kube_metadata_str_field_returns_none_when_metadata_block_absent`
// pin on the scalar-arity peer.
let value = serde_yaml::Value::Mapping(serde_yaml::Mapping::new());
assert_eq!(
kube_metadata_labels(&value),
None,
"kube_metadata_labels must short-circuit to None when the \
top-level `metadata:` block is absent — the prior inline \
three-hop chain's first `.get(KUBE_KEY_METADATA)` hop \
returned None here"
);
// Also verify the shape on non-Mapping outer Value shapes.
for shape in [
serde_yaml::Value::Null,
serde_yaml::Value::String("scalar".into()),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::Number(0.into()),
serde_yaml::Value::Bool(false),
] {
assert_eq!(
kube_metadata_labels(&shape),
None,
"kube_metadata_labels({shape:?}) must return None on \
non-Mapping outer shapes — the prior inline chain's \
`.get(KUBE_KEY_METADATA)` hop yields None on every \
non-Mapping Value, and the lift must preserve that \
contract"
);
}
}
#[test]
fn kube_metadata_labels_returns_none_when_labels_sub_block_absent() {
// The three-way vacuous-None short-circuit's second arm: a
// well-formed CR carrying a `metadata:` block but no
// `labels:` sub-block short-circuits at the middle hop through
// the underlying `.and_then(|m| m.get(KUBE_KEY_LABELS))`. The
// emit-side [`kube_resource_skeleton`]'s
// `labels.is_empty()` short-circuit legally omits the `labels:`
// sub-block for CRs like Gateway that need no per-Aplicacao
// label grouping at the K8s-resource axis today; pin the
// middle-hop None-arm so this readback preserves the emit-
// side's short-circuit semantic on the reverse.
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(
KUBE_KEY_NAME,
serde_yaml::Value::String("checkout-gateway".into()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata_labels(&value),
None,
"kube_metadata_labels must return None when the enclosing \
`metadata:` block is present but omits the `labels:` sub-\
block — the prior inline three-hop chain's middle \
`.and_then(|m| m.get(KUBE_KEY_LABELS))` hop short-\
circuited here, mirroring the emit-side's `labels.is_\
empty()` skip"
);
}
#[test]
fn kube_metadata_labels_returns_none_when_labels_carries_non_mapping_type() {
// The three-way vacuous-None short-circuit's third arm: a
// present-but-non-Mapping `labels:` value (a schema-invalid
// shape per the K8s API-machinery's labels contract, which
// pins the block as `map[string]string`, but tolerated here
// as None so the readback stays a total function). Pin the
// trailing shape gate so a future refactor that reaches for
// `.as_mapping().unwrap()` (which would panic on a numeric
// labels-value) is a test-visible break, not a runtime
// regression at the first schema-invalid CR the reader sees.
for non_mapping in [
serde_yaml::Value::Null,
serde_yaml::Value::Number(42.into()),
serde_yaml::Value::Bool(true),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::String("labels-as-string".into()),
] {
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_LABELS, non_mapping.clone());
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata_labels(&value),
None,
"kube_metadata_labels must return None when \
metadata.labels carries a non-Mapping YAML type \
({non_mapping:?}) — the trailing \
`.and_then(|l| l.as_mapping())` shape gate short-\
circuited here on the prior inline chain, and every \
routed caller depends on that None-arm to keep the \
readback total"
);
}
}
#[test]
fn kube_metadata_labels_matches_prior_inline_chain() {
// Cross-check the helper's output byte-for-byte against the
// prior inline three-hop chain the routed caller previously
// carried. A drift between the helper's return and the inline
// chain would silently regress the caixa-mesh per-CNP labels
// enumeration's `for (k, _) in labels` walk — pin the byte-
// equivalence so the helper remains a drop-in replacement for
// the routed site's prior three-line block. Peer of the
// sibling `kube_metadata_str_field_matches_prior_inline_chain`
// pin on the scalar-arity peer.
let mut labels = serde_yaml::Mapping::new();
labels.insert_str_key(
LABEL_APLICACAO,
serde_yaml::Value::String("checkout".into()),
);
labels.insert_str_key(
LABEL_CONTRATO,
serde_yaml::Value::String("payment-to-cart".into()),
);
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_LABELS, serde_yaml::Value::Mapping(labels));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
let via_helper = kube_metadata_labels(&value);
let via_inline = value
.get(KUBE_KEY_METADATA)
.and_then(|m| m.get(KUBE_KEY_LABELS))
.and_then(|l| l.as_mapping());
assert_eq!(
via_helper, via_inline,
"kube_metadata_labels must yield the same Option<&Mapping> \
as the prior inline three-hop chain — otherwise the \
routed caixa-mesh per-CNP labels enumeration site drifts \
silently at test time"
);
}
#[test]
fn kube_metadata_label_reads_per_label_string_scalar() {
// The composed lift's load-bearing contract: given a Value
// carrying a top-level `metadata.labels.<label>: <str>` scalar,
// the helper returns Some(<str>) borrowing into the input
// Value. Pinned because the two caixa-mesh test-side per-CNP
// label-value probes (contrato-values-collect, LABEL_APLICACAO
// readback) reach through this exact string-scalar readback,
// and a drift on the borrowed-string contract would silently
// regress both routed sites' equality comparison.
let mut labels = serde_yaml::Mapping::new();
labels.insert_str_key(
LABEL_APLICACAO,
serde_yaml::Value::String("checkout".into()),
);
labels.insert_str_key(
LABEL_CONTRATO,
serde_yaml::Value::String("cart-to-catalog".into()),
);
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_LABELS, serde_yaml::Value::Mapping(labels));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata_label(&value, LABEL_APLICACAO),
Some("checkout"),
"kube_metadata_label must read the metadata.labels.\
LABEL_APLICACAO string-scalar — the caixa-mesh per-CNP \
parent-Aplicacao readback site reaches through this axis \
for label-value equality"
);
assert_eq!(
kube_metadata_label(&value, LABEL_CONTRATO),
Some("cart-to-catalog"),
"kube_metadata_label must read the metadata.labels.\
LABEL_CONTRATO string-scalar — the caixa-mesh per-CNP \
contrato-values-collect site reaches through this axis \
for the per-edge label collect"
);
}
#[test]
fn kube_metadata_label_returns_none_when_labels_block_absent() {
// The four-way vacuous-None short-circuit's first-three arms:
// any short-circuit the composed [`kube_metadata_labels`] sub-
// mapping accessor closes on (missing metadata block, missing
// labels sub-block, non-Mapping labels value) folds through
// this composed accessor. Pin the composition-shape here so
// the two routed caixa-mesh label-value probes preserve the
// None-arm semantics that keep their `.expect(...)` /
// `.map(String::from)` follow-ups sound.
let value = serde_yaml::Value::Mapping(serde_yaml::Mapping::new());
assert_eq!(
kube_metadata_label(&value, LABEL_APLICACAO),
None,
"kube_metadata_label must short-circuit to None when the \
top-level `metadata:` block is absent — folds through the \
composed [`kube_metadata_labels`] sub-mapping accessor's \
first-hop None"
);
// Present metadata but absent labels sub-block — the
// composed sub-mapping accessor's middle-hop None-arm.
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(
KUBE_KEY_NAME,
serde_yaml::Value::String("checkout-gateway".into()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata_label(&value, LABEL_APLICACAO),
None,
"kube_metadata_label must short-circuit to None when the \
`labels:` sub-block is absent — folds through the \
composed [`kube_metadata_labels`] sub-mapping accessor's \
middle-hop None"
);
}
#[test]
fn kube_metadata_label_returns_none_when_requested_label_absent() {
// The four-way vacuous-None short-circuit's third arm: a
// labels sub-mapping present but missing the requested label
// key — a legally-omitted per-label surface on a CR that
// carries other labels but not this one (a Gateway that
// carries LABEL_PROGRAM but not LABEL_CONTRATO, a per-tenant
// slice CR that carries LABEL_APLICACAO but not per-`(:de,
// :para)` LABEL_CONTRATO). Pin the middle-hop None-arm so
// future consumers can distinguish "no such label" from
// "wrong shape" in their `.unwrap_or_default(...)` /
// `.expect(...)` follow-ups.
let mut labels = serde_yaml::Mapping::new();
labels.insert_str_key(
LABEL_APLICACAO,
serde_yaml::Value::String("checkout".into()),
);
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_LABELS, serde_yaml::Value::Mapping(labels));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata_label(&value, LABEL_CONTRATO),
None,
"kube_metadata_label must return None when the requested \
`metadata.labels.<label>` axis-key is absent — the prior \
inline four-hop chain's per-label `.and_then(|l| l.get(\
<LABEL>))` sub-hop short-circuited here"
);
}
#[test]
fn kube_metadata_label_returns_none_when_label_carries_non_string_type() {
// The four-way vacuous-None short-circuit's fourth arm: a
// label-value present but carrying a non-string YAML type — a
// schema-invalid label per the K8s labels contract that pins
// values as string scalars, but tolerated here as None so the
// readback stays a total function. Pin the trailing shape gate
// so a future refactor that reaches for `.as_str().unwrap()`
// (which would panic on a numeric label-value) is a test-
// visible break.
for non_string in [
serde_yaml::Value::Null,
serde_yaml::Value::Number(42.into()),
serde_yaml::Value::Bool(true),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
] {
let mut labels = serde_yaml::Mapping::new();
labels.insert_str_key(LABEL_APLICACAO, non_string.clone());
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_LABELS, serde_yaml::Value::Mapping(labels));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata_label(&value, LABEL_APLICACAO),
None,
"kube_metadata_label must return None when \
metadata.labels.LABEL_APLICACAO carries a non-string \
YAML type ({non_string:?}) — the trailing `.and_then(\
|v| v.as_str())` shape gate short-circuited here"
);
}
}
#[test]
fn kube_metadata_label_composes_on_lifted_kube_metadata_labels_accessor() {
// Composition pin: the scalar-arity label-value accessor
// folds onto the sub-mapping-arity sub-block accessor as
// `kube_metadata_labels(value).and_then(|labels| labels.get(
// label)).and_then(|v| v.as_str())`. Pin the composition-shape
// across every combination of the two canonical caixa-mesh
// per-CNP label surface's routed keys (LABEL_APLICACAO,
// LABEL_CONTRATO) so a future accidental rewire of the
// helper's internals to a private four-hop chain (bypassing
// the sub-mapping accessor) is a test-visible break, matching
// the sibling `kube_name_is_composes_on_lifted_kube_name_
// accessor` pin's discipline on the identity-axis composition.
let mut labels = serde_yaml::Mapping::new();
labels.insert_str_key(
LABEL_APLICACAO,
serde_yaml::Value::String("checkout".into()),
);
labels.insert_str_key(
LABEL_CONTRATO,
serde_yaml::Value::String("cart-to-payment".into()),
);
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_LABELS, serde_yaml::Value::Mapping(labels));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
for label in [LABEL_APLICACAO, LABEL_CONTRATO] {
let via_helper = kube_metadata_label(&value, label);
let via_composed = kube_metadata_labels(&value)
.and_then(|labels| labels.get(label))
.and_then(|v| v.as_str());
assert_eq!(
via_helper, via_composed,
"kube_metadata_label(_, {label:?}) must compose on \
kube_metadata_labels(_).and_then(get(label)).and_\
then(as_str) — otherwise the routed caixa-mesh label-\
value sites drift silently from the sub-mapping \
accessor's contract"
);
}
}
#[test]
fn kube_metadata_label_matches_prior_inline_chain() {
// Cross-check the helper's output byte-for-byte against the
// prior inline four-hop chain both routed callers previously
// carried. A drift between the helper's return and the inline
// chain would silently regress the caixa-mesh per-CNP LABEL_
// CONTRATO values collect + the per-CNP LABEL_APLICACAO
// readback — pin the byte-equivalence so the helper remains a
// drop-in replacement for both routed sites' prior four-line
// block. Peer of the sibling `kube_metadata_str_field_matches_
// prior_inline_chain` pin on the scalar-arity peer at the
// shallower `metadata.<field>` navigation depth.
let mut labels = serde_yaml::Mapping::new();
labels.insert_str_key(
LABEL_APLICACAO,
serde_yaml::Value::String("checkout".into()),
);
labels.insert_str_key(
LABEL_CONTRATO,
serde_yaml::Value::String("cart-to-catalog".into()),
);
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_LABELS, serde_yaml::Value::Mapping(labels));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
for label in [LABEL_APLICACAO, LABEL_CONTRATO] {
let via_helper = kube_metadata_label(&value, label);
let via_inline = value
.get(KUBE_KEY_METADATA)
.and_then(|m| m.get(KUBE_KEY_LABELS))
.and_then(|l| l.get(label))
.and_then(|v| v.as_str());
assert_eq!(
via_helper, via_inline,
"kube_metadata_label(_, {label:?}) must yield the same \
Option<&str> as the prior inline four-hop chain — \
otherwise the two routed caixa-mesh test-side per-CNP \
label-value sites drift silently"
);
}
}
// ── kube_metadata_label_is + find_by_label lifts ────────────────────
#[test]
fn kube_metadata_label_is_answers_true_only_when_label_and_value_match() {
// Positive-truth pin: the predicate returns true iff the
// `metadata.labels.<label>` sub-mapping-value byte-equals the
// `expected` axis-value. Pinned across the two canonical
// caixa-mesh per-CNP labels (LABEL_APLICACAO, LABEL_CONTRATO)
// so both the routed test-side call site
// (`cilium_policy_metadata_labels_use_lifted_consts`) and every
// future per-label predicate site (the future
// `app-operator`'s per-Aplicacao selector-based reconciler
// MESH-COMPOSITION §III.2 #5) reach through this exact
// string-scalar equality.
let mut labels = serde_yaml::Mapping::new();
labels.insert_str_key(
LABEL_APLICACAO,
serde_yaml::Value::String("checkout".into()),
);
labels.insert_str_key(
LABEL_CONTRATO,
serde_yaml::Value::String("cart-to-catalog".into()),
);
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_LABELS, serde_yaml::Value::Mapping(labels));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert!(
kube_metadata_label_is(&value, LABEL_APLICACAO, "checkout"),
"kube_metadata_label_is must return true when \
metadata.labels.LABEL_APLICACAO byte-equals `expected` — \
the caixa-mesh per-CNP parent-Aplicacao label-selector \
equality routes through this predicate"
);
assert!(
kube_metadata_label_is(&value, LABEL_CONTRATO, "cart-to-catalog"),
"kube_metadata_label_is must return true when \
metadata.labels.LABEL_CONTRATO byte-equals `expected` — \
the caixa-mesh per-CNP contrato-edge label-selector \
equality routes through this predicate"
);
}
#[test]
fn kube_metadata_label_is_returns_false_when_value_differs() {
// Negative-value pin: the predicate returns false when the
// label is present but the value does not byte-equal
// `expected`. Pin the "label present but wrong value" arm so
// a future selector-consumer's `if kube_metadata_label_is(...)
// { … }` gate does not silently short-circuit onto a
// structurally-adjacent CR whose selector value drifted.
let mut labels = serde_yaml::Mapping::new();
labels.insert_str_key(
LABEL_APLICACAO,
serde_yaml::Value::String("checkout".into()),
);
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_LABELS, serde_yaml::Value::Mapping(labels));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert!(
!kube_metadata_label_is(&value, LABEL_APLICACAO, "orders"),
"kube_metadata_label_is must return false when \
metadata.labels.LABEL_APLICACAO carries a value that does \
not byte-equal `expected` — the underlying \
`kube_metadata_label(_, LABEL_APLICACAO) == Some(expected)` \
equality wraps this arm as a distinct-`Some` inequality"
);
}
#[test]
fn kube_metadata_label_is_returns_false_on_every_accessor_short_circuit_arm() {
// Total-function pin: the predicate collapses every vacuous-
// None arm the underlying `kube_metadata_label` accessor
// closes on (missing metadata block, missing labels sub-block,
// non-Mapping labels value, requested label key absent,
// non-string label value) onto `false` — the same boolean
// verdict every downstream label-selector consumer (Cilium
// `endpointSelector.matchLabels`, `kubectl -l` grep, Hubble
// flow grouping) resolves each non-match to. A drift onto the
// three-way `Option` split would silently reintroduce arm-
// specific branching at every consumer site.
// Arm 1: missing metadata block.
let value = serde_yaml::Value::Mapping(serde_yaml::Mapping::new());
assert!(
!kube_metadata_label_is(&value, LABEL_APLICACAO, "checkout"),
"predicate must return false when metadata block absent"
);
// Arm 2: present metadata but absent labels sub-block.
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(
KUBE_KEY_NAME,
serde_yaml::Value::String("checkout-gateway".into()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert!(
!kube_metadata_label_is(&value, LABEL_APLICACAO, "checkout"),
"predicate must return false when labels sub-block absent"
);
// Arm 3: labels sub-block present but requested label absent.
let mut labels = serde_yaml::Mapping::new();
labels.insert_str_key(
LABEL_APLICACAO,
serde_yaml::Value::String("checkout".into()),
);
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_LABELS, serde_yaml::Value::Mapping(labels));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert!(
!kube_metadata_label_is(&value, LABEL_CONTRATO, "cart-to-catalog"),
"predicate must return false when requested label key absent"
);
// Arm 4: label present but non-string value.
let mut labels = serde_yaml::Mapping::new();
labels.insert_str_key(LABEL_APLICACAO, serde_yaml::Value::Number(42.into()));
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_LABELS, serde_yaml::Value::Mapping(labels));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert!(
!kube_metadata_label_is(&value, LABEL_APLICACAO, "42"),
"predicate must return false when label value is non-string \
(numeric coercion to a matching decimal string must NOT \
satisfy the predicate — the underlying accessor's shape \
gate short-circuits to None, and the equality wrap folds \
None onto false)"
);
}
#[test]
fn kube_metadata_label_is_composes_on_lifted_kube_metadata_label_accessor() {
// Composition pin: the predicate folds onto the composed
// scalar-arity `kube_metadata_label` accessor as
// `kube_metadata_label(value, label) == Some(expected)`. Pin
// the composition-shape across every combination of the two
// canonical caixa-mesh per-CNP labels + both a matching and
// a mismatching `expected` so a future accidental rewire of
// the predicate's internals to a private inline four-hop
// chain (bypassing the accessor) is a test-visible break —
// matching the sibling `kube_name_is_composes_on_lifted_kube_
// name_accessor` pin's discipline on the identity-axis
// composition.
let mut labels = serde_yaml::Mapping::new();
labels.insert_str_key(
LABEL_APLICACAO,
serde_yaml::Value::String("checkout".into()),
);
labels.insert_str_key(
LABEL_CONTRATO,
serde_yaml::Value::String("cart-to-payment".into()),
);
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_LABELS, serde_yaml::Value::Mapping(labels));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
for (label, expected) in [
(LABEL_APLICACAO, "checkout"),
(LABEL_APLICACAO, "orders"),
(LABEL_CONTRATO, "cart-to-payment"),
(LABEL_CONTRATO, "cart-to-catalog"),
] {
let via_helper = kube_metadata_label_is(&value, label, expected);
let via_composed = kube_metadata_label(&value, label) == Some(expected);
assert_eq!(
via_helper, via_composed,
"kube_metadata_label_is(_, {label:?}, {expected:?}) must \
compose on `kube_metadata_label(_, label) == Some(expected)` \
— otherwise the routed caixa-mesh label-selector site \
drifts silently from the sub-mapping accessor's contract"
);
}
}
#[test]
fn find_by_label_returns_first_matching_document() {
// First-match pin: the navigator returns the first document
// in emission order whose `metadata.labels.<label>` byte-
// equals `expected`. Pin the first-match semantics against a
// three-document sequence so a future rewire that accidentally
// returned the last-match (via `.rev().find(...)`) or an all-
// matches filter (via `.filter(...).next()` semantics that
// silently reordered) is a test-visible break, matching the
// sibling `find_by_name` / `find_by_namespace` / `find_by_kind`
// / `find_by_api_version` navigators' first-match contract on
// the sibling scalar-key axes.
let make = |name: &str, program: &str| {
let mut labels = serde_yaml::Mapping::new();
labels.insert_str_key(LABEL_PROGRAM, serde_yaml::Value::String(program.into()));
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String(name.into()));
metadata.insert_str_key(KUBE_KEY_LABELS, serde_yaml::Value::Mapping(labels));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
serde_yaml::Value::Mapping(cr)
};
let docs = vec![
make("cart-a", "cart"),
make("cart-b", "cart"),
make("payment-a", "payment"),
];
let hit = find_by_label(&docs, LABEL_PROGRAM, "cart");
assert_eq!(
hit.and_then(|d| kube_name(d)),
Some("cart-a"),
"find_by_label must return the FIRST document whose \
metadata.labels.LABEL_PROGRAM byte-equals `cart` — the \
first-match contract must not reorder to last-match"
);
let payment = find_by_label(&docs, LABEL_PROGRAM, "payment");
assert_eq!(
payment.and_then(|d| kube_name(d)),
Some("payment-a"),
"find_by_label must locate a document further into the \
sequence when the earlier documents do not match"
);
}
#[test]
fn find_by_label_returns_none_when_no_document_matches() {
// Empty-set pin: the navigator returns None when no document
// in `docs` carries the requested `(<label>, <expected>)`
// binding — the same "no match" arm the sibling navigators
// return on. Pin the None-arm so a future selector-consumer's
// `if let Some(cr) = find_by_label(...) { … }` gate does not
// silently short-circuit onto a structurally-adjacent CR.
let make = |program: &str| {
let mut labels = serde_yaml::Mapping::new();
labels.insert_str_key(LABEL_PROGRAM, serde_yaml::Value::String(program.into()));
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_LABELS, serde_yaml::Value::Mapping(labels));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
serde_yaml::Value::Mapping(cr)
};
let docs = vec![make("cart"), make("payment")];
assert!(
find_by_label(&docs, LABEL_PROGRAM, "catalog").is_none(),
"find_by_label must return None when no document carries \
the requested (label, expected) binding"
);
// Also None on an empty document set — pin the boundary.
assert!(
find_by_label(&[], LABEL_PROGRAM, "cart").is_none(),
"find_by_label must return None on an empty document set"
);
}
#[test]
fn find_by_label_composes_on_lifted_kube_metadata_label_is_predicate() {
// Composition pin: the navigator folds onto the composed
// predicate arity as
// `docs.iter().find(|d| kube_metadata_label_is(d, label, expected))`.
// Pin the composition-shape so a future rewire that bypasses
// the predicate (a private inline `docs.iter().find(|d| {
// kube_metadata_label(d, label) == Some(expected) })` copy)
// is a test-visible break, matching the sibling `find_by_name
// _composes_on_lifted_kube_name_is_predicate` pin's discipline.
let make = |name: &str, program: &str| {
let mut labels = serde_yaml::Mapping::new();
labels.insert_str_key(LABEL_PROGRAM, serde_yaml::Value::String(program.into()));
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String(name.into()));
metadata.insert_str_key(KUBE_KEY_LABELS, serde_yaml::Value::Mapping(labels));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
serde_yaml::Value::Mapping(cr)
};
let docs = vec![make("cart-a", "cart"), make("payment-a", "payment")];
for (label, expected) in [
(LABEL_PROGRAM, "cart"),
(LABEL_PROGRAM, "payment"),
(LABEL_PROGRAM, "catalog"),
] {
let via_helper = find_by_label(&docs, label, expected).and_then(|d| kube_name(d));
let via_composed = docs
.iter()
.find(|d| kube_metadata_label_is(d, label, expected))
.and_then(|d| kube_name(d));
assert_eq!(
via_helper, via_composed,
"find_by_label(_, {label:?}, {expected:?}) must compose \
on `docs.iter().find(|d| kube_metadata_label_is(d, \
label, expected))` — otherwise the routed selector-\
axis site drifts silently from the predicate's \
contract"
);
}
}
// ── kube_spec lift ──────────────────────────────────────────────────
#[test]
fn kube_spec_reads_top_level_spec_sub_mapping() {
// The lift's load-bearing contract: given a Value carrying a
// top-level `spec: { <key>: <value>, ... }` body sub-mapping
// (every K8s CR the emit-side [`kube_resource_skeleton`]
// renders with an `insert_mapping(KUBE_KEY_SPEC, …)` overlay),
// the helper returns Some(&Mapping) borrowing into the input
// Value. Structural mirror of the sibling
// `kube_metadata_labels_reads_metadata_labels_sub_mapping` pin
// on the sub-`metadata.labels` sub-block: both accessors gate
// on Mapping shape and return `Option<&Mapping>` on their
// respective pinned canonical sub-block axis-key.
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(
CILIUM_KEY_INGRESS,
serde_yaml::Value::Sequence(vec![serde_yaml::Value::Mapping(
serde_yaml::Mapping::new(),
)]),
);
spec.insert_str_key(
CILIUM_KEY_ENDPOINT_SELECTOR,
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec.clone()));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_spec(&value),
Some(&spec),
"kube_spec must read the top-level `spec:` sub-mapping — \
every caixa-mesh per-CR test-side readback site reaches \
through this axis for further per-body-key navigation \
(spec.ingress[], spec.listeners[], spec.parentRefs[])"
);
}
#[test]
fn kube_spec_returns_none_when_spec_sub_block_absent() {
// The two-way vacuous-None short-circuit's outer arm: an outer
// Value that legally omits the `spec:` block short-circuits at
// the first hop through the underlying `.get(KUBE_KEY_SPEC)`.
// The K8s CR readback surface accepts arbitrary Value inputs,
// including `List`-shaped documents or spec-less
// `ConfigMap`/`Secret` shapes that legally omit the `spec:`
// block; pin the None-arm so a future refactor that reaches
// for `.get(...).unwrap()` (which would panic on the missing
// block) is a test-visible break. Peer of the sibling
// `kube_metadata_labels_returns_none_when_metadata_block_absent`
// pin on the sub-`metadata.labels` sub-block accessor.
let value = serde_yaml::Value::Mapping(serde_yaml::Mapping::new());
assert_eq!(
kube_spec(&value),
None,
"kube_spec must short-circuit to None when the top-level \
`spec:` block is absent — the prior inline two-hop chain's \
first `.get(KUBE_KEY_SPEC)` hop returned None here"
);
// Also verify the shape on non-Mapping outer Value shapes.
for shape in [
serde_yaml::Value::Null,
serde_yaml::Value::String("scalar".into()),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::Number(0.into()),
serde_yaml::Value::Bool(false),
] {
assert_eq!(
kube_spec(&shape),
None,
"kube_spec({shape:?}) must return None on non-Mapping \
outer shapes — the prior inline chain's \
`.get(KUBE_KEY_SPEC)` hop yields None on every non-\
Mapping Value, and the lift must preserve that \
contract"
);
}
}
#[test]
fn kube_spec_returns_none_when_spec_carries_non_mapping_type() {
// The two-way vacuous-None short-circuit's trailing arm: a
// present-but-non-Mapping `spec:` value (a schema-invalid
// shape per the K8s API-machinery's `CustomResource` contract,
// which pins the per-CR body sub-block as a Mapping, but
// tolerated here as None so the readback stays a total
// function). Pin the trailing `.as_mapping()` shape gate so a
// future refactor that reaches for `.as_mapping().unwrap()`
// (which would panic on a numeric spec-value) is a test-
// visible break, not a runtime regression at the first
// schema-invalid CR the reader sees. Peer of the sibling
// `kube_metadata_labels_returns_none_when_labels_carries_non_mapping_type`
// pin on the sub-`metadata.labels` sub-block accessor.
for non_mapping in [
serde_yaml::Value::Null,
serde_yaml::Value::Number(42.into()),
serde_yaml::Value::Bool(true),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::String("spec-as-string".into()),
] {
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, non_mapping.clone());
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_spec(&value),
None,
"kube_spec must return None when the top-level `spec:` \
axis carries a non-Mapping YAML type ({non_mapping:?}) \
— the trailing `.and_then(|s| s.as_mapping())` shape \
gate short-circuited here on the prior inline chain, \
and every routed caller depends on that None-arm to \
keep the readback total"
);
}
}
#[test]
fn kube_spec_matches_prior_inline_chain() {
// Cross-check the helper's output byte-for-byte against the
// prior inline two-hop chain the routed caller previously
// carried. A drift between the helper's return and the inline
// chain would silently regress the caixa-mesh per-CR spec-
// readback sites' `.and_then(|s| s.get(<SUB_FIELD>))`
// continuations — pin the byte-equivalence so the helper
// remains a drop-in replacement for the routed site's prior
// two-line block. Peer of the sibling
// `kube_metadata_labels_matches_prior_inline_chain` pin on the
// sub-`metadata.labels` sub-block accessor.
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(
GATEWAY_API_KEY_LISTENERS,
serde_yaml::Value::Sequence(vec![]),
);
spec.insert_str_key(
GATEWAY_API_KEY_PARENT_REFS,
serde_yaml::Value::Sequence(vec![]),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
let via_helper = kube_spec(&value);
let via_inline = value.get(KUBE_KEY_SPEC).and_then(|s| s.as_mapping());
assert_eq!(
via_helper, via_inline,
"kube_spec must yield the same Option<&Mapping> as the \
prior inline two-hop chain — otherwise the routed \
caixa-mesh per-CR spec-readback sites drift silently at \
test time"
);
}
#[test]
fn kube_spec_composes_with_further_sub_field_navigation() {
// Composition pin: the routed convergence pattern is
// `kube_spec(v).and_then(|s| s.get(<SUB_FIELD>))` — the outer
// two-hop `spec → as_mapping` navigation happens inside the
// helper, and the trailing `Mapping::get(<SUB_FIELD>)` stays
// composition-symmetric with the prior inline
// `.get(KUBE_KEY_SPEC).and_then(|s| s.get(<SUB_FIELD>))` shape
// (both return `Option<&Value>`, so the fold is a drop-in for
// every routed test-harness callback body). Pin the
// composition-shape across three representative sub-field
// axis-keys (`CILIUM_KEY_INGRESS` for the per-CNP ingress-
// rules readback, `GATEWAY_API_KEY_LISTENERS` for the per-
// Gateway listener-set readback, `GATEWAY_API_KEY_PARENT_REFS`
// for the per-HTTPRoute parent-Gateway readback) so a future
// rewire that bypasses the helper (a private inline two-hop
// chain copy) is a test-visible break.
let ingress =
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("ingress-marker".into())]);
let listeners =
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("listener-marker".into())]);
let parent_refs = serde_yaml::Value::Sequence(vec![serde_yaml::Value::String(
"parent-ref-marker".into(),
)]);
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(CILIUM_KEY_INGRESS, ingress.clone());
spec.insert_str_key(GATEWAY_API_KEY_LISTENERS, listeners.clone());
spec.insert_str_key(GATEWAY_API_KEY_PARENT_REFS, parent_refs.clone());
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
for (sub_field, expected) in [
(CILIUM_KEY_INGRESS, &ingress),
(GATEWAY_API_KEY_LISTENERS, &listeners),
(GATEWAY_API_KEY_PARENT_REFS, &parent_refs),
] {
let via_helper = kube_spec(&value).and_then(|s| s.get(sub_field));
let via_inline = value.get(KUBE_KEY_SPEC).and_then(|s| s.get(sub_field));
assert_eq!(
via_helper,
Some(expected),
"kube_spec(v).and_then(|s| s.get({sub_field:?})) must \
return the sub-field Value the routed caller reaches \
for — otherwise the fold is not a drop-in for the \
prior inline chain"
);
assert_eq!(
via_helper, via_inline,
"kube_spec(v).and_then(|s| s.get({sub_field:?})) must \
match the prior inline \
`.get(KUBE_KEY_SPEC).and_then(|s| s.get(...))` chain — \
the fold's byte-equivalence pin closes the drift \
surface where a rebrand of the outer two-hop \
navigation silently splits the routed sites from the \
unrouted"
);
}
}
#[test]
fn kube_spec_field_reads_sub_spec_field_value() {
// The lift's load-bearing contract: given a Value carrying a
// top-level `spec: { <sub-field>: <value>, ... }` body sub-
// mapping, the composed accessor returns Some(&Value) for the
// routed per-sub-field readback across the three canonical
// sub-field axis-keys the caixa-mesh test-harness reaches for
// (`CILIUM_KEY_INGRESS` for the per-CNP ingress-rules readback,
// `GATEWAY_API_KEY_LISTENERS` for the per-Gateway listener-set
// readback, `GATEWAY_API_KEY_PARENT_REFS` for the per-HTTPRoute
// parent-Gateway readback). Peer of the sibling
// `kube_metadata_label_reads_per_label_string_scalar` pin on
// the composed-scalar-arity accessor on the sub-
// `metadata.labels.<label>` axis.
let ingress =
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("ingress-marker".into())]);
let listeners =
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("listener-marker".into())]);
let parent_refs = serde_yaml::Value::Sequence(vec![serde_yaml::Value::String(
"parent-ref-marker".into(),
)]);
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(CILIUM_KEY_INGRESS, ingress.clone());
spec.insert_str_key(GATEWAY_API_KEY_LISTENERS, listeners.clone());
spec.insert_str_key(GATEWAY_API_KEY_PARENT_REFS, parent_refs.clone());
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
for (sub_field, expected) in [
(CILIUM_KEY_INGRESS, &ingress),
(GATEWAY_API_KEY_LISTENERS, &listeners),
(GATEWAY_API_KEY_PARENT_REFS, &parent_refs),
] {
assert_eq!(
kube_spec_field(&value, sub_field),
Some(expected),
"kube_spec_field must read the per-sub-field Value at \
`spec.{sub_field}` — every caixa-mesh per-CR test-side \
readback site reaches through this axis for further \
per-body-key navigation (`.and_then(|i| \
i.as_sequence())`, `.and_then(|c| c.get(...))`, \
`.and_then(|v| v.as_str())`)"
);
}
}
#[test]
fn kube_spec_field_returns_none_when_spec_sub_block_absent() {
// The composition's outer-arm None short-circuit fold-through:
// any short-circuit the underlying [`kube_spec`] sub-mapping-
// arity accessor closes on folds through this composed
// accessor. Covers the missing top-level `spec:` block arm
// ([`kube_spec`]'s outer-arm) across the same outer-Value
// shape permutations the sibling
// `kube_spec_returns_none_when_spec_sub_block_absent` pin
// covers on the direct primitive. Peer of the sibling
// `kube_metadata_label_returns_none_when_labels_block_absent`
// pin on the composed accessor on the sub-`metadata.labels`
// axis.
for shape in [
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
serde_yaml::Value::Null,
serde_yaml::Value::String("scalar".into()),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::Number(0.into()),
serde_yaml::Value::Bool(false),
] {
assert_eq!(
kube_spec_field(&shape, CILIUM_KEY_INGRESS),
None,
"kube_spec_field({shape:?}, <FIELD>) must short-circuit \
to None through the underlying kube_spec's outer-arm \
when the top-level `spec:` block is absent on the \
outer Value — the composition fold must preserve \
kube_spec's total-function contract"
);
}
}
#[test]
fn kube_spec_field_returns_none_when_spec_carries_non_mapping_type() {
// The composition's shape-gate None short-circuit fold-through:
// a present-but-non-Mapping `spec:` value on the outer Value
// folds through [`kube_spec`]'s trailing `.as_mapping()` shape-
// gate — the composed accessor returns None so the caller's
// `.and_then(|v| v.as_str())` / `.as_sequence()` continuation
// stays a total function. Pin the shape-gate fold-through so a
// future refactor that bypasses [`kube_spec`]'s Mapping-gate
// (a private inline `value.get(KUBE_KEY_SPEC).and_then(|s|
// s.get(field))` chain that would return `Some` on a non-
// Mapping spec-value carrying a numeric-index accidental match)
// is a test-visible break, not a runtime regression at the
// first schema-invalid CR the reader sees. Peer of the sibling
// `kube_metadata_label_returns_none_when_labels_block_absent`
// pin.
for non_mapping in [
serde_yaml::Value::Null,
serde_yaml::Value::Number(42.into()),
serde_yaml::Value::Bool(true),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::String("spec-as-string".into()),
] {
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, non_mapping.clone());
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_spec_field(&value, CILIUM_KEY_INGRESS),
None,
"kube_spec_field must return None when the top-level \
`spec:` axis carries a non-Mapping YAML type \
({non_mapping:?}) — the fold through kube_spec's \
shape-gate arm short-circuits here, and every routed \
caller depends on that None-arm to keep the readback \
total"
);
}
}
#[test]
fn kube_spec_field_returns_none_when_requested_field_absent() {
// The composition's trailing per-key None-arm: the requested
// `<field>` sub-field axis-key is absent from the `spec:`
// sub-mapping. Preserves the "no such sub-field" vs. "wrong
// shape" distinction routed consumers rely on — a per-CNP
// ingress-rules readback that finds no `spec.ingress[]`
// sub-field expects None here (routing the fallback path)
// rather than a panic. Peer of the sibling
// `kube_metadata_label_returns_none_when_requested_label_absent`
// pin on the composed sub-`metadata.labels.<label>` accessor.
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(CILIUM_KEY_INGRESS, serde_yaml::Value::Sequence(vec![]));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_spec_field(&value, GATEWAY_API_KEY_LISTENERS),
None,
"kube_spec_field must return None when the requested \
`spec.<field>` axis-key is absent from the sub-mapping — \
a `Mapping::get(<KEY>)` miss short-circuits the composed \
accessor, and callers rely on the None-arm to route the \
fallback path (rather than the wrong-shape arm)"
);
}
#[test]
fn kube_spec_field_composes_on_lifted_kube_spec_accessor() {
// Composition pin: the composed accessor's body IS
// `kube_spec(value).and_then(|s| s.get(field))` — the outer
// sub-mapping-arity accessor stays load-bearing, the composed
// accessor stands one abstraction step above it. Pin the
// delegation-shape byte-for-byte across three representative
// sub-field axis-keys so a future refactor that bypasses
// [`kube_spec`] (a private inline two-hop chain copy that
// reaches for `.get(KUBE_KEY_SPEC).and_then(|s|
// s.as_mapping()).and_then(|m| m.get(field))` directly instead
// of composing on the sibling accessor) is a test-visible
// break. Peer of the sibling
// `kube_metadata_label_composes_on_lifted_kube_metadata_labels_accessor`
// pin on the composed sub-`metadata.labels.<label>` accessor.
let ingress =
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("ingress-marker".into())]);
let listeners =
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("listener-marker".into())]);
let parent_refs = serde_yaml::Value::Sequence(vec![serde_yaml::Value::String(
"parent-ref-marker".into(),
)]);
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(CILIUM_KEY_INGRESS, ingress);
spec.insert_str_key(GATEWAY_API_KEY_LISTENERS, listeners);
spec.insert_str_key(GATEWAY_API_KEY_PARENT_REFS, parent_refs);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
for sub_field in [
CILIUM_KEY_INGRESS,
GATEWAY_API_KEY_LISTENERS,
GATEWAY_API_KEY_PARENT_REFS,
] {
let via_composed = kube_spec_field(&value, sub_field);
let via_delegation = kube_spec(&value).and_then(|s| s.get(sub_field));
assert_eq!(
via_composed, via_delegation,
"kube_spec_field(v, {sub_field:?}) must equal the \
delegation-shape `kube_spec(v).and_then(|s| \
s.get({sub_field:?}))` — the composition pin closes \
the drift surface where a private inline bypass \
silently desynchronizes from the underlying \
sub-mapping accessor's contract"
);
}
}
#[test]
fn kube_spec_field_matches_prior_inline_chain() {
// Cross-check the composed accessor's output byte-for-byte
// against the prior inline two-hop chain the routed caller
// previously carried. A drift between the composed helper's
// return and the inline chain would silently regress the
// caixa-mesh per-CR sub-spec-field-readback sites' downstream
// continuations (`.and_then(|i| i.as_sequence())`,
// `.and_then(|c| c.get(...))`, `.and_then(|v| v.as_str())`) —
// pin the byte-equivalence across three representative sub-
// field axis-keys so the composed helper remains a drop-in
// replacement for the routed site's prior two-line block.
// Peer of the sibling
// `kube_metadata_label_matches_prior_inline_chain` pin.
let ingress =
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("ingress-marker".into())]);
let listeners =
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("listener-marker".into())]);
let parent_refs = serde_yaml::Value::Sequence(vec![serde_yaml::Value::String(
"parent-ref-marker".into(),
)]);
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(CILIUM_KEY_INGRESS, ingress);
spec.insert_str_key(GATEWAY_API_KEY_LISTENERS, listeners);
spec.insert_str_key(GATEWAY_API_KEY_PARENT_REFS, parent_refs);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
for sub_field in [
CILIUM_KEY_INGRESS,
GATEWAY_API_KEY_LISTENERS,
GATEWAY_API_KEY_PARENT_REFS,
] {
let via_helper = kube_spec_field(&value, sub_field);
let via_inline = value.get(KUBE_KEY_SPEC).and_then(|s| s.get(sub_field));
assert_eq!(
via_helper, via_inline,
"kube_spec_field(v, {sub_field:?}) must yield the same \
Option<&Value> as the prior inline two-hop chain \
`value.get(KUBE_KEY_SPEC).and_then(|s| \
s.get({sub_field:?}))` — otherwise the routed \
caixa-mesh per-CR sub-spec-field-readback sites drift \
silently at test time"
);
}
}
#[test]
fn kube_metadata_reads_top_level_metadata_sub_mapping() {
// The lift's load-bearing contract: given a Value carrying a
// top-level `metadata: { <key>: <value>, ... }` identity/labels
// sub-mapping (every K8s CR the emit-side
// [`kube_resource_skeleton`] renders with a canonical `name +
// namespace [+ labels]` metadata overlay), the helper returns
// Some(&Mapping) borrowing into the input Value. Structural
// mirror of the sibling
// `kube_spec_reads_top_level_spec_sub_mapping` pin on the sub-
// `spec:` sub-block: both accessors gate on Mapping shape and
// return `Option<&Mapping>` on their respective pinned
// canonical sub-block axis-key.
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("checkout".into()));
metadata.insert_str_key(
KUBE_KEY_NAMESPACE,
serde_yaml::Value::String(DEFAULT_NAMESPACE.into()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(
KUBE_KEY_METADATA,
serde_yaml::Value::Mapping(metadata.clone()),
);
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata(&value),
Some(&metadata),
"kube_metadata must read the top-level `metadata:` sub-\
mapping — every caixa-mesh per-CR test-side readback \
site reaches through this axis for further per-metadata-\
key navigation (metadata.name, metadata.namespace, \
metadata.labels, metadata's `.len()` axis-count pin)"
);
}
#[test]
fn kube_metadata_returns_none_when_metadata_sub_block_absent() {
// The two-way vacuous-None short-circuit's outer arm: an outer
// Value that legally omits the `metadata:` block short-circuits
// at the first hop through the underlying
// `.get(KUBE_KEY_METADATA)`. The K8s CR readback surface
// accepts arbitrary Value inputs, including `List`-shaped
// documents or metadata-less external YAML shapes; pin the
// None-arm so a future refactor that reaches for
// `.get(...).unwrap()` (which would panic on the missing
// block) is a test-visible break. Peer of the sibling
// `kube_spec_returns_none_when_spec_sub_block_absent` pin on
// the sub-`spec:` sub-block accessor.
let value = serde_yaml::Value::Mapping(serde_yaml::Mapping::new());
assert_eq!(
kube_metadata(&value),
None,
"kube_metadata must short-circuit to None when the top-\
level `metadata:` block is absent — the prior inline two-\
hop chain's first `.get(KUBE_KEY_METADATA)` hop returned \
None here"
);
// Also verify the shape on non-Mapping outer Value shapes.
for shape in [
serde_yaml::Value::Null,
serde_yaml::Value::String("scalar".into()),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::Number(0.into()),
serde_yaml::Value::Bool(false),
] {
assert_eq!(
kube_metadata(&shape),
None,
"kube_metadata({shape:?}) must return None on non-\
Mapping outer shapes — the prior inline chain's \
`.get(KUBE_KEY_METADATA)` hop yields None on every \
non-Mapping Value, and the lift must preserve that \
contract"
);
}
}
#[test]
fn kube_metadata_returns_none_when_metadata_carries_non_mapping_type() {
// The two-way vacuous-None short-circuit's trailing arm: a
// present-but-non-Mapping `metadata:` value (a schema-invalid
// shape per the K8s API-machinery's `ObjectMeta` contract,
// which pins the per-CR identity sub-block as a Mapping, but
// tolerated here as None so the readback stays a total
// function). Pin the trailing `.as_mapping()` shape gate so a
// future refactor that reaches for `.as_mapping().unwrap()`
// (which would panic on a numeric metadata-value) is a test-
// visible break, not a runtime regression at the first
// schema-invalid CR the reader sees. Peer of the sibling
// `kube_spec_returns_none_when_spec_carries_non_mapping_type`
// pin on the sub-`spec:` sub-block accessor.
for non_mapping in [
serde_yaml::Value::Null,
serde_yaml::Value::Number(42.into()),
serde_yaml::Value::Bool(true),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::String("metadata-as-string".into()),
] {
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, non_mapping.clone());
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata(&value),
None,
"kube_metadata must return None when the top-level \
`metadata:` axis carries a non-Mapping YAML type \
({non_mapping:?}) — the trailing `.and_then(|m| \
m.as_mapping())` shape gate short-circuited here on \
the prior inline chain, and every routed caller \
depends on that None-arm to keep the readback total"
);
}
}
#[test]
fn kube_metadata_matches_prior_inline_chain() {
// Cross-check the helper's output byte-for-byte against the
// prior inline two-hop chain the routed caller previously
// carried. A drift between the helper's return and the inline
// chain would silently regress the caixa-mesh per-CR metadata-
// readback sites' downstream continuations (`.len()`,
// `.get(KUBE_KEY_LABELS)`, `.iter().filter_map(...)`,
// `.get(KUBE_KEY_NAME).and_then(|v| v.as_str())`) — pin the
// byte-equivalence so the helper remains a drop-in replacement
// for the routed site's prior two-line block. Peer of the
// sibling `kube_spec_matches_prior_inline_chain` pin on the
// sub-`spec:` sub-block accessor.
let mut labels = serde_yaml::Mapping::new();
labels.insert_str_key(
LABEL_APLICACAO,
serde_yaml::Value::String("checkout".into()),
);
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(
KUBE_KEY_NAME,
serde_yaml::Value::String("checkout-cart-to-catalog".into()),
);
metadata.insert_str_key(
KUBE_KEY_NAMESPACE,
serde_yaml::Value::String(DEFAULT_NAMESPACE.into()),
);
metadata.insert_str_key(KUBE_KEY_LABELS, serde_yaml::Value::Mapping(labels));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
let via_helper = kube_metadata(&value);
let via_inline = value.get(KUBE_KEY_METADATA).and_then(|m| m.as_mapping());
assert_eq!(
via_helper, via_inline,
"kube_metadata must yield the same Option<&Mapping> as \
the prior inline two-hop chain — otherwise the routed \
caixa-mesh per-CR metadata-readback sites drift silently \
at test time"
);
}
#[test]
fn kube_metadata_composes_with_further_sub_field_navigation() {
// Composition pin: the routed convergence pattern is
// `kube_metadata(v).and_then(|m| m.get(<SUB_FIELD>))` — the
// outer two-hop `metadata → as_mapping` navigation happens
// inside the helper, and the trailing `Mapping::get(<SUB_
// FIELD>)` stays composition-symmetric with the prior inline
// `.get(KUBE_KEY_METADATA).and_then(|m| m.get(<SUB_FIELD>))`
// shape (both return `Option<&Value>`, so the fold is a drop-
// in for every routed test-harness callback body). Pin the
// composition-shape across three representative sub-field
// axis-keys (`KUBE_KEY_NAME` for the per-CR identity readback,
// `KUBE_KEY_NAMESPACE` for the namespace-scoping readback,
// `KUBE_KEY_LABELS` for the labels sub-block presence probe)
// so a future rewire that bypasses the helper (a private
// inline two-hop chain copy) is a test-visible break.
let mut labels = serde_yaml::Mapping::new();
labels.insert_str_key(
LABEL_APLICACAO,
serde_yaml::Value::String("checkout".into()),
);
let name_value = serde_yaml::Value::String("checkout-cart-to-catalog".into());
let namespace_value = serde_yaml::Value::String(DEFAULT_NAMESPACE.into());
let labels_value = serde_yaml::Value::Mapping(labels);
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_NAME, name_value.clone());
metadata.insert_str_key(KUBE_KEY_NAMESPACE, namespace_value.clone());
metadata.insert_str_key(KUBE_KEY_LABELS, labels_value.clone());
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
for (sub_field, expected) in [
(KUBE_KEY_NAME, &name_value),
(KUBE_KEY_NAMESPACE, &namespace_value),
(KUBE_KEY_LABELS, &labels_value),
] {
let via_helper = kube_metadata(&value).and_then(|m| m.get(sub_field));
let via_inline = value.get(KUBE_KEY_METADATA).and_then(|m| m.get(sub_field));
assert_eq!(
via_helper,
Some(expected),
"kube_metadata(v).and_then(|m| m.get({sub_field:?})) \
must return the sub-field Value the routed caller \
reaches for — otherwise the fold is not a drop-in \
for the prior inline chain"
);
assert_eq!(
via_helper, via_inline,
"kube_metadata(v).and_then(|m| m.get({sub_field:?})) \
must match the prior inline \
`.get(KUBE_KEY_METADATA).and_then(|m| m.get(...))` \
chain — the fold's byte-equivalence pin closes the \
drift surface where a rebrand of the outer two-hop \
navigation silently splits the routed sites from \
the unrouted"
);
}
}
#[test]
fn kube_metadata_field_reads_top_level_metadata_sub_field_value() {
// The lift's load-bearing contract: given a Value carrying a
// top-level `metadata:` sub-mapping with a `<field>: <value>`
// sub-key, the helper returns `Some(&value)` borrowing into
// the input Value. Structural mirror of the sibling
// `kube_spec_field_reads_sub_spec_field_value` load-bearing
// pin on the sub-`spec:` axis — same composition-on-lifted-
// sub-mapping shape, same borrowed-`&Value` return contract,
// same three-key coverage across the canonical per-CR
// identity axis-keys ([`KUBE_KEY_NAME`], [`KUBE_KEY_NAMESPACE`],
// [`KUBE_KEY_LABELS`]) that bracket the readback surface the
// sibling scalar-arity pinned accessors ([`kube_name`],
// [`kube_namespace`], [`kube_metadata_labels`]) each close on
// top with a per-axis shape-gate.
let mut labels = serde_yaml::Mapping::new();
labels.insert_str_key(
LABEL_APLICACAO,
serde_yaml::Value::String("checkout".into()),
);
let name_value = serde_yaml::Value::String("checkout-cart-to-catalog".into());
let namespace_value = serde_yaml::Value::String(DEFAULT_NAMESPACE.into());
let labels_value = serde_yaml::Value::Mapping(labels);
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_NAME, name_value.clone());
metadata.insert_str_key(KUBE_KEY_NAMESPACE, namespace_value.clone());
metadata.insert_str_key(KUBE_KEY_LABELS, labels_value.clone());
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata_field(&value, KUBE_KEY_NAME),
Some(&name_value),
"kube_metadata_field must return the metadata.name sub-field \
Value — the load-bearing per-CR identity coordinate every \
emitter's [`kube_resource_skeleton`] renders and every \
readback consumer's `.as_str()` shape-gate consumes"
);
assert_eq!(
kube_metadata_field(&value, KUBE_KEY_NAMESPACE),
Some(&namespace_value),
"kube_metadata_field must return the metadata.namespace \
sub-field Value — the load-bearing per-CR namespace-scoping \
coordinate the emit side pairs with metadata.name as the \
K8s API-machinery per-CR identity pair"
);
assert_eq!(
kube_metadata_field(&value, KUBE_KEY_LABELS),
Some(&labels_value),
"kube_metadata_field must return the metadata.labels sub-\
mapping Value — the load-bearing per-CR selector-surface \
coordinate the sibling [`kube_metadata_labels`] pinned \
accessor closes on top with the `.as_mapping()` shape-gate"
);
}
#[test]
fn kube_metadata_field_returns_none_when_metadata_block_absent() {
// First short-circuit arm: the outer `metadata:` block is
// absent (a legally-omitted per-CR identity sub-block on
// `List`-shaped documents or on external YAML shapes that
// carry no [`ObjectMeta`]-flavoured header). Folds through
// the underlying [`kube_metadata`] outer-arm short-circuit
// — pin here so the composed accessor preserves the None-arm
// the caller's downstream `.as_str()` / `.and_then(...)` /
// `.is_some()` continuations lean on.
//
// Two shapes cover the first-arm surface: a Value carrying
// only unrelated top-level axes (a schema-valid K8s CR
// fragment mid-render), and a Value that is not a Mapping at
// all (a raw scalar / a Sequence-shaped document). Both take
// the outer-arm short-circuit through [`kube_metadata`]'s
// shape-gate.
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(
KUBE_KEY_API_VERSION,
serde_yaml::Value::String(GATEWAY_API_API_VERSION.into()),
);
cr.insert_str_key(
KUBE_KEY_KIND,
serde_yaml::Value::String(GATEWAY_API_KIND_GATEWAY.into()),
);
let value = serde_yaml::Value::Mapping(cr);
for field in [KUBE_KEY_NAME, KUBE_KEY_NAMESPACE, KUBE_KEY_LABELS] {
assert_eq!(
kube_metadata_field(&value, field),
None,
"kube_metadata_field(_, {field:?}) must short-circuit \
to None when the outer `metadata:` sub-block is absent \
— folds through the composed [`kube_metadata`] sub-\
mapping accessor's outer-arm None"
);
}
let non_mapping_shapes = [
serde_yaml::Value::Null,
serde_yaml::Value::Bool(false),
serde_yaml::Value::String("scalar-shaped-doc".into()),
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("seq".into())]),
];
for shape in non_mapping_shapes {
assert_eq!(
kube_metadata_field(&shape, KUBE_KEY_NAME),
None,
"kube_metadata_field must short-circuit to None on a \
non-Mapping outer Value shape — the outer `metadata:` \
key readback yields None on every non-Mapping variant \
through Value::get's non-Mapping return"
);
}
}
#[test]
fn kube_metadata_field_returns_none_when_metadata_carries_non_mapping_type() {
// Second short-circuit arm: the `metadata:` value is present
// but carries a non-Mapping YAML type (a schema-invalid
// identity shape per the K8s API-machinery contract that pins
// the per-CR identity sub-block as a Mapping, but tolerated
// here as `None` so the readback stays a total function).
// Folds through the underlying [`kube_metadata`] shape-gate
// short-circuit — pin here across the four non-Mapping YAML
// shapes so the composed accessor preserves the None-arm the
// caller's downstream continuations depend on.
let non_mapping_shapes = [
("String", serde_yaml::Value::String("checkout".into())),
("Sequence", serde_yaml::Value::Sequence(vec![])),
("Bool", serde_yaml::Value::Bool(true)),
("Null", serde_yaml::Value::Null),
];
for (label, shape) in non_mapping_shapes {
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, shape);
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata_field(&value, KUBE_KEY_NAME),
None,
"kube_metadata_field must short-circuit to None when \
the `metadata:` value is a non-Mapping YAML {label} — \
folds through the composed [`kube_metadata`] sub-\
mapping accessor's `.as_mapping()` shape-gate"
);
}
}
#[test]
fn kube_metadata_field_returns_none_when_requested_field_absent() {
// Third short-circuit arm: the outer `metadata:` block is a
// Mapping AND the value passes [`kube_metadata`]'s shape-gate,
// but the requested `<field>` axis-key is absent from the sub-
// mapping (a legally-omitted per-metadata-sub-field surface on
// a CR that carries other identity sub-fields but not this
// one — the emit-side [`kube_resource_skeleton`] deliberately
// skips `metadata.labels` when the labels input is empty, so
// the `.get(KUBE_KEY_LABELS)` readback on a Gateway/HTTPRoute
// takes exactly this arm). Pin the trailing `Mapping::get`
// none-arm so a caller pattern-matching on `Some(&Value)` gets
// the semantic the sibling `metadata.get(KUBE_KEY_LABELS).
// is_none()` shape spelled inline.
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("checkout".into()));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata_field(&value, KUBE_KEY_LABELS),
None,
"kube_metadata_field must short-circuit to None when the \
requested sub-field axis-key is absent from the metadata \
sub-mapping — the trailing `Mapping::get` none-arm the \
load-bearing empty-labels-skip contract on Gateway / \
HTTPRoute pins downstream"
);
assert_eq!(
kube_metadata_field(&value, KUBE_KEY_NAMESPACE),
None,
"kube_metadata_field must short-circuit to None when the \
requested sub-field axis-key is absent — even a canonical \
per-CR identity coordinate can be legally omitted at author \
time and folded through this trailing None-arm"
);
}
#[test]
fn kube_metadata_field_composes_on_lifted_kube_metadata_accessor() {
// Composition pin: the helper's body must fold to
// `kube_metadata(v).and_then(|m| m.get(field))` — the
// sub-mapping-arity accessor and the trailing `Mapping::get`
// sub-field readback. Pin the composition shape so a future
// rewire that bypasses the lifted [`kube_metadata`] sub-
// mapping accessor (an in-body private re-inlined
// `.get(KUBE_KEY_METADATA).and_then(|m| m.as_mapping())`
// walk that silently drifts on a future rebrand of the
// outer two-hop navigation) is a test-visible break. Peer
// of the sibling `kube_spec_field_composes_on_lifted_
// kube_spec_accessor` pin on the sub-`spec:` axis's
// composed-on-lifted-sub-mapping composition.
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("checkout".into()));
metadata.insert_str_key(
KUBE_KEY_NAMESPACE,
serde_yaml::Value::String(DEFAULT_NAMESPACE.into()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
for field in [KUBE_KEY_NAME, KUBE_KEY_NAMESPACE] {
let via_helper = kube_metadata_field(&value, field);
let via_composed = kube_metadata(&value).and_then(|m| m.get(field));
assert_eq!(
via_helper, via_composed,
"kube_metadata_field(_, {field:?}) must fold to \
`kube_metadata(v).and_then(|m| m.get({field:?}))` — \
pin the composition-on-lifted-[`kube_metadata`] \
shape so a future in-body re-inline of the outer two-\
hop navigation is a test-visible break"
);
}
}
#[test]
fn kube_metadata_field_matches_prior_inline_chain() {
// Cross-check the helper's output byte-for-byte against the
// prior inline two-hop `.get(KUBE_KEY_METADATA).and_then(|m|
// m.get(<FIELD>))` chain the composition-pin sibling
// [`kube_metadata_composes_with_further_sub_field_navigation`]
// already spelled as the drop-in fold shape. Pin the byte-
// equivalence so a future callback that migrates from the
// inline two-hop chain onto the helper preserves the routed
// consumer's downstream continuation (`.as_str()` /
// `.as_mapping()` / `.is_some()` / `.is_none()` /
// `.and_then(|v| v.get(<DEEPER_KEY>))`) unchanged. Peer of
// the sibling `kube_spec_field_matches_prior_inline_chain`
// byte-equivalence pin on the sub-`spec:` axis.
let mut labels = serde_yaml::Mapping::new();
labels.insert_str_key(
LABEL_APLICACAO,
serde_yaml::Value::String("checkout".into()),
);
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(
KUBE_KEY_NAME,
serde_yaml::Value::String("checkout-cart-to-catalog".into()),
);
metadata.insert_str_key(
KUBE_KEY_NAMESPACE,
serde_yaml::Value::String(DEFAULT_NAMESPACE.into()),
);
metadata.insert_str_key(KUBE_KEY_LABELS, serde_yaml::Value::Mapping(labels));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
for field in [KUBE_KEY_NAME, KUBE_KEY_NAMESPACE, KUBE_KEY_LABELS] {
let via_helper = kube_metadata_field(&value, field);
let via_inline = value.get(KUBE_KEY_METADATA).and_then(|m| m.get(field));
assert_eq!(
via_helper, via_inline,
"kube_metadata_field(_, {field:?}) must yield the same \
Option<&Value> as the prior inline two-hop chain — \
otherwise a caller migrating from the inline chain onto \
the helper silently drifts at readback time"
);
}
}
// ── kube_metadata_map_field lift ────────────────────────────────────
#[test]
fn kube_metadata_map_field_reads_sub_metadata_field_mapping() {
// The lift's load-bearing contract: given a Value carrying a
// top-level `metadata: { <sub-field>: { ... }, ... }` identity
// sub-block (every K8s CR the emit-side
// [`kube_resource_skeleton`] renders with a mapping-shaped
// `metadata.<field>` axis — `metadata.labels` the load-bearing
// per-CR label surface every selector join reaches through, and
// the future `metadata.annotations` sub-mapping under per-tenant
// scoping / Server-Side-Apply field-ownership CRs), the composed
// sub-mapping-arity accessor returns `Some(<mapping>)` borrowing
// into the input Value across the routed per-sub-field readback
// axes. Structural mirror of the sibling
// `kube_spec_map_field_reads_sub_spec_field_mapping` pin on the
// sub-`spec.<field>` sub-mapping-arity axis.
let mut labels_body = serde_yaml::Mapping::new();
labels_body.insert_str_key(
LABEL_APLICACAO,
serde_yaml::Value::String("checkout".into()),
);
labels_body.insert_str_key(
"app.kubernetes.io/name",
serde_yaml::Value::String("cart".into()),
);
let mut annotations_body = serde_yaml::Mapping::new();
annotations_body.insert_str_key(
"kubectl.kubernetes.io/last-applied-configuration",
serde_yaml::Value::String("{}".into()),
);
let empty_map = serde_yaml::Mapping::new();
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_LABELS, serde_yaml::Value::Mapping(labels_body));
metadata.insert_str_key("annotations", serde_yaml::Value::Mapping(annotations_body));
metadata.insert_str_key("selector", serde_yaml::Value::Mapping(empty_map));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata_map_field(&value, KUBE_KEY_LABELS).map(serde_yaml::Mapping::len),
Some(2),
"kube_metadata_map_field must read metadata.labels as a two-\
entry mapping — the caixa-mesh per-CNP label-surface \
readbacks reach through this axis for per-`LABEL_APLICACAO`\
selector navigation, and the recomposed \
`kube_metadata_labels` peer pins this same axis"
);
assert_eq!(
kube_metadata_map_field(&value, "annotations").map(serde_yaml::Mapping::len),
Some(1),
"kube_metadata_map_field must read metadata.annotations as a \
one-entry mapping — the parametric `<field>` axis stays \
open-ended on the K8s `ObjectMeta` sub-field surface so a \
future per-tenant / Server-Side-Apply-driven annotations \
readback reaches for the same helper with a different key"
);
assert_eq!(
kube_metadata_map_field(&value, "selector").map(serde_yaml::Mapping::len),
Some(0),
"kube_metadata_map_field must read metadata.selector as an \
empty mapping when the emitter writes a legally-empty block \
(distinct from the requested-field-absent None-arm — \
empty-mapping-present preserves the caller's `.get(<KEY>)` \
lookup contract that always returns None on an empty body)"
);
}
#[test]
fn kube_metadata_map_field_returns_none_when_metadata_block_absent() {
// The composition's outer-arm None short-circuit fold-through:
// any short-circuit the underlying [`kube_metadata_field`]
// closes on (which in turn folds through [`kube_metadata`]'s
// outer-arm and shape-gate) folds through this composed sub-
// mapping-arity accessor. Peer of the sibling
// `kube_spec_map_field_returns_none_when_spec_sub_block_absent`
// pin on the composed sub-`spec.<field>` sub-mapping-arity
// accessor axis.
for shape in [
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
serde_yaml::Value::Null,
serde_yaml::Value::String("scalar".into()),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::Number(0.into()),
serde_yaml::Value::Bool(false),
] {
assert_eq!(
kube_metadata_map_field(&shape, KUBE_KEY_LABELS),
None,
"kube_metadata_map_field({shape:?}, <FIELD>) must short-\
circuit to None through the underlying \
kube_metadata_field's kube_metadata outer-arm when the \
top-level `metadata:` block is absent on the outer \
Value — the composition fold must preserve the total-\
function contract"
);
}
}
#[test]
fn kube_metadata_map_field_returns_none_when_metadata_carries_non_mapping_type() {
// The composition's shape-gate None short-circuit fold-through:
// a present-but-non-Mapping `metadata:` value on the outer
// Value folds through [`kube_metadata`]'s trailing
// `.as_mapping()` shape-gate up through
// [`kube_metadata_field`] up through this composed sub-mapping-
// arity accessor — the caller's `.get(<KEY>)` / `.expect(...)`
// continuation stays a total function.
for non_mapping in [
serde_yaml::Value::Null,
serde_yaml::Value::Number(42.into()),
serde_yaml::Value::Bool(true),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::String("metadata-as-string".into()),
] {
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, non_mapping.clone());
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata_map_field(&value, KUBE_KEY_LABELS),
None,
"kube_metadata_map_field must return None when the top-\
level `metadata:` axis carries a non-Mapping YAML type \
({non_mapping:?}) — the fold through kube_metadata's \
shape-gate arm short-circuits here, and every routed \
caller depends on that None-arm to keep the readback \
total"
);
}
}
#[test]
fn kube_metadata_map_field_returns_none_when_requested_field_absent() {
// The composition's middle per-key None-arm: the requested
// `<field>` sub-field axis-key is absent from the `metadata:`
// sub-mapping. Preserves the "no such sub-field" vs. "wrong
// shape" distinction routed consumers rely on — a per-CR
// `metadata.labels` readback that finds no `metadata.labels`
// sub-field expects None here (routing the "no labels"
// fallback) rather than a panic.
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("cart".into()));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata_map_field(&value, KUBE_KEY_LABELS),
None,
"kube_metadata_map_field must return None when the requested \
`metadata.<field>` axis-key is absent from the sub-mapping \
— a `Mapping::get(<KEY>)` miss short-circuits the composed \
accessor, and callers rely on the None-arm to route the \
fallback path (rather than the wrong-shape arm)"
);
}
#[test]
fn kube_metadata_map_field_returns_none_when_field_carries_non_mapping_type() {
// The composition's trailing `.as_mapping()` shape-gate None
// arm: a `metadata.<field>` axis-key present but carrying a
// non-mapping YAML type. Schema-invalid per the K8s apiserver's
// OpenAPI schema (the routed readback sites —
// `metadata.labels`, `metadata.annotations` — all pin nested-
// object sub-mappings) but tolerated here as None so the
// readback stays a total function. Pin the None-arm so a
// future refactor that reaches for `.as_mapping().unwrap()`
// (which would panic on a scalar axis-value) is a test-visible
// break, not a runtime regression at the first schema-invalid
// CR the reader sees.
for non_mapping in [
serde_yaml::Value::Null,
serde_yaml::Value::Number(42.into()),
serde_yaml::Value::Bool(true),
serde_yaml::Value::String("labels-as-string".into()),
serde_yaml::Value::Sequence(vec![]),
] {
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_LABELS, non_mapping.clone());
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata_map_field(&value, KUBE_KEY_LABELS),
None,
"kube_metadata_map_field must return None when \
metadata.labels carries a non-mapping YAML type \
({non_mapping:?}) — the trailing `.as_mapping()` shape \
gate short-circuits here, and every routed caller \
depends on that None-arm to keep the readback total"
);
}
}
#[test]
fn kube_metadata_map_field_composes_on_lifted_kube_metadata_field_accessor() {
// Composition pin: the composed accessor's body IS
// `kube_metadata_field(value, field).and_then(|v|
// v.as_mapping())` — the composed scalar-arity accessor stays
// load-bearing, this sub-mapping-arity accessor stands one
// abstraction step above it (folding the trailing
// `.as_mapping()` shape-gate closure). Pin the delegation-
// shape byte-for-byte across three representative sub-field
// axis-keys so a future refactor that bypasses
// [`kube_metadata_field`] (a private inline
// `.get(KUBE_KEY_METADATA).and_then(|m| m.as_mapping())
// .and_then(|m| m.get(field)).and_then(|n| n.as_mapping())`
// chain that would silently drift on a future rebrand of the
// outer two-hop navigation) is a test-visible break. Peer of
// the sibling
// `kube_spec_map_field_composes_on_lifted_kube_spec_field_accessor`
// composition pin on the sub-`spec.<field>` sub-mapping-arity
// accessor axis.
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(
KUBE_KEY_LABELS,
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
);
metadata.insert_str_key(
"annotations",
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
);
metadata.insert_str_key(
"selector",
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
for sub_field in [KUBE_KEY_LABELS, "annotations", "selector"] {
let via_composed = kube_metadata_map_field(&value, sub_field);
let via_delegation =
kube_metadata_field(&value, sub_field).and_then(|v| v.as_mapping());
assert_eq!(
via_composed, via_delegation,
"kube_metadata_map_field(v, {sub_field:?}) must equal \
the delegation-shape `kube_metadata_field(v, \
{sub_field:?}).and_then(|v| v.as_mapping())` — the \
composition pin closes the drift surface where a \
private inline bypass silently desynchronizes from the \
underlying composed scalar-arity accessor's contract"
);
}
}
#[test]
fn kube_metadata_map_field_matches_prior_inline_chain() {
// Cross-check the composed accessor's output byte-for-byte
// against the prior inline
// `value.get(KUBE_KEY_METADATA).and_then(|m|
// m.get(<FIELD>)).and_then(|l| l.as_mapping())` three-hop
// chain the recomposed sibling [`kube_metadata_labels`]
// previously carried verbatim (with `<FIELD>` pinned to
// [`KUBE_KEY_LABELS`]). Pin the byte-equivalence across three
// representative sub-field axis-keys so the composed helper
// remains a drop-in replacement for the routed sites' prior
// three-hop block.
let mut labels_body = serde_yaml::Mapping::new();
labels_body.insert_str_key(
LABEL_APLICACAO,
serde_yaml::Value::String("checkout".into()),
);
let mut annotations_body = serde_yaml::Mapping::new();
annotations_body.insert_str_key(
"kubectl.kubernetes.io/last-applied-configuration",
serde_yaml::Value::String("{}".into()),
);
let mut selector_body = serde_yaml::Mapping::new();
selector_body.insert_str_key(
"matchLabels",
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
);
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_LABELS, serde_yaml::Value::Mapping(labels_body));
metadata.insert_str_key("annotations", serde_yaml::Value::Mapping(annotations_body));
metadata.insert_str_key("selector", serde_yaml::Value::Mapping(selector_body));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
for sub_field in [KUBE_KEY_LABELS, "annotations", "selector"] {
let via_helper = kube_metadata_map_field(&value, sub_field);
let via_inline = value
.get(KUBE_KEY_METADATA)
.and_then(|m| m.get(sub_field))
.and_then(|l| l.as_mapping());
assert_eq!(
via_helper, via_inline,
"kube_metadata_map_field(v, {sub_field:?}) must yield \
the same Option<&Mapping> as the prior inline \
`value.get(KUBE_KEY_METADATA).and_then(|m| \
m.get({sub_field:?})).and_then(|l| l.as_mapping())` \
chain — otherwise the recomposed `kube_metadata_labels` \
peer and every future routed per-CR sub-metadata-field \
sub-mapping-readback site drifts silently at test time"
);
}
}
#[test]
fn kube_metadata_labels_recomposes_on_lifted_kube_metadata_map_field() {
// The pinned [`kube_metadata_labels`] accessor's body IS
// `kube_metadata_map_field(value, KUBE_KEY_LABELS)` post-lift.
// Pin the recomposition byte-for-byte across a representative
// CR shape so a future refactor that reinlines the raw three-
// hop `.get(KUBE_KEY_METADATA).and_then(|m|
// m.get(KUBE_KEY_LABELS)).and_then(|l| l.as_mapping())` chain
// (structurally equivalent but no compile-time link to the
// parametric helper) is a test-visible break. Structural
// mirror of the way sibling [`kube_name`] / [`kube_namespace`]
// recompose on the parametric [`kube_metadata_str_field`]
// primitive.
let mut labels_body = serde_yaml::Mapping::new();
labels_body.insert_str_key(
LABEL_APLICACAO,
serde_yaml::Value::String("checkout".into()),
);
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_LABELS, serde_yaml::Value::Mapping(labels_body));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata_labels(&value),
kube_metadata_map_field(&value, KUBE_KEY_LABELS),
"kube_metadata_labels(v) must equal kube_metadata_map_field\
(v, KUBE_KEY_LABELS) — the pinned peer's body composes on \
the parametric helper, and any drift between the two \
disagrees on the load-bearing per-CR label surface"
);
}
// ── kube_metadata_seq_field lift ───────────────────────────────────
#[test]
fn kube_metadata_seq_field_reads_sub_metadata_field_sequence() {
// The lift's load-bearing contract: given a Value carrying a
// top-level `metadata: { <sub-field>: [ ... ], ... }` identity
// sub-block (every K8s CR the emit-side
// [`kube_resource_skeleton`] renders with a sequence-shaped
// `metadata.<field>` axis — `metadata.ownerReferences` on
// controller-owned CRs for GC-cascade wiring,
// `metadata.finalizers` on CRs whose deletion drives an
// operator-side pre-delete hook, `metadata.managedFields` on
// every Server-Side-Apply-authored CR), the composed
// sequence-arity accessor returns `Some(<sequence>)` borrowing
// into the input Value across the routed per-sub-field
// readback axes. Structural mirror of the sibling
// `kube_spec_seq_field_reads_sub_spec_field_sequence` pin on
// the sub-`spec.<field>` sequence-arity axis and of the
// sibling `kube_metadata_map_field_reads_sub_metadata_field_mapping`
// pin on the peer sub-mapping-arity axis of the same
// `metadata:` sub-block.
let mut owner_ref = serde_yaml::Mapping::new();
owner_ref.insert_str_key(
KUBE_KEY_API_VERSION,
serde_yaml::Value::String("mesh.pleme.io/v1alpha1".into()),
);
owner_ref.insert_str_key(KUBE_KEY_KIND, serde_yaml::Value::String("Aplicacao".into()));
owner_ref.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("checkout".into()));
let owner_refs = serde_yaml::Value::Sequence(vec![serde_yaml::Value::Mapping(owner_ref)]);
let finalizers = serde_yaml::Value::Sequence(vec![
serde_yaml::Value::String("caixa.pleme.io/build-cleanup".into()),
serde_yaml::Value::String("caixa.pleme.io/lacre-release".into()),
]);
let empty_seq = serde_yaml::Value::Sequence(vec![]);
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key("ownerReferences", owner_refs);
metadata.insert_str_key("finalizers", finalizers);
metadata.insert_str_key("managedFields", empty_seq);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata_seq_field(&value, "ownerReferences").map(Vec::len),
Some(1),
"kube_metadata_seq_field must read metadata.ownerReferences \
as a one-entry sequence — the app-operator's per-Aplicacao \
CR materializer reaches through this axis for GC-cascade \
wiring (MESH-COMPOSITION §III.2 #5), and the parametric \
`<field>` axis stays open-ended on the K8s `ObjectMeta` \
sub-field surface"
);
assert_eq!(
kube_metadata_seq_field(&value, "finalizers").map(Vec::len),
Some(2),
"kube_metadata_seq_field must read metadata.finalizers as a \
two-entry sequence — the pre-delete-hook coordination \
contract reaches through this axis, and the parametric \
`<field>` axis stays open-ended so a per-controller \
finalizer set reaches for the same helper with a different \
axis-key"
);
assert_eq!(
kube_metadata_seq_field(&value, "managedFields").map(Vec::len),
Some(0),
"kube_metadata_seq_field must read metadata.managedFields \
as an empty sequence when the emitter writes a legally-\
empty block (distinct from the requested-field-absent \
None-arm — empty-sequence-present preserves the caller's \
`.iter()` / `.first()` continuation contract that always \
yields no entries on an empty body)"
);
}
#[test]
fn kube_metadata_seq_field_returns_none_when_metadata_block_absent() {
// The composition's outer-arm None short-circuit fold-through:
// any short-circuit the underlying [`kube_metadata_field`]
// closes on (which in turn folds through [`kube_metadata`]'s
// outer-arm and shape-gate) folds through this composed
// sequence-arity accessor. Peer of the sibling
// `kube_metadata_map_field_returns_none_when_metadata_block_absent`
// pin on the composed sub-`metadata.<field>` sub-mapping-arity
// accessor axis.
for shape in [
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
serde_yaml::Value::Null,
serde_yaml::Value::String("scalar".into()),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::Number(0.into()),
serde_yaml::Value::Bool(false),
] {
assert_eq!(
kube_metadata_seq_field(&shape, "ownerReferences"),
None,
"kube_metadata_seq_field({shape:?}, <FIELD>) must short-\
circuit to None through the underlying \
kube_metadata_field's kube_metadata outer-arm when the \
top-level `metadata:` block is absent on the outer \
Value — the composition fold must preserve the total-\
function contract"
);
}
}
#[test]
fn kube_metadata_seq_field_returns_none_when_metadata_carries_non_mapping_type() {
// The composition's shape-gate None short-circuit fold-through:
// a present-but-non-Mapping `metadata:` value on the outer
// Value folds through [`kube_metadata`]'s trailing
// `.as_mapping()` shape-gate up through
// [`kube_metadata_field`] up through this composed sequence-
// arity accessor — the caller's `.iter()` / `.first()`
// continuation stays a total function.
for non_mapping in [
serde_yaml::Value::Null,
serde_yaml::Value::Number(42.into()),
serde_yaml::Value::Bool(true),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::String("metadata-as-string".into()),
] {
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, non_mapping.clone());
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata_seq_field(&value, "ownerReferences"),
None,
"kube_metadata_seq_field must return None when the \
top-level `metadata:` axis carries a non-Mapping YAML \
type ({non_mapping:?}) — the fold through \
kube_metadata's shape-gate arm short-circuits here, \
and every routed caller depends on that None-arm to \
keep the readback total"
);
}
}
#[test]
fn kube_metadata_seq_field_returns_none_when_requested_field_absent() {
// The composition's middle per-key None-arm: the requested
// `<field>` sub-field axis-key is absent from the `metadata:`
// sub-mapping. Preserves the "no such sub-field" vs. "wrong
// shape" distinction routed consumers rely on — a per-CR
// `metadata.ownerReferences` readback that finds no such
// sub-field expects None here (routing the "no owner
// references" fallback) rather than a panic.
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("cart".into()));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata_seq_field(&value, "ownerReferences"),
None,
"kube_metadata_seq_field must return None when the \
requested `metadata.<field>` axis-key is absent from the \
sub-mapping — a `Mapping::get(<KEY>)` miss short-circuits \
the composed accessor, and callers rely on the None-arm \
to route the fallback path (rather than the wrong-shape \
arm)"
);
}
#[test]
fn kube_metadata_seq_field_returns_none_when_field_carries_non_sequence_type() {
// The composition's trailing `.as_sequence()` shape-gate None
// arm: a `metadata.<field>` axis-key present but carrying a
// non-sequence YAML type. Schema-invalid per the K8s
// apiserver's OpenAPI schema (the routed readback sites —
// `metadata.ownerReferences`, `metadata.finalizers`,
// `metadata.managedFields` — all pin ordered sub-sequences)
// but tolerated here as None so the readback stays a total
// function. Pin the None-arm so a future refactor that
// reaches for `.as_sequence().unwrap()` (which would panic on
// a scalar axis-value) is a test-visible break, not a runtime
// regression at the first schema-invalid CR the reader sees.
for non_sequence in [
serde_yaml::Value::Null,
serde_yaml::Value::Number(42.into()),
serde_yaml::Value::Bool(true),
serde_yaml::Value::String("owner-refs-as-string".into()),
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
] {
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key("ownerReferences", non_sequence.clone());
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_metadata_seq_field(&value, "ownerReferences"),
None,
"kube_metadata_seq_field must return None when \
metadata.ownerReferences carries a non-sequence YAML \
type ({non_sequence:?}) — the trailing `.as_sequence()` \
shape gate short-circuits here, and every routed \
caller depends on that None-arm to keep the readback \
total"
);
}
}
#[test]
fn kube_metadata_seq_field_composes_on_lifted_kube_metadata_field_accessor() {
// Composition pin: the composed accessor's body IS
// `kube_metadata_field(value, field).and_then(|v|
// v.as_sequence())` — the composed scalar-arity accessor stays
// load-bearing, this sequence-arity accessor stands one
// abstraction step above it (folding the trailing
// `.as_sequence()` shape-gate closure). Pin the delegation-
// shape byte-for-byte across three representative sub-field
// axis-keys so a future refactor that bypasses
// [`kube_metadata_field`] (a private inline
// `.get(KUBE_KEY_METADATA).and_then(|m| m.as_mapping())
// .and_then(|m| m.get(field)).and_then(|n| n.as_sequence())`
// chain that would silently drift on a future rebrand of the
// outer two-hop navigation) is a test-visible break. Peer of
// the sibling
// `kube_spec_seq_field_composes_on_lifted_kube_spec_field_accessor`
// composition pin on the sub-`spec.<field>` sequence-arity
// accessor axis and of the sibling
// `kube_metadata_map_field_composes_on_lifted_kube_metadata_field_accessor`
// composition pin on the peer sub-mapping-arity axis of the
// same `metadata:` sub-block.
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key("ownerReferences", serde_yaml::Value::Sequence(vec![]));
metadata.insert_str_key("finalizers", serde_yaml::Value::Sequence(vec![]));
metadata.insert_str_key("managedFields", serde_yaml::Value::Sequence(vec![]));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
for sub_field in ["ownerReferences", "finalizers", "managedFields"] {
let via_composed = kube_metadata_seq_field(&value, sub_field);
let via_delegation =
kube_metadata_field(&value, sub_field).and_then(|v| v.as_sequence());
assert_eq!(
via_composed, via_delegation,
"kube_metadata_seq_field(v, {sub_field:?}) must equal \
the delegation-shape `kube_metadata_field(v, \
{sub_field:?}).and_then(|v| v.as_sequence())` — the \
composition pin closes the drift surface where a \
private inline bypass silently desynchronizes from \
the underlying composed scalar-arity accessor's \
contract"
);
}
}
#[test]
fn kube_metadata_seq_field_matches_prior_inline_chain() {
// Cross-check the composed accessor's output byte-for-byte
// against the raw
// `value.get(KUBE_KEY_METADATA).and_then(|m|
// m.get(<FIELD>)).and_then(|s| s.as_sequence())` three-hop
// chain every future routed per-CR `metadata.<field>`
// sequence-readback site would otherwise re-inline. Pin the
// byte-equivalence across three representative sub-field
// axis-keys so the composed helper remains a drop-in
// replacement for any three-hop block a future
// `metadata.ownerReferences` / `metadata.finalizers` /
// `metadata.managedFields` consumer might reach for.
let mut owner_ref = serde_yaml::Mapping::new();
owner_ref.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("cart".into()));
let owner_refs = serde_yaml::Value::Sequence(vec![serde_yaml::Value::Mapping(owner_ref)]);
let finalizers = serde_yaml::Value::Sequence(vec![serde_yaml::Value::String(
"caixa.pleme.io/build-cleanup".into(),
)]);
let managed_fields = serde_yaml::Value::Sequence(vec![]);
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key("ownerReferences", owner_refs);
metadata.insert_str_key("finalizers", finalizers);
metadata.insert_str_key("managedFields", managed_fields);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
for sub_field in ["ownerReferences", "finalizers", "managedFields"] {
let via_helper = kube_metadata_seq_field(&value, sub_field);
let via_inline = value
.get(KUBE_KEY_METADATA)
.and_then(|m| m.get(sub_field))
.and_then(|s| s.as_sequence());
assert_eq!(
via_helper, via_inline,
"kube_metadata_seq_field(v, {sub_field:?}) must yield \
the same Option<&Sequence> as the raw \
`value.get(KUBE_KEY_METADATA).and_then(|m| \
m.get({sub_field:?})).and_then(|s| s.as_sequence())` \
chain — otherwise every future routed per-CR \
sub-metadata-field sequence-readback site drifts \
silently at test time"
);
}
}
#[test]
fn kube_metadata_str_field_recomposes_on_lifted_kube_metadata_field() {
// The pinned [`kube_metadata_str_field`] accessor's body IS
// `kube_metadata_field(value, field).and_then(|v| v.as_str())`
// post-lift — the last raw two-hop `.get(KUBE_KEY_METADATA)
// .and_then(|m| m.get(field)).and_then(|n| n.as_str())` chain
// in the substrate folds onto the composed scalar-Value
// primitive, closing the three-arity shape-gate family on the
// sub-`metadata:` axis (str + seq + map) onto the same
// [`kube_metadata_field`] two-hop primitive. Pin the
// recomposition byte-for-byte across representative sub-field
// axis-keys so a future refactor that reinlines the raw
// three-hop chain (structurally equivalent but no compile-
// time link to the parametric helper) is a test-visible
// break. Structural mirror of
// `kube_metadata_labels_recomposes_on_lifted_kube_metadata_map_field`
// on the sub-mapping-arity axis.
let mut metadata = serde_yaml::Mapping::new();
metadata.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("cart".into()));
metadata.insert_str_key(
KUBE_KEY_NAMESPACE,
serde_yaml::Value::String("checkout".into()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata));
let value = serde_yaml::Value::Mapping(cr);
for sub_field in [KUBE_KEY_NAME, KUBE_KEY_NAMESPACE] {
assert_eq!(
kube_metadata_str_field(&value, sub_field),
kube_metadata_field(&value, sub_field).and_then(|v| v.as_str()),
"kube_metadata_str_field(v, {sub_field:?}) must equal \
the delegation-shape `kube_metadata_field(v, \
{sub_field:?}).and_then(|v| v.as_str())` — the \
recomposition closes the last raw two-hop chain on \
the sub-`metadata:` axis and any drift between the \
two disagrees on the load-bearing per-CR identity \
coordinate readback"
);
}
}
// ── kube_spec_str_field lift ────────────────────────────────────────
#[test]
fn kube_spec_str_field_reads_sub_spec_field_string_scalar() {
// The lift's load-bearing contract: given a Value carrying a
// top-level `spec: { <sub-field>: <str>, ... }` body sub-mapping
// (every K8s CR the emit-side [`kube_resource_skeleton`] renders
// with a string-scalar `spec.<field>` axis — `spec.path` on
// `Kustomization`, `spec.url` on `GitRepository`, `spec.timeout`
// on `Kustomization`, `spec.interval` on the Flux v2 controller
// CR family, `spec.gatewayClassName` on `Gateway`), the composed
// scalar-str-arity accessor returns `Some(<str>)` borrowing into
// the input Value across the routed per-sub-field readback axes.
// Structural mirror of the sibling
// `kube_metadata_str_field_reads_metadata_name_and_namespace_string_scalars`
// pin on the sub-`metadata:` axis's composed scalar-str-arity
// accessor.
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(
FLUX_KUSTOMIZATION_KEY_PATH,
serde_yaml::Value::String("clusters/rio/apps/checkout".into()),
);
spec.insert_str_key(
FLUX_KUSTOMIZATION_KEY_TIMEOUT,
serde_yaml::Value::String(DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT.into()),
);
spec.insert_str_key(
GATEWAY_API_KEY_GATEWAY_CLASS_NAME,
serde_yaml::Value::String(DEFAULT_GATEWAY_CLASS_NAME.into()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_spec_str_field(&value, FLUX_KUSTOMIZATION_KEY_PATH),
Some("clusters/rio/apps/checkout"),
"kube_spec_str_field must read spec.path as a string scalar \
— the caixa-flux cluster_bundle_kustomization_path pins \
reach through this axis for per-caixa sub-tree navigation"
);
assert_eq!(
kube_spec_str_field(&value, FLUX_KUSTOMIZATION_KEY_TIMEOUT),
Some(DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT),
"kube_spec_str_field must read spec.timeout as a string \
scalar — the caixa-flux per-Kustomization reconcile-cap \
pin reaches through this axis"
);
assert_eq!(
kube_spec_str_field(&value, GATEWAY_API_KEY_GATEWAY_CLASS_NAME),
Some(DEFAULT_GATEWAY_CLASS_NAME),
"kube_spec_str_field must read spec.gatewayClassName as a \
string scalar — the caixa-mesh per-Gateway controller-\
choice pin reaches through this axis"
);
}
#[test]
fn kube_spec_str_field_returns_none_when_spec_sub_block_absent() {
// The composition's outer-arm None short-circuit fold-through:
// any short-circuit the underlying [`kube_spec_field`] closes on
// (which in turn folds through [`kube_spec`]'s outer-arm and
// shape-gate) folds through this composed scalar-str-arity
// accessor. Covers the missing top-level `spec:` block arm
// across the same outer-Value shape permutations the sibling
// `kube_spec_field_returns_none_when_spec_sub_block_absent` pin
// covers on the direct scalar-arity primitive.
for shape in [
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
serde_yaml::Value::Null,
serde_yaml::Value::String("scalar".into()),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::Number(0.into()),
serde_yaml::Value::Bool(false),
] {
assert_eq!(
kube_spec_str_field(&shape, FLUX_KUSTOMIZATION_KEY_PATH),
None,
"kube_spec_str_field({shape:?}, <FIELD>) must short-\
circuit to None through the underlying kube_spec_field's \
kube_spec outer-arm when the top-level `spec:` block is \
absent on the outer Value — the composition fold must \
preserve the total-function contract"
);
}
}
#[test]
fn kube_spec_str_field_returns_none_when_spec_carries_non_mapping_type() {
// The composition's shape-gate None short-circuit fold-through:
// a present-but-non-Mapping `spec:` value on the outer Value
// folds through [`kube_spec`]'s trailing `.as_mapping()` shape-
// gate up through [`kube_spec_field`] up through this composed
// accessor — the caller's `.expect(...)` / `.unwrap_or(...)`
// continuation stays a total function.
for non_mapping in [
serde_yaml::Value::Null,
serde_yaml::Value::Number(42.into()),
serde_yaml::Value::Bool(true),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::String("spec-as-string".into()),
] {
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, non_mapping.clone());
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_spec_str_field(&value, FLUX_KUSTOMIZATION_KEY_PATH),
None,
"kube_spec_str_field must return None when the top-\
level `spec:` axis carries a non-Mapping YAML type \
({non_mapping:?}) — the fold through kube_spec's \
shape-gate arm short-circuits here, and every routed \
caller depends on that None-arm to keep the readback \
total"
);
}
}
#[test]
fn kube_spec_str_field_returns_none_when_requested_field_absent() {
// The composition's middle per-key None-arm: the requested
// `<field>` sub-field axis-key is absent from the `spec:`
// sub-mapping. Preserves the "no such sub-field" vs. "wrong
// shape" distinction routed consumers rely on — a per-
// Kustomization `spec.path` readback that finds no `spec.path`
// sub-field expects None here (routing the fallback path)
// rather than a panic.
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(
FLUX_KUSTOMIZATION_KEY_TIMEOUT,
serde_yaml::Value::String(DEFAULT_FLUX_KUSTOMIZATION_TIMEOUT.into()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_spec_str_field(&value, FLUX_KUSTOMIZATION_KEY_PATH),
None,
"kube_spec_str_field must return None when the requested \
`spec.<field>` axis-key is absent from the sub-mapping — \
a `Mapping::get(<KEY>)` miss short-circuits the composed \
accessor, and callers rely on the None-arm to route the \
fallback path (rather than the wrong-shape arm)"
);
}
#[test]
fn kube_spec_str_field_returns_none_when_field_carries_non_string_type() {
// The composition's trailing `.as_str()` shape-gate None arm:
// a `spec.<field>` axis-key present but carrying a non-string
// YAML type. Schema-invalid per the K8s apiserver's OpenAPI
// schema (the routed readback sites — `spec.path`, `spec.url`,
// `spec.timeout`, `spec.interval`, `spec.gatewayClassName` —
// all pin string scalars) but tolerated here as None so the
// readback stays a total function. Pin the None-arm so a
// future refactor that reaches for `.as_str().unwrap()`
// (which would panic on a numeric axis-value) is a test-
// visible break, not a runtime regression at the first
// schema-invalid CR the reader sees. Peer of the sibling
// `kube_metadata_str_field_returns_none_when_field_carries_non_string_type`
// pin on the composed sub-`metadata.<field>` scalar-str-arity
// accessor.
for non_string in [
serde_yaml::Value::Null,
serde_yaml::Value::Number(42.into()),
serde_yaml::Value::Bool(true),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
] {
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(FLUX_KUSTOMIZATION_KEY_PATH, non_string.clone());
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_spec_str_field(&value, FLUX_KUSTOMIZATION_KEY_PATH),
None,
"kube_spec_str_field must return None when spec.path \
carries a non-string YAML type ({non_string:?}) — the \
trailing `.as_str()` shape gate short-circuits here, \
and every routed caller depends on that None-arm to \
keep the readback total"
);
}
}
#[test]
fn kube_spec_str_field_composes_on_lifted_kube_spec_field_accessor() {
// Composition pin: the composed accessor's body IS
// `kube_spec_field(value, field).and_then(|v| v.as_str())` —
// the composed scalar-arity accessor stays load-bearing, this
// scalar-str-arity accessor stands one abstraction step above
// it (folding the trailing `.as_str()` shape-gate closure).
// Pin the delegation-shape byte-for-byte across three
// representative sub-field axis-keys so a future refactor that
// bypasses [`kube_spec_field`] (a private inline
// `.get(KUBE_KEY_SPEC).and_then(|s| s.as_mapping()).and_then(|m|
// m.get(field)).and_then(|n| n.as_str())` chain that would
// silently drift on a future rebrand of the outer two-hop
// navigation) is a test-visible break. Peer of the sibling
// `kube_spec_field_composes_on_lifted_kube_spec_accessor` +
// `kube_metadata_field_composes_on_lifted_kube_metadata_accessor`
// composition pins on the same composed-on-lifted-primitive
// axis.
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(
FLUX_KUSTOMIZATION_KEY_PATH,
serde_yaml::Value::String("path-marker".into()),
);
spec.insert_str_key(
FLUX_GITREPOSITORY_KEY_URL,
serde_yaml::Value::String("https://example.com/repo.git".into()),
);
spec.insert_str_key(FLUX_KEY_INTERVAL, serde_yaml::Value::String("5m".into()));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
for sub_field in [
FLUX_KUSTOMIZATION_KEY_PATH,
FLUX_GITREPOSITORY_KEY_URL,
FLUX_KEY_INTERVAL,
] {
let via_composed = kube_spec_str_field(&value, sub_field);
let via_delegation = kube_spec_field(&value, sub_field).and_then(|v| v.as_str());
assert_eq!(
via_composed, via_delegation,
"kube_spec_str_field(v, {sub_field:?}) must equal the \
delegation-shape `kube_spec_field(v, {sub_field:?})\
.and_then(|v| v.as_str())` — the composition pin \
closes the drift surface where a private inline bypass \
silently desynchronizes from the underlying composed \
scalar-arity accessor's contract"
);
}
}
#[test]
fn kube_spec_str_field_matches_prior_inline_chain() {
// Cross-check the composed accessor's output byte-for-byte
// against the prior inline three-hop `kube_spec_field(v, F)
// .and_then(|v| v.as_str())` chain the seven routed caller
// sites previously carried. A drift between the composed
// helper's return and the inline chain would silently regress
// the caixa-flux + caixa-mesh per-CR sub-spec-field string-
// readback sites' downstream continuations (`.expect(...)`,
// `.unwrap_or_else(...)`, `== Some(<VALUE>)`) — pin the byte-
// equivalence across three representative sub-field axis-keys
// so the composed helper remains a drop-in replacement for the
// routed site's prior two-line block.
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(
FLUX_KUSTOMIZATION_KEY_PATH,
serde_yaml::Value::String("clusters/rio/apps/hello".into()),
);
spec.insert_str_key(
FLUX_GITREPOSITORY_KEY_URL,
serde_yaml::Value::String("https://github.com/pleme-io/k8s.git".into()),
);
spec.insert_str_key(
GATEWAY_API_KEY_GATEWAY_CLASS_NAME,
serde_yaml::Value::String(DEFAULT_GATEWAY_CLASS_NAME.into()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
for sub_field in [
FLUX_KUSTOMIZATION_KEY_PATH,
FLUX_GITREPOSITORY_KEY_URL,
GATEWAY_API_KEY_GATEWAY_CLASS_NAME,
] {
let via_helper = kube_spec_str_field(&value, sub_field);
let via_inline = kube_spec_field(&value, sub_field).and_then(|v| v.as_str());
assert_eq!(
via_helper, via_inline,
"kube_spec_str_field(v, {sub_field:?}) must yield the \
same Option<&str> as the prior inline \
`kube_spec_field(v, {sub_field:?}).and_then(|v| \
v.as_str())` chain — otherwise the routed caixa-flux \
+ caixa-mesh per-CR sub-spec-field string-readback \
sites drift silently at test time"
);
}
}
// ── kube_spec_seq_field lift ────────────────────────────────────────
#[test]
fn kube_spec_seq_field_reads_sub_spec_field_sequence() {
// The lift's load-bearing contract: given a Value carrying a
// top-level `spec: { <sub-field>: [ ... ], ... }` body sub-
// mapping (every K8s CR the emit-side [`kube_resource_skeleton`]
// renders with a sequence-shaped `spec.<field>` axis —
// `spec.ingress[]` on `CiliumNetworkPolicy`, `spec.listeners[]`
// / `spec.parentRefs[]` / `spec.hostnames[]` on the Gateway API
// `Gateway` / `HTTPRoute`, `spec.rules[]` on the Gateway API
// `HTTPRoute` per-rule fan-out), the composed sequence-arity
// accessor returns `Some(<sequence>)` borrowing into the input
// Value across the routed per-sub-field readback axes.
// Structural mirror of the sibling
// `kube_spec_str_field_reads_sub_spec_field_string_scalar` pin
// on the sub-`spec.<field>` string-scalar arity axis.
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(
CILIUM_KEY_INGRESS,
serde_yaml::Value::Sequence(vec![
serde_yaml::Value::String("rule-a".into()),
serde_yaml::Value::String("rule-b".into()),
]),
);
spec.insert_str_key(
GATEWAY_API_KEY_LISTENERS,
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("listener-0".into())]),
);
spec.insert_str_key(
GATEWAY_API_KEY_HOSTNAMES,
serde_yaml::Value::Sequence(vec![]),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_spec_seq_field(&value, CILIUM_KEY_INGRESS).map(std::vec::Vec::len),
Some(2),
"kube_spec_seq_field must read spec.ingress as a two-entry \
sequence — the caixa-mesh per-CNP ingress-rule fan-out \
sites reach through this axis for per-`:contratos` L4/L7 \
rule navigation"
);
assert_eq!(
kube_spec_seq_field(&value, GATEWAY_API_KEY_LISTENERS).map(std::vec::Vec::len),
Some(1),
"kube_spec_seq_field must read spec.listeners as a one-\
entry sequence — the caixa-mesh per-Gateway listener-set \
pin reaches through this axis"
);
assert_eq!(
kube_spec_seq_field(&value, GATEWAY_API_KEY_HOSTNAMES).map(std::vec::Vec::len),
Some(0),
"kube_spec_seq_field must read spec.hostnames as an empty \
sequence when the emitter writes a legally-empty vector \
(distinct from the requested-field-absent None-arm — \
empty-sequence-present preserves the caller's `.iter()` \
fold contract)"
);
}
#[test]
fn kube_spec_seq_field_returns_none_when_spec_sub_block_absent() {
// The composition's outer-arm None short-circuit fold-through:
// any short-circuit the underlying [`kube_spec_field`] closes on
// (which in turn folds through [`kube_spec`]'s outer-arm and
// shape-gate) folds through this composed sequence-arity
// accessor. Peer of the sibling
// `kube_spec_str_field_returns_none_when_spec_sub_block_absent`
// pin on the composed sub-`spec.<field>` scalar-str-arity
// accessor axis.
for shape in [
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
serde_yaml::Value::Null,
serde_yaml::Value::String("scalar".into()),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::Number(0.into()),
serde_yaml::Value::Bool(false),
] {
assert_eq!(
kube_spec_seq_field(&shape, CILIUM_KEY_INGRESS),
None,
"kube_spec_seq_field({shape:?}, <FIELD>) must short-\
circuit to None through the underlying kube_spec_field's \
kube_spec outer-arm when the top-level `spec:` block is \
absent on the outer Value — the composition fold must \
preserve the total-function contract"
);
}
}
#[test]
fn kube_spec_seq_field_returns_none_when_spec_carries_non_mapping_type() {
// The composition's shape-gate None short-circuit fold-through:
// a present-but-non-Mapping `spec:` value on the outer Value
// folds through [`kube_spec`]'s trailing `.as_mapping()` shape-
// gate up through [`kube_spec_field`] up through this composed
// sequence-arity accessor — the caller's `.iter()` /
// `.expect(...)` continuation stays a total function.
for non_mapping in [
serde_yaml::Value::Null,
serde_yaml::Value::Number(42.into()),
serde_yaml::Value::Bool(true),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::String("spec-as-string".into()),
] {
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, non_mapping.clone());
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_spec_seq_field(&value, CILIUM_KEY_INGRESS),
None,
"kube_spec_seq_field must return None when the top-\
level `spec:` axis carries a non-Mapping YAML type \
({non_mapping:?}) — the fold through kube_spec's \
shape-gate arm short-circuits here, and every routed \
caller depends on that None-arm to keep the readback \
total"
);
}
}
#[test]
fn kube_spec_seq_field_returns_none_when_requested_field_absent() {
// The composition's middle per-key None-arm: the requested
// `<field>` sub-field axis-key is absent from the `spec:`
// sub-mapping. Preserves the "no such sub-field" vs. "wrong
// shape" distinction routed consumers rely on — a per-CNP
// `spec.ingress` readback that finds no `spec.ingress` sub-field
// expects None here (routing the "no ingress rules" fallback)
// rather than a panic.
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(
GATEWAY_API_KEY_LISTENERS,
serde_yaml::Value::Sequence(vec![]),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_spec_seq_field(&value, CILIUM_KEY_INGRESS),
None,
"kube_spec_seq_field must return None when the requested \
`spec.<field>` axis-key is absent from the sub-mapping — \
a `Mapping::get(<KEY>)` miss short-circuits the composed \
accessor, and callers rely on the None-arm to route the \
fallback path (rather than the wrong-shape arm)"
);
}
#[test]
fn kube_spec_seq_field_returns_none_when_field_carries_non_sequence_type() {
// The composition's trailing `.as_sequence()` shape-gate None
// arm: a `spec.<field>` axis-key present but carrying a non-
// sequence YAML type. Schema-invalid per the K8s apiserver's
// OpenAPI schema (the routed readback sites — `spec.ingress`,
// `spec.listeners`, `spec.parentRefs`, `spec.hostnames`,
// `spec.rules` — all pin ordered sequences) but tolerated here
// as None so the readback stays a total function. Pin the None-
// arm so a future refactor that reaches for
// `.as_sequence().unwrap()` (which would panic on a scalar
// axis-value) is a test-visible break, not a runtime regression
// at the first schema-invalid CR the reader sees. Peer of the
// sibling
// `kube_spec_str_field_returns_none_when_field_carries_non_string_type`
// pin on the composed sub-`spec.<field>` scalar-str-arity
// accessor.
for non_sequence in [
serde_yaml::Value::Null,
serde_yaml::Value::Number(42.into()),
serde_yaml::Value::Bool(true),
serde_yaml::Value::String("ingress-as-string".into()),
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
] {
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(CILIUM_KEY_INGRESS, non_sequence.clone());
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_spec_seq_field(&value, CILIUM_KEY_INGRESS),
None,
"kube_spec_seq_field must return None when spec.ingress \
carries a non-sequence YAML type ({non_sequence:?}) — \
the trailing `.as_sequence()` shape gate short-circuits \
here, and every routed caller depends on that None-arm \
to keep the readback total"
);
}
}
#[test]
fn kube_spec_seq_field_composes_on_lifted_kube_spec_field_accessor() {
// Composition pin: the composed accessor's body IS
// `kube_spec_field(value, field).and_then(|v| v.as_sequence())`
// — the composed scalar-arity accessor stays load-bearing, this
// sequence-arity accessor stands one abstraction step above it
// (folding the trailing `.as_sequence()` shape-gate closure).
// Pin the delegation-shape byte-for-byte across three
// representative sub-field axis-keys so a future refactor that
// bypasses [`kube_spec_field`] (a private inline
// `.get(KUBE_KEY_SPEC).and_then(|s| s.as_mapping()).and_then(|m|
// m.get(field)).and_then(|n| n.as_sequence())` chain that would
// silently drift on a future rebrand of the outer two-hop
// navigation) is a test-visible break. Peer of the sibling
// `kube_spec_str_field_composes_on_lifted_kube_spec_field_accessor`
// composition pin on the sub-`spec.<field>` scalar-str-arity
// axis.
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(
CILIUM_KEY_INGRESS,
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("ingress-0".into())]),
);
spec.insert_str_key(
GATEWAY_API_KEY_LISTENERS,
serde_yaml::Value::Sequence(vec![
serde_yaml::Value::String("listener-0".into()),
serde_yaml::Value::String("listener-1".into()),
]),
);
spec.insert_str_key(KUBE_KEY_RULES, serde_yaml::Value::Sequence(vec![]));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
for sub_field in [
CILIUM_KEY_INGRESS,
GATEWAY_API_KEY_LISTENERS,
KUBE_KEY_RULES,
] {
let via_composed = kube_spec_seq_field(&value, sub_field);
let via_delegation = kube_spec_field(&value, sub_field).and_then(|v| v.as_sequence());
assert_eq!(
via_composed, via_delegation,
"kube_spec_seq_field(v, {sub_field:?}) must equal the \
delegation-shape `kube_spec_field(v, {sub_field:?})\
.and_then(|v| v.as_sequence())` — the composition pin \
closes the drift surface where a private inline bypass \
silently desynchronizes from the underlying composed \
scalar-arity accessor's contract"
);
}
}
#[test]
fn kube_spec_seq_field_matches_prior_inline_chain() {
// Cross-check the composed accessor's output byte-for-byte
// against the prior inline `kube_spec_field(v, F).and_then(|v|
// v.as_sequence())` chain the routed caller sites previously
// carried. A drift between the composed helper's return and
// the inline chain would silently regress the caixa-mesh +
// caixa-flux per-CR sub-spec-field sequence-readback sites'
// downstream continuations (`.and_then(|s| s.first())`,
// `.iter().find(...)`, `.expect(...)`) — pin the byte-
// equivalence across three representative sub-field axis-keys
// so the composed helper remains a drop-in replacement for the
// routed sites' prior two-line block.
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(
GATEWAY_API_KEY_PARENT_REFS,
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("gateway-0".into())]),
);
spec.insert_str_key(
GATEWAY_API_KEY_HOSTNAMES,
serde_yaml::Value::Sequence(vec![
serde_yaml::Value::String("a.example.com".into()),
serde_yaml::Value::String("b.example.com".into()),
]),
);
spec.insert_str_key(KUBE_KEY_RULES, serde_yaml::Value::Sequence(vec![]));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
for sub_field in [
GATEWAY_API_KEY_PARENT_REFS,
GATEWAY_API_KEY_HOSTNAMES,
KUBE_KEY_RULES,
] {
let via_helper = kube_spec_seq_field(&value, sub_field);
let via_inline = kube_spec_field(&value, sub_field).and_then(|v| v.as_sequence());
assert_eq!(
via_helper, via_inline,
"kube_spec_seq_field(v, {sub_field:?}) must yield the \
same Option<&Sequence> as the prior inline \
`kube_spec_field(v, {sub_field:?}).and_then(|v| \
v.as_sequence())` chain — otherwise the routed caixa-\
mesh + caixa-flux per-CR sub-spec-field sequence-\
readback sites drift silently at test time"
);
}
}
// ── kube_spec_map_field lift ────────────────────────────────────────
#[test]
fn kube_spec_map_field_reads_sub_spec_field_mapping() {
// The lift's load-bearing contract: given a Value carrying a
// top-level `spec: { <sub-field>: { ... }, ... }` body sub-
// mapping (every K8s CR the emit-side [`kube_resource_skeleton`]
// renders with a mapping-shaped `spec.<field>` axis —
// `spec.values` on `HelmRelease`, `spec.chart` on `HelmRelease`,
// `spec.sourceRef` on `Kustomization`), the composed sub-
// mapping-arity accessor returns `Some(<mapping>)` borrowing
// into the input Value across the routed per-sub-field readback
// axes. Structural mirror of the sibling
// `kube_spec_str_field_reads_sub_spec_field_string_scalar` and
// `kube_spec_seq_field_reads_sub_spec_field_sequence` pins on
// the sub-`spec.<field>` string-scalar and sequence arity axes.
let mut values_body = serde_yaml::Mapping::new();
values_body.insert_str_key("a", serde_yaml::Value::String("1".into()));
values_body.insert_str_key("b", serde_yaml::Value::String("2".into()));
let mut source_ref_body = serde_yaml::Mapping::new();
source_ref_body.insert_str_key(
KUBE_KEY_KIND,
serde_yaml::Value::String("GitRepository".into()),
);
let empty_map = serde_yaml::Mapping::new();
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(FLUX_KEY_VALUES, serde_yaml::Value::Mapping(values_body));
spec.insert_str_key(
FLUX_KEY_SOURCE_REF,
serde_yaml::Value::Mapping(source_ref_body),
);
spec.insert_str_key(FLUX_KEY_CHART, serde_yaml::Value::Mapping(empty_map));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_spec_map_field(&value, FLUX_KEY_VALUES).map(serde_yaml::Mapping::len),
Some(2),
"kube_spec_map_field must read spec.values as a two-entry \
mapping — the caixa-flux per-`HelmRelease` per-cluster \
override wrap sites reach through this axis for per-`\
DEFAULT_LIBRARY_NAME`-wrapped override navigation"
);
assert_eq!(
kube_spec_map_field(&value, FLUX_KEY_SOURCE_REF).map(serde_yaml::Mapping::len),
Some(1),
"kube_spec_map_field must read spec.sourceRef as a one-\
entry mapping — the caixa-flux per-`Kustomization` \
bootstrap source reference readback reaches through this \
axis"
);
assert_eq!(
kube_spec_map_field(&value, FLUX_KEY_CHART).map(serde_yaml::Mapping::len),
Some(0),
"kube_spec_map_field must read spec.chart as an empty \
mapping when the emitter writes a legally-empty block \
(distinct from the requested-field-absent None-arm — \
empty-mapping-present preserves the caller's `.get(<KEY>)` \
lookup contract that always returns None on an empty body)"
);
}
#[test]
fn kube_spec_map_field_returns_none_when_spec_sub_block_absent() {
// The composition's outer-arm None short-circuit fold-through:
// any short-circuit the underlying [`kube_spec_field`] closes on
// (which in turn folds through [`kube_spec`]'s outer-arm and
// shape-gate) folds through this composed sub-mapping-arity
// accessor. Peer of the sibling
// `kube_spec_str_field_returns_none_when_spec_sub_block_absent`
// and `kube_spec_seq_field_returns_none_when_spec_sub_block_absent`
// pins on the composed sub-`spec.<field>` scalar-str-arity and
// sequence-arity accessor axes.
for shape in [
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
serde_yaml::Value::Null,
serde_yaml::Value::String("scalar".into()),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::Number(0.into()),
serde_yaml::Value::Bool(false),
] {
assert_eq!(
kube_spec_map_field(&shape, FLUX_KEY_VALUES),
None,
"kube_spec_map_field({shape:?}, <FIELD>) must short-\
circuit to None through the underlying kube_spec_field's \
kube_spec outer-arm when the top-level `spec:` block is \
absent on the outer Value — the composition fold must \
preserve the total-function contract"
);
}
}
#[test]
fn kube_spec_map_field_returns_none_when_spec_carries_non_mapping_type() {
// The composition's shape-gate None short-circuit fold-through:
// a present-but-non-Mapping `spec:` value on the outer Value
// folds through [`kube_spec`]'s trailing `.as_mapping()` shape-
// gate up through [`kube_spec_field`] up through this composed
// sub-mapping-arity accessor — the caller's `.get(<KEY>)` /
// `.expect(...)` continuation stays a total function.
for non_mapping in [
serde_yaml::Value::Null,
serde_yaml::Value::Number(42.into()),
serde_yaml::Value::Bool(true),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::String("spec-as-string".into()),
] {
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, non_mapping.clone());
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_spec_map_field(&value, FLUX_KEY_VALUES),
None,
"kube_spec_map_field must return None when the top-\
level `spec:` axis carries a non-Mapping YAML type \
({non_mapping:?}) — the fold through kube_spec's \
shape-gate arm short-circuits here, and every routed \
caller depends on that None-arm to keep the readback \
total"
);
}
}
#[test]
fn kube_spec_map_field_returns_none_when_requested_field_absent() {
// The composition's middle per-key None-arm: the requested
// `<field>` sub-field axis-key is absent from the `spec:`
// sub-mapping. Preserves the "no such sub-field" vs. "wrong
// shape" distinction routed consumers rely on — a per-
// `HelmRelease` `spec.values` readback that finds no
// `spec.values` sub-field expects None here (routing the "no
// overrides" fallback) rather than a panic.
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(
FLUX_KEY_SOURCE_REF,
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_spec_map_field(&value, FLUX_KEY_VALUES),
None,
"kube_spec_map_field must return None when the requested \
`spec.<field>` axis-key is absent from the sub-mapping — \
a `Mapping::get(<KEY>)` miss short-circuits the composed \
accessor, and callers rely on the None-arm to route the \
fallback path (rather than the wrong-shape arm)"
);
}
#[test]
fn kube_spec_map_field_returns_none_when_field_carries_non_mapping_type() {
// The composition's trailing `.as_mapping()` shape-gate None
// arm: a `spec.<field>` axis-key present but carrying a non-
// mapping YAML type. Schema-invalid per the K8s apiserver's
// OpenAPI schema (the routed readback sites — `spec.values`,
// `spec.chart`, `spec.sourceRef` — all pin nested-object sub-
// mappings) but tolerated here as None so the readback stays a
// total function. Pin the None-arm so a future refactor that
// reaches for `.as_mapping().unwrap()` (which would panic on a
// scalar axis-value) is a test-visible break, not a runtime
// regression at the first schema-invalid CR the reader sees.
// Peer of the sibling
// `kube_spec_str_field_returns_none_when_field_carries_non_string_type`
// and
// `kube_spec_seq_field_returns_none_when_field_carries_non_sequence_type`
// pins on the composed sub-`spec.<field>` scalar-str-arity and
// sequence-arity accessors.
for non_mapping in [
serde_yaml::Value::Null,
serde_yaml::Value::Number(42.into()),
serde_yaml::Value::Bool(true),
serde_yaml::Value::String("values-as-string".into()),
serde_yaml::Value::Sequence(vec![]),
] {
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(FLUX_KEY_VALUES, non_mapping.clone());
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_spec_map_field(&value, FLUX_KEY_VALUES),
None,
"kube_spec_map_field must return None when spec.values \
carries a non-mapping YAML type ({non_mapping:?}) — \
the trailing `.as_mapping()` shape gate short-\
circuits here, and every routed caller depends on \
that None-arm to keep the readback total"
);
}
}
#[test]
fn kube_spec_map_field_composes_on_lifted_kube_spec_field_accessor() {
// Composition pin: the composed accessor's body IS
// `kube_spec_field(value, field).and_then(|v| v.as_mapping())`
// — the composed scalar-arity accessor stays load-bearing, this
// sub-mapping-arity accessor stands one abstraction step above
// it (folding the trailing `.as_mapping()` shape-gate closure).
// Pin the delegation-shape byte-for-byte across three
// representative sub-field axis-keys so a future refactor that
// bypasses [`kube_spec_field`] (a private inline
// `.get(KUBE_KEY_SPEC).and_then(|s| s.as_mapping()).and_then(|m|
// m.get(field)).and_then(|n| n.as_mapping())` chain that would
// silently drift on a future rebrand of the outer two-hop
// navigation) is a test-visible break. Peer of the sibling
// `kube_spec_str_field_composes_on_lifted_kube_spec_field_accessor`
// and
// `kube_spec_seq_field_composes_on_lifted_kube_spec_field_accessor`
// composition pins on the sub-`spec.<field>` scalar-str-arity
// and sequence-arity accessors.
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(
FLUX_KEY_VALUES,
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
);
spec.insert_str_key(
FLUX_KEY_SOURCE_REF,
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
);
spec.insert_str_key(
FLUX_KEY_CHART,
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
for sub_field in [FLUX_KEY_VALUES, FLUX_KEY_SOURCE_REF, FLUX_KEY_CHART] {
let via_composed = kube_spec_map_field(&value, sub_field);
let via_delegation = kube_spec_field(&value, sub_field).and_then(|v| v.as_mapping());
assert_eq!(
via_composed, via_delegation,
"kube_spec_map_field(v, {sub_field:?}) must equal the \
delegation-shape `kube_spec_field(v, {sub_field:?})\
.and_then(|v| v.as_mapping())` — the composition pin \
closes the drift surface where a private inline bypass \
silently desynchronizes from the underlying composed \
scalar-arity accessor's contract"
);
}
}
#[test]
fn kube_spec_map_field_matches_prior_inline_chain() {
// Cross-check the composed accessor's output byte-for-byte
// against the prior inline `kube_spec_field(v, F).and_then(|v|
// v.as_mapping())` chain the routed caller sites previously
// carried. A drift between the composed helper's return and
// the inline chain would silently regress the caixa-flux
// per-CR sub-spec-field sub-mapping-readback sites' downstream
// continuations (`.get(<KEY>)`, `.is_empty()`, `.len()`,
// `.expect(...)`) — pin the byte-equivalence across three
// representative sub-field axis-keys so the composed helper
// remains a drop-in replacement for the routed sites' prior
// two-line block.
let mut values_body = serde_yaml::Mapping::new();
values_body.insert_str_key("enabled", serde_yaml::Value::Bool(true));
let mut source_ref_body = serde_yaml::Mapping::new();
source_ref_body.insert_str_key(
KUBE_KEY_KIND,
serde_yaml::Value::String("GitRepository".into()),
);
source_ref_body
.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("bootstrap".into()));
let mut chart_body = serde_yaml::Mapping::new();
chart_body.insert_str_key(
KUBE_KEY_SPEC,
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
);
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(FLUX_KEY_VALUES, serde_yaml::Value::Mapping(values_body));
spec.insert_str_key(
FLUX_KEY_SOURCE_REF,
serde_yaml::Value::Mapping(source_ref_body),
);
spec.insert_str_key(FLUX_KEY_CHART, serde_yaml::Value::Mapping(chart_body));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
for sub_field in [FLUX_KEY_VALUES, FLUX_KEY_SOURCE_REF, FLUX_KEY_CHART] {
let via_helper = kube_spec_map_field(&value, sub_field);
let via_inline = kube_spec_field(&value, sub_field).and_then(|v| v.as_mapping());
assert_eq!(
via_helper, via_inline,
"kube_spec_map_field(v, {sub_field:?}) must yield the \
same Option<&Mapping> as the prior inline \
`kube_spec_field(v, {sub_field:?}).and_then(|v| \
v.as_mapping())` chain — otherwise the routed caixa-\
flux per-CR sub-spec-field sub-mapping-readback sites \
drift silently at test time"
);
}
}
// ── contrato-edge-label + cilium-network-policy-name lifts ──────────
#[test]
fn contrato_edge_label_separator_pin() {
// Load-bearing byte-string pin: the M3 `:contratos`
// edge-direction separator every caixa-mesh emitter that
// encodes a typed edge as a K8s-name-shaped scalar reads from.
// Any future rebrand (e.g. `-to-` → `_to_`) lands here as a
// one-const edit; the peer `contrato_edge_label` /
// `cilium_network_policy_name` composers pick up the new
// encoding by construction. A drift on this const would silently
// split the CNP `metadata.name` from its own
// `metadata.labels.pleme.pleme.io/contrato` value, orphaning
// every operator-side grep-by-label query far from the source
// caixa.lisp.
assert_eq!(CONTRATO_EDGE_LABEL_SEPARATOR, "-to-");
}
#[test]
fn contrato_edge_label_matches_inline_de_to_para_encoding() {
// Byte-shape pin: the composer produces the same
// `format!("{de}-to-{para}")` byte-string every caixa-mesh
// per-`(:de, :para)` `CiliumNetworkPolicy` emitter previously
// inlined at its `labels.insert(LABEL_CONTRATO, …)` call. So a
// future rewire of the composer's internals (multi-hop typed
// edges once the M4 per-edge WIT registry lands, unicode
// arrow-shape rebrand for operator display) reaches every
// consumer through one canonical function-pointer edit.
assert_eq!(contrato_edge_label("cart", "catalog"), "cart-to-catalog");
assert_eq!(contrato_edge_label("cart", "payment"), "cart-to-payment");
}
#[test]
fn contrato_edge_label_threads_separator_between_de_and_para() {
// Composition pin: the composer's shape is
// `de + CONTRATO_EDGE_LABEL_SEPARATOR + para`, so a future
// separator rebrand at [`CONTRATO_EDGE_LABEL_SEPARATOR`]
// reaches the composer through one const-edit and every
// consumer picks up the new encoding by construction. Pin the
// structural equation (not just the byte value) so a future
// reorder of the composer's `format!` argument list (a
// `format!("{para}-{sep}-{de}")` typo mid-refactor) fires here
// rather than silently emitting reversed-direction CNP labels.
let de = "svc-a";
let para = "svc-b";
assert_eq!(
contrato_edge_label(de, para),
format!("{de}{CONTRATO_EDGE_LABEL_SEPARATOR}{para}"),
);
}
#[test]
fn cilium_network_policy_name_matches_inline_aplicacao_de_to_para_encoding() {
// Byte-shape pin: the composer produces the same
// `format!("{aplicacao}-{de}-to-{para}")` byte-string every
// caixa-mesh `cilium_network_policies` per-`(:de, :para)`
// group's `kube_resource_skeleton` `name:` argument previously
// inlined. So a future rewire of the composer's internals
// reaches the CNP renderer through one canonical function-
// pointer edit rather than a coordinated two-site rewrite of
// the [`LABEL_CONTRATO`] labels.insert(...) call and the CNP
// name argument.
assert_eq!(
cilium_network_policy_name("checkout", "cart", "catalog"),
"checkout-cart-to-catalog",
);
assert_eq!(
cilium_network_policy_name("checkout", "cart", "payment"),
"checkout-cart-to-payment",
);
}
#[test]
fn cilium_network_policy_name_composes_on_contrato_edge_label() {
// Composition pin: the CNP name is the parent Aplicacao's
// `:nome` joined to the contrato-edge-label by a canonical `-`
// separator (`format!("{aplicacao}-{edge}")`), so the two
// writer-side helpers close the canonical
// `(LABEL_CONTRATO-value, metadata.name)` per-CNP identity
// pair on one shared edge-encoding source of truth
// ([`CONTRATO_EDGE_LABEL_SEPARATOR`]). Pin the structural
// equation so a future refactor of either composer's internals
// that accidentally desynchronizes the two (a CNP-name
// rebrand landing on `format!("{aplicacao}_{edge}")` while
// the label-value composer stays on `{de}-to-{para}`, or a
// label-composer rebrand landing on `->` while the CNP-name
// composer stays on `-to-`) fires here rather than silently
// orphaning every operator-side grep-by-label query at apply
// time.
let aplicacao = "checkout";
let de = "cart";
let para = "catalog";
let edge = contrato_edge_label(de, para);
assert_eq!(
cilium_network_policy_name(aplicacao, de, para),
format!("{aplicacao}-{edge}"),
);
}
// ── gateway-api-http-route-name lift ────────────────────────────────
#[test]
fn gateway_api_http_route_name_matches_inline_aplicacao_para_encoding() {
// Byte-shape pin: the composer produces the same
// `format!("{aplicacao}-{para}")` byte-string the caixa-mesh
// `gateway_routes` per-`:entrada` `kube_resource_skeleton`
// `name:` argument previously inlined as
// `format!("{}-{}", caixa.nome, entrada.para)`. So a future
// rewire of the composer's internals reaches the HTTPRoute
// renderer through one canonical function-pointer edit rather
// than a hand-agreement between the emitter and every
// test-side probe pinning the expected `<aplicacao>-<para>`
// byte-shape at the HTTPRoute `metadata.name` axis.
assert_eq!(
gateway_api_http_route_name("checkout", "cart"),
"checkout-cart",
);
assert_eq!(gateway_api_http_route_name("orders", "cart"), "orders-cart",);
}
#[test]
fn rendered_file_carries_path_and_contents_fields() {
// Field-shape pin: the canonical [`RenderedFile`] every
// per-target `caixa-<target>` renderer's per-artifact leaf
// resolves through carries exactly the `(path, contents)` pair
// the prior per-crate `BundleFile { path: PathBuf, contents:
// String }` (`caixa-flux`) / `ChartFile { path: PathBuf,
// contents: String }` (`caixa-helm`) clones each carried
// verbatim. A future refactor that adds a per-artifact
// hash / provenance / write-mode discriminator on the record
// must land at the canonical struct definition (this file) —
// the two type aliases at `caixa-flux::BundleFile` /
// `caixa-helm::ChartFile` re-export the canonical unchanged, so
// an addition here reaches both per-target renderers at once,
// and a struct-literal drift that inlines the pre-lift shape
// at either alias trips this pin at caixa-core build time
// rather than surfacing as a divergent per-target renderer's
// record shape far from the source.
let f = RenderedFile {
path: PathBuf::from("Chart.yaml"),
contents: "apiVersion: v2\n".to_string(),
};
assert_eq!(f.path, PathBuf::from("Chart.yaml"));
assert_eq!(f.contents, "apiVersion: v2\n");
}
#[test]
fn rendered_file_derives_pattern_pin() {
// Derive-shape pin: the canonical [`RenderedFile`] carries the
// `Debug + Clone + PartialEq + Eq` derive tuple the two per-
// renderer clones (`caixa-flux::BundleFile` /
// `caixa-helm::ChartFile`) each carried verbatim before the
// lift. `Clone::clone` returns a byte-equal record + the
// `PartialEq::eq` impl returns `true` on the round-trip; a
// future refactor that drops one of the four derives (say,
// removes `PartialEq` on a per-artifact-hash addition) trips
// this pin at caixa-core build time and surfaces the
// per-alias downstream `assert_eq!(bundle_file_a,
// bundle_file_b)` / `assert_eq!(chart_file_a, chart_file_b)`
// navigators in `caixa-flux` / `caixa-helm` — every
// per-alias derive-fed navigator threads through this
// canonical derive tuple by construction.
let f = RenderedFile {
path: PathBuf::from("values.yaml"),
contents: "pleme-computeunit:\n enabled: false\n".to_string(),
};
let clone = f.clone();
assert_eq!(f, clone);
let dbg = format!("{f:?}");
assert!(
dbg.contains("RenderedFile"),
"Debug output must name the canonical type, got: {dbg:?}",
);
}
#[test]
fn rendered_file_new_matches_struct_literal_shape() {
// Constructor pin: [`RenderedFile::new(FILENAME, contents)`]
// (the canonical lifted `impl Into<PathBuf>` / `impl Into<String>`
// inherent constructor every per-target renderer's per-artifact
// leaf now routes through) produces the byte-identical record
// the six prior inline struct-literal call sites (three
// per-artifact leaves in
// [`caixa_helm::render_chart_for_servico_with`],
// three per-CR leaves in [`caixa_flux::cluster_bundle`]) each
// open-coded as `<Xxx>File { path: PathBuf::from(FILENAME_CONST),
// contents: <body> }`. Pin the equation on a
// `HELM_VALUES_YAML_FILENAME`-shaped input so a future rebrand
// of the constructor's internals (a per-artifact hash /
// provenance field addition, an
// [`is_sandboxed_relative_path`] check at construction time
// once per-cluster-writer sandboxing lands) fires here rather
// than silently splitting the per-target renderer's per-CR
// record shape from the substrate-canonical `(path, contents)`
// pair at the caixa-core canonical.
let via_new = RenderedFile::new(HELM_VALUES_YAML_FILENAME, "pleme-computeunit:\n");
let via_literal = RenderedFile {
path: PathBuf::from(HELM_VALUES_YAML_FILENAME),
contents: "pleme-computeunit:\n".to_string(),
};
assert_eq!(via_new, via_literal);
// Peer path-side pin: `impl Into<PathBuf>` accepts a `PathBuf`
// directly (the future per-target renderer surface where the
// path is composed from author input rather than picked from a
// substrate-canonical `&'static str` filename constant) —
// exercised so a drift onto a stricter `&str`-only bound
// trips this pin at caixa-core build time rather than at the
// first per-target renderer that reaches for the wider bound.
let via_new_from_pathbuf = RenderedFile::new(
PathBuf::from(FLUX_HELMRELEASE_YAML_FILENAME),
String::from("kind: HelmRelease\n"),
);
assert_eq!(
via_new_from_pathbuf.path,
PathBuf::from(FLUX_HELMRELEASE_YAML_FILENAME),
);
assert_eq!(via_new_from_pathbuf.contents, "kind: HelmRelease\n");
}
#[test]
fn gateway_api_http_route_name_composes_on_canonical_dash_separator() {
// Composition pin: the HTTPRoute `metadata.name` is the parent
// Aplicacao's `:nome` joined to the `:entrada :para`
// destination Servico's `:nome` by a canonical `-` separator
// (`format!("{aplicacao}-{para}")`) — the same
// "aplicacao-prefixed sub-identity" discipline the peer
// [`cilium_network_policy_name`] composer materializes on the
// sibling per-CR K8s-name-shaped-identity-scalar axis
// ([`format!("{aplicacao}-{edge}")`]). Pin the structural
// equation so a future refactor of either composer's internals
// that accidentally desynchronizes the two (an HTTPRoute-name
// rebrand landing on `format!("{aplicacao}.{para}")` while
// the CNP-name composer stays on `{aplicacao}-{edge}`, or a
// per-Aplicacao-K8s-CR-name shared-separator rebrand landing
// on the CNP-name composer without a coordinated edit here)
// fires here rather than silently splitting the two per-CR
// name-encoding axes across the caixa-mesh renderer.
let aplicacao = "checkout";
let para = "cart";
assert_eq!(
gateway_api_http_route_name(aplicacao, para),
format!("{aplicacao}-{para}"),
);
}
// ── kube_root_map_field lift ────────────────────────────────────────
#[test]
fn kube_root_map_field_reads_top_level_field_mapping() {
// The lift's load-bearing contract: given a Value carrying the
// canonical K8s CR skeleton (top-level `metadata:` identity
// sub-block + top-level `spec:` body sub-block — the two-half
// (identity, body) skeleton every `kube_resource_skeleton`
// emission renders and every controller admits), the composed
// sub-mapping-arity root-axis accessor returns `Some(<mapping>)`
// borrowing into the input Value across the two canonical
// pinned axes ([`KUBE_KEY_METADATA`], [`KUBE_KEY_SPEC`]) plus
// an open-ended peer axis-key (`status`) that the parametric
// `<field>` axis leaves reachable without a fresh helper.
let mut labels_body = serde_yaml::Mapping::new();
labels_body.insert_str_key(
LABEL_APLICACAO,
serde_yaml::Value::String("checkout".into()),
);
let mut metadata_body = serde_yaml::Mapping::new();
metadata_body.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("cart".into()));
metadata_body.insert_str_key(KUBE_KEY_LABELS, serde_yaml::Value::Mapping(labels_body));
let mut spec_body = serde_yaml::Mapping::new();
spec_body.insert_str_key(KUBE_KEY_RULES, serde_yaml::Value::Sequence(vec![]));
let mut status_body = serde_yaml::Mapping::new();
status_body.insert_str_key(
"observedGeneration",
serde_yaml::Value::Number(serde_yaml::Number::from(1_u64)),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata_body));
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec_body));
cr.insert_str_key("status", serde_yaml::Value::Mapping(status_body));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_root_map_field(&value, KUBE_KEY_METADATA).map(serde_yaml::Mapping::len),
Some(2),
"kube_root_map_field must read the top-level `metadata:` \
sub-mapping as a two-entry mapping (name + labels) — the \
pinned peer `kube_metadata` now composes on this axis"
);
assert_eq!(
kube_root_map_field(&value, KUBE_KEY_SPEC).map(serde_yaml::Mapping::len),
Some(1),
"kube_root_map_field must read the top-level `spec:` \
sub-mapping as a one-entry mapping (rules) — the pinned \
peer `kube_spec` now composes on this axis"
);
assert_eq!(
kube_root_map_field(&value, "status").map(serde_yaml::Mapping::len),
Some(1),
"kube_root_map_field must read the top-level `status:` \
sub-mapping as a one-entry mapping — the parametric \
`<field>` axis stays open-ended so the future caixa-\
operator `status.observedGeneration` navigation reaches \
the same helper with a different key"
);
}
#[test]
fn kube_root_map_field_returns_none_when_field_absent() {
// Short-circuit arm 1: the requested top-level `<field>:` axis-
// key is absent from the outer Mapping (a legally-omitted top-
// level sub-block per the K8s API-machinery — a `List`-shaped
// document that carries no per-item `metadata:` header, or a
// bare status-scoped document that carries no `spec:` body).
// The helper folds this to `None` so the readback stays a
// total function.
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(
KUBE_KEY_KIND,
serde_yaml::Value::String("CiliumNetworkPolicy".into()),
);
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_root_map_field(&value, KUBE_KEY_METADATA),
None,
"kube_root_map_field must short-circuit to None when the \
requested top-level axis-key is absent from the CR outer \
Mapping"
);
assert_eq!(
kube_root_map_field(&value, KUBE_KEY_SPEC),
None,
"kube_root_map_field must short-circuit to None on both \
canonical pinned axes when either is absent — the two \
canonical pinned peers `kube_metadata` / `kube_spec` \
inherit this None-arm through composition"
);
}
#[test]
fn kube_root_map_field_returns_none_when_field_carries_non_mapping_type() {
// Short-circuit arm 2: the requested top-level `<field>:` value
// is present but carries a non-Mapping YAML type (a schema-
// invalid top-level sub-block per the K8s API-machinery
// contract that pins both `metadata:` and `spec:` as Mappings,
// tolerated here as `None` so the readback stays a total
// function).
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(
KUBE_KEY_METADATA,
serde_yaml::Value::String("this-should-be-a-mapping".into()),
);
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Sequence(vec![]));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_root_map_field(&value, KUBE_KEY_METADATA),
None,
"kube_root_map_field must short-circuit to None when the \
top-level `metadata:` axis-key carries a non-Mapping YAML \
type (here: a String) — the trailing `.as_mapping()` \
shape-gate closure inside the helper folds this to None"
);
assert_eq!(
kube_root_map_field(&value, KUBE_KEY_SPEC),
None,
"kube_root_map_field must short-circuit to None when the \
top-level `spec:` axis-key carries a non-Mapping YAML \
type (here: a Sequence) — the same shape-gate short-\
circuit closes both canonical pinned axes"
);
}
#[test]
fn kube_root_map_field_short_circuits_when_outer_value_is_non_mapping() {
// Third short-circuit: the outer `value` itself carries a
// non-Mapping YAML type. Unlike the sub-`metadata:` / sub-
// `spec:` peers which need an explicit `.as_mapping()` gate
// on the outer sub-block, the root-axis variant needs no
// explicit outer shape gate — [`serde_yaml::Value::get`]
// already short-circuits to None on non-Mapping outer values.
// Pin the None arm across the four non-Mapping outer shapes
// (Null, Bool, Number, String, Sequence) so a future
// serde_yaml revision that changes the `Value::get` outer-
// shape contract lights up here rather than silently
// desynchronizing the root axis from its sub-axis peers.
let outer_shapes = [
serde_yaml::Value::Null,
serde_yaml::Value::Bool(true),
serde_yaml::Value::Number(serde_yaml::Number::from(42_u64)),
serde_yaml::Value::String("not-a-mapping".into()),
serde_yaml::Value::Sequence(vec![]),
];
for outer in outer_shapes {
assert_eq!(
kube_root_map_field(&outer, KUBE_KEY_METADATA),
None,
"kube_root_map_field must short-circuit to None on \
every non-Mapping outer Value shape — Value::get \
handles the outer-shape gate the sub-axis peers \
close explicitly"
);
}
}
#[test]
fn kube_metadata_composes_on_lifted_kube_root_map_field_accessor() {
// Composition pin: after the lift, `kube_metadata(value)` must
// resolve exactly the same `Option<&Mapping>` as
// `kube_root_map_field(value, KUBE_KEY_METADATA)` across every
// routed short-circuit arm. Structural mirror of the sibling
// `kube_metadata_str_field` recomposition pin on
// `kube_metadata_field` — the composition-link between a
// pinned per-axis accessor and its parametric substrate
// primitive is a substrate invariant, not a per-call-site
// coincidence.
let mut metadata_body = serde_yaml::Mapping::new();
metadata_body.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("cart".into()));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata_body));
let with_metadata = serde_yaml::Value::Mapping(cr);
let without_metadata = serde_yaml::Value::Mapping(serde_yaml::Mapping::new());
let non_mapping_metadata = {
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::String("bad".into()));
serde_yaml::Value::Mapping(cr)
};
for value in [&with_metadata, &without_metadata, &non_mapping_metadata] {
assert_eq!(
kube_metadata(value),
kube_root_map_field(value, KUBE_KEY_METADATA),
"kube_metadata must resolve identically to \
kube_root_map_field(value, KUBE_KEY_METADATA) across \
every routed short-circuit arm — the composition-link \
between the pinned peer and its parametric primitive \
is a substrate invariant"
);
}
}
#[test]
fn kube_spec_composes_on_lifted_kube_root_map_field_accessor() {
// Composition pin: after the lift, `kube_spec(value)` must
// resolve exactly the same `Option<&Mapping>` as
// `kube_root_map_field(value, KUBE_KEY_SPEC)` across every
// routed short-circuit arm. Structural mirror of the sibling
// `kube_metadata_composes_on_lifted_kube_root_map_field_accessor`
// pin on the peer canonical top-level sub-mapping-arity axis.
let mut spec_body = serde_yaml::Mapping::new();
spec_body.insert_str_key(KUBE_KEY_RULES, serde_yaml::Value::Sequence(vec![]));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec_body));
let with_spec = serde_yaml::Value::Mapping(cr);
let without_spec = serde_yaml::Value::Mapping(serde_yaml::Mapping::new());
let non_mapping_spec = {
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Sequence(vec![]));
serde_yaml::Value::Mapping(cr)
};
for value in [&with_spec, &without_spec, &non_mapping_spec] {
assert_eq!(
kube_spec(value),
kube_root_map_field(value, KUBE_KEY_SPEC),
"kube_spec must resolve identically to \
kube_root_map_field(value, KUBE_KEY_SPEC) across \
every routed short-circuit arm — the composition-link \
between the pinned peer and its parametric primitive \
is a substrate invariant"
);
}
}
#[test]
fn kube_root_map_field_matches_prior_inline_chain() {
// Byte-equivalence pin: the lifted `kube_root_map_field`
// helper resolves exactly the same `Option<&Mapping>` as the
// two-hop `value.get(field).and_then(|v| v.as_mapping())`
// inline chain the two pinned peers (`kube_metadata`,
// `kube_spec`) previously each carried a copy of. A future
// refactor that mistakenly desynchronizes the lifted primitive
// from that inline shape lights up here rather than silently
// splitting the two pinned peers back apart across the
// substrate.
let mut metadata_body = serde_yaml::Mapping::new();
metadata_body.insert_str_key(
KUBE_KEY_LABELS,
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
);
let mut spec_body = serde_yaml::Mapping::new();
spec_body.insert_str_key(KUBE_KEY_RULES, serde_yaml::Value::Sequence(vec![]));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata_body));
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec_body));
let value = serde_yaml::Value::Mapping(cr);
for field in [KUBE_KEY_METADATA, KUBE_KEY_SPEC, "status", "data"] {
assert_eq!(
kube_root_map_field(&value, field),
value.get(field).and_then(|v| v.as_mapping()),
"kube_root_map_field must be byte-equivalent to the \
prior inline `value.get(field).and_then(|v| \
v.as_mapping())` chain across the pinned canonical \
axes and the open-ended parametric axes — any \
divergence indicates the composition drifted"
);
}
}
// ── kube_root_seq_field lift ────────────────────────────────────────
#[test]
fn kube_root_seq_field_reads_top_level_field_sequence() {
// The lift's load-bearing contract: given a Value carrying the
// canonical fleet-programs values.yaml shape (top-level
// `programs:` sequence — the bare-values.yaml shape every
// [`caixa_flux::upsert_into_programs_yaml`] round-trip pin
// navigates), the composed sequence-arity root-axis accessor
// returns `Some(<sequence>)` borrowing into the input Value
// across the canonical pinned axis
// ([`FLEET_PROGRAMS_KEY_PROGRAMS`]) plus an open-ended peer
// axis-key (`items`, mirroring a `List`-shaped multi-doc
// envelope) that the parametric `<field>` axis leaves reachable
// without a fresh helper. Structural mirror of the sibling
// `kube_root_map_field_reads_top_level_field_mapping` pin on
// the peer sub-mapping-arity root axis.
let mut program = serde_yaml::Mapping::new();
program.insert_str_key(
FLEET_PROGRAMS_KEY_NAME,
serde_yaml::Value::String("hello-rio".into()),
);
let programs = serde_yaml::Value::Sequence(vec![serde_yaml::Value::Mapping(program)]);
let items = serde_yaml::Value::Sequence(vec![]);
let mut values = serde_yaml::Mapping::new();
values.insert_str_key(FLEET_PROGRAMS_KEY_PROGRAMS, programs);
values.insert_str_key("items", items);
let value = serde_yaml::Value::Mapping(values);
assert_eq!(
kube_root_seq_field(&value, FLEET_PROGRAMS_KEY_PROGRAMS).map(Vec::len),
Some(1),
"kube_root_seq_field must read the top-level `programs:` \
sequence as a one-entry sequence — the canonical fleet-\
programs values.yaml pinned axis that the two \
`caixa_flux::upsert_into_programs_yaml` round-trip pins \
now compose on"
);
assert_eq!(
kube_root_seq_field(&value, "items").map(Vec::len),
Some(0),
"kube_root_seq_field must read the top-level `items:` \
sequence as an empty sequence when the emitter writes a \
legally-empty block — the parametric `<field>` axis stays \
open-ended so a future `List`-shaped multi-doc envelope's \
`items:` navigation reaches the same helper with a \
different key"
);
}
#[test]
fn kube_root_seq_field_returns_none_when_field_absent() {
// Short-circuit arm 1: the requested top-level `<field>:`
// axis-key is absent from the outer Mapping (a legally-omitted
// top-level sub-block — e.g. a values.yaml document that
// carries no `programs:` sequence yet, or a bare status-scoped
// document that carries no root-level sequence axis). The
// helper folds this to `None` so the readback stays a total
// function. Structural mirror of the sibling
// `kube_root_map_field_returns_none_when_field_absent` pin on
// the peer sub-mapping-arity root axis.
let mut values = serde_yaml::Mapping::new();
values.insert_str_key(HELM_VALUES_KEY_ENABLED, serde_yaml::Value::Bool(true));
let value = serde_yaml::Value::Mapping(values);
assert_eq!(
kube_root_seq_field(&value, FLEET_PROGRAMS_KEY_PROGRAMS),
None,
"kube_root_seq_field must short-circuit to None when the \
requested top-level axis-key is absent from the outer \
Mapping"
);
}
#[test]
fn kube_root_seq_field_returns_none_when_field_carries_non_sequence_type() {
// Short-circuit arm 2: the requested top-level `<field>:`
// value is present but carries a non-sequence YAML type (a
// schema-invalid top-level sub-block per the fleet-programs
// values contract that pins `programs:` as a Sequence, but
// tolerated here as `None` so the readback stays a total
// function). Pin the None-arm across representative non-
// sequence shapes so a future refactor that reaches for
// `.as_sequence().unwrap()` (which would panic on a scalar
// axis-value) is a test-visible break, not a runtime
// regression at the first schema-invalid document the reader
// sees.
for non_sequence in [
serde_yaml::Value::Null,
serde_yaml::Value::Bool(false),
serde_yaml::Value::Number(serde_yaml::Number::from(0_u64)),
serde_yaml::Value::String("programs-as-string".into()),
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
] {
let mut values = serde_yaml::Mapping::new();
values.insert_str_key(FLEET_PROGRAMS_KEY_PROGRAMS, non_sequence.clone());
let value = serde_yaml::Value::Mapping(values);
assert_eq!(
kube_root_seq_field(&value, FLEET_PROGRAMS_KEY_PROGRAMS),
None,
"kube_root_seq_field must short-circuit to None when \
the top-level `programs:` axis-key carries a non-\
sequence YAML type ({non_sequence:?}) — the trailing \
`.as_sequence()` shape-gate closure inside the helper \
folds this to None"
);
}
}
#[test]
fn kube_root_seq_field_short_circuits_when_outer_value_is_non_mapping() {
// Third short-circuit: the outer `value` itself carries a non-
// Mapping YAML type. Unlike the sub-`metadata:` / sub-`spec:`
// seq peers which need an explicit `.as_mapping()` gate on the
// outer sub-block, the root-axis variant needs no explicit
// outer shape gate — [`serde_yaml::Value::get`] already short-
// circuits to None on non-Mapping outer values. Pin the None-
// arm across the five non-Mapping outer shapes so a future
// serde_yaml revision that changes the `Value::get` outer-
// shape contract lights up here rather than silently
// desynchronizing the root axis from its sub-axis peers.
// Structural mirror of the sibling
// `kube_root_map_field_short_circuits_when_outer_value_is_non_mapping`
// pin on the peer sub-mapping-arity root axis.
let outer_shapes = [
serde_yaml::Value::Null,
serde_yaml::Value::Bool(true),
serde_yaml::Value::Number(serde_yaml::Number::from(42_u64)),
serde_yaml::Value::String("not-a-mapping".into()),
serde_yaml::Value::Sequence(vec![]),
];
for outer in outer_shapes {
assert_eq!(
kube_root_seq_field(&outer, FLEET_PROGRAMS_KEY_PROGRAMS),
None,
"kube_root_seq_field must short-circuit to None on \
every non-Mapping outer Value shape — Value::get \
handles the outer-shape gate the sub-axis peers \
close explicitly"
);
}
}
#[test]
fn kube_root_seq_field_matches_prior_inline_chain() {
// Byte-equivalence pin: the lifted `kube_root_seq_field`
// helper resolves exactly the same `Option<&Sequence>` as the
// two-hop `value.get(field).and_then(|v| v.as_sequence())`
// inline chain the two [`caixa_flux::upsert_into_programs_yaml`]
// round-trip test pins previously each carried inline. A
// future refactor that mistakenly desynchronizes the lifted
// primitive from that inline shape lights up here rather than
// silently splitting the two pinned peers back apart across
// the substrate. Structural mirror of the sibling
// `kube_root_map_field_matches_prior_inline_chain` pin on the
// peer sub-mapping-arity root axis.
let mut program = serde_yaml::Mapping::new();
program.insert_str_key(
FLEET_PROGRAMS_KEY_NAME,
serde_yaml::Value::String("hello-rio".into()),
);
let mut values = serde_yaml::Mapping::new();
values.insert_str_key(
FLEET_PROGRAMS_KEY_PROGRAMS,
serde_yaml::Value::Sequence(vec![serde_yaml::Value::Mapping(program)]),
);
values.insert_str_key("items", serde_yaml::Value::Sequence(vec![]));
let value = serde_yaml::Value::Mapping(values);
for field in [FLEET_PROGRAMS_KEY_PROGRAMS, "items", "documents"] {
assert_eq!(
kube_root_seq_field(&value, field),
value.get(field).and_then(|v| v.as_sequence()),
"kube_root_seq_field must be byte-equivalent to the \
prior inline `value.get(field).and_then(|v| \
v.as_sequence())` chain across the pinned canonical \
axis and the open-ended parametric axes — any \
divergence indicates the composition drifted"
);
}
}
// ── kube_root_field lift ────────────────────────────────────────────
#[test]
fn kube_root_field_reads_top_level_field_value() {
// Positive-arm pin: the lifted `kube_root_field` accessor
// resolves the top-level `<field>:` sub-Value on a K8s CR
// YAML document without any trailing shape gate — the same
// scalar-Value substrate primitive `kube_metadata_field` and
// `kube_spec_field` peer at their respective sub-axes.
let mut metadata_body = serde_yaml::Mapping::new();
metadata_body.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("hello-rio".into()));
let mut spec_body = serde_yaml::Mapping::new();
spec_body.insert_str_key(KUBE_KEY_RULES, serde_yaml::Value::Sequence(vec![]));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(
KUBE_KEY_API_VERSION,
serde_yaml::Value::String("mesh.pleme.io/v1alpha1".into()),
);
cr.insert_str_key(KUBE_KEY_KIND, serde_yaml::Value::String("Aplicacao".into()));
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata_body));
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec_body));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_root_field(&value, KUBE_KEY_API_VERSION).and_then(|v| v.as_str()),
Some("mesh.pleme.io/v1alpha1"),
"root-axis apiVersion sub-Value must round-trip"
);
assert_eq!(
kube_root_field(&value, KUBE_KEY_KIND).and_then(|v| v.as_str()),
Some("Aplicacao"),
"root-axis kind sub-Value must round-trip"
);
assert!(
kube_root_field(&value, KUBE_KEY_METADATA)
.and_then(|v| v.as_mapping())
.is_some(),
"root-axis metadata sub-Value must resolve as a Mapping"
);
assert!(
kube_root_field(&value, KUBE_KEY_SPEC)
.and_then(|v| v.as_mapping())
.is_some(),
"root-axis spec sub-Value must resolve as a Mapping"
);
}
#[test]
fn kube_root_field_returns_none_when_field_absent() {
// None-arm pin (absent axis-key): a legally-omitted top-level
// sub-block (e.g. a bare status-scoped document with no
// `spec:`) short-circuits to `None` via the underlying
// `Value::get` none-arm — the readback stays a total function.
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_KIND, serde_yaml::Value::String("Aplicacao".into()));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_root_field(&value, KUBE_KEY_SPEC),
None,
"absent top-level `spec:` axis-key must short-circuit to None"
);
assert_eq!(
kube_root_field(&value, "status"),
None,
"absent open-ended top-level axis-key must short-circuit to None"
);
}
#[test]
fn kube_root_field_short_circuits_when_outer_value_is_non_mapping() {
// None-arm pin (non-Mapping outer Value): the underlying
// `serde_yaml::Value::get` already short-circuits to `None`
// on any non-Mapping outer value, so the root-axis
// primitive stays a total function without an explicit
// outer shape gate. Structural mirror of the same
// short-circuit behavior the two shape-gated peers
// `kube_root_map_field` and `kube_root_seq_field` pin.
for outer in [
serde_yaml::Value::Null,
serde_yaml::Value::Bool(true),
serde_yaml::Value::String("scalar".into()),
serde_yaml::Value::Sequence(vec![]),
] {
assert_eq!(
kube_root_field(&outer, KUBE_KEY_KIND),
None,
"non-Mapping outer Value must short-circuit to None on any \
top-level axis-key readback — the readback stays a total \
function"
);
}
}
#[test]
fn kube_root_field_matches_prior_inline_chain() {
// Byte-equivalence pin: the lifted `kube_root_field` accessor
// resolves exactly the same `Option<&Value>` as the one-hop
// `value.get(field)` inline chain each of the three shape-
// gated peers (`kube_root_str_field`, `kube_root_map_field`,
// `kube_root_seq_field`) previously each carried a copy of.
// A future refactor that mistakenly desynchronizes the
// lifted primitive from that inline shape lights up here
// rather than silently splitting the three pinned shape-
// gated peers back apart across the substrate. Structural
// mirror of the sibling
// `kube_metadata_field_matches_prior_inline_chain` /
// `kube_spec_field_matches_prior_inline_chain` pins on the
// sub-axis scalar-Value accessor primitives.
let mut metadata_body = serde_yaml::Mapping::new();
metadata_body.insert_str_key(KUBE_KEY_NAME, serde_yaml::Value::String("hello-rio".into()));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(
KUBE_KEY_API_VERSION,
serde_yaml::Value::String("mesh.pleme.io/v1alpha1".into()),
);
cr.insert_str_key(KUBE_KEY_KIND, serde_yaml::Value::String("Aplicacao".into()));
cr.insert_str_key(KUBE_KEY_METADATA, serde_yaml::Value::Mapping(metadata_body));
cr.insert_str_key(
FLEET_PROGRAMS_KEY_PROGRAMS,
serde_yaml::Value::Sequence(vec![]),
);
let value = serde_yaml::Value::Mapping(cr);
for field in [
KUBE_KEY_API_VERSION,
KUBE_KEY_KIND,
KUBE_KEY_METADATA,
FLEET_PROGRAMS_KEY_PROGRAMS,
KUBE_KEY_SPEC,
"status",
] {
assert_eq!(
kube_root_field(&value, field),
value.get(field),
"kube_root_field must be byte-equivalent to the prior \
inline `value.get(field)` chain across the pinned \
canonical axes and the open-ended parametric axes — \
any divergence indicates the composition drifted"
);
}
}
#[test]
fn kube_root_str_field_recomposes_on_lifted_kube_root_field() {
// Recomposition pin: after the lift, `kube_root_str_field`
// composes as `kube_root_field(v, f).and_then(as_str)` and
// must resolve pairwise-identical to that composition across
// every axis. Structural mirror of the sibling
// `kube_metadata_str_field_recomposes_on_lifted_kube_metadata_field`
// / `kube_spec_str_field_composes_on_lifted_kube_spec_field_accessor`
// pins on the sub-axis peers.
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(
KUBE_KEY_API_VERSION,
serde_yaml::Value::String("mesh.pleme.io/v1alpha1".into()),
);
cr.insert_str_key(KUBE_KEY_KIND, serde_yaml::Value::String("Aplicacao".into()));
cr.insert_str_key(
KUBE_KEY_METADATA,
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
);
let value = serde_yaml::Value::Mapping(cr);
for field in [
KUBE_KEY_API_VERSION,
KUBE_KEY_KIND,
KUBE_KEY_METADATA,
"status",
] {
assert_eq!(
kube_root_str_field(&value, field),
kube_root_field(&value, field).and_then(|v| v.as_str()),
"kube_root_str_field must recompose exactly onto \
`kube_root_field(v, field).and_then(|v| v.as_str())` — \
the composed peer and the primitive-plus-shape-gate \
composition must resolve pairwise-identical"
);
}
}
#[test]
fn kube_root_map_field_recomposes_on_lifted_kube_root_field() {
// Recomposition pin: after the lift, `kube_root_map_field`
// composes as `kube_root_field(v, f).and_then(as_mapping)`
// and must resolve pairwise-identical to that composition
// across every axis. Structural mirror of the sibling
// `kube_metadata_map_field_composes_on_lifted_kube_metadata_field_accessor`
// pin on the sub-`metadata:` axis.
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(
KUBE_KEY_METADATA,
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
);
cr.insert_str_key(
KUBE_KEY_SPEC,
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
);
cr.insert_str_key(KUBE_KEY_KIND, serde_yaml::Value::String("Aplicacao".into()));
let value = serde_yaml::Value::Mapping(cr);
for field in [KUBE_KEY_METADATA, KUBE_KEY_SPEC, KUBE_KEY_KIND, "status"] {
assert_eq!(
kube_root_map_field(&value, field),
kube_root_field(&value, field).and_then(|v| v.as_mapping()),
"kube_root_map_field must recompose exactly onto \
`kube_root_field(v, field).and_then(|v| v.as_mapping())` \
— the composed peer and the primitive-plus-shape-gate \
composition must resolve pairwise-identical"
);
}
}
#[test]
fn kube_root_seq_field_recomposes_on_lifted_kube_root_field() {
// Recomposition pin: after the lift, `kube_root_seq_field`
// composes as `kube_root_field(v, f).and_then(as_sequence)`
// and must resolve pairwise-identical to that composition
// across every axis. Structural mirror of the sibling
// `kube_metadata_seq_field_composes_on_lifted_kube_metadata_field_accessor`
// pin on the sub-`metadata:` axis.
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(
FLEET_PROGRAMS_KEY_PROGRAMS,
serde_yaml::Value::Sequence(vec![]),
);
cr.insert_str_key(KUBE_KEY_KIND, serde_yaml::Value::String("Aplicacao".into()));
let value = serde_yaml::Value::Mapping(cr);
for field in [
FLEET_PROGRAMS_KEY_PROGRAMS,
KUBE_KEY_KIND,
"items",
"documents",
] {
assert_eq!(
kube_root_seq_field(&value, field),
kube_root_field(&value, field).and_then(|v| v.as_sequence()),
"kube_root_seq_field must recompose exactly onto \
`kube_root_field(v, field).and_then(|v| v.as_sequence())` \
— the composed peer and the primitive-plus-shape-gate \
composition must resolve pairwise-identical"
);
}
}
// ── kube_match_labels lift ─────────────────────────────────────────
#[test]
fn kube_match_labels_reads_selector_match_labels_sub_mapping() {
// The lift's load-bearing contract: given a Value carrying a
// top-level `matchLabels: { <label>: <str>, ... }` sub-block
// (every LabelSelector-shaped Value the emit-side
// [`label_selector`] / [`pleme_program_selector`] /
// [`pleme_program_in_aplicacao_selector`] renders), the helper
// returns Some(&Mapping) borrowing into the input Value. Pinned
// because the caixa-mesh per-CNP `endpointSelector.matchLabels`
// + `spec.ingress[0].fromEndpoints[0].matchLabels` selector-
// readback sites reach through this exact sub-mapping accessor
// for per-label `.get(<label>)` probes + `.len()` cardinality
// gates; a drift on the borrowed-mapping contract would
// silently regress every routed per-selector selector-value
// probe.
let mut selector = serde_yaml::Mapping::new();
selector.insert_str_key(
LABEL_APLICACAO,
serde_yaml::Value::String("checkout".into()),
);
selector.insert_str_key(LABEL_PROGRAM, serde_yaml::Value::String("cart".into()));
let mut outer = serde_yaml::Mapping::new();
outer.insert_str_key(
KUBE_KEY_MATCH_LABELS,
serde_yaml::Value::Mapping(selector.clone()),
);
let value = serde_yaml::Value::Mapping(outer);
assert_eq!(
kube_match_labels(&value),
Some(&selector),
"kube_match_labels must read the top-level `matchLabels:` \
sub-mapping — the caixa-mesh per-CNP selector-value \
readback sites reach through this axis for per-label \
lookup + cardinality gates"
);
}
#[test]
fn kube_match_labels_returns_none_when_match_labels_sub_block_absent() {
// The two-way vacuous-None short-circuit's first arm: an outer
// Value that legally omits the `matchLabels:` sub-block (the
// `matchExpressions:`-only arm of the K8s LabelSelector schema
// — [`KUBE_KEY_MATCH_LABELS`] docstring at
// caixa-core/src/render.rs:10120 pins matchLabels +
// matchExpressions as an OR-composed pair, either arm may be
// omitted) short-circuits at the first hop through the
// underlying `.get(KUBE_KEY_MATCH_LABELS)`. Pin the None-arm
// so a future refactor that reaches for `.get(...).unwrap()`
// (which would panic on a matchExpressions-only selector) is a
// test-visible break. Peer of the sibling
// `kube_metadata_labels_returns_none_when_labels_sub_block_absent`
// pin on the CR-side labels axis.
let value = serde_yaml::Value::Mapping(serde_yaml::Mapping::new());
assert_eq!(
kube_match_labels(&value),
None,
"kube_match_labels must short-circuit to None when the \
`matchLabels:` sub-block is absent — the prior inline \
two-hop chain's first `.get(KUBE_KEY_MATCH_LABELS)` hop \
returned None here"
);
// Also verify the shape on non-Mapping outer Value shapes.
for shape in [
serde_yaml::Value::Null,
serde_yaml::Value::String("scalar".into()),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::Number(0.into()),
serde_yaml::Value::Bool(false),
] {
assert_eq!(
kube_match_labels(&shape),
None,
"kube_match_labels({shape:?}) must return None on \
non-Mapping outer shapes — the prior inline chain's \
`.get(KUBE_KEY_MATCH_LABELS)` hop yields None on \
every non-Mapping Value, and the lift must preserve \
that contract"
);
}
}
#[test]
fn kube_match_labels_returns_none_when_match_labels_carries_non_mapping_type() {
// The two-way vacuous-None short-circuit's second arm: a
// present-but-non-Mapping `matchLabels:` value (a schema-
// invalid selector shape per the K8s API-machinery's
// LabelSelector contract, which pins the block as
// `map[string]string`, but tolerated here as None so the
// readback stays a total function). Pin the trailing shape
// gate so a future refactor that reaches for
// `.as_mapping().unwrap()` (which would panic on a scalar
// matchLabels-value) is a test-visible break, not a runtime
// regression at the first schema-invalid selector-Value the
// reader sees. Peer of the sibling
// `kube_metadata_labels_returns_none_when_labels_carries_non_mapping_type`
// pin on the CR-side labels axis.
for non_mapping in [
serde_yaml::Value::Null,
serde_yaml::Value::Number(42.into()),
serde_yaml::Value::Bool(true),
serde_yaml::Value::Sequence(vec![]),
serde_yaml::Value::String("matchLabels-as-string".into()),
] {
let mut outer = serde_yaml::Mapping::new();
outer.insert_str_key(KUBE_KEY_MATCH_LABELS, non_mapping.clone());
let value = serde_yaml::Value::Mapping(outer);
assert_eq!(
kube_match_labels(&value),
None,
"kube_match_labels must return None when `matchLabels:` \
carries a non-Mapping YAML type ({non_mapping:?}) — \
the trailing `.and_then(|m| m.as_mapping())` shape \
gate short-circuited here on the prior inline chain, \
and every routed caller depends on that None-arm to \
keep the readback total"
);
}
}
#[test]
fn kube_match_labels_matches_prior_inline_chain() {
// Cross-check the helper's output byte-for-byte against the
// prior inline two-hop chain the routed caller previously
// carried (`value.get(KUBE_KEY_MATCH_LABELS).and_then(|m|
// m.as_mapping())`). A drift between the helper's return and
// the inline chain would silently regress the caixa-mesh
// per-CNP selector-readback sites' `.get(<label>)` +
// `.len()` probes — pin the byte-equivalence so the helper
// remains a drop-in replacement for the routed sites' prior
// two-line block. Peer of the sibling
// `kube_metadata_labels_matches_prior_inline_chain` pin on
// the CR-side labels axis.
let mut selector = serde_yaml::Mapping::new();
selector.insert_str_key(LABEL_PROGRAM, serde_yaml::Value::String("catalog".into()));
selector.insert_str_key(
LABEL_APLICACAO,
serde_yaml::Value::String("checkout".into()),
);
let mut outer = serde_yaml::Mapping::new();
outer.insert_str_key(KUBE_KEY_MATCH_LABELS, serde_yaml::Value::Mapping(selector));
let value = serde_yaml::Value::Mapping(outer);
let via_helper = kube_match_labels(&value);
let via_inline = value
.get(KUBE_KEY_MATCH_LABELS)
.and_then(|m| m.as_mapping());
assert_eq!(
via_helper, via_inline,
"kube_match_labels(&value) must return the byte-identical \
`Option<&Mapping>` the prior inline two-hop chain \
`value.get(KUBE_KEY_MATCH_LABELS).and_then(as_mapping)` \
produced — the lift is a drop-in for the routed sites' \
prior selector-readback path"
);
}
#[test]
fn kube_match_labels_recomposes_on_lifted_kube_root_map_field() {
// Recomposition pin: after the lift, `kube_match_labels`
// composes as `kube_root_map_field(v, KUBE_KEY_MATCH_LABELS)`
// and must resolve pairwise-identical to that composition
// across every selector shape. Structural mirror of the way
// sibling `kube_metadata_labels` composes on
// `kube_metadata_map_field(_, KUBE_KEY_LABELS)` — the
// selector-side sub-mapping accessor stands on the parametric
// sub-mapping-arity primitive with its axis-key pinned inside.
let mut selector = serde_yaml::Mapping::new();
selector.insert_str_key(LABEL_PROGRAM, serde_yaml::Value::String("cart".into()));
let mut outer = serde_yaml::Mapping::new();
outer.insert_str_key(KUBE_KEY_MATCH_LABELS, serde_yaml::Value::Mapping(selector));
let value = serde_yaml::Value::Mapping(outer);
assert_eq!(
kube_match_labels(&value),
kube_root_map_field(&value, KUBE_KEY_MATCH_LABELS),
"kube_match_labels must recompose exactly onto \
`kube_root_map_field(v, KUBE_KEY_MATCH_LABELS)` — the \
composed peer and the primitive-plus-pinned-axis \
composition must resolve pairwise-identical, mirroring \
the way sibling `kube_metadata_labels` composes on \
`kube_metadata_map_field(_, KUBE_KEY_LABELS)` on the \
CR-side labels axis"
);
// Also verify the None arms recompose pairwise.
for empty in [
serde_yaml::Value::Mapping(serde_yaml::Mapping::new()),
serde_yaml::Value::Null,
serde_yaml::Value::String("scalar".into()),
] {
assert_eq!(
kube_match_labels(&empty),
kube_root_map_field(&empty, KUBE_KEY_MATCH_LABELS),
"kube_match_labels({empty:?}) must recompose pairwise \
to `kube_root_map_field({empty:?}, \
KUBE_KEY_MATCH_LABELS)` on every vacuous-None arm"
);
}
}
// ── kube_match_label + kube_match_label_is lift ─────────────────────
fn match_labels_selector(entries: &[(&str, &str)]) -> serde_yaml::Value {
// Test-side selector builder: `{matchLabels: {<k>: <v>, ...}}`
// — the canonical outer shape every LabelSelector-consumer in
// caixa-mesh reads via [`kube_match_labels`] / [`kube_match_label`]
// / [`kube_match_label_is`].
let mut inner = serde_yaml::Mapping::new();
for (k, v) in entries {
inner.insert_str_key(k, serde_yaml::Value::String((*v).into()));
}
let mut outer = serde_yaml::Mapping::new();
outer.insert_str_key(KUBE_KEY_MATCH_LABELS, serde_yaml::Value::Mapping(inner));
serde_yaml::Value::Mapping(outer)
}
#[test]
fn kube_match_label_reads_per_label_string_scalar_under_match_labels() {
// Pin the load-bearing three-hop contract: given a
// LabelSelector-shaped Value carrying `matchLabels.<label>` as
// a string scalar, the helper returns Some(<label-value>) — the
// per-label surface every caixa-mesh per-selector per-label
// readback site keys off (`fromEndpoints[0].matchLabels
// .LABEL_APLICACAO` on the aplicacao-scope pin,
// `endpointSelector.matchLabels.LABEL_PROGRAM` on the
// destination program-name pin). A drift on the returned scalar
// would silently regress every routed selector-value probe.
// Peer of the sibling `kube_metadata_label_reads_per_label_...`
// pin on the CR-side labels axis.
let selector =
match_labels_selector(&[(LABEL_APLICACAO, "checkout"), (LABEL_PROGRAM, "cart")]);
assert_eq!(
kube_match_label(&selector, LABEL_APLICACAO),
Some("checkout"),
"kube_match_label must read the `matchLabels.LABEL_APLICACAO` \
string-scalar — the caixa-mesh per-CNP fromEndpoints \
aplicacao-scope pin routes through this axis"
);
assert_eq!(
kube_match_label(&selector, LABEL_PROGRAM),
Some("cart"),
"kube_match_label must read the `matchLabels.LABEL_PROGRAM` \
string-scalar — the caixa-mesh per-CNP endpointSelector \
program-name pin routes through this axis"
);
}
#[test]
fn kube_match_label_returns_none_on_every_short_circuit_arm() {
// The four-way vacuous-None short-circuit: (1) outer
// `matchLabels:` sub-block absent (the matchExpressions-only
// arm of the K8s LabelSelector schema), (2) `matchLabels:`
// present but non-Mapping (a schema-invalid selector shape),
// (3) requested `<label>` key absent under the selector, (4)
// requested `<label>` key present but non-string. Pin every arm
// so a future refactor that reaches for a `.unwrap()` on any
// intermediate is a test-visible break. Peer of the sibling
// `kube_metadata_label`'s three-way short-circuit — the fourth
// arm here is the outer sub-block absence which the CR-side
// sibling opens as a shape-gate arm through its
// `kube_metadata_map_field` pre-navigation.
// Arm 1: outer `matchLabels:` absent.
let bare = serde_yaml::Value::Mapping(serde_yaml::Mapping::new());
assert_eq!(
kube_match_label(&bare, LABEL_APLICACAO),
None,
"kube_match_label must short-circuit to None when the \
outer `matchLabels:` sub-block is absent"
);
// Arm 2: `matchLabels:` present but non-Mapping.
let mut outer = serde_yaml::Mapping::new();
outer.insert_str_key(
KUBE_KEY_MATCH_LABELS,
serde_yaml::Value::String("bogus".into()),
);
let non_map = serde_yaml::Value::Mapping(outer);
assert_eq!(
kube_match_label(&non_map, LABEL_APLICACAO),
None,
"kube_match_label must short-circuit to None when the \
`matchLabels:` value carries a non-Mapping YAML type"
);
// Arm 3: `<label>` key absent from a present `matchLabels:`.
let selector = match_labels_selector(&[(LABEL_PROGRAM, "cart")]);
assert_eq!(
kube_match_label(&selector, LABEL_APLICACAO),
None,
"kube_match_label must short-circuit to None when the \
requested `<label>` key is absent from a present \
`matchLabels:` sub-mapping"
);
// Arm 4: `<label>` present but non-string.
let mut inner = serde_yaml::Mapping::new();
inner.insert_str_key(LABEL_APLICACAO, serde_yaml::Value::Number(42.into()));
let mut outer = serde_yaml::Mapping::new();
outer.insert_str_key(KUBE_KEY_MATCH_LABELS, serde_yaml::Value::Mapping(inner));
let non_string = serde_yaml::Value::Mapping(outer);
assert_eq!(
kube_match_label(&non_string, LABEL_APLICACAO),
None,
"kube_match_label must short-circuit to None when the \
requested `<label>` value carries a non-string YAML type"
);
}
#[test]
fn kube_match_label_matches_prior_inline_chain() {
// Cross-check the helper's output byte-for-byte against the
// prior three-hop inline chain (`kube_match_labels(v)
// .and_then(|m| m.get(<label>)).and_then(|v| v.as_str())`) so
// the lift is a drop-in for the routed sites' prior selector-
// per-label readback path. Peer of the sibling
// `kube_metadata_label_matches_prior_inline_chain` pin on the
// CR-side labels axis.
let selector =
match_labels_selector(&[(LABEL_APLICACAO, "checkout"), (LABEL_PROGRAM, "cart")]);
for label in [LABEL_APLICACAO, LABEL_PROGRAM] {
let via_helper = kube_match_label(&selector, label);
let via_inline = kube_match_labels(&selector)
.and_then(|m| m.get(label))
.and_then(|v| v.as_str());
assert_eq!(
via_helper, via_inline,
"kube_match_label(&value, {label:?}) must return the \
byte-identical `Option<&str>` the prior three-hop chain \
`kube_match_labels(v).and_then(|m| m.get({label:?})) \
.and_then(as_str)` produced"
);
}
}
#[test]
fn kube_match_label_is_wraps_kube_match_label_scalar_equality() {
// Pin the predicate's composition on the scalar accessor:
// `kube_match_label_is(v, l, e) == (kube_match_label(v, l)
// == Some(e))` on every arm — the same one-hop
// `readback → equality-wrap` shape the sibling CR-side per-
// label predicate [`kube_metadata_label_is`] carries on the
// `metadata.labels.<label>` axis, mirrored onto the selector-
// side `matchLabels.<label>` axis.
let selector =
match_labels_selector(&[(LABEL_APLICACAO, "checkout"), (LABEL_PROGRAM, "cart")]);
// Positive match on both axes.
assert!(
kube_match_label_is(&selector, LABEL_APLICACAO, "checkout"),
"kube_match_label_is must resolve true when the selector \
carries `matchLabels.LABEL_APLICACAO = \"checkout\"` — \
the fromEndpoints aplicacao-scope contract this predicate \
closes"
);
assert!(
kube_match_label_is(&selector, LABEL_PROGRAM, "cart"),
"kube_match_label_is must resolve true when the selector \
carries `matchLabels.LABEL_PROGRAM = \"cart\"`"
);
// Negative arms: wrong value, absent label, absent `matchLabels`.
assert!(
!kube_match_label_is(&selector, LABEL_APLICACAO, "other"),
"kube_match_label_is must resolve false when the label \
value byte-differs from `<expected>`"
);
assert!(
!kube_match_label_is(&selector, "pleme.pleme.io/absent", "any"),
"kube_match_label_is must resolve false when the requested \
`<label>` key is absent from the selector"
);
let bare = serde_yaml::Value::Mapping(serde_yaml::Mapping::new());
assert!(
!kube_match_label_is(&bare, LABEL_APLICACAO, "checkout"),
"kube_match_label_is must resolve false when the outer \
`matchLabels:` sub-block is absent — the two-way vacuous-\
None short-circuit through `kube_match_label` folds every \
non-match arm onto the false verdict"
);
}
#[test]
fn kube_match_label_recomposes_on_lifted_kube_match_labels() {
// Recomposition pin: after the lift, `kube_match_label`
// composes as `kube_match_labels(v).and_then(|m| m.get(label))
// .and_then(|v| v.as_str())` and must resolve pairwise-identical
// to that composition across every selector shape. Structural
// mirror at the selector-side of the way sibling
// `kube_metadata_label` composes on `kube_metadata_labels` on
// the CR-side.
let selector =
match_labels_selector(&[(LABEL_APLICACAO, "checkout"), (LABEL_PROGRAM, "cart")]);
for label in [LABEL_APLICACAO, LABEL_PROGRAM, "pleme.pleme.io/absent"] {
assert_eq!(
kube_match_label(&selector, label),
kube_match_labels(&selector)
.and_then(|m| m.get(label))
.and_then(|v| v.as_str()),
"kube_match_label(&v, {label:?}) must recompose exactly \
onto `kube_match_labels(v).and_then(|m| m.get({label:?})) \
.and_then(as_str)` — the composed peer and the primitive-\
plus-sub-hop-plus-shape-gate composition must resolve \
pairwise-identical"
);
}
}
#[test]
fn kube_spec_seq_first_reads_first_entry_of_sub_spec_field_sequence() {
// The lift's load-bearing contract: given a Value carrying a
// top-level `spec: { <sub-field>: [ ... ], ... }` body sub-
// mapping with a non-empty sequence at `<sub-field>`, the
// composed first-entry-arity accessor returns `Some(<first>)`
// borrowing into the input Value across the routed per-sub-
// field first-entry readback axes. Structural peer of the
// sibling `kube_spec_seq_field_reads_sub_spec_field_sequence`
// pin on the same sub-`spec.<field>` axis at the head-selector
// arity.
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(
CILIUM_KEY_INGRESS,
serde_yaml::Value::Sequence(vec![
serde_yaml::Value::String("ingress-0".into()),
serde_yaml::Value::String("ingress-1".into()),
]),
);
spec.insert_str_key(
GATEWAY_API_KEY_LISTENERS,
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("listener-0".into())]),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_spec_seq_first(&value, CILIUM_KEY_INGRESS).and_then(|v| v.as_str()),
Some("ingress-0"),
"kube_spec_seq_first must read the first entry of \
spec.ingress[] — the caixa-mesh per-CNP first-ingress-\
rule pin (each emitted CiliumNetworkPolicy carries exactly \
one ingress rule bracketing one (from, toPorts) pair per \
MESH-COMPOSITION §III.2) reaches through this axis"
);
assert_eq!(
kube_spec_seq_first(&value, GATEWAY_API_KEY_LISTENERS).and_then(|v| v.as_str()),
Some("listener-0"),
"kube_spec_seq_first must read the first entry of a one-\
entry sequence (spec.listeners[]) — same head-selector \
semantics regardless of tail cardinality"
);
}
#[test]
fn kube_spec_seq_first_returns_none_on_every_short_circuit_arm() {
// Fold-through pin: every short-circuit the underlying
// [`kube_spec_seq_field`] closes on plus the trailing
// `.first()` head-selector's empty-arm folds through the
// composed first-entry-arity accessor to `None`. Pin all five
// arms so a future refactor that reaches for
// `.first().unwrap()` (which would panic on any of the four
// upstream short-circuits) or that changes the empty-sequence
// arm's verdict is a test-visible break.
// Arm 1: outer `spec:` block absent (folds through kube_spec
// outer-arm short-circuit).
let bare = serde_yaml::Value::Mapping(serde_yaml::Mapping::new());
assert_eq!(
kube_spec_seq_first(&bare, CILIUM_KEY_INGRESS),
None,
"kube_spec_seq_first must short-circuit to None when the \
top-level `spec:` sub-block is absent — the kube_spec \
outer-arm fold-through preserves the total-function \
contract"
);
// Arm 2: `spec:` present but non-Mapping (folds through
// kube_spec shape-gate short-circuit).
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(
KUBE_KEY_SPEC,
serde_yaml::Value::String("scalar-spec".into()),
);
let non_mapping_spec = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_spec_seq_first(&non_mapping_spec, CILIUM_KEY_INGRESS),
None,
"kube_spec_seq_first must short-circuit to None when the \
`spec:` axis carries a non-Mapping YAML type — the \
kube_spec shape-gate fold-through short-circuits here"
);
// Arm 3: requested `<field>` axis-key absent from `spec:`
// sub-mapping (folds through kube_spec_field trailing miss).
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(
GATEWAY_API_KEY_LISTENERS,
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("only-listener".into())]),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let no_ingress = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_spec_seq_first(&no_ingress, CILIUM_KEY_INGRESS),
None,
"kube_spec_seq_first must short-circuit to None when the \
requested `spec.<field>` axis-key is absent — the \
kube_spec_field trailing `Mapping::get` miss folds through"
);
// Arm 4: `<field>` present but non-sequence (folds through
// kube_spec_seq_field trailing `.as_sequence()` shape-gate).
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(
CILIUM_KEY_INGRESS,
serde_yaml::Value::String("ingress-as-string".into()),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let non_seq_ingress = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_spec_seq_first(&non_seq_ingress, CILIUM_KEY_INGRESS),
None,
"kube_spec_seq_first must short-circuit to None when \
spec.<field> carries a non-sequence YAML type — the \
kube_spec_seq_field trailing `.as_sequence()` shape-gate \
fold-through short-circuits here"
);
// Arm 5: sequence present but empty (trailing `.first()`
// head-selector empty-arm). Distinct from the four upstream
// arms — the sequence is legally-emitted, it just carries no
// head entry to read.
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(CILIUM_KEY_INGRESS, serde_yaml::Value::Sequence(vec![]));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let empty_ingress = serde_yaml::Value::Mapping(cr);
assert_eq!(
kube_spec_seq_first(&empty_ingress, CILIUM_KEY_INGRESS),
None,
"kube_spec_seq_first must return None on a present-but-\
empty sequence — the trailing `.first()` head-selector \
folds the zero-cardinality arm onto the same None verdict \
every routed caller downstream treats it as"
);
}
#[test]
fn kube_spec_seq_first_composes_on_lifted_kube_spec_seq_field_accessor() {
// Composition pin: the composed accessor's body IS
// `kube_spec_seq_field(value, field).and_then(|s| s.first())`
// — the composed sequence-arity accessor stays load-bearing,
// this first-entry-arity accessor stands one abstraction step
// above it (folding the trailing `.first()` head-selector).
// Pin the delegation-shape byte-for-byte across three
// representative sub-field axis-keys so a future refactor that
// bypasses [`kube_spec_seq_field`] (a private inline
// `.get(KUBE_KEY_SPEC).and_then(as_mapping).and_then(|m|
// m.get(field)).and_then(as_sequence).and_then(|s| s.first())`
// chain that would silently drift on a future rebrand of the
// outer three-hop navigation) is a test-visible break.
// Structural peer of the sibling
// `kube_spec_seq_field_composes_on_lifted_kube_spec_field_accessor`
// composition pin on the parent sub-`spec.<field>` sequence-
// arity axis.
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(
CILIUM_KEY_INGRESS,
serde_yaml::Value::Sequence(vec![
serde_yaml::Value::String("ingress-0".into()),
serde_yaml::Value::String("ingress-1".into()),
]),
);
spec.insert_str_key(
GATEWAY_API_KEY_PARENT_REFS,
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("gateway-0".into())]),
);
spec.insert_str_key(KUBE_KEY_RULES, serde_yaml::Value::Sequence(vec![]));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
for sub_field in [
CILIUM_KEY_INGRESS,
GATEWAY_API_KEY_PARENT_REFS,
KUBE_KEY_RULES,
] {
let via_composed = kube_spec_seq_first(&value, sub_field);
let via_delegation = kube_spec_seq_field(&value, sub_field).and_then(|s| s.first());
assert_eq!(
via_composed, via_delegation,
"kube_spec_seq_first(v, {sub_field:?}) must equal the \
delegation-shape `kube_spec_seq_field(v, {sub_field:?})\
.and_then(|s| s.first())` — the composition pin closes \
the drift surface where a private inline bypass \
silently desynchronizes from the underlying composed \
sequence-arity accessor's contract"
);
}
}
#[test]
fn kube_spec_seq_first_matches_prior_inline_chain() {
// Cross-check the composed accessor's output byte-for-byte
// against the prior inline
// `kube_spec_seq_field(v, F).and_then(|s| s.first())` chain
// the 26 routed caller sites across caixa-mesh + caixa-flux
// previously carried. A drift between the composed helper's
// return and the inline chain would silently regress the
// downstream continuations (`.and_then(|i| i.get(<SUB_KEY>))`,
// `.and_then(kube_kind)`, `.expect(...)`) — pin the byte-
// equivalence across three representative sub-field axis-keys
// (routed CNP, HTTPRoute, and Gateway first-entry sites) so
// the composed helper remains a drop-in replacement for the
// routed sites' prior two-line block.
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(
CILIUM_KEY_INGRESS,
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("only-ingress".into())]),
);
spec.insert_str_key(
GATEWAY_API_KEY_LISTENERS,
serde_yaml::Value::Sequence(vec![
serde_yaml::Value::String("listener-0".into()),
serde_yaml::Value::String("listener-1".into()),
]),
);
spec.insert_str_key(KUBE_KEY_RULES, serde_yaml::Value::Sequence(vec![]));
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
for sub_field in [
CILIUM_KEY_INGRESS,
GATEWAY_API_KEY_LISTENERS,
KUBE_KEY_RULES,
] {
let via_helper = kube_spec_seq_first(&value, sub_field);
let via_inline = kube_spec_seq_field(&value, sub_field).and_then(|s| s.first());
assert_eq!(
via_helper, via_inline,
"kube_spec_seq_first(v, {sub_field:?}) must byte-equal \
the prior inline `kube_spec_seq_field(v, {sub_field:?})\
.and_then(|s| s.first())` chain — the lift must stay a \
drop-in for every routed caller's downstream \
continuation posture"
);
}
}
#[test]
fn kube_seq_reads_whole_sub_field_sequence_borrowing_into_input_value() {
// Load-bearing contract: given a `&Value` mapping receiver
// carrying a nested `<field>: [ ... ]` sequence at a per-key
// axis, the value-level two-hop sequence-arity primitive
// returns `Some(<sequence>)` borrowing into the input Value so
// downstream `.iter()` / `.len()` / `.filter_map(...)`
// continuations reach the entries without a further clone.
// Sequence-arity peer of the sibling [`kube_seq_first`]
// head-selector pin one arity above — this exercises the same
// two-hop `get → as_sequence` navigation but stops before the
// trailing head-selector so the whole tail (not just the head)
// reaches the caller.
let mut inner = serde_yaml::Mapping::new();
inner.insert_str_key(
CILIUM_KEY_TO_PORTS,
serde_yaml::Value::Sequence(vec![
serde_yaml::Value::String("to-ports-0".into()),
serde_yaml::Value::String("to-ports-1".into()),
serde_yaml::Value::String("to-ports-2".into()),
]),
);
inner.insert_str_key(
CILIUM_KEY_FROM_ENDPOINTS,
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("from-endpoints-0".into())]),
);
let value = serde_yaml::Value::Mapping(inner);
let to_ports = kube_seq(&value, CILIUM_KEY_TO_PORTS)
.expect("kube_seq must return the full toPorts sequence");
assert_eq!(
to_ports.len(),
3,
"kube_seq must return the whole sequence — the routed \
per-CNP `ingress[0].toPorts[]` tail-enumeration site \
reaches through this arity for `.len()` / `.iter()` \
continuations"
);
let from_endpoints = kube_seq(&value, CILIUM_KEY_FROM_ENDPOINTS)
.expect("kube_seq must return the full fromEndpoints sequence");
assert_eq!(
from_endpoints.len(),
1,
"kube_seq must return a one-entry sequence unchanged — \
same shape-gate semantics regardless of tail cardinality, \
mirroring the sibling head-selector arity"
);
}
#[test]
fn kube_seq_returns_none_on_every_short_circuit_arm() {
// Fold-through pin: every short-circuit the underlying two-hop
// `get → as_sequence` closes on folds through this accessor to
// `None`. Pin all three arms so a future refactor reaching for
// `.as_sequence().unwrap()` (which would panic on any of the
// three arms) is a test-visible break.
// Arm 1: receiver carries a scalar YAML type with no
// `get(<field>)` surface.
let scalar_receiver = serde_yaml::Value::String("scalar".into());
assert_eq!(
kube_seq(&scalar_receiver, CILIUM_KEY_TO_PORTS),
None,
"kube_seq must short-circuit to None when the receiver \
Value carries a scalar YAML type with no `get` navigation \
surface — Value::get's scalar-arm None folds through, \
preserving the total-function contract"
);
// Arm 2: requested `<field>` axis-key absent from receiver's
// mapping.
let mut only_other = serde_yaml::Mapping::new();
only_other.insert_str_key(
CILIUM_KEY_FROM_ENDPOINTS,
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("only-fe".into())]),
);
let missing_field = serde_yaml::Value::Mapping(only_other);
assert_eq!(
kube_seq(&missing_field, CILIUM_KEY_TO_PORTS),
None,
"kube_seq must short-circuit to None when the requested \
`<field>` axis-key is absent from the receiver's mapping \
— Value::get's trailing miss folds through"
);
// Arm 3: `<field>` present but non-sequence.
let mut non_seq = serde_yaml::Mapping::new();
non_seq.insert_str_key(
CILIUM_KEY_TO_PORTS,
serde_yaml::Value::String("scalar-not-sequence".into()),
);
let non_seq_field = serde_yaml::Value::Mapping(non_seq);
assert_eq!(
kube_seq(&non_seq_field, CILIUM_KEY_TO_PORTS),
None,
"kube_seq must short-circuit to None when the `<field>` \
value carries a non-sequence YAML type — the trailing \
`.as_sequence()` shape-gate fold-through short-circuits \
here"
);
}
#[test]
fn kube_seq_preserves_empty_sequence_distinctly_from_missing() {
// Cardinality-preservation pin: unlike the sibling
// [`kube_seq_first`] which collapses the "sequence present but
// empty" arm onto the same `None` verdict as the three upstream
// short-circuits, this accessor returns `Some(&[])` on a
// present-but-empty sequence — the caller sees the empty
// sequence as a legitimate reading, distinct from the missing-
// field arm. This is the load-bearing distinction that lets
// the tail-enumeration caller distinguish "no such field" from
// "field present but zero entries" at the cost of one arm's
// fold-through — the sibling head-selector accessor's
// fold-through is right for its callers, this accessor's
// preservation is right for its callers.
let mut m = serde_yaml::Mapping::new();
m.insert_str_key(CILIUM_KEY_TO_PORTS, serde_yaml::Value::Sequence(vec![]));
let empty_seq_value = serde_yaml::Value::Mapping(m);
let seq = kube_seq(&empty_seq_value, CILIUM_KEY_TO_PORTS);
assert!(
seq.is_some(),
"kube_seq must return Some on a present-but-empty sequence \
— the shape-gate accepts empty sequences (they are \
sequences), the distinction between empty and missing \
carries through to the caller"
);
assert_eq!(
seq.expect("kube_seq must return Some on present-but-empty")
.len(),
0,
"kube_seq's return on an empty sequence must carry the \
empty length through — distinct from the sibling \
kube_seq_first's fold-through-to-None arm"
);
}
#[test]
fn kube_seq_matches_prior_inline_two_hop_chain() {
// Cross-check the value-level primitive's output byte-for-byte
// against the prior inline
// `.get(<F>).and_then(|v| v.as_sequence())` two-hop chain the
// ~11 routed caller sites in caixa-mesh + caixa-flux previously
// carried. A drift between the helper's return and the inline
// chain would silently regress every downstream continuation
// (`.iter()`, `.len()`, `.expect(...)`) — pin the byte-
// equivalence across four representative sub-field axis-keys
// spanning both CNP nested brackets (toPorts, fromEndpoints)
// and the Gateway API HTTPRoute nested brackets (matches) so
// the composed helper stays a drop-in replacement for the
// routed sites' prior two-line block.
let mut inner = serde_yaml::Mapping::new();
inner.insert_str_key(
CILIUM_KEY_TO_PORTS,
serde_yaml::Value::Sequence(vec![
serde_yaml::Value::String("tp-0".into()),
serde_yaml::Value::String("tp-1".into()),
]),
);
inner.insert_str_key(
CILIUM_KEY_FROM_ENDPOINTS,
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("fe-0".into())]),
);
inner.insert_str_key(GATEWAY_API_KEY_MATCHES, serde_yaml::Value::Sequence(vec![]));
inner.insert_str_key(
CILIUM_KEY_HTTP,
serde_yaml::Value::String("scalar-not-a-seq".into()),
);
let value = serde_yaml::Value::Mapping(inner);
for sub_field in [
CILIUM_KEY_TO_PORTS,
CILIUM_KEY_FROM_ENDPOINTS,
GATEWAY_API_KEY_MATCHES,
CILIUM_KEY_HTTP,
] {
let via_helper = kube_seq(&value, sub_field);
let via_inline = value.get(sub_field).and_then(|v| v.as_sequence());
assert_eq!(
via_helper, via_inline,
"kube_seq(v, {sub_field:?}) must byte-equal the prior \
inline `.get({sub_field:?}).and_then(|v| v.as_sequence())` \
two-hop chain — the lift must stay a drop-in for \
every routed caller's downstream continuation posture"
);
}
}
#[test]
fn kube_seq_first_reads_first_entry_of_nested_sub_field_sequence() {
// Load-bearing contract: given a `&Value` mapping receiver
// carrying a nested `<field>: [ ... ]` sequence at a per-key
// axis, the value-level three-hop primitive returns
// `Some(<first>)` borrowing into the input Value across the
// routed per-nested-sub-field first-entry readback axes. Peer
// of the spec-anchored [`kube_spec_seq_first`] pass-through pin
// one altitude below — this exercises the same head-selector
// semantics on the inner nested-mapping receiver the
// spec-anchored pin exercises on the outer per-CR body.
let mut inner = serde_yaml::Mapping::new();
inner.insert_str_key(
CILIUM_KEY_TO_PORTS,
serde_yaml::Value::Sequence(vec![
serde_yaml::Value::String("to-ports-0".into()),
serde_yaml::Value::String("to-ports-1".into()),
]),
);
inner.insert_str_key(
CILIUM_KEY_FROM_ENDPOINTS,
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("from-endpoints-0".into())]),
);
let value = serde_yaml::Value::Mapping(inner);
assert_eq!(
kube_seq_first(&value, CILIUM_KEY_TO_PORTS).and_then(|v| v.as_str()),
Some("to-ports-0"),
"kube_seq_first must read the first entry of a nested \
`<field>[]` sequence — the routed per-CNP \
`spec.ingress[0].toPorts[0]` L4-tuple bracket reaches \
through this axis"
);
assert_eq!(
kube_seq_first(&value, CILIUM_KEY_FROM_ENDPOINTS).and_then(|v| v.as_str()),
Some("from-endpoints-0"),
"kube_seq_first must read the first entry of a one-entry \
nested sequence — same head-selector semantics regardless \
of tail cardinality, mirroring the sibling spec-anchored \
head-selector contract"
);
}
#[test]
fn kube_seq_first_returns_none_on_every_short_circuit_arm() {
// Fold-through pin: every short-circuit the underlying three-
// hop `get → as_sequence → first` closes on folds through this
// accessor to `None`. Pin all four arms so a future refactor
// that reaches for `.first().unwrap()` (which would panic on
// any of the three upstream short-circuits) or that changes
// the empty-sequence arm's verdict is a test-visible break.
// Arm 1: receiver carries a scalar YAML type with no
// `get(<field>)` surface (Value::get returns None on scalar
// arms — string, bool, number, null — that expose no per-key
// lookup).
let scalar_receiver = serde_yaml::Value::String("scalar".into());
assert_eq!(
kube_seq_first(&scalar_receiver, CILIUM_KEY_TO_PORTS),
None,
"kube_seq_first must short-circuit to None when the \
receiver Value carries a scalar YAML type with no `get` \
navigation surface — Value::get's scalar-arm None folds \
through, preserving the total-function contract"
);
// Arm 2: requested `<field>` axis-key absent from receiver's
// mapping (Value::get trailing miss).
let mut only_other = serde_yaml::Mapping::new();
only_other.insert_str_key(
CILIUM_KEY_FROM_ENDPOINTS,
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("only-fe".into())]),
);
let missing_field = serde_yaml::Value::Mapping(only_other);
assert_eq!(
kube_seq_first(&missing_field, CILIUM_KEY_TO_PORTS),
None,
"kube_seq_first must short-circuit to None when the \
requested `<field>` axis-key is absent from the receiver's \
mapping — Value::get's trailing miss folds through"
);
// Arm 3: `<field>` present but non-sequence (trailing
// `.as_sequence()` shape-gate short-circuit).
let mut non_seq = serde_yaml::Mapping::new();
non_seq.insert_str_key(
CILIUM_KEY_TO_PORTS,
serde_yaml::Value::String("scalar-not-sequence".into()),
);
let non_seq_field = serde_yaml::Value::Mapping(non_seq);
assert_eq!(
kube_seq_first(&non_seq_field, CILIUM_KEY_TO_PORTS),
None,
"kube_seq_first must short-circuit to None when the \
`<field>` value carries a non-sequence YAML type — the \
trailing `.as_sequence()` shape-gate fold-through short-\
circuits here"
);
// Arm 4: sequence present but empty (trailing `.first()`
// head-selector empty-arm — distinct from the three upstream
// arms, the sequence is legally-emitted, it just carries no
// head entry to read).
let mut empty = serde_yaml::Mapping::new();
empty.insert_str_key(CILIUM_KEY_TO_PORTS, serde_yaml::Value::Sequence(vec![]));
let empty_seq = serde_yaml::Value::Mapping(empty);
assert_eq!(
kube_seq_first(&empty_seq, CILIUM_KEY_TO_PORTS),
None,
"kube_seq_first must return None on a present-but-empty \
sequence — the trailing `.first()` head-selector folds \
the zero-cardinality arm onto the same None verdict \
every routed caller downstream treats it as"
);
}
#[test]
fn kube_seq_first_matches_prior_inline_three_hop_chain() {
// Cross-check the value-level primitive's output byte-for-byte
// against the prior inline
// `.get(<F>).and_then(|v| v.as_sequence()).and_then(|s| s.first())`
// three-hop chain the 20 routed caller sites in caixa-mesh
// previously carried. A drift between the helper's return and
// the inline chain would silently regress every downstream
// continuation (`.get(<KEY>)`, `.as_u64()`,
// `.and_then(kube_match_labels)`, `.expect(...)`) — pin the
// byte-equivalence across four representative sub-field axis-
// keys spanning both CNP nested brackets (toPorts,
// fromEndpoints) and the Gateway API HTTPRoute nested brackets
// (backendRefs, matches) so the composed helper stays a drop-
// in replacement for the routed sites' prior three-line block.
let mut inner = serde_yaml::Mapping::new();
inner.insert_str_key(
CILIUM_KEY_TO_PORTS,
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("tp-0".into())]),
);
inner.insert_str_key(
CILIUM_KEY_FROM_ENDPOINTS,
serde_yaml::Value::Sequence(vec![
serde_yaml::Value::String("fe-0".into()),
serde_yaml::Value::String("fe-1".into()),
]),
);
inner.insert_str_key(
GATEWAY_API_KEY_BACKEND_REFS,
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("br-0".into())]),
);
inner.insert_str_key(GATEWAY_API_KEY_MATCHES, serde_yaml::Value::Sequence(vec![]));
let value = serde_yaml::Value::Mapping(inner);
for sub_field in [
CILIUM_KEY_TO_PORTS,
CILIUM_KEY_FROM_ENDPOINTS,
GATEWAY_API_KEY_BACKEND_REFS,
GATEWAY_API_KEY_MATCHES,
] {
let via_helper = kube_seq_first(&value, sub_field);
let via_inline = value
.get(sub_field)
.and_then(|v| v.as_sequence())
.and_then(|s| s.first());
assert_eq!(
via_helper, via_inline,
"kube_seq_first(v, {sub_field:?}) must byte-equal the \
prior inline `.get({sub_field:?}).and_then(|v| v.as_sequence())\
.and_then(|s| s.first())` three-hop chain — the lift \
must stay a drop-in for every routed caller's \
downstream continuation posture"
);
}
}
#[test]
fn kube_seq_first_composes_naturally_below_kube_spec_seq_first() {
// Peer-composition pin: the two accessors stack cleanly on the
// outer `spec.<outer>[0].<inner>[0]` two-hop-nested first-entry
// readback path — outer hop via [`kube_spec_seq_first`], inner
// hop via this accessor. The four-line
// `kube_spec_seq_first(v, OUTER).and_then(|o| o.get(INNER))
// .and_then(|s| s.as_sequence()).and_then(|s| s.first())`
// becomes the two-line
// `kube_spec_seq_first(v, OUTER).and_then(|o| kube_seq_first(o, INNER))`
// — pin the two-line composition byte-for-byte against the
// four-line inline across the canonical routed sites
// (spec.ingress[0].toPorts[0] on CNP, spec.rules[0].backendRefs[0]
// on HTTPRoute) so a future refactor bypassing either peer is
// a test-visible break.
let mut inner_ingress = serde_yaml::Mapping::new();
inner_ingress.insert_str_key(
CILIUM_KEY_TO_PORTS,
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("cnp-tp-0".into())]),
);
let mut inner_rule = serde_yaml::Mapping::new();
inner_rule.insert_str_key(
GATEWAY_API_KEY_BACKEND_REFS,
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String("route-br-0".into())]),
);
let mut spec = serde_yaml::Mapping::new();
spec.insert_str_key(
CILIUM_KEY_INGRESS,
serde_yaml::Value::Sequence(vec![serde_yaml::Value::Mapping(inner_ingress)]),
);
spec.insert_str_key(
KUBE_KEY_RULES,
serde_yaml::Value::Sequence(vec![serde_yaml::Value::Mapping(inner_rule)]),
);
let mut cr = serde_yaml::Mapping::new();
cr.insert_str_key(KUBE_KEY_SPEC, serde_yaml::Value::Mapping(spec));
let value = serde_yaml::Value::Mapping(cr);
for (outer, inner) in [
(CILIUM_KEY_INGRESS, CILIUM_KEY_TO_PORTS),
(KUBE_KEY_RULES, GATEWAY_API_KEY_BACKEND_REFS),
] {
let via_composed =
kube_spec_seq_first(&value, outer).and_then(|o| kube_seq_first(o, inner));
let via_inline = kube_spec_seq_first(&value, outer)
.and_then(|o| o.get(inner))
.and_then(|s| s.as_sequence())
.and_then(|s| s.first());
assert_eq!(
via_composed, via_inline,
"kube_spec_seq_first(v, {outer:?}) + kube_seq_first(o, {inner:?}) \
must byte-equal the four-hop inline `.get({inner:?}).as_sequence().first()` \
chain the routed per-`spec.<outer>[0].<inner>[0]` \
sites previously carried below the outer spec-anchored \
first-entry accessor"
);
}
}
}