Skip to main content

caixa_helm/
lib.rs

1//! caixa-helm — typed renderer that emits a per-program `lareira-<name>`
2//! Helm chart from a [`Caixa`] manifest plus its `servicos/<name>.computeunit.yaml`.
3//!
4//! ## Output shape
5//!
6//! Every chart emitted here mirrors the canonical
7//! `pleme-io/helmworks/charts/lareira-<name>/` layout, which is *thin*:
8//!
9//!   Chart.yaml      ; metadata + dependency on pleme-computeunit
10//!   values.yaml     ; pleme-computeunit values block (the typed L2 ComputeUnit shape)
11//!   README.md       ; one-line elevator pitch for the chart
12//!
13//! There are no `templates/` — the rendering is delegated to the
14//! `pleme-computeunit` library chart in helmworks (per `theory/META-FRAMEWORK.md`
15//! §I, Layer 3 → Layer 2 transformation). caixa-helm's job is to derive the
16//! values block from a Caixa, not to render Kubernetes objects directly.
17//!
18//! ## Why a separate crate
19//!
20//! Same pattern as [`caixa_flake`] (renders flake.nix) and [`caixa_pangea`]
21//! (renders pangea Ruby) — `caixa-<target>` crates take a typed Caixa and
22//! emit the canonical source for `<target>`. Naming is uniform across the
23//! workspace.
24//!
25//! ## V0 contract
26//!
27//! ```rust,ignore
28//! use caixa_core::Caixa;
29//! use caixa_helm::{ChartDir, render_chart_for_servico};
30//!
31//! let caixa: Caixa = Caixa::from_lisp(src)?;
32//! let cu_yaml: serde_yaml::Value =
33//!     serde_yaml::from_str(std::fs::read_to_string("servicos/hello-rio.computeunit.yaml")?)?;
34//! let dir: ChartDir = render_chart_for_servico(&caixa, &cu_yaml)?;
35//! dir.write_to(std::path::Path::new("/tmp/lareira-hello-rio"))?;
36//! ```
37//!
38//! ## What this is NOT
39//!
40//! - Not a chart for the `caixa-operator` itself — that lives in
41//!   `pleme-io/caixa/operator-chart/`.
42//! - Not a Helm CLI wrapper — emitting bytes only; consumers (`feira chart`,
43//!   eventually) drive the I/O.
44//! - Not a renderer of K8s resources — `pleme-computeunit` library chart owns
45//!   the templates that turn this values block into ComputeUnit + Service +
46//!   ScaledObject + ConfigMap.
47
48#![allow(clippy::module_name_repetitions)]
49
50use std::collections::BTreeMap;
51use std::path::Path;
52
53use caixa_core::{Caixa, MappingExt};
54use serde::{Deserialize, Serialize};
55use thiserror::Error;
56
57/// Errors caixa-helm can raise.
58#[derive(Debug, Error)]
59pub enum Error {
60    /// The caixa's `:kind` doesn't match what `caixa-helm` targets
61    /// (this renderer only emits per-program `lareira-<nome>` charts
62    /// for `:kind Servico`). Lifted from a prior `NotAServico(CaixaKind)`
63    /// arm to wrap [`caixa_core::KindMismatch`] so the diagnostic
64    /// names the offending caixa's `:nome` (not just its kind),
65    /// shared verbatim with `caixa-flux` and `caixa-mesh`.
66    #[error("{0}")]
67    NotAServico(#[from] caixa_core::KindMismatch),
68    /// The caixa's `:servicos` list doesn't carry exactly one entry —
69    /// the V0 contract every Servico-kind caixa satisfies (one
70    /// ComputeUnit YAML pointer per Servico, matching the one Helm
71    /// chart this renderer emits). Lifted from a prior
72    /// `UnsupportedServicoCount(usize)` arm to wrap
73    /// [`caixa_core::ServicoCountMismatch`] so the diagnostic names
74    /// the offending caixa's `:nome` (not just the count), shared
75    /// verbatim with `caixa-flux` (the peer per-Servico renderer
76    /// running the same V0 invariant on the programs.yaml-entry axis).
77    #[error("{0}")]
78    UnsupportedServicoCount(#[from] caixa_core::ServicoCountMismatch),
79    #[error("computeunit yaml missing required field: {0}")]
80    MissingField(&'static str),
81    #[error("yaml: {0}")]
82    Yaml(#[from] serde_yaml::Error),
83    #[error("render: {0}")]
84    Render(#[from] caixa_core::RenderError),
85    #[error("io: {0}")]
86    Io(#[from] std::io::Error),
87}
88
89/// One file in the rendered chart — `(path, contents)` pair every
90/// [`render_chart_for_servico`]-rendered `lareira-<nome>` chart-tree
91/// leaf lands at (`Chart.yaml`, `values.yaml`, `README.md`).
92///
93/// Type-aliased to the canonical [`caixa_core::RenderedFile`] so the
94/// substrate-side "one rendered leaf artifact" shape lives at one
95/// struct definition across every per-target renderer — the peer
96/// [`caixa_flux::BundleFile`] alias resolves to the same canonical, so
97/// a future rebrand on either axis (a per-artifact hash / provenance
98/// field addition, a per-artifact write-mode discriminator once
99/// per-cluster-writer sandboxing lands) lands at one caixa-core `pub
100/// struct RenderedFile` edit and reaches both crates by construction.
101/// Prior to this lift both crates carried an inline `pub struct
102/// <Xxx>File { pub path: PathBuf, pub contents: String }` with
103/// identical `#[derive(Debug, Clone, PartialEq, Eq)]` shapes and no
104/// per-type impls; a future per-target renderer (`caixa-otel`, the
105/// future `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer, the
106/// future per-Supervisor reconciler renderer) would have carried a
107/// third and fourth clone of the same record. Type aliases preserve
108/// every existing struct-literal construction site
109/// (`ChartFile { path, contents }`), every field-access site (`f.path`,
110/// `f.contents`), and every derive-fed navigator by construction —
111/// Rust type aliases inherit the aliased type's `#[derive]`-generated
112/// `Debug`/`Clone`/`PartialEq`/`Eq` impls with no per-alias glue.
113pub type ChartFile = caixa_core::RenderedFile;
114
115/// The rendered chart — a flat list of files, plus the chart name.
116#[derive(Debug, Clone, PartialEq, Eq)]
117pub struct ChartDir {
118    /// Chart name — e.g. `lareira-hello-rio`. Used as the output dir name.
119    pub name: String,
120    pub files: Vec<ChartFile>,
121}
122
123impl ChartDir {
124    /// Write every file to `<dest>/<self.name>/`. Creates parent dirs.
125    pub fn write_to(&self, dest: &Path) -> Result<(), Error> {
126        let root = dest.join(&self.name);
127        std::fs::create_dir_all(&root)?;
128        for f in &self.files {
129            let target = root.join(&f.path);
130            if let Some(parent) = target.parent() {
131                std::fs::create_dir_all(parent)?;
132            }
133            std::fs::write(&target, &f.contents)?;
134        }
135        Ok(())
136    }
137}
138
139/// Top-level `Chart.yaml` shape for a generated lareira-<name> chart.
140///
141/// Mirrors `helmworks/charts/lareira-hello-world/Chart.yaml` 1:1 in
142/// structural slots — versions, deps, keywords, maintainers.
143#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
144pub struct ChartYaml {
145    #[serde(rename = "apiVersion")]
146    pub api_version: String,
147    pub name: String,
148    pub description: String,
149    #[serde(rename = "type")]
150    pub chart_type: String,
151    pub version: String,
152    #[serde(rename = "appVersion")]
153    pub app_version: String,
154    #[serde(default, skip_serializing_if = "Vec::is_empty")]
155    pub keywords: Vec<String>,
156    #[serde(default, skip_serializing_if = "Vec::is_empty")]
157    pub maintainers: Vec<Maintainer>,
158    #[serde(default, skip_serializing_if = "Option::is_none")]
159    pub home: Option<String>,
160    pub dependencies: Vec<ChartDependency>,
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
164pub struct Maintainer {
165    pub name: String,
166    #[serde(default, skip_serializing_if = "Option::is_none")]
167    pub email: Option<String>,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
171pub struct ChartDependency {
172    pub name: String,
173    pub version: String,
174    pub repository: String,
175    #[serde(default, skip_serializing_if = "Option::is_none")]
176    pub alias: Option<String>,
177}
178
179/// Canonical Helm library-chart name every `lareira-<nome>` chart depends
180/// on — re-export of the lifted [`caixa_core::DEFAULT_LIBRARY_NAME`] so
181/// the load-bearing string lives in exactly one place across every
182/// caixa renderer (caixa-helm's `RenderOpts::library_name` default
183/// here + caixa-flux's `cluster_bundle` `helmrelease.yaml` wrap key).
184/// A future per-edition library-chart fork — every entry on the
185/// absorption-roadmap that names a per-cluster / per-namespace /
186/// per-tenant variant of the canonical library chart — reaches both
187/// consumers through one `&'static str` by construction. Same shape
188/// as the [`caixa_core::DEFAULT_NAMESPACE`] (a085b26) /
189/// [`caixa_core::DEFAULT_SERVICO_PORT`] (1e22add) lifts on the peer
190/// canonical-K8s-axis-constant surface.
191pub use caixa_core::DEFAULT_LIBRARY_NAME;
192/// Canonical Helm 3 `Chart.yaml` `dependencies[0].repository` chart-source
193/// URL every rendered `lareira-<nome>` chart declares against the
194/// substrate-canonical [`DEFAULT_LIBRARY_NAME`] library chart — the
195/// `file://` per-chart-dep resolver scheme pointing at the sibling
196/// helmworks directory on disk that `helm dependency build` vendors the
197/// library chart bytes from. Re-export of the lifted
198/// [`caixa_core::DEFAULT_LIBRARY_REPO`] so the load-bearing per-dep
199/// resolver URL lives in exactly one place across every caixa renderer —
200/// the caixa-helm `RenderOpts::library_repo` default here + every future
201/// substrate-side per-Servico renderer consumer (the future M4
202/// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-Servico
203/// library-chart-dep repo resolver, the future per-Aplicacao library-
204/// chart's per-Servico per-dep repo emitter, the future per-cluster
205/// per-registry chart-source mirror the operator pins through a future
206/// [`caixa_flux::ClusterBundleOpts`]-scoped library-repo override) reach
207/// through one `&'static str` by construction. Same shape as the peer
208/// [`DEFAULT_LIBRARY_NAME`] (41438dc) re-export on the sibling per-dep
209/// `Chart.yaml` `dependencies[0].name` axis — extends the substrate-
210/// primitive-residence discipline onto the paired per-dep repository
211/// axis every emitted per-Servico `Chart.yaml` `dependencies[0]` entry
212/// carries.
213pub use caixa_core::DEFAULT_LIBRARY_REPO;
214/// Canonical Helm 3 `Chart.yaml` `dependencies[0].version` semver-
215/// requirement scalar every rendered `lareira-<nome>` chart declares
216/// against the substrate-canonical [`DEFAULT_LIBRARY_NAME`] library
217/// chart — re-export of the lifted [`caixa_core::DEFAULT_LIBRARY_VERSION`]
218/// so the load-bearing per-dep semver-requirement string lives in
219/// exactly one place across every caixa renderer. The single
220/// production-code call site consuming it, the
221/// `RenderOpts::library_version` field seed at
222/// [`RenderOpts::default()`], now consults exactly one substrate-
223/// primitive `&'static str` allocation, sibling to the paired
224/// [`caixa_core::DEFAULT_LIBRARY_NAME`] / [`caixa_core::DEFAULT_LIBRARY_REPO`]
225/// re-exports on the peer per-dep name / repository axes. Completes
226/// the `(name, repository, version)` per-Chart.yaml-dep canonical-
227/// scalar triple's substrate-primitive-residence pass at the caixa-
228/// helm re-export surface — every future substrate-side per-Servico
229/// renderer consumer (the future per-Aplicacao library-chart's per-
230/// Servico per-dep version emitter, the future per-cluster library-
231/// chart-version pin the operator threads through a future
232/// [`caixa_flux::ClusterBundleOpts`]-scoped library-version override)
233/// inherits the same `&'static str` by construction.
234pub use caixa_core::DEFAULT_LIBRARY_VERSION;
235
236/// Canonical substrate-side per-[`caixa_core::Caixa`] author-omitted
237/// `:licenca` SPDX-shaped license-expression fallback every
238/// [`build_readme`]-emitted `lareira-<nome>` chart `README.md` `## License`
239/// section body seeds when the author-omitted `:licenca` slot lands past
240/// the [`caixa_core::Caixa::licenca`] `Option<&str>` accessor's `None` arm.
241/// Re-export of the lifted [`caixa_core::CAIXA_LICENCA_DEFAULT`] so the
242/// load-bearing byte-string lives in exactly one place across the
243/// substrate — the caixa-helm `build_readme` fold here + every future
244/// substrate-side per-`Caixa` registry-facing renderer consumer (the M4
245/// `Chart.yaml annotations["artifacthub.io/license"]` emitter the
246/// [`caixa_core::Caixa::validate_licenca`] docstring roadmap names) reach
247/// through one `&'static str` by construction. Same shape as the peer
248/// [`caixa_core::DEFAULT_LIBRARY_NAME`] / [`caixa_core::DEFAULT_NAMESPACE`]
249/// / [`caixa_core::DEFAULT_SERVICO_PORT`] lifts on the sibling
250/// canonical-load-bearing-scalar surface.
251pub use caixa_core::CAIXA_LICENCA_DEFAULT;
252
253/// Canonical Helm 3 `Chart.yaml` `apiVersion` every rendered
254/// `lareira-<nome>` chart declares. Re-export of the lifted
255/// [`caixa_core::HELM_CHART_API_VERSION`] so the Helm-side
256/// chart-schema apiVersion — the discriminator the Helm binary's
257/// chart-schema parser (`helm dependency build`, `helm lint`,
258/// `helm template`) consults to select the schema that reads the
259/// rendered Chart.yaml — lives in exactly one place across every
260/// caixa renderer. The single production-code call site consuming
261/// it is [`build_chart_yaml`]'s `api_version` field assignment; a
262/// drifted local `pub const HELM_CHART_API_VERSION: &str = "…"` at
263/// this crate (or any sibling per-chart-schema renderer the
264/// absorption roadmap acknowledges — the future per-Aplicacao
265/// library chart, the future per-cluster snapshot chart) would
266/// silently reroute the rendered Chart.yaml through a stale
267/// chart-schema parser at `helm template` time far from the
268/// rebrand commit's source, so the equality + `&'static` static-data
269/// identity pin
270/// (`helm_chart_api_version_re_export_points_at_caixa_core_canonical`)
271/// closes the drift footgun at caixa-helm build time. Same shape as
272/// the [`DEFAULT_LIBRARY_NAME`] / [`KUBE_KEY_SPEC`] re-exports on the
273/// sibling canonical-Helm-load-bearing-string / canonical-K8s-CR-key
274/// axes.
275pub use caixa_core::HELM_CHART_API_VERSION;
276
277/// Canonical Helm 3 `Chart.yaml` `type` field per-chart-kind
278/// discriminator scalar-value every rendered `lareira-<nome>` chart
279/// declares. Re-export of the lifted
280/// [`caixa_core::HELM_CHART_TYPE_APPLICATION`] so the Helm chart-schema
281/// per-chart-kind discriminator — the scalar Helm's per-release install-
282/// shape dispatch loop keys off to select the per-chart-kind install
283/// pathway — lives in exactly one place across every caixa renderer.
284/// The single production-code call site consuming it is
285/// [`build_chart_yaml`]'s `chart_type` field assignment (the sole
286/// emitter site the prior inline `"application".into()` literal sat at);
287/// a drifted local `pub const HELM_CHART_TYPE_APPLICATION: &str = "…"`
288/// at this crate (or any sibling per-chart renderer the absorption
289/// roadmap acknowledges — the future per-Aplicacao library chart, the
290/// future per-cluster snapshot chart) would surface as one of two
291/// silent failure modes at `helm install` time: a value outside the
292/// schema's admitted set (`{"application", "library"}`) that Helm's
293/// chart-schema parser silently treats as the default `application`
294/// shape (masking the schema violation with no process-log signal), or
295/// an accidental collapse onto the sibling `"library"` shape that Helm
296/// refuses to install directly ("Error: library charts cannot be
297/// installed") with no field naming the chart-kind-drift root cause.
298/// The equality + `&'static` static-data identity pin
299/// (`helm_chart_type_application_re_export_points_at_caixa_core_canonical`)
300/// closes the drift footgun at caixa-helm build time. Peer to the
301/// [`HELM_CHART_API_VERSION`] re-export on the sibling canonical-Helm-
302/// chart-schema-axis — completes the per-Chart.yaml `(apiVersion, type)`
303/// canonical-scalar-axis re-export pair every rendered `lareira-<nome>`
304/// chart declares at its top-level Chart.yaml body.
305pub use caixa_core::HELM_CHART_TYPE_APPLICATION;
306
307/// Canonical Helm 3 `Chart.yaml` `type` field per-chart-kind discriminator
308/// scalar-value the sibling library-chart shape lands on — re-export of
309/// the lifted [`caixa_core::HELM_CHART_TYPE_LIBRARY`] so the second and
310/// only other arm of the Helm chart-schema's closed set `{"application",
311/// "library"}` lives in exactly one place across every caixa renderer.
312/// No production emitter here consumes it today — the caixa-helm
313/// renderer emits per-Servico `application`-typed `lareira-<nome>`
314/// charts, and the sibling [`DEFAULT_LIBRARY_NAME`] `pleme-computeunit`
315/// library chart is out-of-tree at `pleme-io/helmworks` (not this
316/// crate's authority) — but every future substrate-side per-chart-kind
317/// classifier and every emitter for the future per-Aplicacao library
318/// chart the [`HELM_CHART_TYPE_APPLICATION`] docstring names as a
319/// trajectory item reads the same `&'static str` by construction.
320/// Peer to the [`HELM_CHART_TYPE_APPLICATION`] re-export on the sibling
321/// closed-set arm — completes the two-arm re-export pair of the Helm
322/// chart-schema's per-chart-kind axis at the caixa-helm surface so
323/// consumers reaching for either shape read from one canonical source
324/// per arm. The paired identity pins
325/// (`helm_chart_type_library_re_export_points_at_caixa_core_canonical`)
326/// close the drift footgun at caixa-helm build time.
327pub use caixa_core::HELM_CHART_TYPE_LIBRARY;
328
329/// Canonical Helm 3 `Chart.yaml` top-level YAML axis-key naming the
330/// per-chart-kind discriminator field — re-export of the lifted
331/// [`caixa_core::HELM_CHART_KEY_TYPE`] so the top-level per-chart-kind
332/// discriminator YAML key the sibling [`HELM_CHART_TYPE_APPLICATION`] /
333/// [`HELM_CHART_TYPE_LIBRARY`] axis-value re-exports carry the closed-
334/// set admitted scalars for lives in exactly one place across every
335/// caixa renderer. The production emitter today is [`ChartYaml`]'s
336/// `chart_type` field `#[serde(rename = "type")]` attribute — Rust's
337/// attribute grammar admits only string literals so the const cannot
338/// substitute for the literal syntactically, but the drift-detection
339/// pin at
340/// [`tests::chart_yaml_serializes_type_axis_under_lifted_helm_chart_key_type`]
341/// round-trips a rendered `Chart.yaml` through
342/// `serde_yaml::from_str::<serde_yaml::Value>` and asserts the top-
343/// level `Mapping::get(HELM_CHART_KEY_TYPE)` resolves, closing the
344/// drift the attribute-literal-only grammar leaves silent (a future
345/// refactor that dropped the `#[serde(rename = "type")]` attribute
346/// would silently serialize the field as Rust's default snake_case
347/// `chart_type:`, which Helm's chart-schema parser silently ignores
348/// as an unknown top-level key, defaulting the per-chart-kind axis
349/// to `application` with no process-log drift-signal). Peer to the
350/// [`HELM_CHART_TYPE_APPLICATION`] / [`HELM_CHART_TYPE_LIBRARY`]
351/// re-exports on the sibling per-chart-kind axis-value canonical
352/// surface — completes the per-Chart.yaml per-chart-kind
353/// discriminator axis's `(key, value-set)` canonical re-export trio
354/// at the caixa-helm surface.
355pub use caixa_core::HELM_CHART_KEY_TYPE;
356
357/// Canonical Helm 3 `Chart.yaml` top-level YAML axis-key naming the
358/// per-chart underlying-application-version field — re-export of the
359/// lifted [`caixa_core::HELM_CHART_KEY_APP_VERSION`] so the top-level
360/// per-chart-app-version YAML key the [`ChartYaml`] `app_version`
361/// field's `#[serde(rename = "appVersion")]` attribute encodes lives
362/// in exactly one place across every caixa renderer. Rust's attribute
363/// grammar admits only string literals so the const cannot substitute
364/// for the literal syntactically, but the drift-detection pin at
365/// [`tests::chart_yaml_serializes_app_version_axis_under_lifted_helm_chart_key_app_version`]
366/// round-trips a rendered `Chart.yaml` and asserts the top-level
367/// `Mapping::get(HELM_CHART_KEY_APP_VERSION)` resolves, closing the
368/// drift the attribute-literal-only grammar leaves silent (a future
369/// refactor that dropped the `#[serde(rename = "appVersion")]`
370/// attribute would silently serialize the field as Rust's default
371/// snake_case `app_version:`, which Helm's chart-schema parser
372/// silently drops from the parsed chart-metadata shape, and every
373/// downstream Artifact Hub / `helm search` per-chart index falls back
374/// to "no application version" for the rendered chart far from the
375/// drift site). Peer to [`HELM_CHART_KEY_TYPE`] on the sibling
376/// per-Chart.yaml top-level YAML axis-key re-export surface —
377/// completes the per-Chart.yaml top-level YAML axis-key re-export
378/// pair at the caixa-helm surface for the two serde-rename-literal-
379/// only axes on this crate's [`ChartYaml`] struct that Rust's
380/// attribute-argument grammar leaves un-substitutable syntactically
381/// (the third top-level axis-key `apiVersion` re-exports through
382/// [`HELM_CHART_KEY_API_VERSION`] below, whose byte-shape coincides
383/// with the sibling [`caixa_core::KUBE_KEY_API_VERSION`] by Helm's
384/// design decision to inherit the K8s CR top-level shape verbatim —
385/// the paired substrate-side
386/// [`caixa_core`-side
387/// `helm_chart_key_api_version_matches_kube_key_api_version`] pin
388/// makes the byte-shape coincidence load-bearing rather than
389/// accidental).
390pub use caixa_core::HELM_CHART_KEY_APP_VERSION;
391
392/// Canonical Helm 3 `Chart.yaml` top-level YAML axis-key naming the
393/// per-chart chart-schema-apiVersion field — re-export of the lifted
394/// [`caixa_core::HELM_CHART_KEY_API_VERSION`] so the top-level chart-
395/// schema-apiVersion YAML key the [`ChartYaml`] `api_version` field's
396/// `#[serde(rename = "apiVersion")]` attribute encodes lives in
397/// exactly one place across every caixa renderer. Rust's attribute
398/// grammar admits only string literals so the const cannot substitute
399/// for the literal syntactically, but the drift-detection pin at
400/// [`tests::chart_yaml_serializes_api_version_axis_under_lifted_helm_chart_key_api_version`]
401/// round-trips a rendered `Chart.yaml` and asserts the top-level
402/// `Mapping::get(HELM_CHART_KEY_API_VERSION)` resolves, closing the
403/// drift the attribute-literal-only grammar leaves silent (a future
404/// refactor that dropped the `#[serde(rename = "apiVersion")]`
405/// attribute would silently serialize the field as Rust's default
406/// snake_case `api_version:`, which Helm's chart-schema parser
407/// rejects at `helm lint` / `helm dependency build` / `helm template`
408/// time with an "apiVersion is required" error far from the drift
409/// site). Peer to [`HELM_CHART_KEY_TYPE`] / [`HELM_CHART_KEY_APP_VERSION`]
410/// on the sibling per-Chart.yaml top-level YAML axis-key re-export
411/// surface — completes the per-Chart.yaml top-level YAML axis-key
412/// re-export trio at the caixa-helm surface for the three serde-
413/// rename-literal-only axes on this crate's [`ChartYaml`] struct.
414pub use caixa_core::HELM_CHART_KEY_API_VERSION;
415
416/// Canonical Helm 3 `Chart.yaml` top-level YAML axis-key naming the
417/// per-chart dependency-list field — re-export of the lifted
418/// [`caixa_core::HELM_CHART_KEY_DEPENDENCIES`] so the load-bearing serde
419/// field-name at [`ChartYaml`]'s `dependencies` field (the parent
420/// list-container the already-re-exported per-`dependencies[]`-entry
421/// sub-mapping tetrad [`HELM_CHART_DEPENDENCY_KEY_NAME`] /
422/// [`HELM_CHART_DEPENDENCY_KEY_VERSION`] /
423/// [`HELM_CHART_DEPENDENCY_KEY_REPOSITORY`] /
424/// [`HELM_CHART_DEPENDENCY_KEY_ALIAS`] mounts one level down under)
425/// lives in exactly one place across every caixa renderer. The Rust
426/// field name and the wire key coincide by default (no
427/// `#[serde(rename)]` attribute today), so the const doesn't substitute
428/// for the field-name syntactically at the struct definition, but the
429/// drift-detection pin at
430/// [`tests::chart_yaml_serializes_dependencies_axis_under_lifted_helm_chart_key_dependencies`]
431/// round-trips a rendered `Chart.yaml` through
432/// `serde_yaml::from_str::<serde_yaml::Value>` and asserts the top-
433/// level `Mapping::get(HELM_CHART_KEY_DEPENDENCIES)` resolves — closing
434/// the drift a future field rename (`dependencies` → `deps` /
435/// `chartDependencies`) or a `#[serde(rename_all = "camelCase")]`
436/// attribute addition on [`ChartYaml`] would otherwise leave silent
437/// (Helm's chart-schema parser silently drops the entire dep list from
438/// the parsed chart-metadata, `helm dependency build` finds no chart to
439/// vendor, and every rendered `lareira-<nome>` chart's install fails at
440/// apply time far from the drift site). Peer to
441/// [`HELM_CHART_KEY_TYPE`] / [`HELM_CHART_KEY_APP_VERSION`] /
442/// [`HELM_CHART_KEY_API_VERSION`] on the sibling per-Chart.yaml
443/// top-level YAML axis-key re-export surface — extends the per-
444/// Chart.yaml top-level YAML axis-key re-export trio at the caixa-helm
445/// surface onto the fourth top-level axis-key, the parent list-
446/// container whose per-entry sub-mapping tetrad is already re-exported
447/// under [`HELM_CHART_DEPENDENCY_KEY_*`].
448pub use caixa_core::HELM_CHART_KEY_DEPENDENCIES;
449
450/// Canonical Helm 3 `Chart.yaml` per-`dependencies[]`-entry sub-mapping
451/// YAML axis-key naming the per-dep chart-name field — re-export of the
452/// lifted [`caixa_core::HELM_CHART_DEPENDENCY_KEY_NAME`] so the load-
453/// bearing serde field-name at [`ChartDependency`]'s `name` field lives
454/// in exactly one place across every caixa renderer. Peer to
455/// [`HELM_CHART_DEPENDENCY_KEY_VERSION`] /
456/// [`HELM_CHART_DEPENDENCY_KEY_REPOSITORY`] /
457/// [`HELM_CHART_DEPENDENCY_KEY_ALIAS`] on the sibling per-dep sub-key
458/// axes. The drift-detection round-trip pin at
459/// [`tests::chart_dependency_serializes_tetrad_under_lifted_helm_chart_dependency_keys`]
460/// serializes a fully-populated [`ChartDependency`] and asserts each of
461/// the four per-dep sub-mapping wire keys resolves — closing the drift
462/// a rename of the Rust field or a `#[serde(rename_all)]` attribute
463/// addition would otherwise leave silent.
464pub use caixa_core::HELM_CHART_DEPENDENCY_KEY_NAME;
465
466/// Canonical Helm 3 `Chart.yaml` per-`dependencies[]`-entry sub-mapping
467/// YAML axis-key naming the per-dep chart-version-constraint field —
468/// re-export of the lifted [`caixa_core::HELM_CHART_DEPENDENCY_KEY_VERSION`]
469/// so the load-bearing serde field-name at [`ChartDependency`]'s
470/// `version` field lives in exactly one place across every caixa
471/// renderer. Peer to [`HELM_CHART_DEPENDENCY_KEY_NAME`] on the sibling
472/// per-dep sub-key axes. See [`HELM_CHART_DEPENDENCY_KEY_NAME`] for the
473/// shared per-entry-sub-mapping lift rationale.
474pub use caixa_core::HELM_CHART_DEPENDENCY_KEY_VERSION;
475
476/// Canonical Helm 3 `Chart.yaml` per-`dependencies[]`-entry sub-mapping
477/// YAML axis-key naming the per-dep chart-registry URL field — re-export
478/// of the lifted [`caixa_core::HELM_CHART_DEPENDENCY_KEY_REPOSITORY`]
479/// so the load-bearing serde field-name at [`ChartDependency`]'s
480/// `repository` field lives in exactly one place across every caixa
481/// renderer. Peer to [`HELM_CHART_DEPENDENCY_KEY_NAME`] on the sibling
482/// per-dep sub-key axes.
483pub use caixa_core::HELM_CHART_DEPENDENCY_KEY_REPOSITORY;
484
485/// Canonical Helm 3 `Chart.yaml` per-`dependencies[]`-entry sub-mapping
486/// YAML axis-key naming the per-dep chart-alias override field —
487/// re-export of the lifted [`caixa_core::HELM_CHART_DEPENDENCY_KEY_ALIAS`]
488/// so the load-bearing serde field-name at [`ChartDependency`]'s
489/// `alias` field lives in exactly one place across every caixa
490/// renderer. Peer to [`HELM_CHART_DEPENDENCY_KEY_NAME`] on the sibling
491/// per-dep sub-key axes.
492pub use caixa_core::HELM_CHART_DEPENDENCY_KEY_ALIAS;
493
494/// Canonical Helm 3 per-chart-directory metadata-file filename every
495/// rendered `lareira-<nome>` chart carries at its top-level directory —
496/// re-export of the lifted [`caixa_core::HELM_CHART_YAML_FILENAME`] so
497/// the fixed lookup name Helm's chart-schema parser (`helm dependency
498/// build`, `helm lint`, `helm template`, `helm install`) consults at
499/// chart-open time to locate the per-chart schema-body scalars
500/// ([`HELM_CHART_API_VERSION`], [`HELM_CHART_TYPE_APPLICATION`], the
501/// name/version/dependencies fields) lives in exactly one place across
502/// every caixa renderer. The single production-code call site
503/// consuming it is [`render_chart_for_servico`]'s `ChartDir` assembly
504/// where the metadata file's per-`ChartFile` `path` axis is set (the
505/// sole emitter site the prior inline `PathBuf::from("Chart.yaml")`
506/// literal sat at); every test-side round-trip navigator that reaches
507/// into the rendered `ChartDir` by the metadata filename (the
508/// per-chart-metadata-field sweep tests +
509/// [`ChartDir::write_to`] post-write existence pin) now consults the
510/// same `&'static str`, so a rebrand of the Helm 3 metadata-file axis
511/// (any per-fork `Chartfile.yaml` / Helm 4 metadata-file rename the
512/// upstream packaging spec might adopt) lands at one const and reaches
513/// every consumer by construction. A drifted local `pub const
514/// HELM_CHART_YAML_FILENAME: &str = "…"` at this crate — the canonical
515/// drift footgun where a sibling local `pub const` could happen to
516/// carry the same string at the source while pointing at a different
517/// `&'static` allocation — surfaces as one of two silent failure modes
518/// at chart-consumption time: Helm's chart-schema parser refuses to
519/// open the rendered chart-directory ("Error: Chart.yaml file is
520/// missing") far from the drift commit, or the sibling
521/// [`caixa_flux::cluster_bundle`]'s future per-chart-directory
522/// resolver — a per-cluster snapshot bundle that re-lists the
523/// chart-dir contents by filename — silently returns `None` at
524/// cluster-side `feira app deploy` time. The equality + `&'static`
525/// static-data identity pin
526/// (`helm_chart_yaml_filename_re_export_points_at_caixa_core_canonical`)
527/// closes the drift footgun at caixa-helm build time. Peer to the
528/// [`HELM_CHART_API_VERSION`] / [`HELM_CHART_TYPE_APPLICATION`]
529/// re-exports on the sibling canonical-Helm-chart-schema-body-axis
530/// surface — completes the per-`lareira-<nome>`-chart-directory
531/// `(filename, apiVersion, type)` canonical-scalar-axis re-export
532/// triple every rendered chart declares at its top-level metadata file.
533pub use caixa_core::HELM_CHART_YAML_FILENAME;
534
535/// Canonical Helm 3 per-chart-directory values-file filename every
536/// rendered `lareira-<nome>` chart carries at its top-level directory —
537/// re-export of the lifted [`caixa_core::HELM_VALUES_YAML_FILENAME`] so
538/// the fixed lookup name Helm's chart-schema parser (`helm dependency
539/// build`, `helm lint`, `helm template`, `helm install`) consults at
540/// chart-open time to locate the per-chart values block that
541/// [`HELM_VALUES_KEY_ENABLED`] toggles under its
542/// [`DEFAULT_LIBRARY_NAME`] wrap key lives in exactly one place across
543/// every caixa renderer. The single production-code call site
544/// consuming it is [`render_chart_for_servico`]'s `ChartDir` assembly
545/// where the values file's per-`ChartFile` `path` axis is set (the
546/// sole emitter site the prior inline `PathBuf::from("values.yaml")`
547/// literal sat at); every test-side round-trip navigator that reaches
548/// into the rendered `ChartDir` by the values filename (the
549/// per-chart-values-field sweep tests +
550/// [`ChartDir::write_to`] post-write existence pin) now consults the
551/// same `&'static str`, so a rebrand of the Helm 3 values-file axis
552/// (any per-fork `defaults.yaml` / Helm 4 values-file rename the
553/// upstream packaging spec might adopt) lands at one const and reaches
554/// every consumer by construction. A drifted local `pub const
555/// HELM_VALUES_YAML_FILENAME: &str = "…"` at this crate — the canonical
556/// drift footgun where a sibling local `pub const` could happen to
557/// carry the same string at the source while pointing at a different
558/// `&'static` allocation — surfaces as one of two silent failure modes
559/// at chart-consumption time: Helm's per-chart values-loader silently
560/// falls back to the empty values block (`helm template` emits the
561/// library chart under its admission-time defaults, the workload comes
562/// up disabled or without any per-Servico M2 overlay applied) far from
563/// the drift commit, or the sibling
564/// [`caixa_flux::cluster_bundle`]'s future per-chart-directory
565/// resolver — a per-cluster snapshot bundle that re-lists the
566/// chart-dir contents by filename to route per-cluster values overlays
567/// through the canonical values file — silently returns `None` at
568/// cluster-side `feira app deploy` time. The equality + `&'static`
569/// static-data identity pin
570/// (`helm_values_yaml_filename_re_export_points_at_caixa_core_canonical`)
571/// closes the drift footgun at caixa-helm build time. Peer to the
572/// [`HELM_CHART_YAML_FILENAME`] re-export on the sibling
573/// canonical-Helm-per-chart-directory-metadata-file-axis surface —
574/// completes the per-`lareira-<nome>`-chart-directory
575/// `(Chart.yaml, values.yaml)` canonical-per-chart-directory-filename-
576/// axis re-export pair every rendered chart declares as its two
577/// schema-load-bearing `ChartDir::files` entries.
578pub use caixa_core::HELM_VALUES_YAML_FILENAME;
579
580/// Canonical `lareira-<nome>` chart-directory human-facing readme
581/// filename every rendered chart carries at its top-level directory —
582/// re-export of the lifted [`caixa_core::HELM_CHART_README_FILENAME`] so
583/// the third leg of the canonical `{Chart.yaml, values.yaml, README.md}`
584/// per-`lareira-<nome>` chart-directory `ChartFile` triple lives in
585/// exactly one place across every caixa renderer. The single
586/// production-code call site consuming it is
587/// [`render_chart_for_servico`]'s `ChartDir` assembly where the readme
588/// file's per-`ChartFile` `path` axis is set (the sole emitter site the
589/// prior inline `"README.md"` string literal sat at); every test-side
590/// round-trip navigator that reaches into the rendered `ChartDir` by
591/// the readme filename (the [`render_chart_for_servico`] files-vec-
592/// membership pin + the [`ChartDir::write_to`] post-write existence
593/// pin — two sites) now consults the same `&'static str`. A drifted
594/// local `pub const HELM_CHART_README_FILENAME: &str = "…"` at this
595/// crate — the canonical drift footgun where a sibling local
596/// `pub const` could happen to carry the same string at the source
597/// while pointing at a different `&'static` allocation — surfaces as
598/// GitHub / Artifact Hub / any downstream per-chart README-surfacing
599/// UI silently falling back to "no README available" for the rendered
600/// `lareira-<nome>` chart far from the drift commit's source, with no
601/// field naming the readme-filename-drift root cause. The equality +
602/// `&'static` static-data identity pin
603/// (`helm_chart_readme_filename_re_export_points_at_caixa_core_canonical`)
604/// closes the drift footgun at caixa-helm build time. Peer to the
605/// [`HELM_CHART_YAML_FILENAME`] / [`HELM_VALUES_YAML_FILENAME`]
606/// re-exports on the sibling canonical-Helm-per-chart-directory-
607/// filename axes — completes the per-`lareira-<nome>`-chart-directory
608/// `(Chart.yaml, values.yaml, README.md)` canonical-per-chart-directory-
609/// filename-axis re-export triple every rendered chart declares as its
610/// three `ChartDir::files` entries.
611pub use caixa_core::HELM_CHART_README_FILENAME;
612
613/// Canonical K8s CR top-level `spec` key. Re-export of the canonical
614/// [`caixa_core::KUBE_KEY_SPEC`] so the per-kind body key lives in
615/// exactly one place across every caixa renderer — caixa-helm's
616/// `build_values_yaml` (the upstream ComputeUnit YAML's `spec.*` axis
617/// the rendered `lareira-<nome>` chart's values block re-routes
618/// through the library alias) now consults the same `&'static str` as
619/// the peer caixa-flux / caixa-mesh renderers' `KUBE_KEY_SPEC`
620/// re-exports. The prior inline `"spec"` literal at the production-
621/// code call site would have let a typo (e.g. `"Spec"`, `"specs"`,
622/// `"spec_"`) silently emit a values block that drops every typed
623/// ComputeUnit-side field (`module`, `trigger`, `capabilities`,
624/// `resources`, `serviceAccount`) at the rendered chart's landing
625/// site — the `Error::MissingField("spec")` diagnostic now threads
626/// the same `&'static str` through the diagnostic surface so the
627/// error message stays byte-identical to the key it failed to find.
628/// Same shape as the [`DEFAULT_LIBRARY_NAME`] re-export on the
629/// sibling canonical-Helm-load-bearing-string axis.
630pub use caixa_core::KUBE_KEY_SPEC;
631
632/// Canonical `pleme-computeunit` library-chart values-block enable-toggle
633/// key — re-export of the lifted [`caixa_core::HELM_VALUES_KEY_ENABLED`]
634/// so the values-block toggle every rendered `lareira-<nome>` chart's
635/// values.yaml carries under its [`DEFAULT_LIBRARY_NAME`] wrap key lives
636/// in exactly one place across every caixa renderer. The single
637/// production-code call site consuming it is [`build_values_yaml`]'s
638/// `block.insert(HELM_VALUES_KEY_ENABLED.to_string(), …)` (formerly an
639/// inline `"enabled".to_string()` literal at `caixa-helm/src/lib.rs:389`);
640/// the peer test-fixture navigators pinning the default-off round-trip
641/// (`values_yaml_wraps_under_pleme_computeunit_key`,
642/// `values_yaml_wrap_key_follows_library_name_override`) also consult the
643/// re-export so a rebrand of the library-chart's per-values enable-toggle
644/// axis lands at one const and reaches every consumer by construction.
645/// A drifted local `pub const HELM_VALUES_KEY_ENABLED: &str = "…"` (or
646/// any sibling per-renderer variant that inlined a stale
647/// `"enabled"` / `"enable"` / `"disabled"` literal) would silently emit
648/// a values block whose per-values enable-toggle lands under one key
649/// while [`caixa_flux::cluster_bundle`]'s `HelmRelease`
650/// `spec.values.<library>.enabled` per-cluster override lands under
651/// another — Helm's per-values merge treats them as sibling scalars, the
652/// enable-toggle the library chart's own template consults never sees the
653/// flip, and the workload silently comes up with the library chart's
654/// admission-time defaults instead of the per-cluster override the
655/// operator set. Same shape as the [`DEFAULT_LIBRARY_NAME`] /
656/// [`KUBE_KEY_SPEC`] / [`HELM_CHART_API_VERSION`] re-exports on the
657/// sibling canonical-Helm-load-bearing-string / canonical-K8s-CR-body-
658/// key / canonical-Helm-chart-schema-apiVersion axes.
659pub use caixa_core::HELM_VALUES_KEY_ENABLED;
660
661/// Canonical substrate-side default for the
662/// `values.<library>.enabled` scalar-value toggle every
663/// [`render_chart_for_servico`]-emitted standalone `lareira-<nome>`
664/// chart's `values.yaml` document seeds inside its per-caixa
665/// [`DEFAULT_LIBRARY_NAME`] wrap block to leave the paired
666/// [`DEFAULT_LIBRARY_NAME`] child chart opted-out at the per-cluster
667/// `helm template` / `helm install` apply step. Re-export of the
668/// canonical [`caixa_core::STANDALONE_LAREIRA_ENABLED_DEFAULT`] so the
669/// substrate-side default the standalone per-chart path seeds under
670/// the sibling [`HELM_VALUES_KEY_ENABLED`] leaf-scalar-key lives in
671/// exactly one place across every caixa renderer. Consumed by
672/// [`RenderOpts::default`]'s `enabled_default` field seed (formerly
673/// an inline `false` scalar-value literal at
674/// `caixa-helm/src/lib.rs:700`); the peer test-fixture navigators
675/// pinning the default-off round-trip also consult the re-export so a
676/// rebrand of the per-values-block child-chart-enablement-toggle scalar
677/// on the standalone per-chart path lands at one const and reaches
678/// every consumer by construction. Semantically distinct from — and
679/// inverse of — the peer
680/// [`caixa_flux::CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT`] on the
681/// composition per-cluster-`HelmRelease` values-overlay path (which
682/// force-ons the child chart under the substrate-side composition
683/// path); the two peer scalar-value defaults name mirror-symmetric
684/// per-path child-chart-enablement-toggle-scalar-value defaults at the
685/// exact same `values.<library>.enabled` sub-block position on the
686/// standalone per-chart-`values.yaml` path (this re-export) and the
687/// composition per-cluster-`HelmRelease` values-overlay path (the peer
688/// re-export). Same shape as the [`DEFAULT_LIBRARY_NAME`] /
689/// [`KUBE_KEY_SPEC`] / [`HELM_VALUES_KEY_ENABLED`] re-exports on the
690/// sibling canonical-Helm-load-bearing-string / canonical-K8s-CR-body-
691/// key / canonical-Helm-per-values-block-enable-toggle-key axes. See
692/// [`caixa_core::STANDALONE_LAREIRA_ENABLED_DEFAULT`] for the full lift
693/// rationale.
694pub use caixa_core::STANDALONE_LAREIRA_ENABLED_DEFAULT;
695
696/// Local re-export of the canonical
697/// [`caixa_core::COMPUTEUNIT_SPEC_KEY_MODULE`] — the
698/// `wasm.pleme.io/v1alpha1/ComputeUnit` CRD per-CR wasm-module-reference
699/// `spec.module` sub-block key every rendered `values.yaml`'s
700/// [`DEFAULT_LIBRARY_NAME`]-wrapped block carries so the
701/// `pleme-computeunit` library chart's per-Servico module-source axis
702/// binds to the exact source the caixa.lisp's `:servicos` fixture
703/// pins. Two per-values drift-detection navigators in this crate's
704/// test module (the canonical-wrap-key round-trip + the
705/// `library-name`-override wrap-key round-trip) now consult the same
706/// `&'static str` as the peer caixa-flux writer's per-Servico
707/// `programs[]`-entry module-source navigators. Same re-export shape
708/// as the peer [`HELM_VALUES_KEY_ENABLED`] / [`KUBE_KEY_SPEC`]
709/// surfaces on the sibling canonical-Helm-load-bearing-string /
710/// canonical-K8s-CR-body-key axes — extends the discipline the
711/// M2-typed-slot / K8s-CR key re-export families establish onto the
712/// substrate-side ComputeUnit-CRD per-`spec.*` sub-block axis. See
713/// [`caixa_core::COMPUTEUNIT_SPEC_KEY_MODULE`] for the full lift
714/// rationale.
715pub use caixa_core::COMPUTEUNIT_SPEC_KEY_MODULE;
716
717/// Local re-export of the canonical
718/// [`caixa_core::COMPUTEUNIT_SPEC_KEY_TRIGGER`] — the
719/// `wasm.pleme.io/v1alpha1/ComputeUnit` CRD per-CR invocation-shape
720/// `spec.trigger` sub-block key every rendered `values.yaml`'s
721/// [`DEFAULT_LIBRARY_NAME`]-wrapped block carries so the
722/// `pleme-computeunit` library chart's per-Servico
723/// `trigger.service.{port, paths, breathability}` routing binds to
724/// the exact axis the caixa.lisp's `:servicos` fixture pins. Peer of
725/// [`COMPUTEUNIT_SPEC_KEY_MODULE`] on the same ComputeUnit CRD
726/// per-`spec.*` sub-block axis — see
727/// [`caixa_core::COMPUTEUNIT_SPEC_KEY_TRIGGER`] for the full lift
728/// rationale.
729pub use caixa_core::COMPUTEUNIT_SPEC_KEY_TRIGGER;
730
731/// Local re-export of the canonical
732/// [`caixa_core::COMPUTEUNIT_SPEC_KEY_CAPABILITIES`] — the
733/// `wasm.pleme.io/v1alpha1/ComputeUnit` CRD per-CR WASI-capability-list
734/// `spec.capabilities` sub-block key every rendered `values.yaml`'s
735/// [`DEFAULT_LIBRARY_NAME`]-wrapped block carries so the
736/// `pleme-computeunit` library chart's per-Servico WASI-preview-2
737/// capability-token binding fires exactly against the axis the
738/// caixa.lisp's `:servicos` fixture pins. Peer of
739/// [`COMPUTEUNIT_SPEC_KEY_MODULE`] and [`COMPUTEUNIT_SPEC_KEY_TRIGGER`]
740/// on the same ComputeUnit CRD per-`spec.*` sub-block axis — completes
741/// the substrate-side ComputeUnit-CRD per-`spec.*` sub-block re-export
742/// triple in this crate. See
743/// [`caixa_core::COMPUTEUNIT_SPEC_KEY_CAPABILITIES`] for the full lift
744/// rationale.
745pub use caixa_core::COMPUTEUNIT_SPEC_KEY_CAPABILITIES;
746
747/// Local re-export of the canonical
748/// [`caixa_core::servico_spec_and_m2_overlay_entries`] — the composed
749/// per-Servico value-block splice helper this crate's
750/// [`build_values_yaml`] and the peer
751/// [`caixa_flux::programs_yaml_entry`] both now route their two-step
752/// `spec.*` field-splice + M2 typed-slot overlay through. The single
753/// production-code call site consuming it is [`build_values_yaml`]'s
754/// inner splice loop (formerly two hand-written for-loops chained
755/// around `string_keyed_entries` + `servico_m2_overlay`); re-exported
756/// so the shared composition contract lives in exactly one place
757/// across both per-Servico renderers — a future author reading
758/// `caixa_helm::build_values_yaml` finds the composition helper
759/// immediately without an extra `use caixa_core::…` line, and a
760/// rebrand of the composition axis (e.g. a swap of the `or_insert`
761/// precedence rule once per-Aplicacao operator overrides land)
762/// reaches both renderers through one canonical `&'static` function
763/// pointer. Same shape as the peer [`COMPUTEUNIT_SPEC_KEY_MODULE`] /
764/// [`COMPUTEUNIT_SPEC_KEY_TRIGGER`] / [`COMPUTEUNIT_SPEC_KEY_CAPABILITIES`]
765/// re-exports on the sibling canonical-ComputeUnit-CRD `spec.*` axis
766/// — extends the shared-composition discipline the per-`spec.*`
767/// sub-block re-export triple establishes onto the composed
768/// spec.*+M2 splice axis every per-Servico renderer navigates.
769pub use caixa_core::servico_spec_and_m2_overlay_entries;
770
771/// Knobs that don't come from the Caixa manifest.
772#[derive(Debug, Clone)]
773pub struct RenderOpts {
774    /// Where the library chart lives. Default = `file://../pleme-computeunit`.
775    pub library_repo: String,
776    pub library_version: String,
777    pub library_name: String,
778    /// Whether the rendered values block is `enabled: false` by default
779    /// (matching `lareira-hello-world` so cluster operators flip it on
780    /// per-cluster). Default: [`STANDALONE_LAREIRA_ENABLED_DEFAULT`]
781    /// (`false` — the substrate's chosen standalone per-chart
782    /// opt-out seed, inverse of the composition per-cluster-`HelmRelease`
783    /// values-overlay path's [`caixa_flux::CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT`]
784    /// force-on).
785    pub enabled_default: bool,
786}
787
788impl Default for RenderOpts {
789    fn default() -> Self {
790        Self {
791            library_repo: DEFAULT_LIBRARY_REPO.into(),
792            library_version: DEFAULT_LIBRARY_VERSION.into(),
793            library_name: DEFAULT_LIBRARY_NAME.into(),
794            enabled_default: STANDALONE_LAREIRA_ENABLED_DEFAULT,
795        }
796    }
797}
798
799/// Render a per-program lareira-<name> chart from a Caixa Servico + its
800/// loaded ComputeUnit YAML.
801///
802/// The ComputeUnit YAML is passed in as a `serde_yaml::Value` because the
803/// authoritative schema lives in the wasm-operator's CRD — we don't want
804/// caixa-helm to drift from that schema. It's enough that we can locate
805/// `spec` and pass it through.
806pub fn render_chart_for_servico(
807    caixa: &Caixa,
808    computeunit_yaml: &serde_yaml::Value,
809) -> Result<ChartDir, Error> {
810    render_chart_for_servico_with(caixa, computeunit_yaml, &RenderOpts::default())
811}
812
813/// `render_chart_for_servico` with explicit options.
814pub fn render_chart_for_servico_with(
815    caixa: &Caixa,
816    computeunit_yaml: &serde_yaml::Value,
817    opts: &RenderOpts,
818) -> Result<ChartDir, Error> {
819    caixa_core::require_v0_servico_shape::<Error>(caixa)?;
820
821    // Route the per-`ChartDir.name` `lareira-<nome>` chart-identity
822    // composer through the substrate-canonical
823    // [`caixa_core::Caixa::lareira_chart_name`] resolved-chart-name
824    // dispatch rather than the two-step
825    // [`caixa_core::lareira_chart_name`]-of-[`caixa_core::Caixa::nome`]
826    // open-coded compose — the load-bearing per-Servico Helm chart
827    // identity now reaches through exactly one typed dispatch on the
828    // substrate primitive. Peer of the sibling
829    // [`Caixa::canonical_git_url`] (124f864) / [`Caixa::publish_tag`]
830    // (07e05b8) resolved-composers on the paired per-`Caixa` published-
831    // artifact-identity axis — every substrate-side consumer that
832    // resolves "which chart does this caixa publish under?" now travels
833    // through one accessor rather than a per-caller two-step compose.
834    // Pinned by the drift-detection test
835    // [`chart_dir_name_routes_through_caixa_lareira_chart_name_accessor`]
836    // in the tests module.
837    let chart_name = caixa.lareira_chart_name();
838    let chart_yaml = build_chart_yaml(caixa, &chart_name, opts);
839    let values_yaml = build_values_yaml(caixa, computeunit_yaml, opts)?;
840    let readme = build_readme(caixa, &chart_name);
841
842    // Each per-artifact leaf routes through the canonical
843    // [`caixa_core::RenderedFile::new`] `impl Into<PathBuf>` /
844    // `impl Into<String>` constructor (re-exported by the peer
845    // [`ChartFile`] alias since Rust inherent methods travel through
846    // type aliases to the aliased type at name resolution). The prior
847    // three inline `ChartFile { path: PathBuf::from(FILENAME_CONST),
848    // contents: <body> }` blocks each re-derived the same
849    // `PathBuf::from(&str)` wrap + the same two-field assembly — a
850    // byte-identical duplicate of the peer [`caixa_flux::cluster_bundle`]
851    // Flux v2 CR trio's three per-CR emit sites. Sweeping both trios
852    // onto [`RenderedFile::new`] collapses the six substrate-side
853    // per-artifact-construction sites onto one canonical constructor,
854    // so a future rebrand on the record shape (a per-artifact hash /
855    // provenance field addition, a per-artifact write-mode discriminator
856    // once per-cluster-writer sandboxing lands, the
857    // [`caixa_core::is_sandboxed_relative_path`] discipline the
858    // [`RenderedFile`] docstring acknowledges is not yet run at emit
859    // time) reaches every per-target renderer through one caixa-core
860    // edit instead of a coordinated six-site rewrite.
861    Ok(ChartDir {
862        name: chart_name,
863        files: vec![
864            ChartFile::new(
865                HELM_CHART_YAML_FILENAME,
866                serde_yaml::to_string(&chart_yaml)?,
867            ),
868            ChartFile::new(HELM_VALUES_YAML_FILENAME, values_yaml),
869            ChartFile::new(HELM_CHART_README_FILENAME, readme),
870        ],
871    })
872}
873
874fn build_chart_yaml(caixa: &Caixa, chart_name: &str, opts: &RenderOpts) -> ChartYaml {
875    let description = caixa
876        .descricao()
877        .map(str::to_owned)
878        .unwrap_or_else(|| format!("Generated chart for caixa Servico {}", caixa.nome()));
879    let keywords: Vec<String> = caixa
880        .etiquetas()
881        .iter()
882        .cloned()
883        .chain(
884            caixa_core::LAREIRA_CHART_KEYWORDS
885                .iter()
886                .copied()
887                .map(String::from),
888        )
889        .collect::<Vec<_>>()
890        .into_iter()
891        .collect::<std::collections::BTreeSet<_>>()
892        .into_iter()
893        .collect();
894    let maintainers = caixa
895        .autores()
896        .iter()
897        .map(|a| Maintainer {
898            name: a.clone(),
899            email: None,
900        })
901        .collect();
902    // Canonical typed `String`-carry of the per-`Caixa` `:versao`
903    // universal-axis SemVer-2 pinned-version scalar into the two
904    // per-`Chart.yaml` version-carrier fields Helm's chart-schema
905    // parser routes per-chart identity through — `Chart.yaml`'s
906    // top-level `version:` (the axis Helm's per-chart resolver keys
907    // per-release reconciliation off, the paired `HelmRelease`
908    // `spec.chart.spec.version` binds through, and every `helm
909    // template <chart>` / `helm install <release> <chart>` /
910    // `helm upgrade <release> <chart> --version` invocation names
911    // through) and `Chart.yaml`'s top-level `appVersion:` (the
912    // axis Helm chart-consumers key per-application-version
913    // documentation / release-note / OCI-tag / operator-side
914    // per-Caixa CR revision off), both routing through the typed
915    // [`caixa_core::Caixa::versao`] accessor's canonical
916    // `to_string()` extension of `&self.versao`. Peer of the
917    // sibling 4a363bf / 54bf2f3 `caixa.nome.clone()` converges
918    // on the outer-Caixa `:nome` `String`-carry axis in caixa-flux
919    // / caixa-mesh — this converges the last unlifted per-Caixa
920    // `.versao.clone()` raw-field `String`-carry axis in
921    // caixa-helm on the same "one typed dispatch per axis"
922    // discipline.
923    let versao = caixa.versao().to_string();
924    ChartYaml {
925        api_version: HELM_CHART_API_VERSION.into(),
926        name: chart_name.into(),
927        description,
928        chart_type: HELM_CHART_TYPE_APPLICATION.into(),
929        version: versao.clone(),
930        app_version: versao,
931        keywords,
932        maintainers,
933        home: caixa.repositorio().map(str::to_owned),
934        dependencies: vec![ChartDependency {
935            name: opts.library_name.clone(),
936            version: opts.library_version.clone(),
937            repository: opts.library_repo.clone(),
938            alias: None,
939        }],
940    }
941}
942
943fn build_values_yaml(
944    caixa: &Caixa,
945    computeunit_yaml: &serde_yaml::Value,
946    opts: &RenderOpts,
947) -> Result<String, Error> {
948    // The library chart consumes its values under the key matching its
949    // Helm chart `dependencies[].name` (Helm's per-dep alias convention
950    // — when no `alias:` is set on the dependency, values are scoped
951    // under the dependency's `name`). This renderer wires both axes
952    // through the same `opts.library_name`: the chart's dep `name:`
953    // (build_chart_yaml at line 277) and this site's values wrap key
954    // both consult one `&str`, so a future fork that overrides
955    // `RenderOpts::library_name` to point at `acme-computeunit` /
956    // `pleme-computeunit-mirror` / the future per-edition library name
957    // reaches both axes by construction. Until this lift landed the
958    // wrap key was hardcoded `"pleme-computeunit"` while the dep name
959    // followed `opts.library_name`, so an override silently emitted
960    // values keyed under one name (the literal) while the rendered
961    // Chart.yaml's dep was declared under another (the override) —
962    // Helm's per-dep values router would route nothing to the
963    // configured dependency at `helm template` / `helm install` time,
964    // and every typed value the values block carries (`enabled`,
965    // `module`, `trigger`, the M2 overlay's `:limits`/`:behavior`/
966    // `:upgrade-from`) would silently no-op at the rendered chart's
967    // landing site. The wrap key now reads from the same `&str` the
968    // dep name reads from, structurally closing the drift footgun
969    // peer with the [`caixa_core::DEFAULT_NAMESPACE`] /
970    // [`caixa_core::DEFAULT_SERVICO_PORT`] lifts on the sibling
971    // canonical-K8s-axis constants (where two production-code call
972    // sites of the same load-bearing value would drift apart on
973    // any rebrand without a shared source of truth).
974    let library_alias = opts.library_name.as_str();
975    let spec = computeunit_yaml
976        .get(KUBE_KEY_SPEC)
977        .ok_or(Error::MissingField(KUBE_KEY_SPEC))?;
978
979    // Prepend a comment header so the file is human-friendly.
980    let header = format!(
981        "# Auto-generated by caixa-helm from caixa.lisp + servicos/{nome}.computeunit.yaml.\n\
982         # Edits to this file are overwritten by `feira chart`.\n\
983         #\n\
984         # `{library_alias}:` is the alias under which the library chart\n\
985         # in pleme-io/helmworks/charts/{library_alias} consumes its values.\n\n",
986        nome = caixa.nome()
987    );
988
989    let mut block = BTreeMap::new();
990    block.insert(
991        HELM_VALUES_KEY_ENABLED.to_string(),
992        serde_yaml::Value::Bool(opts.enabled_default),
993    );
994    // Two-step per-Servico value-block splice — the `spec.*` field
995    // splice (module / trigger / capabilities / config / resources /
996    // serviceAccount) and the M2 typed-slot overlay (limits / behavior
997    // / upgradeFrom, `or_insert` semantics so `spec.*` wins on
998    // collision) now route through the canonical
999    // [`caixa_core::servico_spec_and_m2_overlay_entries`] composition
1000    // helper — the two prior inline for-loops chained around
1001    // `string_keyed_entries` + `servico_m2_overlay` this call site
1002    // (and the peer [`caixa_flux::programs_yaml_entry`] site) each
1003    // re-derived collapse onto one canonical composition, so a future
1004    // change to the per-Servico splice / overlay shape (the M4 typed
1005    // per-edge policy overlay slot addition MESH-COMPOSITION §III.2 #3
1006    // acknowledges, a change to the precedence rule once per-Aplicacao
1007    // operator overrides land, a canonicalization pass on the merged
1008    // key set) reaches both renderers by construction instead of a
1009    // coordinated two-file rewrite. See the helper's docstring for the
1010    // full lift rationale. The target `BTreeMap` re-sorts by key on
1011    // insert, so the final rendered values block stays byte-identical
1012    // to the prior inline block's alphabetical shape.
1013    for (k, v) in caixa_core::servico_spec_and_m2_overlay_entries(caixa, spec)? {
1014        block.entry(k).or_insert(v);
1015    }
1016
1017    let mut wrapped = serde_yaml::Mapping::new();
1018    wrapped.insert_str_key(library_alias, serde_yaml::to_value(block)?);
1019    let body = serde_yaml::to_string(&serde_yaml::Value::Mapping(wrapped))?;
1020    Ok(format!("{header}{body}"))
1021}
1022
1023fn build_readme(caixa: &Caixa, chart_name: &str) -> String {
1024    let descricao = caixa
1025        .descricao()
1026        .map(str::to_owned)
1027        .unwrap_or_else(|| format!("caixa Servico {}", caixa.nome()));
1028    // Route the per-`README.md` `## Origin` line's `{repo}` interpolation
1029    // through the substrate-canonical [`caixa_core::Caixa::canonical_git_url`]
1030    // resolved-git-URL composer (124f864) rather than the prior
1031    // `caixa.repositorio().unwrap_or(caixa.nome())` two-arm inline fallback.
1032    // The prior shape's `:repositorio`-null arm folded to `caixa.nome()`
1033    // verbatim, so the rendered README's `## Origin` line emitted the
1034    // meaningless `Generated by `caixa-helm` from `<nome>/caixa.lisp``
1035    // scalar on every `:repositorio`-null caixa — a bare `<nome>` prefix
1036    // that told a downstream reader nothing about where the caixa's
1037    // source actually lives, and disagreed on that same axis with the
1038    // sibling [`caixa_flux::ClusterBundleOpts::for_caixa`] `git_url`
1039    // seed which had already routed through the resolved-URL composer
1040    // and emitted the canonical `https://github.com/{DEFAULT_PLEME_GIT_ORG}/<nome>`
1041    // pleme-org fallback on the same author-omitted-`:repositorio` case.
1042    // The 124f864 commit body explicitly anticipated this converge (the
1043    // "same accessor consumed by every renderer" convergence surface it
1044    // called out as the live-behavior-correcting fold distinct from that
1045    // commit's pure byte-preserving convergence). Post-lift the two
1046    // renderers agree byte-for-byte on the resolved-URL substrate
1047    // primitive on both arms, and the README's `## Origin` line names a
1048    // concrete pleme-org URL on the fallback path.
1049    format!(
1050        "# {chart_name}\n\
1051         \n\
1052         {descricao}\n\
1053         \n\
1054         ## Origin\n\
1055         \n\
1056         Generated by `caixa-helm` from `{repo}/caixa.lisp` v{versao}.\n\
1057         Edits here are overwritten by `feira chart`.\n\
1058         \n\
1059         ## Install\n\
1060         \n\
1061         ```bash\n\
1062         helm dependency build\n\
1063         helm template {chart_name} . --values values.yaml\n\
1064         ```\n\
1065         \n\
1066         ## License\n\
1067         \n\
1068         {license}.\n",
1069        chart_name = chart_name,
1070        descricao = descricao,
1071        repo = caixa.canonical_git_url(),
1072        versao = caixa.versao(),
1073        // Route the per-`README.md` `## License` line's `{license}`
1074        // interpolation through the substrate-canonical
1075        // [`caixa_core::CAIXA_LICENCA_DEFAULT`] typed `pub const` rather
1076        // than the prior raw `"MIT"` byte literal fallback arm — one
1077        // source of truth for the substrate-side per-`Caixa`
1078        // author-omitted-`:licenca` SPDX-shaped license-expression
1079        // fallback that every substrate-side consumer of the
1080        // author-omitted `:licenca` slot degrades onto. Peer of the
1081        // sibling `## Origin` line's `{repo}` interpolation already
1082        // routed through [`caixa_core::Caixa::canonical_git_url`]
1083        // (124f864) on the paired per-`README.md` universal-axis
1084        // fallback surface — the two typed dispatches jointly close
1085        // the `lareira-<nome>` chart's `README.md` universal-axis
1086        // author-omitted-slot fallback resolution at the substrate
1087        // primitive. Same "close the substrate-side default at one
1088        // canonical arm on the substrate primitive, converge every
1089        // prior open-coded caller onto the arm" discipline the sibling
1090        // M3 per-`:placement :estrategia`
1091        // [`caixa_core::PLACEMENT_ESTRATEGIA_DEFAULT`] (29c21ca) and
1092        // the M2 per-`:supervisor` default set carry on the paired
1093        // typed-slot-default axes, extended to the outer top-level
1094        // [`caixa_core::Caixa`] universal-axis `:licenca` fallback.
1095        // Pinned by
1096        // `build_readme_license_line_routes_through_lifted_caixa_licenca_default`
1097        // in the tests module.
1098        license = caixa.licenca().unwrap_or(caixa_core::CAIXA_LICENCA_DEFAULT),
1099    )
1100}
1101
1102#[cfg(test)]
1103mod tests {
1104    use super::*;
1105    use caixa_core::{
1106        Caixa, CaixaKind, M2_BEHAVIOR_KEY_ON_CALL, M2_BEHAVIOR_KEY_ON_INIT, M2_KEY_BEHAVIOR,
1107        M2_KEY_LIMITS, M2_KEY_UPGRADE_FROM, M2_LIMITS_KEY_CPU, M2_LIMITS_KEY_FUEL,
1108        M2_LIMITS_KEY_MEMORY, M2_LIMITS_KEY_WALL_CLOCK, find_file_by_path, kube_has, kube_str,
1109        kube_u64, mapping_string_keys, parse_yaml_at_path, parse_yaml_at_path_as,
1110    };
1111    use std::path::PathBuf;
1112
1113    fn sample_caixa() -> Caixa {
1114        Caixa {
1115            nome: "hello-rio".into(),
1116            versao: "0.1.0".into(),
1117            kind: CaixaKind::Servico,
1118            edicao: Some("2026".into()),
1119            descricao: Some("Canonical Rust→wasm32-wasip2 caixa Servico.".into()),
1120            repositorio: Some("github:pleme-io/hello-rio".into()),
1121            licenca: Some("MIT".into()),
1122            autores: vec!["pleme-io".into()],
1123            etiquetas: vec!["hello-world".into(), "wasm".into(), "rust".into()],
1124            deps: vec![],
1125            deps_dev: vec![],
1126            exe: vec![],
1127            bibliotecas: vec![],
1128            servicos: vec!["servicos/hello-rio.computeunit.yaml".into()],
1129            limits: None,
1130            behavior: None,
1131            upgrade_from: vec![],
1132            estrategia: None,
1133            max_restarts: None,
1134            restart_window: None,
1135            children: vec![],
1136            membros: vec![],
1137            contratos: vec![],
1138            politicas: None,
1139            placement: None,
1140            entrada: None,
1141            ci: None,
1142        }
1143    }
1144
1145    fn sample_cu_yaml() -> serde_yaml::Value {
1146        serde_yaml::from_str(
1147            r#"
1148apiVersion: wasm.pleme.io/v1alpha1
1149kind: ComputeUnit
1150metadata:
1151  name: hello-rio
1152spec:
1153  module:
1154    source: oci://ghcr.io/pleme-io/hello-rio:v0.1.0
1155  trigger:
1156    service:
1157      port: 8080
1158      paths: ["/", "/hello", "/healthz"]
1159      breathability:
1160        enabled: true
1161        minReplicas: 0
1162        maxReplicas: 5
1163        cooldownPeriod: 600
1164  capabilities:
1165    - http-in:0.0.0.0:8080
1166    - env
1167"#,
1168        )
1169        .unwrap()
1170    }
1171
1172    #[test]
1173    fn renders_three_files() {
1174        let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
1175        assert_eq!(dir.name, "lareira-hello-rio");
1176        let names: Vec<_> = dir
1177            .files
1178            .iter()
1179            .map(|f| f.path.to_string_lossy().to_string())
1180            .collect();
1181        assert!(names.contains(&HELM_CHART_YAML_FILENAME.to_string()));
1182        assert!(names.contains(&HELM_VALUES_YAML_FILENAME.to_string()));
1183        assert!(names.contains(&HELM_CHART_README_FILENAME.to_string()));
1184    }
1185
1186    #[test]
1187    fn chart_yaml_metadata_propagates() {
1188        let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
1189        let chart: ChartYaml = parse_yaml_at_path_as(&dir.files, HELM_CHART_YAML_FILENAME);
1190        assert_eq!(chart.api_version, "v2");
1191        assert_eq!(chart.name, "lareira-hello-rio");
1192        assert_eq!(chart.version, "0.1.0");
1193        assert_eq!(chart.app_version, "0.1.0");
1194        assert_eq!(chart.dependencies.len(), 1);
1195        assert_eq!(chart.dependencies[0].name, DEFAULT_LIBRARY_NAME);
1196        assert!(chart.keywords.contains(&"caixa-servico".to_string()));
1197        assert!(chart.keywords.contains(&"hello-world".to_string()));
1198        assert_eq!(chart.maintainers[0].name, "pleme-io");
1199    }
1200
1201    #[test]
1202    fn chart_yaml_keywords_union_pins_every_lareira_chart_keywords_entry() {
1203        // Structural pin: `build_chart_yaml`'s substrate-fixed
1204        // chart-keyword union routes through the canonical
1205        // `caixa_core::LAREIRA_CHART_KEYWORDS` array — every rendered
1206        // `lareira-<nome>` chart's emitted `Chart.yaml` `keywords:`
1207        // sequence carries every substrate-fixed entry the array
1208        // declares. A future substrate-fixed keyword addition
1209        // (an `"opentelemetry"` entry once the caixa-otel collector-
1210        // pipeline chart lands, a `"lunatic"` entry once the wasm-
1211        // process-runtime marker lands, a `"gen_server"` entry once
1212        // the OTP-shape callback marker lands per the
1213        // [`caixa_core::behavior`] surface) that lands in the array
1214        // reaches this crate's production emit site by construction
1215        // through the shared `&[&str]` reference — a drift where the
1216        // production emit at `build_chart_yaml` re-inlines the pre-
1217        // lift `["lareira", "wasm", "tatara-lisp", "caixa-servico"]`
1218        // literal set (or drops a per-entry axis on a rebrand
1219        // sweep) fails this pin at caixa-helm build time rather than
1220        // surfacing as a `helm search hub caixa-servico` miss on the
1221        // Artifact Hub keyword-search index at chart-publish time
1222        // downstream. Peer to
1223        // [`chart_yaml_metadata_propagates`] on the
1224        // per-`Chart.yaml`-body-field propagation surface.
1225        let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
1226        let chart: ChartYaml = parse_yaml_at_path_as(&dir.files, HELM_CHART_YAML_FILENAME);
1227        for keyword in caixa_core::LAREIRA_CHART_KEYWORDS {
1228            assert!(
1229                chart.keywords.contains(&(*keyword).to_string()),
1230                "rendered Chart.yaml keywords {:?} must contain the \
1231                 substrate-fixed LAREIRA_CHART_KEYWORDS entry {keyword:?}",
1232                chart.keywords,
1233            );
1234        }
1235    }
1236
1237    #[test]
1238    fn values_yaml_wraps_under_pleme_computeunit_key() {
1239        let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
1240        let parsed = parse_yaml_at_path(&dir.files, HELM_VALUES_YAML_FILENAME);
1241        let cu_block = parsed
1242            .get(DEFAULT_LIBRARY_NAME)
1243            .expect("must wrap under DEFAULT_LIBRARY_NAME");
1244        assert_eq!(
1245            cu_block.get(HELM_VALUES_KEY_ENABLED),
1246            Some(&serde_yaml::Value::Bool(false))
1247        );
1248        assert!(kube_has(cu_block, COMPUTEUNIT_SPEC_KEY_MODULE));
1249        assert!(kube_has(cu_block, COMPUTEUNIT_SPEC_KEY_TRIGGER));
1250        assert!(kube_has(cu_block, COMPUTEUNIT_SPEC_KEY_CAPABILITIES));
1251    }
1252
1253    #[test]
1254    fn values_yaml_wrap_key_follows_library_name_override() {
1255        // Pinning the canonical alignment between the Helm chart's
1256        // `dependencies[].name` axis (build_chart_yaml at line 277) and
1257        // the values block's wrap key (build_values_yaml at the
1258        // `wrapped.insert(...)` site): both consult the same
1259        // `opts.library_name`, so an override on either axis reaches the
1260        // other by construction. Helm's per-dep alias convention — when
1261        // no `alias:` is set on a dependency, values are scoped under
1262        // its `name:` — makes wrap-key drift a silent value-routing
1263        // no-op at `helm template` / `helm install` time, so the
1264        // structural pin is load-bearing.
1265        let opts = RenderOpts {
1266            library_name: "acme-computeunit".into(),
1267            ..RenderOpts::default()
1268        };
1269        let dir = render_chart_for_servico_with(&sample_caixa(), &sample_cu_yaml(), &opts).unwrap();
1270        let parsed = parse_yaml_at_path(&dir.files, HELM_VALUES_YAML_FILENAME);
1271        assert!(
1272            kube_has(&parsed, "acme-computeunit"),
1273            "values wrap key must follow opts.library_name override \
1274             (got top-level keys: {keys:?})",
1275            keys = parsed
1276                .as_mapping()
1277                .map(mapping_string_keys)
1278                .unwrap_or_default()
1279        );
1280        assert!(
1281            !kube_has(&parsed, DEFAULT_LIBRARY_NAME),
1282            "values wrap key must not retain the default `{DEFAULT_LIBRARY_NAME}` literal \
1283             when opts.library_name overrides it"
1284        );
1285        let cu_block = parsed.get("acme-computeunit").unwrap();
1286        assert_eq!(
1287            cu_block.get(HELM_VALUES_KEY_ENABLED),
1288            Some(&serde_yaml::Value::Bool(false))
1289        );
1290        assert!(kube_has(cu_block, COMPUTEUNIT_SPEC_KEY_MODULE));
1291        assert!(kube_has(cu_block, COMPUTEUNIT_SPEC_KEY_TRIGGER));
1292        assert!(kube_has(cu_block, COMPUTEUNIT_SPEC_KEY_CAPABILITIES));
1293    }
1294
1295    #[test]
1296    fn values_yaml_wrap_key_matches_chart_dependency_name() {
1297        // The structural invariant the lift defends: every rendered
1298        // chart's values.yaml wrap key equals its Chart.yaml
1299        // `dependencies[0].name`. Sweeping the canonical default + a
1300        // typed override on the same axis pins the alignment across the
1301        // accepted set of `RenderOpts::library_name` values rather than
1302        // at a single canonical literal.
1303        for library_name in [DEFAULT_LIBRARY_NAME, "acme-computeunit", "fork-pleme-cu"] {
1304            let opts = RenderOpts {
1305                library_name: library_name.into(),
1306                ..RenderOpts::default()
1307            };
1308            let dir =
1309                render_chart_for_servico_with(&sample_caixa(), &sample_cu_yaml(), &opts).unwrap();
1310            let chart: ChartYaml = parse_yaml_at_path_as(&dir.files, HELM_CHART_YAML_FILENAME);
1311            let dep_name = &chart.dependencies[0].name;
1312            let parsed = parse_yaml_at_path(&dir.files, HELM_VALUES_YAML_FILENAME);
1313            assert!(
1314                parsed.get(dep_name.as_str()).is_some(),
1315                "values.yaml wrap key must match Chart.yaml dependencies[0].name {dep_name:?} \
1316                 (library_name = {library_name:?}); Helm's per-dep alias convention scopes \
1317                 values under the dep's `name` when no `alias:` is set, so any drift between \
1318                 the two axes silently routes the values block nowhere"
1319            );
1320        }
1321    }
1322
1323    #[test]
1324    fn values_yaml_header_comment_follows_library_name_override() {
1325        // The human-facing values.yaml header's `<library_alias>:` /
1326        // `pleme-io/helmworks/charts/<library_alias>` references both
1327        // resolve through `opts.library_name`, peer with the wrap key
1328        // itself, so an override leaves the header self-consistent
1329        // with the rendered structure rather than naming a drifted
1330        // default literal.
1331        let opts = RenderOpts {
1332            library_name: "acme-computeunit".into(),
1333            ..RenderOpts::default()
1334        };
1335        let dir = render_chart_for_servico_with(&sample_caixa(), &sample_cu_yaml(), &opts).unwrap();
1336        let values = find_file_by_path(&dir.files, HELM_VALUES_YAML_FILENAME).unwrap();
1337        assert!(
1338            values.contents.contains("`acme-computeunit:`"),
1339            "header must name the overriding library alias verbatim \
1340             (got: {contents:?})",
1341            contents = values.contents
1342        );
1343        assert!(
1344            values
1345                .contents
1346                .contains("pleme-io/helmworks/charts/acme-computeunit"),
1347            "header's helmworks path must follow the overriding library alias \
1348             (got: {contents:?})",
1349            contents = values.contents
1350        );
1351        assert!(
1352            !values.contents.contains("`pleme-computeunit:`"),
1353            "header must not retain the default library alias literal \
1354             when overridden (got: {contents:?})",
1355            contents = values.contents
1356        );
1357    }
1358
1359    #[test]
1360    fn refuses_non_servico() {
1361        let mut c = sample_caixa();
1362        c.kind = CaixaKind::Biblioteca;
1363        c.servicos = vec![];
1364        let err = render_chart_for_servico(&c, &sample_cu_yaml()).unwrap_err();
1365        assert!(matches!(err, Error::NotAServico(_)));
1366    }
1367
1368    #[test]
1369    fn kind_mismatch_error_names_offending_caixa_nome() {
1370        // Pinning the lifted [`caixa_core::KindMismatch`] view's
1371        // load-bearing property: a kind-mismatched caixa surfaces a
1372        // diagnostic that *names the offending caixa* (`hello-rio`),
1373        // not just the rejected kind. Before the lift the renderer
1374        // raised `Error::NotAServico(CaixaKind::Biblioteca)` whose
1375        // Display said "caixa :kind must be Servico for caixa-helm
1376        // rendering, got Biblioteca" — the user had to grep their
1377        // source tree for which caixa.lisp triggered it. After the
1378        // lift the wrapped KindMismatch carries the `:nome`, the
1379        // renderer's `#[error("{0}")]` arm prints it through, and
1380        // the diagnostic is self-locating.
1381        let mut c = sample_caixa();
1382        c.kind = CaixaKind::Biblioteca;
1383        c.servicos = vec![];
1384        let err = render_chart_for_servico(&c, &sample_cu_yaml()).unwrap_err();
1385        let msg = format!("{err}");
1386        assert!(
1387            msg.contains("hello-rio"),
1388            "kind-mismatch diagnostic must name the offending caixa nome \
1389             (got: {msg:?})"
1390        );
1391        assert!(
1392            msg.contains("Servico"),
1393            "diagnostic must name the expected kind (got: {msg:?})"
1394        );
1395        assert!(
1396            msg.contains("Biblioteca"),
1397            "diagnostic must name the actual kind (got: {msg:?})"
1398        );
1399    }
1400
1401    #[test]
1402    fn kind_mismatch_carries_typed_view_via_from_conversion() {
1403        // The renderer's `Error::NotAServico` variant wraps the typed
1404        // [`caixa_core::KindMismatch`] view via `#[from]`, so the `?`
1405        // operator at the call site converts without manual glue.
1406        // Pinning the typed payload (not just the variant) so a
1407        // future refactor can't silently switch the variant to a
1408        // raw-`CaixaKind` payload (which would regress the lift's
1409        // shared-shape contract with caixa-flux + caixa-mesh).
1410        let mut c = sample_caixa();
1411        c.kind = CaixaKind::Aplicacao;
1412        c.servicos = vec![];
1413        let err = render_chart_for_servico(&c, &sample_cu_yaml()).unwrap_err();
1414        match err {
1415            Error::NotAServico(km) => {
1416                assert_eq!(km.nome, "hello-rio");
1417                assert_eq!(km.expected, CaixaKind::Servico);
1418                assert_eq!(km.actual, CaixaKind::Aplicacao);
1419            }
1420            other => panic!("expected Error::NotAServico, got {other:?}"),
1421        }
1422    }
1423
1424    #[test]
1425    fn servico_count_mismatch_carries_typed_view_with_nome() {
1426        // Peer to the [`KindMismatch`]-lift pin above on the V0
1427        // `:servicos`-singularity axis: a Servico-kind caixa whose
1428        // `:servicos` list is non-singleton fails
1429        // [`render_chart_for_servico`] with the renderer's
1430        // `Error::UnsupportedServicoCount` variant wrapping the typed
1431        // [`caixa_core::ServicoCountMismatch`] view (carrying the
1432        // offending caixa's `:nome` + the actual count). Before the
1433        // lift the variant carried only `usize` — the user had to grep
1434        // their source tree for which `caixa.lisp` triggered it; after
1435        // the lift the wrapped typed view names the offending caixa
1436        // verbatim. Pins both the variant routing (via `#[from]`) and
1437        // the typed payload so a future refactor can't silently switch
1438        // back to the raw-`usize` payload (which would regress the
1439        // shared-shape contract with caixa-flux on the peer
1440        // programs.yaml-entry path).
1441        let mut c = sample_caixa();
1442        c.servicos = vec![
1443            "servicos/hello-rio.computeunit.yaml".into(),
1444            "servicos/extra.computeunit.yaml".into(),
1445        ];
1446        let err = render_chart_for_servico(&c, &sample_cu_yaml()).unwrap_err();
1447        match err {
1448            Error::UnsupportedServicoCount(scm) => {
1449                assert_eq!(scm.nome, "hello-rio");
1450                assert_eq!(scm.count, 2);
1451            }
1452            other => panic!("expected Error::UnsupportedServicoCount, got {other:?}"),
1453        }
1454    }
1455
1456    #[test]
1457    fn servico_count_mismatch_diagnostic_names_offending_caixa_nome() {
1458        // The renderer's `#[error("{0}")] UnsupportedServicoCount(
1459        // #[from] ServicoCountMismatch)` arm prints the typed view's
1460        // Display through verbatim, so the offending caixa's `:nome`
1461        // appears in the rendered diagnostic. Pinning the
1462        // self-locating property end-to-end (renderer entry-point →
1463        // typed view's Display → final diagnostic string) so a future
1464        // refactor that re-wraps the variant in a Display impl that
1465        // drops the `:nome` surfaces here as a test failure rather
1466        // than as silent fragmentation of the diagnostic. Peer to the
1467        // `kind_mismatch_error_names_offending_caixa_nome` test above
1468        // on the sibling V0 Servico-shape axis.
1469        let mut c = sample_caixa();
1470        c.servicos = vec![
1471            "servicos/hello-rio.computeunit.yaml".into(),
1472            "servicos/extra.computeunit.yaml".into(),
1473        ];
1474        let err = render_chart_for_servico(&c, &sample_cu_yaml()).unwrap_err();
1475        let msg = format!("{err}");
1476        assert!(
1477            msg.contains("hello-rio"),
1478            ":servicos-count-mismatch diagnostic must name the offending caixa nome \
1479             (got: {msg:?})"
1480        );
1481        assert!(
1482            msg.contains("2"),
1483            "diagnostic must name the actual count (got: {msg:?})"
1484        );
1485        assert!(
1486            msg.contains(":servicos"),
1487            "diagnostic must name the offending field axis (got: {msg:?})"
1488        );
1489    }
1490
1491    #[test]
1492    fn limits_slot_propagates_into_values_block() {
1493        use caixa_core::LimitsSpec;
1494        use std::time::Duration;
1495        let mut c = sample_caixa();
1496        c.limits = Some(LimitsSpec {
1497            memory: Some(64 * 1024 * 1024),
1498            fuel: Some(1_000_000),
1499            wall_clock: Some(Duration::from_secs(30)),
1500            cpu: Some(500),
1501        });
1502        let dir = render_chart_for_servico(&c, &sample_cu_yaml()).unwrap();
1503        let parsed = parse_yaml_at_path(&dir.files, HELM_VALUES_YAML_FILENAME);
1504        let cu_block = parsed.get(DEFAULT_LIBRARY_NAME).unwrap();
1505        let limits = cu_block.get(M2_KEY_LIMITS).expect("limits must propagate");
1506        assert_eq!(kube_str(limits, M2_LIMITS_KEY_MEMORY), Some("64MiB"));
1507        assert_eq!(kube_u64(limits, M2_LIMITS_KEY_FUEL), Some(1_000_000));
1508        assert_eq!(kube_str(limits, M2_LIMITS_KEY_WALL_CLOCK), Some("30s"));
1509        assert_eq!(kube_str(limits, M2_LIMITS_KEY_CPU), Some("500m"));
1510    }
1511
1512    #[test]
1513    fn behavior_slot_propagates_into_values_block() {
1514        use caixa_core::BehaviorSpec;
1515        let mut c = sample_caixa();
1516        c.behavior = Some(BehaviorSpec {
1517            on_init: Some(PathBuf::from("lib/init.lisp")),
1518            on_call: Some(PathBuf::from("lib/handlers.lisp")),
1519            ..Default::default()
1520        });
1521        let dir = render_chart_for_servico(&c, &sample_cu_yaml()).unwrap();
1522        let parsed = parse_yaml_at_path(&dir.files, HELM_VALUES_YAML_FILENAME);
1523        let cu_block = parsed.get(DEFAULT_LIBRARY_NAME).unwrap();
1524        let behavior = cu_block
1525            .get(M2_KEY_BEHAVIOR)
1526            .expect("behavior must propagate");
1527        assert_eq!(
1528            kube_str(behavior, M2_BEHAVIOR_KEY_ON_INIT),
1529            Some("lib/init.lisp")
1530        );
1531        assert_eq!(
1532            kube_str(behavior, M2_BEHAVIOR_KEY_ON_CALL),
1533            Some("lib/handlers.lisp")
1534        );
1535    }
1536
1537    #[test]
1538    fn upgrade_from_slot_propagates_into_values_block() {
1539        use caixa_core::{UpgradeFromEntry, UpgradeInstruction};
1540        let mut c = sample_caixa();
1541        c.upgrade_from = vec![UpgradeFromEntry {
1542            from: "0.0.9".into(),
1543            instructions: vec![UpgradeInstruction::LoadModule {
1544                module: "hello-rio".into(),
1545            }],
1546        }];
1547        let dir = render_chart_for_servico(&c, &sample_cu_yaml()).unwrap();
1548        let parsed = parse_yaml_at_path(&dir.files, HELM_VALUES_YAML_FILENAME);
1549        let cu_block = parsed.get(DEFAULT_LIBRARY_NAME).unwrap();
1550        assert!(kube_has(cu_block, M2_KEY_UPGRADE_FROM));
1551    }
1552
1553    #[test]
1554    fn empty_m2_slots_do_not_appear() {
1555        // Existing caixa with no M2 slots → values.yaml carries no
1556        // limits/behavior/upgradeFrom keys (forward-compat invariant).
1557        let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
1558        let parsed = parse_yaml_at_path(&dir.files, HELM_VALUES_YAML_FILENAME);
1559        let cu_block = parsed.get(DEFAULT_LIBRARY_NAME).unwrap();
1560        assert!(!kube_has(cu_block, M2_KEY_LIMITS));
1561        assert!(!kube_has(cu_block, M2_KEY_BEHAVIOR));
1562        assert!(!kube_has(cu_block, M2_KEY_UPGRADE_FROM));
1563    }
1564
1565    #[test]
1566    fn write_to_creates_files() {
1567        let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
1568        let tmp = tempfile::tempdir().unwrap();
1569        dir.write_to(tmp.path()).unwrap();
1570        let chart_root = tmp.path().join("lareira-hello-rio");
1571        assert!(chart_root.join(HELM_CHART_YAML_FILENAME).exists());
1572        assert!(chart_root.join(HELM_VALUES_YAML_FILENAME).exists());
1573        assert!(chart_root.join(HELM_CHART_README_FILENAME).exists());
1574    }
1575
1576    #[test]
1577    fn default_library_name_re_export_points_at_caixa_core_canonical() {
1578        // The renderer's `pub const DEFAULT_LIBRARY_NAME` was lifted to a
1579        // re-export of [`caixa_core::DEFAULT_LIBRARY_NAME`] so the Helm
1580        // library-chart name lives in exactly one place across every
1581        // caixa renderer (caixa-helm's `RenderOpts::library_name`
1582        // default here + caixa-flux's `cluster_bundle` `helmrelease.yaml`
1583        // wrap key on the sibling deploy-path crate). Pin the equality
1584        // here so any local re-introduction of a sibling `pub const
1585        // DEFAULT_LIBRARY_NAME: &str = "…"` (the canonical drift footgun
1586        // the prior `DEFAULT_NAMESPACE` / `DEFAULT_SERVICO_PORT` lift
1587        // commits' bodies acknowledged as the recurring shape) is a
1588        // build-time test failure naming the offending drift, not a
1589        // silent apply-time wrap-key mismatch routing the per-cluster
1590        // `enabled: true` override nowhere on `helm template` /
1591        // `helm install`. Peer to
1592        // `caixa_flux::tests::default_library_name_re_export_points_at_caixa_core_canonical`
1593        // on the sibling renderer crate.
1594        caixa_core::assert_str_reexport_identity(
1595            "DEFAULT_LIBRARY_NAME",
1596            DEFAULT_LIBRARY_NAME,
1597            caixa_core::DEFAULT_LIBRARY_NAME,
1598        );
1599    }
1600
1601    #[test]
1602    fn default_library_repo_re_export_points_at_caixa_core_canonical() {
1603        // The renderer's `pub const DEFAULT_LIBRARY_REPO: &str =
1604        // "file://../pleme-computeunit"` was lifted to a re-export of
1605        // [`caixa_core::DEFAULT_LIBRARY_REPO`] so the Helm 3
1606        // `Chart.yaml` `dependencies[0].repository` chart-source URL
1607        // every rendered `lareira-<nome>` chart declares against the
1608        // substrate-canonical [`DEFAULT_LIBRARY_NAME`] library chart
1609        // lives in exactly one place across every caixa renderer
1610        // (caixa-helm's `RenderOpts::library_repo` default here + every
1611        // future substrate-side per-Servico renderer consumer the
1612        // caixa-core [`caixa_core::DEFAULT_LIBRARY_REPO`] docstring
1613        // enumerates). Pin the equality + `&'static` static-data
1614        // identity here so any local re-introduction of a sibling
1615        // `pub const DEFAULT_LIBRARY_REPO: &str = "…"` at this crate —
1616        // the canonical drift footgun where a sibling local `pub const`
1617        // could happen to carry the same bytes at the source while
1618        // pointing at a different `&'static str` allocation (silently
1619        // splitting the substrate-canonical per-dep resolver URL from
1620        // the caixa-core-owned canonical), or, worse, could drift on a
1621        // future scheme migration (`file://` → `oci://` once
1622        // `pleme-io/helmworks/charts` lands as an OCI-registry-backed
1623        // chart-source) that landed on the caixa-core canonical without
1624        // rebrand of the sibling local — is a build-time test failure
1625        // naming the offending drift, not a silent apply-time
1626        // per-chart-dep vendoring floor mismatch surfacing at operator-
1627        // side `helm dependency build` time far from the drift site.
1628        // Peer to the sibling
1629        // [`default_library_name_re_export_points_at_caixa_core_canonical`]
1630        // pin on the sibling per-dep name-axis re-export.
1631        caixa_core::assert_str_reexport_identity(
1632            "DEFAULT_LIBRARY_REPO",
1633            DEFAULT_LIBRARY_REPO,
1634            caixa_core::DEFAULT_LIBRARY_REPO,
1635        );
1636    }
1637
1638    #[test]
1639    fn default_library_version_re_export_points_at_caixa_core_canonical() {
1640        // The renderer's `pub const DEFAULT_LIBRARY_VERSION: &str =
1641        // "~0.1.0"` was lifted to a re-export of
1642        // [`caixa_core::DEFAULT_LIBRARY_VERSION`] so the Helm 3
1643        // `Chart.yaml` `dependencies[0].version` semver-requirement
1644        // scalar every rendered `lareira-<nome>` chart declares against
1645        // the substrate-canonical [`DEFAULT_LIBRARY_NAME`] library
1646        // chart lives in exactly one place across every caixa renderer
1647        // (caixa-helm's `RenderOpts::library_version` default here +
1648        // every future substrate-side per-Servico renderer consumer the
1649        // caixa-core [`caixa_core::DEFAULT_LIBRARY_VERSION`] docstring
1650        // enumerates). Pin the equality + `&'static` static-data
1651        // identity here so any local re-introduction of a sibling
1652        // `pub const DEFAULT_LIBRARY_VERSION: &str = "…"` at this crate —
1653        // the canonical drift footgun where a sibling local `pub const`
1654        // could happen to carry the same bytes at the source while
1655        // pointing at a different `&'static str` allocation (silently
1656        // splitting the substrate-canonical per-dep semver-requirement
1657        // from the caixa-core-owned canonical), or, worse, could drift
1658        // on a future library-chart-version bump (`~0.1.0` → `~0.2.0`
1659        // on the `pleme-io/helmworks` `pleme-computeunit` next-minor
1660        // cut, `~0.1.0` → `^0.1.0` on a wider-acceptance sigil swap)
1661        // that landed on the caixa-core canonical without rebump of
1662        // the sibling local — is a build-time test failure naming the
1663        // offending drift, not a silent apply-time per-chart-dep
1664        // resolver-intersection mismatch surfacing at operator-side
1665        // `helm dependency build` time far from the drift site. Peer
1666        // to the sibling
1667        // [`default_library_name_re_export_points_at_caixa_core_canonical`]
1668        // / [`default_library_repo_re_export_points_at_caixa_core_canonical`]
1669        // pins on the co-resident `(name, repository, version)`
1670        // per-Chart.yaml-dep re-export triple — completes the triple's
1671        // per-caixa-helm re-export identity pin surface.
1672        caixa_core::assert_str_reexport_identity(
1673            "DEFAULT_LIBRARY_VERSION",
1674            DEFAULT_LIBRARY_VERSION,
1675            caixa_core::DEFAULT_LIBRARY_VERSION,
1676        );
1677    }
1678
1679    #[test]
1680    fn kube_key_spec_re_export_points_at_caixa_core_canonical() {
1681        // The renderer's `KUBE_KEY_SPEC` was lifted from the production-
1682        // code inline `"spec"` literal at `build_values_yaml`'s
1683        // `computeunit_yaml.get("spec")` ComputeUnit-side spec read (+
1684        // its matching `Error::MissingField("spec")` diagnostic) to a
1685        // re-export of [`caixa_core::KUBE_KEY_SPEC`] so the canonical
1686        // K8s-CR top-level spec-axis string lives in exactly one place
1687        // across every caixa renderer. Pin the equality + static-data
1688        // identity here so any local re-introduction of a sibling
1689        // `pub const KUBE_KEY_SPEC: &str = "…"` (the canonical drift
1690        // footgun where a sibling local `pub const` could happen to
1691        // carry the same string at the source while pointing at a
1692        // different `&'static` allocation) is a build-time test
1693        // failure naming the offending drift. Peer to
1694        // [`default_library_name_re_export_points_at_caixa_core_canonical`]
1695        // on the sibling re-export axis +
1696        // `caixa_flux::tests::kube_key_spec_re_export_points_at_caixa_core_canonical`
1697        // / `caixa_mesh::tests::kube_key_spec_re_export_points_at_caixa_core_canonical`
1698        // on the sibling renderer crates.
1699        caixa_core::assert_str_reexport_identity(
1700            "KUBE_KEY_SPEC",
1701            KUBE_KEY_SPEC,
1702            caixa_core::KUBE_KEY_SPEC,
1703        );
1704    }
1705
1706    #[test]
1707    fn helm_chart_api_version_re_export_points_at_caixa_core_canonical() {
1708        // The renderer's `HELM_CHART_API_VERSION` was lifted from the
1709        // production-code inline `"v2".into()` literal at
1710        // [`build_chart_yaml`]'s `api_version` field assignment (formerly
1711        // `caixa-helm/src/lib.rs:298`) to a re-export of
1712        // [`caixa_core::HELM_CHART_API_VERSION`] so the Helm 3
1713        // chart-schema apiVersion the rendered Chart.yaml declares lives
1714        // in exactly one place across every caixa renderer. Pin the
1715        // equality + `&'static` static-data identity here so any local
1716        // re-introduction of a sibling `pub const HELM_CHART_API_VERSION:
1717        // &str = "…"` at this crate — the canonical drift footgun where
1718        // a sibling local `pub const` could happen to carry the same
1719        // string at the source while pointing at a different `&'static`
1720        // allocation — is a build-time test failure naming the offending
1721        // drift, not a silent chart-schema-parser reroute at
1722        // `helm template` time far from the drift site. Peer to
1723        // [`kube_key_spec_re_export_points_at_caixa_core_canonical`] /
1724        // [`default_library_name_re_export_points_at_caixa_core_canonical`]
1725        // on the sibling re-export axes.
1726        caixa_core::assert_str_reexport_identity(
1727            "HELM_CHART_API_VERSION",
1728            HELM_CHART_API_VERSION,
1729            caixa_core::HELM_CHART_API_VERSION,
1730        );
1731    }
1732
1733    #[test]
1734    fn chart_yaml_uses_lifted_helm_chart_api_version() {
1735        // Fail-before-pass-after pin on the production-code
1736        // substitution: [`build_chart_yaml`]'s `api_version` field
1737        // consults the lifted [`HELM_CHART_API_VERSION`] re-export at
1738        // its assignment site, so the rendered Chart.yaml's top-level
1739        // `apiVersion` axis is byte-identical to the canonical constant
1740        // by construction. Before the lift the field carried an inline
1741        // `"v2".into()` literal at [`build_chart_yaml`]; a future
1742        // refactor that accidentally reverted the substitution — or
1743        // any parallel per-renderer variant that inlined a stale
1744        // Helm 2 `"v1"` literal — would silently reroute the rendered
1745        // Chart.yaml through the wrong chart-schema parser at
1746        // `helm dependency build` / `helm template` time, so this pin
1747        // trips at caixa-helm build time. Peer to
1748        // `values_yaml_wrap_key_matches_chart_dependency_name` on the
1749        // sibling structural-cross-axis-invariant surface.
1750        let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
1751        let chart: ChartYaml = parse_yaml_at_path_as(&dir.files, HELM_CHART_YAML_FILENAME);
1752        assert_eq!(
1753            chart.api_version, HELM_CHART_API_VERSION,
1754            "rendered Chart.yaml `apiVersion` must equal the lifted \
1755             HELM_CHART_API_VERSION verbatim — a drifted value silently \
1756             reroutes the rendered chart through the wrong Helm chart-schema \
1757             parser at `helm template` time"
1758        );
1759    }
1760
1761    #[test]
1762    fn helm_chart_type_application_re_export_points_at_caixa_core_canonical() {
1763        // The renderer's `HELM_CHART_TYPE_APPLICATION` was lifted from
1764        // the production-code inline `"application".into()` literal at
1765        // [`build_chart_yaml`]'s `chart_type` field assignment (formerly
1766        // `caixa-helm/src/lib.rs:354`) to a re-export of
1767        // [`caixa_core::HELM_CHART_TYPE_APPLICATION`] so the Helm 3
1768        // chart-schema per-chart-kind discriminator scalar-value the
1769        // rendered `lareira-<nome>` chart declares lives in exactly one
1770        // place across every caixa renderer. Pin the equality +
1771        // `&'static` static-data identity here so any local
1772        // re-introduction of a sibling `pub const
1773        // HELM_CHART_TYPE_APPLICATION: &str = "…"` at this crate — the
1774        // canonical drift footgun where a sibling local `pub const`
1775        // could happen to carry the same string at the source while
1776        // pointing at a different `&'static` allocation — is a
1777        // build-time test failure naming the offending drift, not a
1778        // silent per-release install-shape dispatch reroute at
1779        // `helm install` time far from the drift site. Peer to
1780        // [`helm_chart_api_version_re_export_points_at_caixa_core_canonical`]
1781        // on the sibling canonical-Helm-chart-schema-axis re-export
1782        // surface — completes the per-Chart.yaml `(apiVersion, type)`
1783        // canonical-scalar-axis re-export pair every rendered
1784        // `lareira-<nome>` chart declares at its top-level Chart.yaml
1785        // body.
1786        caixa_core::assert_str_reexport_identity(
1787            "HELM_CHART_TYPE_APPLICATION",
1788            HELM_CHART_TYPE_APPLICATION,
1789            caixa_core::HELM_CHART_TYPE_APPLICATION,
1790        );
1791    }
1792
1793    #[test]
1794    fn helm_chart_type_library_re_export_points_at_caixa_core_canonical() {
1795        // Re-export identity pin on the peer closed-set arm the
1796        // renderer's `HELM_CHART_TYPE_LIBRARY` alias resolves to. Peer
1797        // of `helm_chart_type_application_re_export_points_at_caixa_core_canonical`
1798        // on the sibling closed-set arm — the two pins together enshrine
1799        // the two-arm `{"application", "library"}` closed set at the
1800        // caixa-helm re-export surface as byte-identical `&'static`
1801        // static-data views onto the canonical caixa-core lifts, so any
1802        // local re-introduction of a sibling `pub const
1803        // HELM_CHART_TYPE_LIBRARY: &str = "…"` at this crate (the same
1804        // drift footgun the peer pin closes on the sibling arm) is a
1805        // build-time test failure naming the offending drift. The pin
1806        // also structurally forbids the two arms from converging on the
1807        // same `&'static` allocation — a future rebrand that
1808        // accidentally aliased `HELM_CHART_TYPE_LIBRARY` at the
1809        // [`caixa_core::HELM_CHART_TYPE_APPLICATION`] canonical would
1810        // pass this identity check but trip the caixa-core-side
1811        // `helm_chart_type_application_and_library_are_distinct` pin
1812        // paired to the two arms' distinctness contract.
1813        caixa_core::assert_str_reexport_identity(
1814            "HELM_CHART_TYPE_LIBRARY",
1815            HELM_CHART_TYPE_LIBRARY,
1816            caixa_core::HELM_CHART_TYPE_LIBRARY,
1817        );
1818    }
1819
1820    #[test]
1821    fn chart_yaml_uses_lifted_helm_chart_type_application() {
1822        // Fail-before-pass-after pin on the production-code substitution:
1823        // [`build_chart_yaml`]'s `chart_type` field consults the lifted
1824        // [`HELM_CHART_TYPE_APPLICATION`] re-export at its assignment
1825        // site, so the rendered Chart.yaml's top-level `type` axis is
1826        // byte-identical to the canonical constant by construction.
1827        // Before the lift the field carried an inline `"application".into()`
1828        // literal at [`build_chart_yaml`]; a future refactor that
1829        // accidentally reverted the substitution — or any parallel
1830        // per-renderer variant that inlined a `"library"` literal (the
1831        // sibling closed-set value from the Helm chart-schema's
1832        // per-chart-kind enum) — would silently reroute the rendered
1833        // Chart.yaml through the wrong per-release install-shape
1834        // dispatch at `helm install` time (Helm refuses to install a
1835        // `library` chart directly), so this pin trips at caixa-helm
1836        // build time. Peer to `chart_yaml_uses_lifted_helm_chart_api_version`
1837        // on the sibling per-Chart.yaml top-level `(apiVersion, type)`
1838        // canonical-scalar-axis pin pair — extends the per-Chart.yaml
1839        // top-level canonical-scalar-axis production-emit-pin
1840        // discipline from the `apiVersion` half onto the sibling `type`
1841        // half.
1842        let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
1843        let chart: ChartYaml = parse_yaml_at_path_as(&dir.files, HELM_CHART_YAML_FILENAME);
1844        assert_eq!(
1845            chart.chart_type, HELM_CHART_TYPE_APPLICATION,
1846            "rendered Chart.yaml `type` must equal the lifted \
1847             HELM_CHART_TYPE_APPLICATION verbatim — a drifted value \
1848             silently reroutes the rendered chart through the wrong \
1849             per-release install-shape dispatch at `helm install` time \
1850             (Helm refuses to install a `library` chart directly, or \
1851             silently treats an unrecognized value as the default \
1852             `application` shape masking the schema violation)"
1853        );
1854    }
1855
1856    #[test]
1857    fn helm_chart_key_type_re_export_points_at_caixa_core_canonical() {
1858        // The renderer's `HELM_CHART_KEY_TYPE` was lifted from the
1859        // sole production-side inline `"type"` literal at [`ChartYaml`]'s
1860        // `chart_type` field `#[serde(rename = "type")]` attribute
1861        // (formerly `caixa-helm/src/lib.rs:149`) to a re-export of
1862        // [`caixa_core::HELM_CHART_KEY_TYPE`] so the Helm 3 top-level
1863        // per-chart-kind discriminator YAML axis-key lives in exactly
1864        // one place across every caixa renderer. Pin the equality +
1865        // `&'static` static-data identity here so any local
1866        // re-introduction of a sibling `pub const HELM_CHART_KEY_TYPE:
1867        // &str = "…"` at this crate — the canonical drift footgun
1868        // where a sibling local `pub const` could happen to carry the
1869        // same string at the source while pointing at a different
1870        // `&'static` allocation — is a build-time test failure naming
1871        // the offending drift, not a silent Helm-chart-schema-parser
1872        // per-chart-kind-defaulting reroute at `helm dependency build`
1873        // / `helm lint` / `helm template` / `helm install` time far
1874        // from the drift site. Peer to
1875        // [`helm_chart_type_application_re_export_points_at_caixa_core_canonical`]
1876        // / [`helm_chart_type_library_re_export_points_at_caixa_core_canonical`]
1877        // on the sibling per-chart-kind axis-value re-export surface —
1878        // completes the per-Chart.yaml per-chart-kind discriminator
1879        // axis's `(key, value-set)` canonical re-export trio at the
1880        // caixa-helm surface.
1881        caixa_core::assert_str_reexport_identity(
1882            "HELM_CHART_KEY_TYPE",
1883            HELM_CHART_KEY_TYPE,
1884            caixa_core::HELM_CHART_KEY_TYPE,
1885        );
1886    }
1887
1888    #[test]
1889    fn helm_chart_key_app_version_re_export_points_at_caixa_core_canonical() {
1890        // The renderer's `HELM_CHART_KEY_APP_VERSION` was lifted from
1891        // the sole production-side inline `"appVersion"` literal at
1892        // [`ChartYaml`]'s `app_version` field
1893        // `#[serde(rename = "appVersion")]` attribute (formerly
1894        // `caixa-helm/src/lib.rs:152`) to a re-export of
1895        // [`caixa_core::HELM_CHART_KEY_APP_VERSION`] so the Helm 3
1896        // top-level per-chart-app-version YAML axis-key lives in
1897        // exactly one place across every caixa renderer. Pin the
1898        // equality + `&'static` static-data identity here so any
1899        // local re-introduction of a sibling `pub const
1900        // HELM_CHART_KEY_APP_VERSION: &str = "…"` at this crate — the
1901        // canonical drift footgun where a sibling local `pub const`
1902        // could happen to carry the same string at the source while
1903        // pointing at a different `&'static` allocation — is a
1904        // build-time test failure naming the offending drift, not a
1905        // silent Helm-chart-schema-parser field-drop at every
1906        // downstream Artifact Hub / `helm search` chart-consumer far
1907        // from the drift site. Peer to
1908        // [`helm_chart_key_type_re_export_points_at_caixa_core_canonical`]
1909        // on the sibling per-Chart.yaml top-level YAML axis-key
1910        // re-export surface — completes the per-Chart.yaml top-level
1911        // YAML axis-key re-export pair at the caixa-helm surface for
1912        // the two serde-rename-literal-only axes.
1913        caixa_core::assert_str_reexport_identity(
1914            "HELM_CHART_KEY_APP_VERSION",
1915            HELM_CHART_KEY_APP_VERSION,
1916            caixa_core::HELM_CHART_KEY_APP_VERSION,
1917        );
1918    }
1919
1920    #[test]
1921    fn chart_yaml_serializes_type_axis_under_lifted_helm_chart_key_type() {
1922        // Fail-before-pass-after drift-detection pin on the
1923        // `#[serde(rename = "type")]` attribute at [`ChartYaml`]'s
1924        // `chart_type` field. Rust's attribute grammar admits only
1925        // string literals so the lifted [`HELM_CHART_KEY_TYPE`]
1926        // constant cannot substitute for the literal syntactically at
1927        // the attribute-argument site — a future refactor that
1928        // dropped the `#[serde(rename = "type")]` attribute (or
1929        // changed the target key to `"Type"` / `"kind"` /
1930        // `"chartType"`) would silently serialize the field under
1931        // Rust's default snake_case `chart_type:` key, which Helm's
1932        // chart-schema parser silently ignores as an unknown top-
1933        // level key, defaulting the per-chart-kind axis to
1934        // `application` with no process-log signal. This pin closes
1935        // the drift by round-tripping a rendered `Chart.yaml` through
1936        // `serde_yaml::from_str::<serde_yaml::Value>` and asserting
1937        // the top-level `Mapping::get(HELM_CHART_KEY_TYPE)` resolves
1938        // (rather than serializing through the [`ChartYaml`]-typed
1939        // deserializer that would silently absorb the rename drift
1940        // via `#[serde(default)]` fall-through at the struct-side).
1941        // Peer to
1942        // [`chart_yaml_uses_lifted_helm_chart_type_application`] on
1943        // the sibling per-Chart.yaml per-chart-kind axis-value
1944        // production-emit pin — the two pins together enforce the
1945        // full `(key, value)` production-emit pair at the caixa-helm
1946        // surface for the per-Chart.yaml per-chart-kind discriminator
1947        // axis.
1948        let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
1949        let doc = parse_yaml_at_path(&dir.files, HELM_CHART_YAML_FILENAME);
1950        let mapping = doc.as_mapping().expect(
1951            "rendered Chart.yaml must be a top-level YAML mapping per \
1952             the Helm 3 chart-schema shape",
1953        );
1954        assert!(
1955            kube_has(mapping, HELM_CHART_KEY_TYPE),
1956            "rendered Chart.yaml must carry a top-level {HELM_CHART_KEY_TYPE:?} \
1957             axis-key — a drift on the `#[serde(rename = {HELM_CHART_KEY_TYPE:?})]` \
1958             attribute at ChartYaml's `chart_type` field silently reroutes the \
1959             per-chart-kind discriminator axis through Rust's default snake_case \
1960             serialization (`chart_type:`), which Helm's chart-schema parser \
1961             silently ignores as an unknown top-level key (defaulting the \
1962             per-chart-kind axis to `application` with no process-log signal)"
1963        );
1964    }
1965
1966    #[test]
1967    fn chart_yaml_serializes_app_version_axis_under_lifted_helm_chart_key_app_version() {
1968        // Fail-before-pass-after drift-detection pin on the
1969        // `#[serde(rename = "appVersion")]` attribute at [`ChartYaml`]'s
1970        // `app_version` field. Same attribute-literal-only-grammar
1971        // constraint the peer
1972        // [`chart_yaml_serializes_type_axis_under_lifted_helm_chart_key_type`]
1973        // pin closes on the sibling per-Chart.yaml top-level YAML
1974        // axis-key applies here: a future refactor that dropped the
1975        // `#[serde(rename = "appVersion")]` attribute (or changed the
1976        // target key to `"AppVersion"` / `"applicationVersion"` /
1977        // `"appversion"`) would silently serialize the field under
1978        // Rust's default snake_case `app_version:` key, which Helm's
1979        // chart-schema parser silently drops from the parsed
1980        // chart-metadata shape, and every downstream Artifact Hub /
1981        // `helm search` per-chart index falls back to "no application
1982        // version" for the rendered chart. This pin closes the drift
1983        // by round-tripping a rendered `Chart.yaml` through
1984        // `serde_yaml::from_str::<serde_yaml::Value>` (rather than
1985        // through the [`ChartYaml`]-typed deserializer that would
1986        // silently absorb the rename drift). Peer to
1987        // [`chart_yaml_serializes_type_axis_under_lifted_helm_chart_key_type`]
1988        // on the sibling per-Chart.yaml top-level YAML axis-key
1989        // serialization pin surface — completes the per-Chart.yaml
1990        // top-level YAML axis-key production-emit pin pair at the
1991        // caixa-helm surface for the two serde-rename-literal-only
1992        // axes.
1993        let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
1994        let doc = parse_yaml_at_path(&dir.files, HELM_CHART_YAML_FILENAME);
1995        let mapping = doc.as_mapping().expect(
1996            "rendered Chart.yaml must be a top-level YAML mapping per \
1997             the Helm 3 chart-schema shape",
1998        );
1999        assert!(
2000            kube_has(mapping, HELM_CHART_KEY_APP_VERSION),
2001            "rendered Chart.yaml must carry a top-level \
2002             {HELM_CHART_KEY_APP_VERSION:?} axis-key — a drift on the \
2003             `#[serde(rename = {HELM_CHART_KEY_APP_VERSION:?})]` attribute at \
2004             ChartYaml's `app_version` field silently reroutes the per-chart \
2005             underlying-application-version axis through Rust's default \
2006             snake_case serialization (`app_version:`), which Helm's \
2007             chart-schema parser silently drops from the parsed chart-metadata \
2008             shape (every downstream Artifact Hub / `helm search` per-chart \
2009             index falls back to \"no application version\" for the rendered \
2010             chart with no process-log signal at the substrate-side emitter site)"
2011        );
2012    }
2013
2014    #[test]
2015    fn chart_yaml_serializes_api_version_axis_under_lifted_helm_chart_key_api_version() {
2016        // Fail-before-pass-after drift-detection pin on the
2017        // `#[serde(rename = "apiVersion")]` attribute at [`ChartYaml`]'s
2018        // `api_version` field. Same attribute-literal-only-grammar
2019        // constraint the peer
2020        // [`chart_yaml_serializes_type_axis_under_lifted_helm_chart_key_type`]
2021        // / [`chart_yaml_serializes_app_version_axis_under_lifted_helm_chart_key_app_version`]
2022        // pins close on the sibling per-Chart.yaml top-level YAML
2023        // axis-keys applies here: a future refactor that dropped the
2024        // `#[serde(rename = "apiVersion")]` attribute (or changed the
2025        // target key to `"ApiVersion"` / `"apiversion"` /
2026        // `"schemaVersion"`) would silently serialize the field under
2027        // Rust's default snake_case `api_version:` key, which Helm's
2028        // chart-schema parser rejects at `helm lint` / `helm
2029        // dependency build` / `helm template` time with an "apiVersion
2030        // is required" error far from the drift site — every downstream
2031        // `lareira-<nome>` chart consumer drops with no field naming
2032        // the serde-rename-drift root cause. This pin closes the drift
2033        // by round-tripping a rendered `Chart.yaml` through
2034        // `serde_yaml::from_str::<serde_yaml::Value>` (rather than
2035        // through the [`ChartYaml`]-typed deserializer that would
2036        // silently absorb the rename drift via `#[serde(default)]`
2037        // fall-through at the struct-side). Peer to
2038        // [`chart_yaml_serializes_type_axis_under_lifted_helm_chart_key_type`]
2039        // / [`chart_yaml_serializes_app_version_axis_under_lifted_helm_chart_key_app_version`]
2040        // on the sibling per-Chart.yaml top-level YAML axis-key
2041        // serialization pin surface — completes the per-Chart.yaml
2042        // top-level YAML axis-key production-emit pin trio at the
2043        // caixa-helm surface for the three serde-rename-literal-only
2044        // axes.
2045        let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
2046        let doc = parse_yaml_at_path(&dir.files, HELM_CHART_YAML_FILENAME);
2047        let mapping = doc.as_mapping().expect(
2048            "rendered Chart.yaml must be a top-level YAML mapping per \
2049             the Helm 3 chart-schema shape",
2050        );
2051        assert!(
2052            kube_has(mapping, HELM_CHART_KEY_API_VERSION),
2053            "rendered Chart.yaml must carry a top-level \
2054             {HELM_CHART_KEY_API_VERSION:?} axis-key — a drift on the \
2055             `#[serde(rename = {HELM_CHART_KEY_API_VERSION:?})]` attribute at \
2056             ChartYaml's `api_version` field silently reroutes the per-chart \
2057             chart-schema-apiVersion axis through Rust's default snake_case \
2058             serialization (`api_version:`), which Helm's chart-schema parser \
2059             rejects at `helm lint` / `helm template` time with an \"apiVersion \
2060             is required\" error far from the drift site"
2061        );
2062    }
2063
2064    #[test]
2065    fn chart_dependency_serializes_tetrad_under_lifted_helm_chart_dependency_keys() {
2066        // Fail-before-pass-after drift-detection pin on the per-
2067        // `dependencies[]`-entry sub-mapping serde field-name tetrad
2068        // at [`ChartDependency`]. The four fields are identity-mapped
2069        // to their target wire keys today (no `#[serde(rename)]` or
2070        // `#[serde(rename_all)]` attribute on the struct), so a drift
2071        // would surface as one of two shapes: a rename of the Rust
2072        // field (`pub name` → `pub nome`) that silently rebrands the
2073        // wire key, or a `#[serde(rename_all = "camelCase")]` attribute
2074        // addition that stays a no-op on the four lowercase-identity
2075        // fields today but silently activates on a future field
2076        // addition (e.g. an `import_values: Option<Vec<String>>` axis
2077        // matching Helm 3's per-dep `import-values` sub-key). Either
2078        // shape silently reroutes the per-dep sub-mapping through a
2079        // Helm-per-dep-resolver drop at `helm dependency build` time
2080        // far from the drift site (Helm silently drops the drifted
2081        // per-dep sub-mapping field and the per-dep resolver falls
2082        // back to the parsed-shape defaults). This pin closes the
2083        // drift by serializing a fully-populated [`ChartDependency`]
2084        // (`alias` set to a non-`None` value so the
2085        // `#[serde(skip_serializing_if = "Option::is_none")]`
2086        // attribute doesn't elide the axis from the emitted YAML)
2087        // through `serde_yaml::to_value` and asserting each of the
2088        // four per-dep sub-mapping wire keys resolves via
2089        // `Mapping::get(HELM_CHART_DEPENDENCY_KEY_*)`. Peer to
2090        // [`chart_yaml_serializes_type_axis_under_lifted_helm_chart_key_type`]
2091        // / [`chart_yaml_serializes_app_version_axis_under_lifted_helm_chart_key_app_version`]
2092        // / [`chart_yaml_serializes_api_version_axis_under_lifted_helm_chart_key_api_version`]
2093        // on the sibling per-Chart.yaml top-level serde-rename-literal-
2094        // only axis-key drift-detection pin trio (cc44e4b / d29bc23) —
2095        // extends the drift-detection discipline from the per-Chart.yaml
2096        // top-level serde-rename-literal axes onto the per-
2097        // `dependencies[]`-entry sub-mapping serde-field-name tetrad.
2098        let dep = ChartDependency {
2099            name: "pleme-computeunit".into(),
2100            version: "~0.1.0".into(),
2101            repository: "file://../pleme-computeunit".into(),
2102            alias: Some("acme-alias".into()),
2103        };
2104        let doc = serde_yaml::to_value(&dep).unwrap();
2105        let mapping = doc.as_mapping().expect(
2106            "ChartDependency must serialize to a top-level YAML mapping per \
2107             the Helm 3 per-dep sub-mapping shape",
2108        );
2109        for key in [
2110            HELM_CHART_DEPENDENCY_KEY_NAME,
2111            HELM_CHART_DEPENDENCY_KEY_VERSION,
2112            HELM_CHART_DEPENDENCY_KEY_REPOSITORY,
2113            HELM_CHART_DEPENDENCY_KEY_ALIAS,
2114        ] {
2115            assert!(
2116                kube_has(mapping, key),
2117                "serialized ChartDependency must carry a top-level {key:?} \
2118                 axis-key — a drift on the `ChartDependency` struct's serde \
2119                 field-name (a Rust-side rename, an added \
2120                 `#[serde(rename_all)]` attribute, an added `#[serde(rename)]` \
2121                 per-field override) silently reroutes the per-dep sub-mapping \
2122                 through a Helm-per-dep-resolver drop at `helm dependency \
2123                 build` time far from the drift site (Helm silently drops the \
2124                 drifted per-dep sub-mapping field and the per-dep resolver \
2125                 falls back to the parsed-shape defaults); the emitted mapping \
2126                 keys are {keys:?}",
2127                keys = mapping_string_keys(mapping)
2128            );
2129        }
2130    }
2131
2132    #[test]
2133    fn chart_yaml_serializes_dependencies_axis_under_lifted_helm_chart_key_dependencies() {
2134        // Fail-before-pass-after drift-detection pin on the top-level
2135        // per-chart dependency-list YAML axis-key at [`ChartYaml`]'s
2136        // `dependencies` field. The Rust field name and the emitted
2137        // wire key coincide by default today (no `#[serde(rename)]`
2138        // attribute on the field, no `#[serde(rename_all = "…")]`
2139        // attribute on the struct — so serde emits `dependencies:`
2140        // verbatim as the top-level list-container YAML key). A future
2141        // hostile refactor could silently rebrand the wire key in
2142        // three shapes:
2143        //
2144        //   - a rename of the Rust field itself (`pub dependencies:
2145        //     Vec<ChartDependency>` → `pub deps: Vec<ChartDependency>`
2146        //     / `pub chart_dependencies: …`), which serde would then
2147        //     serialize as `deps:` / `chart_dependencies:` verbatim;
2148        //   - an added `#[serde(rename_all = "camelCase")]` /
2149        //     `"snake_case"` / `"kebab-case"` attribute on the struct
2150        //     itself — a no-op on the four identity-mapped top-level
2151        //     lowercase keys (`name` / `description` / `version` /
2152        //     `dependencies`) today but silently activates on a future
2153        //     multi-word field addition (e.g. an `icon_url` axis
2154        //     matching Helm 3's per-chart `icon:` future-schema slot);
2155        //   - an added `#[serde(rename = "deps")]` per-field override
2156        //     at the site of the `dependencies` field.
2157        //
2158        // Under any of the three shapes Helm's chart-schema parser
2159        // silently drops the entire per-chart dep list from the
2160        // parsed chart-metadata (unknown top-level YAML keys silently
2161        // ignored per the Helm 3 chart-schema fallthrough), `helm
2162        // dependency build` finds no chart to vendor, and every
2163        // rendered `lareira-<nome>` chart's install fails with
2164        // `template: no template ... associated with template ...`
2165        // far from the drift site with no field naming the top-level-
2166        // list-key-drift root cause. This pin closes the drift by
2167        // round-tripping a rendered `Chart.yaml` through
2168        // `serde_yaml::from_str::<serde_yaml::Value>` and asserting
2169        // the top-level `Mapping::get(HELM_CHART_KEY_DEPENDENCIES)`
2170        // resolves — rather than through the [`ChartYaml`]-typed
2171        // deserializer that would silently absorb any of the three
2172        // drift shapes via `#[serde(default)]` fall-through at the
2173        // struct-side. Peer to
2174        // [`chart_yaml_serializes_type_axis_under_lifted_helm_chart_key_type`]
2175        // / [`chart_yaml_serializes_app_version_axis_under_lifted_helm_chart_key_app_version`]
2176        // / [`chart_yaml_serializes_api_version_axis_under_lifted_helm_chart_key_api_version`]
2177        // on the sibling per-Chart.yaml top-level YAML axis-key
2178        // serialization pin surface (d29bc23, cc44e4b) — extends the
2179        // per-Chart.yaml top-level YAML axis-key production-emit pin
2180        // trio those closed onto the fourth top-level axis-key, the
2181        // parent list-container the already-lifted per-
2182        // `dependencies[]`-entry sub-mapping tetrad
2183        // [`HELM_CHART_DEPENDENCY_KEY_NAME`] /
2184        // [`HELM_CHART_DEPENDENCY_KEY_VERSION`] /
2185        // [`HELM_CHART_DEPENDENCY_KEY_REPOSITORY`] /
2186        // [`HELM_CHART_DEPENDENCY_KEY_ALIAS`] mounts one level down.
2187        let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
2188        let doc = parse_yaml_at_path(&dir.files, HELM_CHART_YAML_FILENAME);
2189        let mapping = doc.as_mapping().expect(
2190            "rendered Chart.yaml must be a top-level YAML mapping per \
2191             the Helm 3 chart-schema shape",
2192        );
2193        assert!(
2194            kube_has(mapping, HELM_CHART_KEY_DEPENDENCIES),
2195            "rendered Chart.yaml must carry a top-level \
2196             {HELM_CHART_KEY_DEPENDENCIES:?} axis-key — a drift on the \
2197             `ChartYaml.dependencies` field's serde field-name (a Rust-side \
2198             rename to `deps` / `chart_dependencies`, an added \
2199             `#[serde(rename_all)]` attribute on the enclosing struct, an \
2200             added `#[serde(rename)]` per-field override) silently reroutes \
2201             the per-chart dependency-list axis through an unrecognized \
2202             top-level YAML key (`deps:` / `chartDependencies:` / \
2203             `chart_dependencies:`), which Helm's chart-schema parser \
2204             silently drops from the parsed chart-metadata shape (every \
2205             rendered `lareira-<nome>` chart's install fails with \
2206             `template: no template ... associated with template ...` at \
2207             `helm dependency build` / `helm template` time far from the \
2208             drift site with no field naming the top-level-list-key-drift \
2209             root cause); the emitted top-level mapping keys are {keys:?}",
2210            keys = mapping_string_keys(mapping)
2211        );
2212    }
2213
2214    #[test]
2215    fn helm_chart_key_dependencies_re_export_points_at_caixa_core_canonical() {
2216        // The renderer's [`HELM_CHART_KEY_DEPENDENCIES`] was lifted onto
2217        // a re-export of [`caixa_core::HELM_CHART_KEY_DEPENDENCIES`] so
2218        // the Helm 3 per-Chart.yaml top-level dependency-list-container
2219        // YAML axis-key — the parent whose per-`dependencies[]`-entry
2220        // sub-mapping tetrad [`HELM_CHART_DEPENDENCY_KEY_NAME`] /
2221        // [`HELM_CHART_DEPENDENCY_KEY_VERSION`] /
2222        // [`HELM_CHART_DEPENDENCY_KEY_REPOSITORY`] /
2223        // [`HELM_CHART_DEPENDENCY_KEY_ALIAS`] mounts one level down —
2224        // lives in exactly one place across every caixa renderer. Pin
2225        // the equality + `&'static` static-data identity here so any
2226        // local re-introduction of a sibling `pub const
2227        // HELM_CHART_KEY_DEPENDENCIES: &str = "…"` at this crate — the
2228        // canonical drift footgun where a sibling local `pub const`
2229        // could happen to carry the same string at the source while
2230        // pointing at a different `&'static` allocation — is a build-
2231        // time test failure naming the offending drift, not a silent
2232        // per-Chart.yaml top-level-list-key reroute at `helm dependency
2233        // build` / `helm lint` / `helm template` time far from the
2234        // drift site. Peer to
2235        // [`helm_chart_yaml_filename_re_export_points_at_caixa_core_canonical`]
2236        // and every other `helm_chart_*_re_export_points_at_caixa_core_canonical`
2237        // pin on the sibling canonical-Helm-chart-schema-body-axis
2238        // re-export surface — extends the per-Chart.yaml top-level
2239        // YAML axis-key re-export identity discipline onto the fourth
2240        // top-level axis-key at the caixa-helm surface.
2241        caixa_core::assert_str_reexport_identity(
2242            "HELM_CHART_KEY_DEPENDENCIES",
2243            HELM_CHART_KEY_DEPENDENCIES,
2244            caixa_core::HELM_CHART_KEY_DEPENDENCIES,
2245        );
2246    }
2247
2248    #[test]
2249    fn render_opts_default_library_name_follows_lifted_constant() {
2250        // [`RenderOpts::default()`] sets `library_name` from
2251        // [`DEFAULT_LIBRARY_NAME`]; pin that the lift preserves the
2252        // default-knob value bit-for-bit. A future refactor that
2253        // detaches `RenderOpts::default()` from the lifted constant —
2254        // accidentally re-introducing an inline `"pleme-computeunit"`
2255        // literal in the impl — would silently break the shared-shape
2256        // contract with caixa-flux (which uses the same constant
2257        // directly for its `helmrelease.yaml` wrap key); this test
2258        // surfaces the regression at build time rather than at
2259        // apply time as a silent values-routing no-op.
2260        let opts = RenderOpts::default();
2261        assert_eq!(opts.library_name, caixa_core::DEFAULT_LIBRARY_NAME);
2262        assert_eq!(opts.library_name, "pleme-computeunit");
2263    }
2264
2265    #[test]
2266    fn helm_chart_yaml_filename_re_export_points_at_caixa_core_canonical() {
2267        // The renderer's `HELM_CHART_YAML_FILENAME` was lifted from the
2268        // seven production + test-side inline `"Chart.yaml"` /
2269        // `PathBuf::from("Chart.yaml")` / `chart_root.join("Chart.yaml")`
2270        // literals across [`render_chart_for_servico`]'s `ChartDir`
2271        // metadata-file `path` emit site + every test-side round-trip
2272        // navigator that reaches into the rendered `ChartDir` by the
2273        // metadata filename to a re-export of
2274        // [`caixa_core::HELM_CHART_YAML_FILENAME`] so the Helm 3
2275        // per-chart-directory metadata-file filename lives in exactly one
2276        // place across every caixa renderer. Pin the equality +
2277        // `&'static` static-data identity here so any local
2278        // re-introduction of a sibling `pub const
2279        // HELM_CHART_YAML_FILENAME: &str = "…"` at this crate — the
2280        // canonical drift footgun where a sibling local `pub const` could
2281        // happen to carry the same string at the source while pointing
2282        // at a different `&'static` allocation — is a build-time test
2283        // failure naming the offending drift, not a silent
2284        // Helm-chart-schema-parser "Chart.yaml file is missing" reroute
2285        // at `helm dependency build` / `helm lint` / `helm template` /
2286        // `helm install` time far from the drift site. Peer to
2287        // [`helm_chart_api_version_re_export_points_at_caixa_core_canonical`]
2288        // / [`helm_chart_type_application_re_export_points_at_caixa_core_canonical`]
2289        // on the sibling canonical-Helm-chart-schema-body-axis re-export
2290        // surfaces — completes the per-`lareira-<nome>`-chart-directory
2291        // `(filename, apiVersion, type)` canonical-scalar-axis re-export
2292        // triple every rendered chart declares at its top-level metadata
2293        // file.
2294        caixa_core::assert_str_reexport_identity(
2295            "HELM_CHART_YAML_FILENAME",
2296            HELM_CHART_YAML_FILENAME,
2297            caixa_core::HELM_CHART_YAML_FILENAME,
2298        );
2299    }
2300
2301    #[test]
2302    fn helm_values_yaml_filename_re_export_points_at_caixa_core_canonical() {
2303        // The renderer's `HELM_VALUES_YAML_FILENAME` was lifted from the
2304        // twelve production + test-side inline `"values.yaml"` /
2305        // `PathBuf::from("values.yaml")` / `chart_root.join("values.yaml")`
2306        // literals across [`render_chart_for_servico`]'s `ChartDir`
2307        // values-file `path` emit site + every test-side round-trip
2308        // navigator that reaches into the rendered `ChartDir` by the
2309        // values filename to a re-export of
2310        // [`caixa_core::HELM_VALUES_YAML_FILENAME`] so the Helm 3
2311        // per-chart-directory values-file filename lives in exactly one
2312        // place across every caixa renderer. Pin the equality +
2313        // `&'static` static-data identity here so any local
2314        // re-introduction of a sibling `pub const
2315        // HELM_VALUES_YAML_FILENAME: &str = "…"` at this crate — the
2316        // canonical drift footgun where a sibling local `pub const` could
2317        // happen to carry the same string at the source while pointing
2318        // at a different `&'static` allocation — is a build-time test
2319        // failure naming the offending drift, not a silent
2320        // Helm-per-chart-values-loader fall-through to the empty values
2321        // block at `helm template` / `helm install` time far from the
2322        // drift site (where the workload silently comes up under the
2323        // library chart's admission-time defaults with no per-Servico
2324        // M2 overlay applied). Peer to
2325        // [`helm_chart_yaml_filename_re_export_points_at_caixa_core_canonical`]
2326        // on the sibling canonical-Helm-per-chart-directory-metadata-file-
2327        // axis re-export surface — completes the
2328        // per-`lareira-<nome>`-chart-directory `(Chart.yaml, values.yaml)`
2329        // canonical-per-chart-directory-filename-axis re-export pair
2330        // every rendered chart declares as its two schema-load-bearing
2331        // `ChartDir::files` entries.
2332        caixa_core::assert_str_reexport_identity(
2333            "HELM_VALUES_YAML_FILENAME",
2334            HELM_VALUES_YAML_FILENAME,
2335            caixa_core::HELM_VALUES_YAML_FILENAME,
2336        );
2337    }
2338
2339    #[test]
2340    fn helm_chart_readme_filename_re_export_points_at_caixa_core_canonical() {
2341        // The renderer's `HELM_CHART_README_FILENAME` was lifted from the
2342        // three production + test-side inline `"README.md"` literals
2343        // across [`render_chart_for_servico`]'s `ChartDir` readme-file
2344        // `path` emit site + every test-side round-trip navigator that
2345        // reaches into the rendered `ChartDir` by the readme filename
2346        // (the [`renders_three_files`] files-vec-membership pin + the
2347        // [`ChartDir::write_to`] post-write existence pin) to a
2348        // re-export of [`caixa_core::HELM_CHART_README_FILENAME`] so the
2349        // per-`lareira-<nome>` chart-directory human-facing readme
2350        // filename lives in exactly one place across every caixa
2351        // renderer. Pin the equality + `&'static` static-data identity
2352        // here so any local re-introduction of a sibling `pub const
2353        // HELM_CHART_README_FILENAME: &str = "…"` at this crate — the
2354        // canonical drift footgun where a sibling local `pub const`
2355        // could happen to carry the same string at the source while
2356        // pointing at a different `&'static` allocation — is a build-
2357        // time test failure naming the offending drift, not a silent
2358        // GitHub / Artifact Hub / any per-chart README-surfacing UI
2359        // fall-through to "no README available" at chart-consumption
2360        // time far from the drift site. Peer to
2361        // [`helm_chart_yaml_filename_re_export_points_at_caixa_core_canonical`]
2362        // / [`helm_values_yaml_filename_re_export_points_at_caixa_core_canonical`]
2363        // on the sibling canonical-Helm-per-chart-directory-filename
2364        // axis re-export surfaces — completes the
2365        // per-`lareira-<nome>`-chart-directory `(Chart.yaml,
2366        // values.yaml, README.md)` canonical-per-chart-directory-
2367        // filename-axis re-export triple every rendered chart declares
2368        // as its three `ChartDir::files` entries.
2369        caixa_core::assert_str_reexport_identity(
2370            "HELM_CHART_README_FILENAME",
2371            HELM_CHART_README_FILENAME,
2372            caixa_core::HELM_CHART_README_FILENAME,
2373        );
2374    }
2375
2376    #[test]
2377    fn helm_values_key_enabled_re_export_points_at_caixa_core_canonical() {
2378        // The renderer's `HELM_VALUES_KEY_ENABLED` was lifted from the
2379        // production-code inline `"enabled".to_string()` literal at
2380        // [`build_values_yaml`]'s
2381        // `block.insert("enabled".to_string(), Value::Bool(…))` values-
2382        // block-toggle insert (formerly `caixa-helm/src/lib.rs:389`) plus
2383        // its two test-side round-trip navigators
2384        // (`values_yaml_wraps_under_pleme_computeunit_key`,
2385        // `values_yaml_wrap_key_follows_library_name_override`) to a
2386        // re-export of [`caixa_core::HELM_VALUES_KEY_ENABLED`] so the
2387        // canonical `pleme-computeunit` library-chart values-block
2388        // enable-toggle key lives in exactly one place across every
2389        // caixa renderer. Pin the equality + `&'static` static-data
2390        // identity here so any local re-introduction of a sibling
2391        // `pub const HELM_VALUES_KEY_ENABLED: &str = "…"` at this crate
2392        // — the canonical drift footgun where a sibling local
2393        // `pub const` could happen to carry the same string at the
2394        // source while pointing at a different `&'static` allocation —
2395        // is a build-time test failure naming the offending drift, not
2396        // a silent per-values enable-toggle reroute at `helm template` /
2397        // `helm install` time far from the drift site (where the
2398        // workload silently comes up with the library chart's
2399        // admission-time defaults instead of the per-cluster override
2400        // the operator set). Peer to
2401        // [`helm_chart_api_version_re_export_points_at_caixa_core_canonical`]
2402        // / [`kube_key_spec_re_export_points_at_caixa_core_canonical`] /
2403        // [`default_library_name_re_export_points_at_caixa_core_canonical`]
2404        // on the sibling re-export axes +
2405        // `caixa_flux::tests::helm_values_key_enabled_re_export_points_at_caixa_core_canonical`
2406        // on the peer bundle-path renderer crate.
2407        caixa_core::assert_str_reexport_identity(
2408            "HELM_VALUES_KEY_ENABLED",
2409            HELM_VALUES_KEY_ENABLED,
2410            caixa_core::HELM_VALUES_KEY_ENABLED,
2411        );
2412    }
2413
2414    #[test]
2415    fn values_yaml_enable_toggle_key_pins_lifted_helm_values_key_enabled() {
2416        // Fail-before-pass-after pin on the production-code substitution:
2417        // [`build_values_yaml`]'s `block.insert(…, Value::Bool(…))`
2418        // consults the lifted [`HELM_VALUES_KEY_ENABLED`] re-export at
2419        // its insert site, so the rendered `values.yaml`'s per-values
2420        // enable-toggle axis is byte-identical to the canonical constant
2421        // by construction. Before the lift the field carried an inline
2422        // `"enabled".to_string()` literal; a future refactor that
2423        // accidentally reverted the substitution — or any parallel per-
2424        // renderer variant that inlined a stale `"enable"` /
2425        // `"disabled"` literal — would silently emit a values block
2426        // whose per-values enable-toggle lands under one key while
2427        // [`caixa_flux::cluster_bundle`]'s `HelmRelease`
2428        // `spec.values.<library>.enabled` per-cluster override lands
2429        // under another, so this pin trips at caixa-helm build time.
2430        // Peer to `chart_yaml_uses_lifted_helm_chart_api_version` on the
2431        // sibling structural-cross-axis-invariant surface — both close
2432        // the drift between a rendered-value navigator's `.get(…)` /
2433        // struct-field read on the constant and the production-code
2434        // emit site that consumes the same constant.
2435        let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
2436        let parsed = parse_yaml_at_path(&dir.files, HELM_VALUES_YAML_FILENAME);
2437        let cu_block = parsed
2438            .get(DEFAULT_LIBRARY_NAME)
2439            .expect("must wrap under DEFAULT_LIBRARY_NAME");
2440        assert_eq!(
2441            cu_block.get(HELM_VALUES_KEY_ENABLED),
2442            Some(&serde_yaml::Value::Bool(false)),
2443            "rendered values.yaml `{DEFAULT_LIBRARY_NAME}.{HELM_VALUES_KEY_ENABLED}` must \
2444             equal the default-off toggle the lifted HELM_VALUES_KEY_ENABLED axis carries — \
2445             a drifted enable-toggle key silently splits the per-values enable-flip across \
2446             two sibling scalar names on the caixa-helm / caixa-flux consumer split"
2447        );
2448    }
2449
2450    #[test]
2451    fn computeunit_spec_key_module_re_export_points_at_caixa_core_canonical() {
2452        // The renderer's `COMPUTEUNIT_SPEC_KEY_MODULE` was lifted from
2453        // the two inline `"module"` test-side call sites in this crate
2454        // (`values_yaml_wraps_under_pleme_computeunit_key`'s per-values
2455        // module-block present-check + the peer navigator on the
2456        // `library_name`-override wrap-key axis
2457        // `values_yaml_wrap_key_follows_library_name_override`) — every
2458        // per-Servico ComputeUnit CRD `spec.module` sub-block readback
2459        // in this crate now navigates through the same `&'static str`
2460        // re-exported to a re-export of
2461        // [`caixa_core::COMPUTEUNIT_SPEC_KEY_MODULE`] so the canonical
2462        // ComputeUnit-CRD per-`spec.*` wasm-module-reference axis lives
2463        // in exactly one place across every caixa renderer. Pin the
2464        // equality + static-data identity here so any local re-
2465        // introduction of a sibling `pub const COMPUTEUNIT_SPEC_KEY_MODULE:
2466        // &str = "…"` at this crate is a build-time test failure naming
2467        // the offending drift, not a silent per-Servico wasm-runtime-
2468        // binding drop at cluster-apply time. Peer to
2469        // [`helm_values_key_enabled_re_export_points_at_caixa_core_canonical`]
2470        // /
2471        // [`kube_key_spec_re_export_points_at_caixa_core_canonical`]
2472        // on the sibling canonical-Helm-load-bearing-string /
2473        // canonical-K8s-CR-body-key re-export axes +
2474        // `caixa_flux::tests::computeunit_spec_key_module_re_export_points_at_caixa_core_canonical`
2475        // on the peer per-Servico renderer crate.
2476        caixa_core::assert_str_reexport_identity(
2477            "COMPUTEUNIT_SPEC_KEY_MODULE",
2478            COMPUTEUNIT_SPEC_KEY_MODULE,
2479            caixa_core::COMPUTEUNIT_SPEC_KEY_MODULE,
2480        );
2481    }
2482
2483    #[test]
2484    fn computeunit_spec_key_trigger_re_export_points_at_caixa_core_canonical() {
2485        // Peer to
2486        // [`computeunit_spec_key_module_re_export_points_at_caixa_core_canonical`]
2487        // on the same ComputeUnit-CRD per-`spec.*` sub-block re-export
2488        // surface — pins the per-Servico invocation-shape sub-block
2489        // key's identity on the same trajectory.
2490        caixa_core::assert_str_reexport_identity(
2491            "COMPUTEUNIT_SPEC_KEY_TRIGGER",
2492            COMPUTEUNIT_SPEC_KEY_TRIGGER,
2493            caixa_core::COMPUTEUNIT_SPEC_KEY_TRIGGER,
2494        );
2495    }
2496
2497    #[test]
2498    fn computeunit_spec_key_capabilities_re_export_points_at_caixa_core_canonical() {
2499        // Peer to
2500        // [`computeunit_spec_key_module_re_export_points_at_caixa_core_canonical`]
2501        // and
2502        // [`computeunit_spec_key_trigger_re_export_points_at_caixa_core_canonical`]
2503        // on the same ComputeUnit-CRD per-`spec.*` sub-block re-export
2504        // surface — completes the substrate-side ComputeUnit-CRD
2505        // per-`spec.*` sub-block re-export triple in this crate on the
2506        // WASI-capability-token-list axis.
2507        caixa_core::assert_str_reexport_identity(
2508            "COMPUTEUNIT_SPEC_KEY_CAPABILITIES",
2509            COMPUTEUNIT_SPEC_KEY_CAPABILITIES,
2510            caixa_core::COMPUTEUNIT_SPEC_KEY_CAPABILITIES,
2511        );
2512    }
2513
2514    #[test]
2515    fn chart_file_alias_resolves_to_caixa_core_rendered_file() {
2516        // Type-alias identity pin: the [`ChartFile`] alias at this
2517        // crate's boundary resolves to the canonical
2518        // [`caixa_core::RenderedFile`] the substrate-side "one rendered
2519        // leaf artifact" shape lives at. `let _: ChartFile = <a
2520        // RenderedFile>` type-checks *iff* [`ChartFile`] is the aliased
2521        // canonical (not a sibling pub-struct re-declaration that
2522        // happens to carry the same field pair — that would compile
2523        // past the struct-literal navigators below but fail this
2524        // assignment). A drifted local `pub struct ChartFile { pub
2525        // path: PathBuf, pub contents: String }` at this crate — the
2526        // canonical drift footgun that would carry the same field pair
2527        // at the source while pointing at a different struct
2528        // definition — trips this pin at caixa-helm build time rather
2529        // than surfacing as a downstream `caixa_core::RenderedFile`
2530        // consumer refusing a `ChartFile`-shaped value at type-check
2531        // time far from the drift commit. Peer to the sibling
2532        // [`caixa_flux::BundleFile`]-alias-identity pin on the same
2533        // per-target-renderer canonical [`caixa_core::RenderedFile`]
2534        // re-export surface — both crates' per-artifact leaf type now
2535        // resolves through the same canonical struct definition, so a
2536        // future rebrand on the record shape lands at one caixa-core
2537        // edit and reaches both consumers by construction.
2538        let canonical: caixa_core::RenderedFile = caixa_core::RenderedFile {
2539            path: PathBuf::from(HELM_CHART_YAML_FILENAME),
2540            contents: String::new(),
2541        };
2542        let aliased: ChartFile = canonical.clone();
2543        assert_eq!(aliased, canonical);
2544        // Struct-literal construction still resolves through the alias
2545        // — the pre-lift `ChartFile { path, contents }` shape at every
2546        // production emit site (three sites in
2547        // `render_chart_for_servico_with`'s `ChartDir::files` assembly)
2548        // continues to compile, and the derive tuple travels through
2549        // the alias so downstream `ChartDir::files.iter().find(|f|
2550        // f.path == PathBuf::from(HELM_VALUES_YAML_FILENAME))`
2551        // navigators keep matching by `PartialEq` on `PathBuf`.
2552        let via_alias = ChartFile {
2553            path: PathBuf::from(HELM_VALUES_YAML_FILENAME),
2554            contents: format!("{DEFAULT_LIBRARY_NAME}:\n  enabled: false\n"),
2555        };
2556        assert_eq!(via_alias.path.to_string_lossy(), HELM_VALUES_YAML_FILENAME);
2557    }
2558
2559    #[test]
2560    fn chart_file_new_constructor_travels_through_alias_to_canonical() {
2561        // Inherent-method-through-alias pin: the canonical
2562        // [`caixa_core::RenderedFile::new`] `impl Into<PathBuf>` /
2563        // `impl Into<String>` constructor every per-artifact leaf in
2564        // [`render_chart_for_servico_with`] now routes through
2565        // resolves at `ChartFile::new(…)` — Rust inherent methods
2566        // travel through a `pub type ChartFile = caixa_core::RenderedFile`
2567        // alias to the aliased canonical at name resolution, so a
2568        // drifted local `pub struct ChartFile { pub path: PathBuf, pub
2569        // contents: String }` at this crate would carry the field
2570        // pair the sibling type-alias-identity pin above still
2571        // accepts (both records share `pub path` / `pub contents`
2572        // shape) while dropping the constructor — the six sweep sites
2573        // in [`render_chart_for_servico_with`] would stop compiling
2574        // and the failing calls would name `ChartFile` directly,
2575        // making the drift-source unambiguous. This test pins the
2576        // constructor's per-alias reachability + the byte-identical
2577        // record shape against a `HELM_CHART_YAML_FILENAME`-keyed
2578        // probe so the pin fires at caixa-helm build time.
2579        let via_alias_new: ChartFile = ChartFile::new(HELM_CHART_YAML_FILENAME, "apiVersion: v2\n");
2580        let via_canonical_new = caixa_core::RenderedFile::new(
2581            HELM_CHART_YAML_FILENAME,
2582            String::from("apiVersion: v2\n"),
2583        );
2584        assert_eq!(via_alias_new, via_canonical_new);
2585        assert_eq!(via_alias_new.path, PathBuf::from(HELM_CHART_YAML_FILENAME));
2586        assert_eq!(via_alias_new.contents, "apiVersion: v2\n");
2587    }
2588
2589    #[test]
2590    fn render_opts_default_library_version_follows_lifted_constant() {
2591        // Peer of [`render_opts_default_library_name_follows_lifted_constant`]
2592        // (which pins the same alignment on the sibling
2593        // [`RenderOpts::library_name`] / [`DEFAULT_LIBRARY_NAME`] axis). The
2594        // [`RenderOpts::default()`] impl sets `library_version` from
2595        // [`DEFAULT_LIBRARY_VERSION`]; a future refactor that detached the
2596        // default-knob from the lifted constant — accidentally re-inlining
2597        // `"~0.1.0"` in the impl body — would silently split the value the
2598        // default knob threads into every rendered `Chart.yaml`
2599        // `dependencies[0].version` axis from the const the const's callers
2600        // (and this crate's future per-`DEFAULT_LIBRARY_VERSION` drift pins)
2601        // read. The two `Chart.yaml`-dep `(name, version)` scalar-axes now
2602        // share the same "default-knob follows lifted constant, byte for
2603        // byte" pin discipline the peer library-name axis carries.
2604        let opts = RenderOpts::default();
2605        assert_eq!(opts.library_version, DEFAULT_LIBRARY_VERSION);
2606        assert_eq!(opts.library_version, "~0.1.0");
2607    }
2608
2609    #[test]
2610    fn render_opts_default_enabled_default_follows_lifted_constant() {
2611        // Peer of [`render_opts_default_library_name_follows_lifted_constant`]
2612        // /
2613        // [`render_opts_default_library_version_follows_lifted_constant`]
2614        // / [`render_opts_default_library_repo_follows_lifted_constant`] —
2615        // the fourth leg of the [`RenderOpts::default()`]-body
2616        // default-knob-follows-lifted-constant quartet. The
2617        // [`RenderOpts::default()`] impl seeds `enabled_default` from
2618        // [`STANDALONE_LAREIRA_ENABLED_DEFAULT`]; every rendered
2619        // `lareira-<nome>` chart's `values.yaml` under-`<library>.enabled`
2620        // scalar reads through this knob, so a future refactor that
2621        // detached the default-knob from the lifted constant —
2622        // accidentally re-inlining `false` in the impl body — would
2623        // silently split the `bool` the default seed writes from the
2624        // const the drift-detection pin
2625        // [`standalone_lareira_enabled_default_pins_canonical_value`]
2626        // (in caixa-core) reads. The rendered values block would then
2627        // carry one `bool` at the emit site while the const-consuming
2628        // sibling test-fixture navigators (this crate's future per-
2629        // `STANDALONE_LAREIRA_ENABLED_DEFAULT` drift pins) read another,
2630        // and the substrate's chosen mirror-symmetric standalone /
2631        // composition per-values-block child-chart-enablement-toggle-
2632        // scalar-value default pair would silently disagree on the
2633        // standalone-path half. Peer with the sibling
2634        // `caixa_flux::tests::cluster_bundle_lareira_enabled_default_re_export_matches_caixa_core_canonical_value`
2635        // pin on the composition-path half of the same
2636        // [`HELM_VALUES_KEY_ENABLED`] scalar-axis pair.
2637        let opts = RenderOpts::default();
2638        assert_eq!(opts.enabled_default, STANDALONE_LAREIRA_ENABLED_DEFAULT);
2639        assert!(!opts.enabled_default);
2640    }
2641
2642    #[test]
2643    fn standalone_lareira_enabled_default_re_export_matches_caixa_core_canonical_value() {
2644        // The renderer's `STANDALONE_LAREIRA_ENABLED_DEFAULT` was lifted
2645        // from the [`RenderOpts::default()`] impl-body inline `false`
2646        // scalar-value literal at `caixa-helm/src/lib.rs:700` to a
2647        // re-export of [`caixa_core::STANDALONE_LAREIRA_ENABLED_DEFAULT`]
2648        // so the substrate-side default the standalone per-chart path
2649        // seeds under the sibling [`HELM_VALUES_KEY_ENABLED`]
2650        // leaf-scalar-key lives in exactly one place across every caixa
2651        // renderer (this crate's standalone per-chart path + the peer
2652        // `caixa_flux::cluster_bundle`'s composition per-cluster-
2653        // `HelmRelease` values-overlay path, which reads through the
2654        // inverse [`caixa_flux::CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT`]
2655        // re-export). Pin the equality here so any local re-introduction
2656        // of a sibling `pub const STANDALONE_LAREIRA_ENABLED_DEFAULT:
2657        // bool = …` at this crate (the canonical drift footgun the peer
2658        // `CLUSTER_BUNDLE_LAREIRA_ENABLED_DEFAULT` re-export identity
2659        // pin's rationale names as the recurring shape) is a build-time
2660        // test failure naming the offending drift, not a silent
2661        // apply-time toggle-mismatch routing the standalone per-chart
2662        // `values.<library>.enabled` seed onto one substrate-side
2663        // opt-out convention while the peer composition-path override
2664        // routes onto another. Peer to the sibling
2665        // `caixa_flux::tests::cluster_bundle_lareira_enabled_default_re_export_matches_caixa_core_canonical_value`
2666        // on the composition-path half of the same
2667        // [`HELM_VALUES_KEY_ENABLED`] scalar-axis pair — the two
2668        // per-path re-export identity pins together lock the two peer
2669        // scalar-value defaults' per-crate re-exports onto their shared
2670        // caixa-core canonical.
2671        assert_eq!(
2672            STANDALONE_LAREIRA_ENABLED_DEFAULT,
2673            caixa_core::STANDALONE_LAREIRA_ENABLED_DEFAULT,
2674            "STANDALONE_LAREIRA_ENABLED_DEFAULT re-export must remain the \
2675             same `bool` as its caixa-core canonical — a drifted local \
2676             `pub const STANDALONE_LAREIRA_ENABLED_DEFAULT: bool = …` at \
2677             caixa-helm would silently split the substrate's chosen \
2678             standalone per-chart opt-out seed from the peer \
2679             composition-path force-on inversion the caixa-core canonical \
2680             encodes."
2681        );
2682        assert!(
2683            !STANDALONE_LAREIRA_ENABLED_DEFAULT,
2684            "STANDALONE_LAREIRA_ENABLED_DEFAULT must remain `false` — the \
2685             standalone per-chart path is the substrate-side opt-out path \
2686             where cluster operators must opt each caixa in per-cluster, \
2687             inverse of the composition per-cluster-HelmRelease values-\
2688             overlay path's opt-in force-on."
2689        );
2690    }
2691
2692    #[test]
2693    fn render_opts_default_library_repo_follows_lifted_constant() {
2694        // Peer of [`render_opts_default_library_name_follows_lifted_constant`]
2695        // /
2696        // [`render_opts_default_library_version_follows_lifted_constant`] —
2697        // the third leg of the per-`Chart.yaml`-dep
2698        // `(repository, name, version)` default-knob triple. The
2699        // [`RenderOpts::default()`] impl seeds `library_repo` from
2700        // [`DEFAULT_LIBRARY_REPO`]; every rendered `lareira-<nome>` chart's
2701        // `Chart.yaml` `dependencies[0].repository` field reads through
2702        // this knob, so a future refactor that detached the default-knob
2703        // from the lifted constant — re-inlining
2704        // `"file://../pleme-computeunit"` in the impl body — would silently
2705        // split the URL the default seed writes from the const the
2706        // drift-detection pin below
2707        // ([`default_library_repo_ends_with_lifted_default_library_name`])
2708        // reads.
2709        let opts = RenderOpts::default();
2710        assert_eq!(opts.library_repo, DEFAULT_LIBRARY_REPO);
2711        assert_eq!(opts.library_repo, "file://../pleme-computeunit");
2712    }
2713
2714    #[test]
2715    fn default_library_version_parses_as_valid_semver_requirement() {
2716        // Structural pin: [`DEFAULT_LIBRARY_VERSION`] carries a Cargo-shaped
2717        // semver-requirement string that lands verbatim in every rendered
2718        // `lareira-<nome>` chart's `Chart.yaml` `dependencies[0].version`
2719        // field. Helm 3's chart-schema parser (`helm dependency build`,
2720        // `helm lint`, `helm template`, `helm install`) validates the
2721        // scalar against the same `semver::VersionReq` grammar
2722        // [`caixa_core::parse_requirement`] wraps, and rejects a malformed
2723        // shape (`"~0.1.,0"` — paste-from-typography stray comma;
2724        // `"v0.1.0"` — accidental Zig-style publish-tag prefix leaking back
2725        // from [`caixa_core::DEFAULT_PUBLISH_TAG_PREFIX`] into the
2726        // requirement axis; `"0.1"` with a trailing sigil dropped by a
2727        // fat-fingered edit) with the load-bearing `Error: found operator
2728        // …, expected version` diagnostic surfacing at chart-consumption
2729        // time — far from the constant-drift commit's source, with no
2730        // field naming the offending caixa or the drifted default. Routing
2731        // through [`caixa_core::parse_requirement`] here — the same
2732        // requirement-parser entry-point every peer typed `:versao`
2733        // requirement slot (`:deps`, `:deps-dev`, `:membros`, `:children`)
2734        // routes through via
2735        // [`caixa_core::require_valid_versao_requirement`] — closes the
2736        // drift structurally at caixa-helm build time and pins the const's
2737        // accepted set to exactly the set the peer author-facing
2738        // requirement axes accept: any shape a caixa author cannot write
2739        // in `:deps :versao` is a shape the substrate cannot seed as the
2740        // library-chart-dep default. Peer of the sibling
2741        // [`default_library_repo_ends_with_lifted_default_library_name`]
2742        // structural pin on the co-resident `(name, version)` per-Chart.yaml
2743        // dep-scalar pair.
2744        caixa_core::parse_requirement(DEFAULT_LIBRARY_VERSION).unwrap_or_else(|e| {
2745            panic!(
2746                "DEFAULT_LIBRARY_VERSION {DEFAULT_LIBRARY_VERSION:?} must parse as a valid \
2747                 semver::VersionReq — every rendered lareira-<nome> chart's Chart.yaml \
2748                 dependencies[0].version axis lands this scalar verbatim, and Helm 3's \
2749                 chart-schema parser rejects a malformed shape at chart-consumption time \
2750                 far from the constant-drift commit's source: {e}",
2751            )
2752        });
2753    }
2754
2755    #[test]
2756    fn default_library_repo_ends_with_lifted_default_library_name() {
2757        // Structural cross-const coherence pin: [`DEFAULT_LIBRARY_REPO`]
2758        // embeds the [`DEFAULT_LIBRARY_NAME`] byte-string verbatim as its
2759        // trailing directory-name component (the canonical
2760        // `file://../<library-chart-name>` shape every sibling
2761        // `lareira-<nome>` chart's `Chart.yaml` `dependencies[0]` entry
2762        // consults for a two-axis `(name, repository)` per-dep tuple that
2763        // Helm's per-chart-dep resolver `(chart-source-scheme + chart-name)`
2764        // navigator round-trips). The two axes must stay coupled: the
2765        // library-chart-directory on disk (the repo's trailing component)
2766        // and the library-chart's declared `name:` in its own
2767        // [`DEFAULT_LIBRARY_NAME`]-published `Chart.yaml` are the same
2768        // load-bearing chart-name identity. Prior to this pin the two
2769        // consts were independently authored — a future substrate-side
2770        // library-chart rebrand (`pleme-computeunit` → `pleme-cu` on a
2771        // shorter-form migration, `pleme-computeunit` →
2772        // `caixa-computeunit` on a substrate-alignment migration, a
2773        // per-edition library-chart fork the [`DEFAULT_LIBRARY_NAME`]
2774        // docstring names as a trajectory item) on the
2775        // [`caixa_core::DEFAULT_LIBRARY_NAME`] canonical without a
2776        // coordinated edit on this crate's [`DEFAULT_LIBRARY_REPO`] would
2777        // silently emit rendered `Chart.yaml` documents whose
2778        // `dependencies[0].name` names the new chart while
2779        // `dependencies[0].repository` points at the old directory —
2780        // `helm dependency build` would refuse to resolve the dep ("chart
2781        // <new-name> not found in file://../<old-name>") at chart-
2782        // consumption time, far from the constant-rebrand commit's source,
2783        // with no field naming the two-axis coherence drift root cause.
2784        // Pinning the structural `ends_with(DEFAULT_LIBRARY_NAME)` invariant
2785        // here surfaces the drift as a caixa-helm build-time test failure
2786        // and forces the coordinated `(REPO, NAME)` edit to move together.
2787        // Peer of the sibling
2788        // [`default_library_version_parses_as_valid_semver_requirement`]
2789        // structural pin on the co-resident `(name, version)` per-Chart.yaml
2790        // dep-scalar pair — completes the `(repository, name, version)`
2791        // per-Chart.yaml-dep default-triple's structural pin surface.
2792        assert!(
2793            DEFAULT_LIBRARY_REPO.ends_with(DEFAULT_LIBRARY_NAME),
2794            "DEFAULT_LIBRARY_REPO {DEFAULT_LIBRARY_REPO:?} must terminate with the lifted \
2795             DEFAULT_LIBRARY_NAME {DEFAULT_LIBRARY_NAME:?} — the two-axis (repository, name) \
2796             per-Chart.yaml-dep tuple must resolve to the same library-chart identity on \
2797             disk, so a rebrand on either axis must move both",
2798        );
2799    }
2800
2801    #[test]
2802    fn chart_yaml_version_routes_through_caixa_versao_accessor() {
2803        // Fail-before-pass-after pin: the emit-side per-`Chart.yaml`
2804        // top-level `version:` scalar the [`build_chart_yaml`] fn
2805        // writes must derive from the typed
2806        // [`caixa_core::Caixa::versao`] accessor byte-for-byte.
2807        // Before this converge the emit site carried a raw
2808        // `caixa.versao.clone()` field access at
2809        // [`build_chart_yaml`]'s per-`Chart.yaml` version-field
2810        // insert position — one of the two production-code
2811        // `String`-carry sites of `Caixa::versao` on this fn's
2812        // emit path — and a future extension of the accessor
2813        // (a build-metadata canonicalization pass the CAIXA-SDLC
2814        // §I SemVer-2 pin acknowledges, an OCI-tag normalization
2815        // the M4 registry-alignment slot lands, a per-edition
2816        // pre-release-tag overlay the sibling `Caixa::edicao`
2817        // universal-axis 4-digit-ASCII-decimal-year scalar
2818        // dispatches through) that landed on the accessor but
2819        // not on this emit site would silently split the
2820        // per-`Chart.yaml` `version:` axis (the discriminator
2821        // Helm's per-chart resolver keys per-release
2822        // reconciliation off, the paired `HelmRelease`
2823        // `spec.chart.spec.version` binds through, and every
2824        // `helm template <chart>` / `helm install <release>
2825        // <chart>` / `helm upgrade <release> <chart>
2826        // --version` invocation names through) from every peer
2827        // read-side consumer of `Caixa::versao` (the
2828        // README-body `v{versao}` scalar at
2829        // [`build_readme`]:958 the paired `feira chart`
2830        // Nord-themed emit round-trips through, every peer
2831        // per-axis navigator via `Caixa::versao()`, the
2832        // `caixa_flux::programs_yaml_entry` per-entry
2833        // `versao:` scalar at caixa-flux/src/lib.rs:2031, the
2834        // `caixa_feira::cmd::publish` per-tag `caixa {nome} v{versao}`
2835        // git-tag scalar at caixa-feira/src/cmd/publish.rs:72).
2836        // Byte-equal today (the accessor is `&self.versao`);
2837        // the pin catches any future accessor extension whose
2838        // emit-side write regresses to the raw field. Peer to
2839        // [`chart_yaml_app_version_routes_through_caixa_versao_accessor`]
2840        // on the sibling per-`Chart.yaml` `appVersion:` axis.
2841        let caixa = sample_caixa();
2842        let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
2843        let chart: ChartYaml = parse_yaml_at_path_as(&dir.files, HELM_CHART_YAML_FILENAME);
2844        assert_eq!(
2845            chart.version.as_str(),
2846            caixa.versao(),
2847            "Chart.yaml `version:` must derive from the typed \
2848             `caixa_core::Caixa::versao` accessor byte-for-byte — a regression \
2849             that re-inlines `caixa.versao.clone()` at the emit site silently \
2850             splits the per-`Chart.yaml` `version:` axis from every future \
2851             accessor extension (SemVer-2 build-metadata canonicalization, \
2852             OCI-tag normalization, per-edition pre-release-tag overlay) that \
2853             lands on the accessor",
2854        );
2855    }
2856
2857    #[test]
2858    fn chart_yaml_app_version_routes_through_caixa_versao_accessor() {
2859        // Fail-before-pass-after pin: the emit-side per-`Chart.yaml`
2860        // top-level `appVersion:` scalar the [`build_chart_yaml`] fn
2861        // writes must derive from the typed
2862        // [`caixa_core::Caixa::versao`] accessor byte-for-byte.
2863        // Same single-source `let versao = caixa.versao().to_string()`
2864        // binding as the peer `version:` sibling pin — this test
2865        // pins the derived `Chart.yaml` `appVersion:` axis (the
2866        // axis Helm chart-consumers key per-application-version
2867        // documentation / release-note / OCI-tag / operator-side
2868        // per-Caixa CR revision off). Peer to
2869        // [`chart_yaml_version_routes_through_caixa_versao_accessor`]
2870        // on the sibling per-`Chart.yaml` `version:` axis — the
2871        // two together pin every per-`Chart.yaml` version-carrier
2872        // field on the typed accessor.
2873        let caixa = sample_caixa();
2874        let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
2875        let chart: ChartYaml = parse_yaml_at_path_as(&dir.files, HELM_CHART_YAML_FILENAME);
2876        assert_eq!(
2877            chart.app_version.as_str(),
2878            caixa.versao(),
2879            "Chart.yaml `appVersion:` must derive from the typed \
2880             `caixa_core::Caixa::versao` accessor byte-for-byte — a regression \
2881             that re-inlines `caixa.versao.clone()` at the emit site silently \
2882             splits the per-`Chart.yaml` `appVersion:` axis from every future \
2883             accessor extension (SemVer-2 build-metadata canonicalization, \
2884             OCI-tag normalization, per-edition pre-release-tag overlay) that \
2885             lands on the accessor",
2886        );
2887    }
2888
2889    #[test]
2890    fn chart_yaml_name_routes_through_caixa_nome_accessor() {
2891        // Emit-path pin: the per-`Chart.yaml` top-level `name:`
2892        // scalar the [`build_chart_yaml`] fn writes must derive
2893        // from the typed [`caixa_core::Caixa::nome`] accessor
2894        // byte-for-byte through the substrate-canonical
2895        // [`caixa_core::lareira_chart_name`] identity composer.
2896        // Before this converge the outer `lareira_chart_name(&caixa.nome)`
2897        // call at [`render_chart_for_servico_with`] carried a raw
2898        // `&caixa.nome` borrow-then-deref of the underlying `String`
2899        // field, bypassing the typed accessor. Peer of the sibling
2900        // eb912de `caixa.versao().to_string()` converge on the
2901        // co-resident `Caixa::versao` `String`-carry axis in this
2902        // crate and the sibling 4a363bf / 54bf2f3 `caixa.nome().to_string()`
2903        // converges on the outer-Caixa `:nome` `String`-carry axis
2904        // in caixa-flux / caixa-mesh — extends the "one typed
2905        // dispatch on the substrate primitive, thin projections at
2906        // each consumer" discipline onto the non-`.clone()` raw-
2907        // field-access axis of `Caixa::nome` in caixa-helm. Byte-
2908        // equal today (the accessor is `&self.nome`); the pin
2909        // catches any future accessor extension (a per-cluster
2910        // alias overlay, an M4 CR-materializer name rewrite, a
2911        // future `:nome-suffix` slot) whose emit-side write
2912        // regresses to the raw `&caixa.nome` field access.
2913        let caixa = sample_caixa();
2914        let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
2915        let chart: ChartYaml = parse_yaml_at_path_as(&dir.files, HELM_CHART_YAML_FILENAME);
2916        assert_eq!(
2917            chart.name,
2918            caixa_core::lareira_chart_name(caixa.nome()),
2919            "Chart.yaml `name:` must derive from the typed \
2920             `caixa_core::Caixa::nome` accessor through \
2921             `caixa_core::lareira_chart_name` byte-for-byte — a regression \
2922             that re-inlines `lareira_chart_name(&caixa.nome)` at the emit \
2923             site silently splits the per-`Chart.yaml` `name:` axis from \
2924             every future accessor extension (per-cluster alias overlay, \
2925             M4 CR-materializer name rewrite, `:nome-suffix` slot) that \
2926             lands on the accessor",
2927        );
2928    }
2929
2930    #[test]
2931    fn chart_dir_name_routes_through_caixa_lareira_chart_name_accessor() {
2932        // Emit-path pin: the per-`ChartDir` top-level `name` axis the
2933        // [`render_chart_for_servico_with`] fn writes must derive from
2934        // the substrate-canonical
2935        // [`caixa_core::Caixa::lareira_chart_name`] resolved-chart-name
2936        // dispatch. Before this converge the outer `lareira_chart_name
2937        // (caixa.nome())` two-step compose at the emit site re-derived
2938        // the `LAREIRA_CHART_NAME_PREFIX + :nome` composition inline,
2939        // bypassing the substrate primitive's single-`&Caixa` dispatch.
2940        // Peer of the sibling [`Caixa::canonical_git_url`] (124f864) /
2941        // [`Caixa::publish_tag`] (07e05b8) resolved-composers'
2942        // paired-site convergence pins the sibling caixa-flux
2943        // [`cluster_bundle_opts_for_caixa_git_url_routes_through_canonical_git_url_accessor`]
2944        // + `_git_ref_routes_through_publish_tag_accessor` byte-parity
2945        // tests carry on the per-Servico-renderer emit surface. Byte-
2946        // equal today (the accessor is
2947        // `caixa_core::lareira_chart_name(self.nome())`); the pin
2948        // catches any future accessor extension (a per-cluster alias
2949        // overlay, an M4 CR-materializer name rewrite, a
2950        // `:nome-suffix` slot, the [`LAREIRA_CHART_NAME_PREFIX`]
2951        // rebrand once the chart-family scoping intent shifts) whose
2952        // emit-side write regresses to the two-step open-coded compose.
2953        let caixa = sample_caixa();
2954        let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
2955        assert_eq!(
2956            dir.name,
2957            caixa.lareira_chart_name(),
2958            "ChartDir.name must derive from the substrate-canonical \
2959             `caixa_core::Caixa::lareira_chart_name` accessor byte-for-\
2960             byte — a regression that re-inlines \
2961             `caixa_core::lareira_chart_name(caixa.nome())` at the emit \
2962             site silently splits the per-`ChartDir` `name` axis from \
2963             every future accessor extension (per-cluster alias overlay, \
2964             M4 CR-materializer name rewrite, `:nome-suffix` slot, \
2965             `LAREIRA_CHART_NAME_PREFIX` rebrand) that lands on the \
2966             accessor",
2967        );
2968    }
2969
2970    #[test]
2971    fn chart_yaml_description_fallback_routes_through_caixa_nome_accessor() {
2972        // Emit-path pin: on a `:descricao`-null caixa the
2973        // [`build_chart_yaml`] `description:` fallback substitutes
2974        // `format!("Generated chart for caixa Servico {}", caixa.nome())`,
2975        // which must derive its terminal identity byte-string from the
2976        // typed [`caixa_core::Caixa::nome`] accessor. Before this
2977        // converge the fallback carried a raw `caixa.nome` Display of
2978        // the underlying `String` field, bypassing the typed accessor.
2979        // Byte-equal today; the pin catches any future accessor
2980        // extension whose fallback emit regresses to the raw field.
2981        let caixa = Caixa {
2982            descricao: None,
2983            ..sample_caixa()
2984        };
2985        let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
2986        let chart: ChartYaml = parse_yaml_at_path_as(&dir.files, HELM_CHART_YAML_FILENAME);
2987        assert_eq!(
2988            chart.description,
2989            format!("Generated chart for caixa Servico {}", caixa.nome()),
2990            "Chart.yaml `description:` `:descricao`-null fallback must \
2991             derive from the typed `caixa_core::Caixa::nome` accessor \
2992             byte-for-byte — a regression that re-inlines \
2993             `format!(\"Generated chart for caixa Servico {{}}\", caixa.nome)` \
2994             at the emit site silently splits the per-`Chart.yaml` \
2995             `description:` axis from every future accessor extension \
2996             that lands on the accessor",
2997        );
2998    }
2999
3000    #[test]
3001    fn values_yaml_header_nome_routes_through_caixa_nome_accessor() {
3002        // Emit-path pin: the [`build_values_yaml`] `# Auto-generated
3003        // by caixa-helm from caixa.lisp + servicos/{nome}.computeunit.yaml.`
3004        // comment header carries the parent-caixa's `:nome` identity
3005        // byte-string verbatim through the typed
3006        // [`caixa_core::Caixa::nome`] accessor. Before this converge
3007        // the site carried a raw `nome = caixa.nome` Display of the
3008        // underlying `String` field, bypassing the typed accessor.
3009        // Byte-equal today; the pin catches any future accessor
3010        // extension whose header-emit regresses to the raw field.
3011        let caixa = sample_caixa();
3012        let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
3013        let values_file =
3014            find_file_by_path(&dir.files, HELM_VALUES_YAML_FILENAME).expect("values.yaml present");
3015        let expected = format!("servicos/{}.computeunit.yaml", caixa.nome());
3016        assert!(
3017            values_file.contents.contains(&expected),
3018            "values.yaml header comment must carry the typed \
3019             `caixa_core::Caixa::nome` accessor's byte-string \
3020             ({expected:?}) verbatim — a regression that re-inlines \
3021             `caixa.nome` in the header format silently splits the \
3022             values.yaml provenance-annotation axis from every future \
3023             accessor extension that lands on the accessor. \
3024             Full contents:\n{contents}",
3025            contents = values_file.contents,
3026        );
3027    }
3028
3029    #[test]
3030    fn readme_descricao_fallback_routes_through_caixa_nome_accessor() {
3031        // Emit-path pin: on a `:descricao`-null caixa the
3032        // [`build_readme`] descricao-line fallback substitutes
3033        // `format!("caixa Servico {}", caixa.nome())`, which must
3034        // derive its terminal identity byte-string from the typed
3035        // [`caixa_core::Caixa::nome`] accessor. Before this converge
3036        // the fallback carried a raw `caixa.nome` Display of the
3037        // underlying `String` field, bypassing the typed accessor.
3038        // Byte-equal today; the pin catches any future accessor
3039        // extension whose fallback emit regresses to the raw field.
3040        let caixa = Caixa {
3041            descricao: None,
3042            ..sample_caixa()
3043        };
3044        let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
3045        let readme_file =
3046            find_file_by_path(&dir.files, HELM_CHART_README_FILENAME).expect("README.md present");
3047        let expected = format!("caixa Servico {}", caixa.nome());
3048        assert!(
3049            readme_file.contents.contains(&expected),
3050            "README.md `:descricao`-null fallback must carry the typed \
3051             `caixa_core::Caixa::nome` accessor's byte-string ({expected:?}) \
3052             verbatim — a regression that re-inlines `format!(\"caixa \
3053             Servico {{}}\", caixa.nome)` at the emit site silently \
3054             splits the README fallback-descricao axis from every future \
3055             accessor extension. Full contents:\n{contents}",
3056            contents = readme_file.contents,
3057        );
3058    }
3059
3060    #[test]
3061    fn readme_body_version_routes_through_caixa_versao_accessor() {
3062        // Emit-path pin: the per-`README.md` `Origin` line the
3063        // [`build_readme`] fn writes carries the terminal
3064        // `v{versao}` scalar the `feira chart` Nord-themed emit
3065        // round-trips through — that scalar must derive from the
3066        // typed [`caixa_core::Caixa::versao`] accessor byte-for-byte.
3067        // Before this converge the emit site carried a raw
3068        // `caixa.versao` `Display` field-access, bypassing the
3069        // typed accessor. Sibling of the 162e2e2 (caixa-flux) /
3070        // 980c059 (caixa-mesh) / 22461ef (caixa-helm) `Caixa::nome`
3071        // Display-axis converges — this closes the co-resident
3072        // `Caixa::versao` Display-axis in caixa-helm the eb912de
3073        // `caixa.versao().to_string()` `String`-carry converge
3074        // left open on the read-only Display-borrow arm. Byte-equal
3075        // today (the accessor is `&self.versao`); the pin catches
3076        // any future accessor extension (SemVer-2 build-metadata
3077        // canonicalization, OCI-tag normalization, per-edition
3078        // pre-release-tag overlay) whose emit-side Display regresses
3079        // to the raw field.
3080        let caixa = sample_caixa();
3081        let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
3082        let readme_file =
3083            find_file_by_path(&dir.files, HELM_CHART_README_FILENAME).expect("README.md present");
3084        let expected = format!("caixa.lisp` v{}.", caixa.versao());
3085        assert!(
3086            readme_file.contents.contains(&expected),
3087            "README.md `Origin`-line `v{{versao}}` scalar must derive from \
3088             the typed `caixa_core::Caixa::versao` accessor byte-for-byte \
3089             ({expected:?}) — a regression that re-inlines `caixa.versao` \
3090             in the format silently splits the README origin-line version \
3091             axis from every future accessor extension (SemVer-2 \
3092             build-metadata canonicalization, OCI-tag normalization, \
3093             per-edition pre-release-tag overlay) that lands on the \
3094             accessor. Full contents:\n{contents}",
3095            contents = readme_file.contents,
3096        );
3097    }
3098
3099    #[test]
3100    fn contains_key_bare_str_key_byte_equals_value_string_wrapped_form_across_swept_axis_keys() {
3101        // Per-crate fail-before-pass-after equivalence pin covering the
3102        // five test-side `assert!(mapping.contains_key(<KEY>))`
3103        // drift-detection sites that this commit swept off the
3104        // three-token `serde_yaml::Value::String(<KEY>.to_string())`
3105        // wrapped shape onto the shorter bare-`&str` form
3106        // ([`HELM_CHART_KEY_TYPE`] on the per-Chart.yaml top-level
3107        // chart-kind discriminator axis, [`HELM_CHART_KEY_APP_VERSION`]
3108        // on the underlying-application-version axis,
3109        // [`HELM_CHART_KEY_API_VERSION`] on the chart-schema-apiVersion
3110        // axis, the per-`dependencies[]`-entry tetrad iterator
3111        // ([`HELM_CHART_DEPENDENCY_KEY_NAME`] /
3112        // [`HELM_CHART_DEPENDENCY_KEY_VERSION`] /
3113        // [`HELM_CHART_DEPENDENCY_KEY_REPOSITORY`] /
3114        // [`HELM_CHART_DEPENDENCY_KEY_ALIAS`]), and
3115        // [`HELM_CHART_KEY_DEPENDENCIES`] on the top-level
3116        // dependency-list container axis).
3117        //
3118        // The equivalence is a serde_yaml crate-level property (the
3119        // `impl Index for str` and `impl Index for Value` paths both
3120        // route through the same `HashLikeValue(&str)` bucket lookup)
3121        // pinned at the caixa-core-side sibling
3122        // `mapping_get_bare_str_key_byte_equals_value_string_wrapped_form`'s
3123        // trailing `contains_key` parity block for the generic axis
3124        // (0e84fb9). This pin lifts the same equivalence to the
3125        // caixa-helm-side lifted axis-key set every swept site keys
3126        // off, so a future `serde_yaml` upgrade whose `Index for str`
3127        // path diverges from `Index for Value` for the specific
3128        // key-string byte-shape at any of the seven swept keys is a
3129        // caixa-helm-build-time failure naming the offending axis-key,
3130        // not a silent regression that would let the swept
3131        // `assert!(_.contains_key(K))` shape probes drift past the
3132        // emitter's [`MappingExt::insert_str_key`]-promoted key.
3133        //
3134        // Peer to the caixa-core-side
3135        // `mapping_get_bare_str_key_byte_equals_value_string_wrapped_form`
3136        // pin (0e84fb9) on the generic `contains_key` parity axis —
3137        // the two together partition the equivalence-pin surface
3138        // exactly on the axis-key set specificity: caixa-core-side pin
3139        // covers the generic KUBE_KEY_KIND / KUBE_KEY_SPEC probe;
3140        // this caixa-helm-side pin covers the seven Chart.yaml-shape
3141        // axis-keys the swept sites key off.
3142        let mut m = serde_yaml::Mapping::new();
3143        m.insert_string(HELM_CHART_KEY_TYPE, "application");
3144        m.insert_string(HELM_CHART_KEY_APP_VERSION, "1.0.0");
3145        m.insert_string(HELM_CHART_KEY_API_VERSION, "v2");
3146        m.insert_string(HELM_CHART_KEY_DEPENDENCIES, "placeholder");
3147        m.insert_string(HELM_CHART_DEPENDENCY_KEY_NAME, "pleme-computeunit");
3148        m.insert_string(HELM_CHART_DEPENDENCY_KEY_VERSION, "0.1.0");
3149        m.insert_string(
3150            HELM_CHART_DEPENDENCY_KEY_REPOSITORY,
3151            "oci://ghcr.io/pleme-io",
3152        );
3153        m.insert_string(HELM_CHART_DEPENDENCY_KEY_ALIAS, "cu");
3154        for key in [
3155            HELM_CHART_KEY_TYPE,
3156            HELM_CHART_KEY_APP_VERSION,
3157            HELM_CHART_KEY_API_VERSION,
3158            HELM_CHART_KEY_DEPENDENCIES,
3159            HELM_CHART_DEPENDENCY_KEY_NAME,
3160            HELM_CHART_DEPENDENCY_KEY_VERSION,
3161            HELM_CHART_DEPENDENCY_KEY_REPOSITORY,
3162            HELM_CHART_DEPENDENCY_KEY_ALIAS,
3163        ] {
3164            // Present-key path: both forms find the same insertion.
3165            assert_eq!(
3166                m.contains_key(key),
3167                m.contains_key(serde_yaml::Value::String(key.to_string())),
3168                "present-key mapping.contains_key({key:?}) via bare-&str must \
3169                 byte-equal mapping.contains_key(Value::String({key:?}.to_string())) \
3170                 — otherwise the swept `assert!(_.contains_key({key:?}))` sites \
3171                 in this file drift silently past the emitter's `insert_str_key` \
3172                 promotion at every downstream drift-detection pin call site"
3173            );
3174        }
3175        // Absent-key path: both forms return false on a key that was
3176        // never inserted (mirroring the peer caixa-core-side
3177        // KUBE_KEY_SPEC absent-key parity assertion).
3178        let absent = "this_key_never_appears_in_any_chart_yaml_axis";
3179        assert_eq!(
3180            m.contains_key(absent),
3181            m.contains_key(serde_yaml::Value::String(absent.to_string())),
3182            "absent-key mapping.contains_key(<bare-&str>) must byte-equal \
3183             absent-key mapping.contains_key(Value::String(<key>.to_string())) \
3184             — otherwise a future swept drift-detection pin's absent-key arm \
3185             could silently disagree between the two forms"
3186        );
3187    }
3188
3189    #[test]
3190    fn readme_repositorio_null_fallback_routes_through_canonical_git_url_accessor() {
3191        // Emit-path pin: on a `:repositorio`-null caixa the
3192        // [`build_readme`] `## Origin` line's `{repo}` interpolation must
3193        // derive from the substrate-canonical
3194        // [`caixa_core::Caixa::canonical_git_url`] resolved-git-URL
3195        // composer's None-arm fallback (`https://github.com/{DEFAULT_PLEME_GIT_ORG}/<nome>`)
3196        // rather than the prior `caixa.repositorio().unwrap_or(caixa.nome())`
3197        // two-arm inline whose fallback folded to the bare `<nome>` scalar
3198        // and emitted the meaningless `Generated by `caixa-helm` from
3199        // `<nome>/caixa.lisp`` line the 124f864 commit body explicitly
3200        // called out as the live-behavior-correcting converge surface
3201        // waiting on this lift. Fail-before-pass-after: the pre-lift
3202        // shape carried the bare `hello-rio/caixa.lisp` substring and
3203        // this pin asserts the post-lift canonical pleme-org URL substring
3204        // in its place, so a regression that re-inlines the raw two-arm
3205        // fallback at the emit site surfaces at caixa-helm build time on
3206        // this test's failure rather than at a downstream README-consuming
3207        // UI's "where does this caixa live?" broken-URL trail.
3208        let caixa = Caixa {
3209            repositorio: None,
3210            ..sample_caixa()
3211        };
3212        let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
3213        let readme_file =
3214            find_file_by_path(&dir.files, HELM_CHART_README_FILENAME).expect("README.md present");
3215        let expected = format!(
3216            "Generated by `caixa-helm` from `{}/caixa.lisp`",
3217            caixa.canonical_git_url()
3218        );
3219        assert!(
3220            readme_file.contents.contains(&expected),
3221            "README.md `:repositorio`-null fallback must derive its \
3222             `repo` interpolation from the typed \
3223             `caixa_core::Caixa::canonical_git_url` resolved-git-URL \
3224             composer byte-for-byte ({expected:?}) — a regression that \
3225             re-inlines the prior `caixa.repositorio().unwrap_or(caixa.nome())` \
3226             two-arm fallback at the emit site silently regresses the \
3227             README origin-line repo axis to the meaningless bare `<nome>` \
3228             prefix and splits the caixa-helm README axis from the sibling \
3229             [`caixa_flux::ClusterBundleOpts::for_caixa`] `git_url` seed \
3230             which already resolves through the canonical composer. \
3231             Full contents:\n{contents}",
3232            contents = readme_file.contents,
3233        );
3234    }
3235
3236    #[test]
3237    fn readme_repositorio_null_fallback_no_longer_emits_bare_nome_prefix() {
3238        // Fail-before-pass-after regression guard: pins the negative arm
3239        // the sibling
3240        // [`readme_repositorio_null_fallback_routes_through_canonical_git_url_accessor`]
3241        // pin's positive arm implies — the pre-lift bare `<nome>/caixa.lisp`
3242        // substring (the shape the prior `caixa.repositorio().unwrap_or(caixa.nome())`
3243        // inline two-arm fallback emitted on every `:repositorio`-null
3244        // caixa) must NOT appear in the rendered README's `## Origin` line.
3245        // A regression that re-inlines the two-arm fallback at
3246        // [`build_readme`]'s emit site would satisfy the positive
3247        // canonical-composer substring check on the `Some(<full-URL>)`
3248        // arm and drop the fallback arm back to `<nome>/caixa.lisp`
3249        // silently — so an explicit negative-substring pin closes the
3250        // remaining converge surface the sibling positive pin does not
3251        // catch on its own.
3252        let caixa = Caixa {
3253            repositorio: None,
3254            ..sample_caixa()
3255        };
3256        let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
3257        let readme_file =
3258            find_file_by_path(&dir.files, HELM_CHART_README_FILENAME).expect("README.md present");
3259        let regressed = format!(
3260            "Generated by `caixa-helm` from `{}/caixa.lisp`",
3261            caixa.nome()
3262        );
3263        assert!(
3264            !readme_file.contents.contains(&regressed),
3265            "README.md `:repositorio`-null fallback must NOT emit the \
3266             pre-lift bare `<nome>/caixa.lisp` prefix ({regressed:?}) — \
3267             the `caixa.repositorio().unwrap_or(caixa.nome())` two-arm \
3268             inline fallback was retired in favor of \
3269             `caixa.canonical_git_url()` so the fallback arm now names \
3270             the substrate-canonical pleme-org URL; a regression that \
3271             re-inlines the two-arm fallback at the emit site would \
3272             silently split this caixa-helm README axis from the sibling \
3273             [`caixa_flux::ClusterBundleOpts::for_caixa`] `git_url` seed. \
3274             Full contents:\n{contents}",
3275            contents = readme_file.contents,
3276        );
3277    }
3278
3279    #[test]
3280    fn readme_repositorio_declared_arm_routes_through_canonical_git_url_accessor() {
3281        // Emit-path pin (peer of the `:repositorio`-null fallback sibling):
3282        // on a `:repositorio`-declared caixa the [`build_readme`] `## Origin`
3283        // line's `{repo}` interpolation must also derive from the substrate-
3284        // canonical [`caixa_core::Caixa::canonical_git_url`] resolved-git-URL
3285        // composer — the Some-arm return byte-string is the author-declared
3286        // `:repositorio` verbatim, so the substring the rendered README
3287        // carries matches the composer output on both arms and the pair
3288        // of pins jointly pins the entire two-arm dispatch through one
3289        // canonical composer. A regression that split the emit site into
3290        // a Some-arm-only bypass through the raw
3291        // [`caixa_core::Caixa::repositorio`] accessor while leaving the
3292        // fallback arm routed through the canonical composer would
3293        // silently drift the two-arm shape and be caught here (the raw
3294        // accessor path bypasses any future extension of the resolved-URL
3295        // composer, e.g. a `github:` → `https://github.com/` canonicalization
3296        // pass or the per-cluster repo-mirror overlay the M4 CR materializer
3297        // will apply).
3298        let caixa = sample_caixa();
3299        assert!(
3300            caixa.repositorio().is_some(),
3301            "sample_caixa() must carry a `Some(...)` `:repositorio` for \
3302             the Some-arm to exercise the canonical composer's non-fallback \
3303             path — a future sample_caixa() edit that drops the field must \
3304             re-establish it here or the pin regresses to a no-op",
3305        );
3306        let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
3307        let readme_file =
3308            find_file_by_path(&dir.files, HELM_CHART_README_FILENAME).expect("README.md present");
3309        let expected = format!(
3310            "Generated by `caixa-helm` from `{}/caixa.lisp`",
3311            caixa.canonical_git_url()
3312        );
3313        assert!(
3314            readme_file.contents.contains(&expected),
3315            "README.md `:repositorio`-declared arm must derive its `repo` \
3316             interpolation from the typed `caixa_core::Caixa::canonical_git_url` \
3317             resolved-git-URL composer byte-for-byte ({expected:?}) so the \
3318             two-arm dispatch routes through one canonical composer end to \
3319             end. Full contents:\n{contents}",
3320            contents = readme_file.contents,
3321        );
3322    }
3323
3324    #[test]
3325    #[allow(clippy::cmp_owned)] // Deliberate: the pin reproduces the
3326    // prior owning-`PathBuf::from(...)` comparand byte-for-byte to
3327    // guarantee the lift's substitution is behavior-preserving on
3328    // exactly the shape the 26 converged caixa-helm callers previously
3329    // carried.
3330    fn find_file_by_path_matches_prior_inline_iter_find_pathbuf_from_shape() {
3331        // Per-crate byte-equivalence pin on the lifted
3332        // [`caixa_core::find_file_by_path`] navigator: for every leaf
3333        // the `render_chart_for_servico` `lareira-<nome>` chart-
3334        // directory emit writes ([`HELM_CHART_YAML_FILENAME`] /
3335        // [`HELM_VALUES_YAML_FILENAME`] /
3336        // [`HELM_CHART_README_FILENAME`]), the lifted navigator must
3337        // byte-equal the prior three-line inline
3338        // `dir.files.iter().find(|f| f.path ==
3339        // PathBuf::from(<FILENAME_CONST>))` combinator the 26 test-
3340        // side per-artifact readback sites previously carried. Mirrors
3341        // the sibling `caixa-flux`
3342        // `find_file_by_path_matches_prior_inline_iter_find_pathbuf_from_shape`
3343        // pin at the peer caller-crate altitude and the substrate-
3344        // level `find_file_by_path_matches_inline_iter_find_pathbuf_from_shape`
3345        // pin at the primitive definition — the three-arm closure
3346        // that closes the discipline the sibling `sequence_str_values`
3347        // / `kube_has` sweeps established.
3348        let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
3349        for filename in [
3350            HELM_CHART_YAML_FILENAME,
3351            HELM_VALUES_YAML_FILENAME,
3352            HELM_CHART_README_FILENAME,
3353        ] {
3354            let via_helper = find_file_by_path(&dir.files, filename);
3355            let via_inline = dir.files.iter().find(|f| f.path == PathBuf::from(filename));
3356            assert_eq!(
3357                via_helper, via_inline,
3358                "find_file_by_path(&dir.files, {filename:?}) must \
3359                 byte-equal the prior three-line `dir.files.iter()\
3360                 .find(|f| f.path == PathBuf::from({filename:?}))` \
3361                 combinator on every leaf the `lareira-<nome>` chart-\
3362                 directory emit writes — otherwise the 26 routed per-\
3363                 artifact readback sites regress silently on the \
3364                 leaf-path axis",
3365            );
3366        }
3367    }
3368
3369    #[test]
3370    fn parse_yaml_at_path_matches_prior_inline_find_then_from_str_shape() {
3371        // Per-crate byte-equivalence pin on the lifted
3372        // [`caixa_core::parse_yaml_at_path`] composed navigator: for
3373        // every YAML leaf the `render_chart_for_servico` `lareira-
3374        // <nome>` chart-directory emit writes that the test-side
3375        // routed callers parse to a [`serde_yaml::Value`]
3376        // ([`HELM_CHART_YAML_FILENAME`] as `doc` /
3377        // [`HELM_VALUES_YAML_FILENAME`] as `parsed`), the lifted helper
3378        // must return a `Value` byte-equal to the prior two-step
3379        //
3380        //   let f = find_file_by_path(&dir.files, <FILENAME>).unwrap();
3381        //   let parsed: serde_yaml::Value =
3382        //       serde_yaml::from_str(&f.contents).unwrap();
3383        //
3384        // the 12 test-side per-artifact YAML readback sites previously
3385        // carried. Mirrors the sibling `caixa-flux`
3386        // `parse_yaml_at_path_matches_prior_inline_find_then_from_str_shape`
3387        // pin at the peer caller-crate altitude and the substrate-
3388        // level `parse_yaml_at_path_matches_prior_inline_two_step_shape`
3389        // pin at the primitive definition — the three-arm closure that
3390        // closes the same discipline the sibling `find_file_by_path`
3391        // sweep established. [`HELM_CHART_README_FILENAME`] is
3392        // deliberately not swept: the routed callers only assert on its
3393        // raw-byte content, never parse it to YAML.
3394        let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
3395        for filename in [HELM_CHART_YAML_FILENAME, HELM_VALUES_YAML_FILENAME] {
3396            let via_helper = parse_yaml_at_path(&dir.files, filename);
3397            let via_inline_file = find_file_by_path(&dir.files, filename).unwrap();
3398            let via_inline: serde_yaml::Value =
3399                serde_yaml::from_str(&via_inline_file.contents).unwrap();
3400            assert_eq!(
3401                via_helper, via_inline,
3402                "parse_yaml_at_path(&dir.files, {filename:?}) must \
3403                 byte-equal the prior two-step `find_file_by_path(&dir.files, \
3404                 {filename:?}).unwrap() + serde_yaml::from_str(&_.contents).unwrap()` \
3405                 combinator on every YAML leaf the `lareira-<nome>` chart-\
3406                 directory emit writes — otherwise the 12 routed per-\
3407                 artifact YAML readback sites regress silently on the \
3408                 parse-target axis",
3409            );
3410        }
3411    }
3412
3413    #[test]
3414    fn parse_yaml_at_path_as_matches_prior_inline_find_then_from_str_typed_shape() {
3415        // Per-crate byte-equivalence pin on the lifted
3416        // [`caixa_core::parse_yaml_at_path_as`] caller-typed composed
3417        // navigator: for the `Chart.yaml` YAML leaf the
3418        // `render_chart_for_servico` `lareira-<nome>` chart-directory
3419        // emit writes that the test-side routed callers parse to a
3420        // [`ChartYaml`]-typed body, the lifted helper must return a
3421        // `ChartYaml` byte-equal to the prior two-step
3422        //
3423        //   let chart_file = find_file_by_path(&dir.files, HELM_CHART_YAML_FILENAME)
3424        //       .unwrap(); // or .expect("Chart.yaml present")
3425        //   let chart: ChartYaml =
3426        //       serde_yaml::from_str(&chart_file.contents).unwrap();
3427        //
3428        // the 9 test-side per-`Chart.yaml`-typed-parse readback sites
3429        // previously carried. Mirrors the sibling
3430        // [`parse_yaml_at_path_matches_prior_inline_find_then_from_str_shape`]
3431        // untyped-Value-arity pin in this crate at the caller-typed-
3432        // arity peer, and the substrate-level
3433        // `parse_yaml_at_path_as_matches_prior_inline_typed_two_step_shape`
3434        // pin at the primitive definition — the three-arm closure
3435        // that closes the same discipline the sibling
3436        // [`parse_yaml_at_path`] sweep established. Only
3437        // [`HELM_CHART_YAML_FILENAME`] is swept: the routed
3438        // caller-typed-parse sites in this crate reach exclusively
3439        // through the `ChartYaml`-typed body at the `Chart.yaml`
3440        // leaf; [`HELM_VALUES_YAML_FILENAME`] carries no typed-mirror
3441        // struct in this crate (the routed callers navigate its
3442        // parsed [`serde_yaml::Value`] through `kube_*` / `mapping_*`
3443        // primitives), and [`HELM_CHART_README_FILENAME`] is not
3444        // parsed as YAML at all.
3445        let dir = render_chart_for_servico(&sample_caixa(), &sample_cu_yaml()).unwrap();
3446        let filename = HELM_CHART_YAML_FILENAME;
3447        let via_helper: ChartYaml = parse_yaml_at_path_as(&dir.files, filename);
3448        let via_inline_file = find_file_by_path(&dir.files, filename).unwrap();
3449        let via_inline: ChartYaml = serde_yaml::from_str(&via_inline_file.contents).unwrap();
3450        assert_eq!(
3451            via_helper, via_inline,
3452            "parse_yaml_at_path_as::<ChartYaml>(&dir.files, {filename:?}) \
3453             must byte-equal the prior two-step \
3454             `find_file_by_path(&dir.files, {filename:?}).unwrap() + \
3455             serde_yaml::from_str::<ChartYaml>(&_.contents).unwrap()` \
3456             combinator on the `Chart.yaml` YAML leaf — otherwise \
3457             the 9 routed per-artifact typed-parse readback sites \
3458             regress silently on the parse-target axis",
3459        );
3460    }
3461
3462    #[test]
3463    fn caixa_licenca_default_re_export_points_at_caixa_core_canonical() {
3464        // The renderer's `CAIXA_LICENCA_DEFAULT` was lifted from the
3465        // production-code inline `"MIT"` byte literal at [`build_readme`]'s
3466        // `caixa.licenca().unwrap_or("MIT")` `README.md` `## License`
3467        // fallback (formerly `caixa-helm/src/lib.rs:1018`) to a re-export
3468        // of [`caixa_core::CAIXA_LICENCA_DEFAULT`] so the substrate-side
3469        // per-`Caixa` author-omitted-`:licenca` SPDX-shaped license-
3470        // expression fallback lives in exactly one place across every
3471        // caixa renderer. Pin the equality + `&'static` static-data
3472        // identity here so any local re-introduction of a sibling `pub
3473        // const CAIXA_LICENCA_DEFAULT: &str = "…"` at this crate — the
3474        // canonical drift footgun where a sibling local `pub const` could
3475        // happen to carry the same byte at the source while pointing at a
3476        // different `&'static` allocation — is a build-time test failure
3477        // naming the offending drift, not a silent per-README license-line
3478        // fall-through to a stale SPDX identifier at chart-consumption
3479        // time far from the drift site. Peer to
3480        // [`helm_chart_readme_filename_re_export_points_at_caixa_core_canonical`]
3481        // / [`helm_values_key_enabled_re_export_points_at_caixa_core_canonical`]
3482        // on the sibling canonical-Helm-per-lifted-const re-export
3483        // surfaces.
3484        caixa_core::assert_str_reexport_identity(
3485            "CAIXA_LICENCA_DEFAULT",
3486            CAIXA_LICENCA_DEFAULT,
3487            caixa_core::CAIXA_LICENCA_DEFAULT,
3488        );
3489    }
3490
3491    #[test]
3492    fn build_readme_license_line_routes_through_lifted_caixa_licenca_default() {
3493        // Emit-path pin: on a `:licenca`-null caixa the [`build_readme`]
3494        // license-line fallback substitutes
3495        // `caixa.licenca().unwrap_or(caixa_core::CAIXA_LICENCA_DEFAULT)`,
3496        // which must derive its terminal SPDX byte-string from the lifted
3497        // [`caixa_core::CAIXA_LICENCA_DEFAULT`] `pub const` rather than the
3498        // pre-lift inline `"MIT"` byte literal at the emit site. Before
3499        // this converge the fallback carried the raw `"MIT"` scalar,
3500        // expressing no compile-time link back to the substrate-side
3501        // per-`Caixa` author-omitted-`:licenca` SPDX-shaped license-
3502        // expression fallback the sibling caixa-core
3503        // [`caixa_licenca_default_pins_canonical_mit_byte`] pinning test
3504        // anchors. Byte-equal today (the lifted const resolves to `"MIT"`);
3505        // the pin catches any future substrate-side license-fallback
3506        // rebrand whose emit-side regresses to the raw literal. Sibling of
3507        // the [`readme_repositorio_null_fallback_routes_through_canonical_git_url_accessor`]
3508        // (aaf1028) / [`readme_body_version_routes_through_caixa_versao_accessor`]
3509        // (eb912de) peer emit-path pins on the paired per-`README.md`
3510        // universal-axis fallback surfaces — completes the
3511        // per-`lareira-<nome>` chart-directory `README.md`
3512        // universal-axis author-omitted-slot fallback resolution's
3513        // substrate-primitive routing at the sole rendered leaf.
3514        let caixa = Caixa {
3515            licenca: None,
3516            ..sample_caixa()
3517        };
3518        let dir = render_chart_for_servico(&caixa, &sample_cu_yaml()).unwrap();
3519        let readme_file =
3520            find_file_by_path(&dir.files, HELM_CHART_README_FILENAME).expect("README.md present");
3521        let expected = format!(
3522            "## License\n\n{license}.\n",
3523            license = caixa_core::CAIXA_LICENCA_DEFAULT,
3524        );
3525        assert!(
3526            readme_file.contents.contains(&expected),
3527            "README.md `:licenca`-null fallback must carry the lifted \
3528             `caixa_core::CAIXA_LICENCA_DEFAULT` byte-string ({expected:?}) \
3529             verbatim — a regression that re-inlines `\"MIT\"` at the emit \
3530             site silently splits the README license-fallback axis from \
3531             every future substrate-side license-fallback rebrand. Full \
3532             contents:\n{contents}",
3533            contents = readme_file.contents,
3534        );
3535    }
3536}